Eliminate / replace remaining cPickle references in test scripts.
[scons.git] / doc / user / output.in
1 <!--
2
3   __COPYRIGHT__
4
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:
12
13   The above copyright notice and this permission notice shall be included
14   in all copies or substantial portions of the Software.
15
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.
23
24 -->
25
26   <para>
27
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.
37
38   </para>
39
40   <section>
41   <title>Providing Build Help:  the &Help; Function</title>
42
43     <para>
44
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:
51
52     </para>
53
54     <scons_example name="ex1">
55        <file name="SConstruct" printme="1">
56        Help("""
57        Type: 'scons program' to build the production program,
58              'scons debug' to build the debug version.
59        """)
60        </file>
61     </scons_example>
62
63     <para>
64
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.)
68
69     </para>
70
71     <para>
72
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:
77
78     </para>
79
80     <scons_output example="ex1">
81        <scons_output_command>scons -h</scons_output_command>
82     </scons_output>
83
84     <para>
85
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.
97
98     </para>
99
100     <para>
101
102     Another use would be to make the help text conditional
103     on some variable.
104     For example, suppose you only want to display
105     a line about building a Windows-only
106     version of a program when actually
107     run on Windows.
108     The following &SConstruct; file:
109
110     </para>
111
112     <scons_example name="ex2">
113        <file name="SConstruct" printme="1">
114        env = Environment()
115
116        Help("\nType: 'scons program' to build the production program.\n")
117
118        if env['PLATFORM'] == 'win32':
119            Help("\nType: 'scons windebug' to build the Windows debug version.\n")
120        </file>
121     </scons_example>
122
123     <para>
124
125     Will display the complete help text on Windows:
126
127     </para>
128
129     <scons_output example="ex2" os="win32">
130        <scons_output_command>scons -h</scons_output_command>
131     </scons_output>
132
133     <para>
134
135     But only show the relevant option on a Linux or UNIX system:
136
137     </para>
138
139     <scons_output example="ex2" os="posix">
140        <scons_output_command>scons -h</scons_output_command>
141     </scons_output>
142
143     <para>
144
145     If there is no &Help; text in the &SConstruct; or
146     &SConscript; files,
147     &SCons; will revert to displaying its
148     standard list that describes the &SCons; command-line
149     options.
150     This list is also always displayed whenever
151     the <literal>-H</literal> option is used.
152
153     </para>
154
155   </section>
156
157   <section>
158   <title>Controlling How &SCons; Prints Build Commands:  the <envar>$*COMSTR</envar> Variables</title>
159
160     <para>
161
162     Sometimes the commands executed
163     to compile object files or link programs
164     (or build other targets)
165     can get very long,
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.
177
178     </para>
179
180     <para>
181
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,
186     and a
187     <literal>"Linking"</literal>
188     when it's linking an executable.
189     You could write a &SConstruct; file
190     that looks like:
191
192     </para>
193
194     <scons_example name="COMSTR">
195        <file name="SConstruct" printme="1">
196        env = Environment(CCCOMSTR = "Compiling $TARGET",
197                          LINKCOMSTR = "Linking $TARGET")
198        env.Program('foo.c')
199        </file>
200        <file name="foo.c">
201        foo.c
202        </file>
203     </scons_example>
204
205     <para>
206
207     Which would then yield the output:
208
209     </para>
210
211     <!--
212
213     <scons_output example="COMSTR" os="posix">
214        <scons_output_command>scons -Q</scons_output_command>
215     </scons_output>
216
217     -->
218
219     <screen>
220        % <userinput>scons -Q</userinput>
221        Compiling foo.o
222        Linking foo
223     </screen>
224
225     <para>
226
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.
235
236     </para>
237
238     <para>
239
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
248     to add a few options
249     for a custom test.
250
251     </para>
252
253     <para>
254
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:
263
264     </para>
265
266     <scons_example name="COMSTR-VERBOSE">
267        <file name="SConstruct" printme="1">
268        env = Environment()
269        if ARGUMENTS.get('VERBOSE') != "1':
270            env['CCCOMSTR'] = "Compiling $TARGET"
271            env['LINKCOMSTR'] = "Linking $TARGET"
272        env.Program('foo.c')
273        </file>
274        <file name="foo.c">
275        foo.c
276        </file>
277     </scons_example>
278
279     <para>
280
281
282     By only setting the appropriate
283     <envar>$*COMSTR</envar> variables
284     if the user specifies
285     <literal>VERBOSE=1</literal>
286     on the command line,
287     the user has control
288     over how &SCons;
289     displays these particular command lines:
290
291     </para>
292
293     <!--
294
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>
299     </scons_output>
300
301     -->
302
303     <screen>
304        % <userinput>scons -Q</userinput>
305        Compiling foo.o
306        Linking foo
307        % <userinput>scons -Q -c</userinput>
308        Removed foo.o
309        Removed foo
310        % <userinput>scons -Q VERBOSE=1</userinput>
311        cc -o foo.o -c foo.c
312        cc -o foo foo.o
313     </screen>
314
315   </section>
316
317   <section>
318   <title>Providing Build Progress Output:  the &Progress; Function</title>
319
320     <para>
321
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.
335
336     </para>
337
338     <para>
339
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.
349
350     </para>
351
352     <scons_example name="Progress-TARGET">
353       <file name="SConstruct" printme="1">
354         Progress('Evaluating $TARGET\n')
355         Program('f1.c')
356         Program('f2.c')
357       </file>
358       <file name="f1.c">
359         f1.c
360       </file>
361       <file name="f2.c">
362         f2.c
363       </file>
364     </scons_example>
365
366     <para>
367
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,
376     will have &SCons;
377     print that it is <literal>Evaluating</literal>
378     each file that it encounters
379     in turn as it traverses the dependency graph:
380
381     </para>
382
383     <scons_output example="Progress-TARGET" os="posix">
384        <scons_output_command>scons -Q</scons_output_command>
385     </scons_output>
386
387     <para>
388
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:
403
404     </para>
405
406     <sconstruct>
407         Progress('$TARGET\r',
408                  file=open('/dev/tty', 'w'),
409                  overwrite=True)
410         Program('f1.c')
411         Program('f2.c')
412     </sconstruct>
413
414     <para>
415
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.
422     Without the
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 
426     precedes it,
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
432     the user's screen.
433     On Windows, the equivalent would be to open
434     the <filename>con:</filename> file name.
435
436     </para>
437
438     <para>
439
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).
448
449     </para>
450
451     <para>
452
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
459     sequence of strings:
460
461     </para>
462
463     <sconstruct>
464         Progress(['-\r', '\\\r', '|\r', '/\r'], interval=5)
465         Program('f1.c')
466         Program('f2.c')
467     </sconstruct>
468
469     <para>
470
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
478     our examples above,
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.
484
485     </para>
486
487     <para>
488
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,
496     allowing you to
497     implement more sophisticated logic
498     like adding a counter:
499
500     </para>
501
502     <scons_example name="Progress-callable">
503       <file name="SConstruct" printme="1">
504         screen = open('/dev/tty', 'w')
505         count = 0
506         def progress_function(node)
507             count += 1
508             screen.write('Node %4d: %s\r' % (count, node))
509
510         Progress(progress_function)
511       </file>
512     </scons_example>
513
514     <para>
515
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.
521
522     </para>
523
524     <para>
525
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
540     to be built.
541     The entire build may consist of thousands of Nodes,
542     for example,
543     but maybe the user specifically requested
544     that only a single object file be built.)
545
546     </para>
547
548   </section>
549
550   <section>
551   <title>Printing Detailed Build Status:  the &GetBuildFailures; Function</title>
552
553     <para>
554
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.
561
562     </para>
563
564     <para>
565
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 
571     simple example:
572
573     </para>
574
575     <scons_example name="gbf1">
576       <file name="SConstruct" printme="1">
577         import atexit
578
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)
584       </file>
585     </scons_example>
586
587     <para>
588
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.
594     See the man page
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.
609
610     </para>
611
612     <para>   
613
614     Here is a more complete example showing how to
615     turn each element of &GetBuildFailures; into a string:
616
617     </para>
618
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'])
624
625         def bf_to_str(bf):
626             """Convert an element of GetBuildFailures() to a string
627             in a useful way."""
628             import SCons.Errors
629             if bf is None: # unknown targets product None in list
630                 return '(unknown tgt)'
631             elif isinstance(bf, SCons.Errors.StopError):
632                 return str(bf)
633             elif bf.node:
634                 return str(bf.node) + ': ' + bf.errstr
635             elif bf.filename:
636                 return bf.filename + ': ' + bf.errstr
637             return 'unknown failure: ' + bf.errstr
638         import atexit
639
640         def build_status():
641             """Convert the build status to a 2-tuple, (status, msg)."""
642             from SCons.Script import GetBuildFailures
643             bf = GetBuildFailures()
644             if bf:
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.
647                 status = 'failed'
648                 failures_message = "\n".join(["Failed building %s" % bf_to_str(x)
649                                    for x in bf if x is not None])
650             else:
651                 # if bf is None, the build completed successfully.
652                 status = 'ok'
653                 failures_message = ''
654             return (status, failures_message)
655
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.
662             elif status == 'ok':
663                print "Build succeeded."
664             print failures_message
665
666         atexit.register(display_build_status)
667       </file>
668     </scons_example>
669
670     <para>
671     
672     When this runs, you'll see the appropriate output:
673
674     </para>
675
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>
679     </scons_output>
680
681   </section>