5 Permission is hereby granted, free of charge, to any person obtaining
6 a copy of this software and associated documentation files (the
7 "Software"), to deal in the Software without restriction, including
8 without limitation the rights to use, copy, modify, merge, publish,
9 distribute, sublicense, and/or sell copies of the Software, and to
10 permit persons to whom the Software is furnished to do so, subject to
11 the following conditions:
13 The above copyright notice and this permission notice shall be included
14 in all copies or substantial portions of the Software.
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
17 KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
18 WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 A key aspect of creating a usable build configuration
29 is providing good output from the build
30 so its users can readily understand
31 what the build is doing
32 and get information about how to control the build.
33 &SCons; provides several ways of
34 controlling output from the build configuration
35 to help make the build
36 more useful and understandable.
41 <title>Providing Build Help: the &Help; Function</title>
45 It's often very useful to be able to give
46 users some help that describes the
47 specific targets, build options, etc.,
48 that can be used for your build.
49 &SCons; provides the &Help; function
50 to allow you to specify this help text:
54 <scons_example name="ex1">
55 <file name="SConstruct" printme="1">
57 Type: 'scons program' to build the production program,
58 'scons debug' to build the debug version.
65 (Note the above use of the Python triple-quote syntax,
66 which comes in very handy for
67 specifying multi-line strings like help text.)
73 When the &SConstruct; or &SConscript; files
74 contain such a call to the &Help; function,
75 the specified help text will be displayed in response to
76 the &SCons; <literal>-h</literal> option:
80 <scons_output example="ex1">
81 <scons_output_command>scons -h</scons_output_command>
86 The &SConscript; files may contain
87 multiple calls to the &Help; function,
88 in which case the specified text(s)
89 will be concatenated when displayed.
90 This allows you to split up the
91 help text across multiple &SConscript; files.
92 In this situation, the order in
93 which the &SConscript; files are called
94 will determine the order in which the &Help; functions are called,
95 which will determine the order in which
96 the various bits of text will get concatenated.
102 Another use would be to make the help text conditional
104 For example, suppose you only want to display
105 a line about building a Windows-only
106 version of a program when actually
108 The following &SConstruct; file:
112 <scons_example name="ex2">
113 <file name="SConstruct" printme="1">
116 Help("\nType: 'scons program' to build the production program.\n")
118 if env['PLATFORM'] == 'win32':
119 Help("\nType: 'scons windebug' to build the Windows debug version.\n")
125 Will display the complete help text on Windows:
129 <scons_output example="ex2" os="win32">
130 <scons_output_command>scons -h</scons_output_command>
135 But only show the relevant option on a Linux or UNIX system:
139 <scons_output example="ex2" os="posix">
140 <scons_output_command>scons -h</scons_output_command>
145 If there is no &Help; text in the &SConstruct; or
147 &SCons; will revert to displaying its
148 standard list that describes the &SCons; command-line
150 This list is also always displayed whenever
151 the <literal>-H</literal> option is used.
158 <title>Controlling How &SCons; Prints Build Commands: the <envar>$*COMSTR</envar> Variables</title>
162 Sometimes the commands executed
163 to compile object files or link programs
164 (or build other targets)
166 long enough to make it difficult for users
167 to distinguish error messages or
168 other important build output
169 from the commands themselves.
170 All of the default <envar>$*COM</envar> variables
171 that specify the command lines
172 used to build various types of target files
173 have a corresponding <envar>$*COMSTR</envar> variable
174 that can be set to an alternative
175 string that will be displayed
176 when the target is built.
182 For example, suppose you want to
183 have &SCons; display a
184 <literal>"Compiling"</literal>
185 message whenever it's compiling an object file,
187 <literal>"Linking"</literal>
188 when it's linking an executable.
189 You could write a &SConstruct; file
194 <scons_example name="COMSTR">
195 <file name="SConstruct" printme="1">
196 env = Environment(CCCOMSTR = "Compiling $TARGET",
197 LINKCOMSTR = "Linking $TARGET")
207 Which would then yield the output:
213 <scons_output example="COMSTR" os="posix">
214 <scons_output_command>scons -Q</scons_output_command>
220 % <userinput>scons -Q</userinput>
227 &SCons; performs complete variable substitution
228 on <envar>$*COMSTR</envar> variables,
229 so they have access to all of the
230 standard variables like &cv-TARGET; &cv-SOURCES;, etc.,
231 as well as any construction variables
232 that happen to be configured in
233 the construction environment
234 used to build a specific target.
240 Of course, sometimes it's still important to
241 be able to see the exact command
242 that &SCons; will execute to build a target.
243 For example, you may simply need to verify
244 that &SCons; is configured to supply
245 the right options to the compiler,
246 or a developer may want to
247 cut-and-paste a compile command
255 One common way to give users
256 control over whether or not
257 &SCons; should print the actual command line
258 or a short, configured summary
259 is to add support for a
260 <varname>VERBOSE</varname>
261 command-line variable to your &SConstruct; file.
262 A simple configuration for this might look like:
266 <scons_example name="COMSTR-VERBOSE">
267 <file name="SConstruct" printme="1">
269 if ARGUMENTS.get('VERBOSE') != "1':
270 env['CCCOMSTR'] = "Compiling $TARGET"
271 env['LINKCOMSTR'] = "Linking $TARGET"
282 By only setting the appropriate
283 <envar>$*COMSTR</envar> variables
284 if the user specifies
285 <literal>VERBOSE=1</literal>
289 displays these particular command lines:
295 <scons_output example="COMSTR-VERBOSE" os="posix">
296 <scons_output_command>scons -Q</scons_output_command>
297 <scons_output_command>scons -Q -c</scons_output_command>
298 <scons_output_command>scons -Q VERBOSE=1</scons_output_command>
304 % <userinput>scons -Q</userinput>
307 % <userinput>scons -Q -c</userinput>
310 % <userinput>scons -Q VERBOSE=1</userinput>
318 <title>Providing Build Progress Output: the &Progress; Function</title>
322 Another aspect of providing good build output
323 is to give the user feedback
324 about what &SCons; is doing
325 even when nothing is being built at the moment.
326 This can be especially true for large builds
327 when most of the targets are already up-to-date.
328 Because &SCons; can take a long time
329 making absolutely sure that every
330 target is, in fact, up-to-date
331 with respect to a lot of dependency files,
332 it can be easy for users to mistakenly
333 conclude that &SCons; is hung
334 or that there is some other problem with the build.
340 One way to deal with this perception
341 is to configure &SCons; to print something to
342 let the user know what it's "thinking about."
343 The &Progress; function
344 allows you to specify a string
345 that will be printed for every file
346 that &SCons; is "considering"
347 while it is traversing the dependency graph
348 to decide what targets are or are not up-to-date.
352 <scons_example name="Progress-TARGET">
353 <file name="SConstruct" printme="1">
354 Progress('Evaluating $TARGET\n')
368 Note that the &Progress; function does not
369 arrange for a newline to be printed automatically
370 at the end of the string (as does the Python
371 <literal>print</literal> statement),
372 and we must specify the
373 <literal>\n</literal>
374 that we want printed at the end of the configured string.
375 This configuration, then,
377 print that it is <literal>Evaluating</literal>
378 each file that it encounters
379 in turn as it traverses the dependency graph:
383 <scons_output example="Progress-TARGET" os="posix">
384 <scons_output_command>scons -Q</scons_output_command>
389 Of course, normally you don't want to add
390 all of these additional lines to your build output,
391 as that can make it difficult for the user
392 to find errors or other important messages.
393 A more useful way to display
394 this progress might be
395 to have the file names printed
396 directly to the user's screen,
397 not to the same standard output
398 stream where build output is printed,
399 and to use a carriage return character
400 (<literal>\r</literal>)
401 so that each file name gets re-printed on the same line.
402 Such a configuration would look like:
407 Progress('$TARGET\r',
408 file=open('/dev/tty', 'w'),
416 Note that we also specified the
417 <literal>overwrite=True</literal> argument
418 to the &Progress; function,
419 which causes &SCons; to
420 "wipe out" the previous string with space characters
421 before printing the next &Progress; string.
423 <literal>overwrite=True</literal> argument,
424 a shorter file name would not overwrite
425 all of the charactes in a longer file name that
427 making it difficult to tell what the
428 actual file name is on the output.
429 Also note that we opened up the
430 <filename>/dev/tty</filename> file
431 for direct access (on POSIX) to
433 On Windows, the equivalent would be to open
434 the <filename>con:</filename> file name.
440 Also, it's important to know that although you can use
441 <literal>$TARGET</literal> to substitute the name of
442 the node in the string,
443 the &Progress; function does <emphasis>not</emphasis>
444 perform general variable substitution
445 (because there's not necessarily a construction
446 environment involved in evaluating a node
447 like a source file, for example).
453 You can also specify a list of strings
454 to the &Progress; function,
455 in which case &SCons; will
456 display each string in turn.
457 This can be used to implement a "spinner"
458 by having &SCons; cycle through a
464 Progress(['-\r', '\\\r', '|\r', '/\r'], interval=5)
471 Note that here we have also used the
472 <literal>interval=</literal>
473 keyword argument to have &SCons;
474 only print a new "spinner" string
475 once every five evaluated nodes.
476 Using an <literal>interval=</literal> count,
477 even with strings that use <literal>$TARGET</literal> like
479 can be a good way to lessen the
480 work that &SCons; expends printing &Progress; strings,
481 while still giving the user feedback
482 that indicates &SCons; is still
483 working on evaluating the build.
489 Lastly, you can have direct control
490 over how to print each evaluated node
491 by passing a Python function
492 (or other Python callable)
493 to the &Progress function.
494 Your function will be called
495 for each evaluated node,
497 implement more sophisticated logic
498 like adding a counter:
502 <scons_example name="Progress-callable">
503 <file name="SConstruct" printme="1">
504 screen = open('/dev/tty', 'w')
506 def progress_function(node)
508 screen.write('Node %4d: %s\r' % (count, node))
510 Progress(progress_function)
516 Of course, if you choose,
517 you could completely ignore the
518 <varname>node</varname> argument to the function,
519 and just print a count,
520 or anything else you wish.
526 (Note that there's an obvious follow-on question here:
527 how would you find the total number of nodes
528 that <emphasis>will be</emphasis>
529 evaluated so you can tell the user how
530 close the build is to finishing?
531 Unfortunately, in the general case,
532 there isn't a good way to do that,
533 short of having &SCons; evaluate its
534 dependency graph twice,
535 first to count the total and
536 the second time to actually build the targets.
537 This would be necessary because
538 you can't know in advance which
539 target(s) the user actually requested
541 The entire build may consist of thousands of Nodes,
543 but maybe the user specifically requested
544 that only a single object file be built.)
551 <title>Printing Detailed Build Status: the &GetBuildFailures; Function</title>
555 SCons, like most build tools, returns zero status to
556 the shell on success and nonzero status on failure.
557 Sometimes it's useful to give more information about
558 the build status at the end of the run, for instance
559 to print an informative message, send an email, or
560 page the poor slob who broke the build.
566 SCons provides a &GetBuildFailures; method that
567 you can use in a python <function>atexit</function> function
568 to get a list of objects describing the actions that failed
569 while attempting to build targets. There can be more
570 than one if you're using <literal>-j</literal>. Here's a
575 <scons_example name="gbf1">
576 <file name="SConstruct" printme="1">
579 def print_build_failures():
580 from SCons.Script import GetBuildFailures
581 for bf in GetBuildFailures():
582 print "%s failed: %s" % (bf.node, bf.errstr)
583 atexit.register(print_build_failures)
589 The <function>atexit.register</function> call
590 registers <function>print_build_failures</function>
591 as an <function>atexit</function> callback, to be called
592 before &SCons; exits. When that function is called,
593 it calls &GetBuildFailures; to fetch the list of failed objects.
595 for the detailed contents of the returned objects;
596 some of the more useful attributes are
597 <literal>.node</literal>,
598 <literal>.errstr</literal>,
599 <literal>.filename</literal>, and
600 <literal>.command</literal>.
601 The <literal>filename</literal> is not necessarily
602 the same file as the <literal>node</literal>; the
603 <literal>node</literal> is the target that was
604 being built when the error occurred, while the
605 <literal>filename</literal>is the file or dir that
606 actually caused the error.
607 Note: only call &GetBuildFailures; at the end of the
608 build; calling it at any other time is undefined.
614 Here is a more complete example showing how to
615 turn each element of &GetBuildFailures; into a string:
619 <scons_example name="gbf2">
620 <file name="SConstruct" printme="1">
621 # Make the build fail if we pass fail=1 on the command line
622 if ARGUMENTS.get('fail', 0):
623 Command('target', 'source', ['/bin/false'])
626 """Convert an element of GetBuildFailures() to a string
629 if bf is None: # unknown targets product None in list
630 return '(unknown tgt)'
631 elif isinstance(bf, SCons.Errors.StopError):
634 return str(bf.node) + ': ' + bf.errstr
636 return bf.filename + ': ' + bf.errstr
637 return 'unknown failure: ' + bf.errstr
641 """Convert the build status to a 2-tuple, (status, msg)."""
642 from SCons.Script import GetBuildFailures
643 bf = GetBuildFailures()
645 # bf is normally a list of build failures; if an element is None,
646 # it's because of a target that scons doesn't know anything about.
648 failures_message = "\n".join(["Failed building %s" % bf_to_str(x)
649 for x in bf if x is not None])
651 # if bf is None, the build completed successfully.
653 failures_message = ''
654 return (status, failures_message)
656 def display_build_status():
657 """Display the build status. Called by atexit.
658 Here you could do all kinds of complicated things."""
659 status, failures_message = build_status()
660 if status == 'failed':
661 print "FAILED!!!!" # could display alert, ring bell, etc.
663 print "Build succeeded."
664 print failures_message
666 atexit.register(display_build_status)
672 When this runs, you'll see the appropriate output:
676 <scons_output example="gbf2">
677 <scons_output_command>scons -Q</scons_output_command>
678 <scons_output_command>scons -Q fail=1</scons_output_command>