3 This file implements the main() function used by the scons script.
5 Architecturally, this *is* the scons script, and will likely only be
6 called from the external "scons" wrapper. Consequently, anything here
7 should not be, or be considered, part of the build engine. If it's
8 something that we expect other software to want to use, it should go in
9 some other module. If it's specific to the "scons" script invocation,
17 # Permission is hereby granted, free of charge, to any person obtaining
18 # a copy of this software and associated documentation files (the
19 # "Software"), to deal in the Software without restriction, including
20 # without limitation the rights to use, copy, modify, merge, publish,
21 # distribute, sublicense, and/or sell copies of the Software, and to
22 # permit persons to whom the Software is furnished to do so, subject to
23 # the following conditions:
25 # The above copyright notice and this permission notice shall be included
26 # in all copies or substantial portions of the Software.
28 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
29 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
30 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
31 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
32 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
33 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
34 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
37 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
47 # Strip the script directory from sys.path() so on case-insensitive
48 # (WIN32) systems Python doesn't think that the "scons" script is the
49 # "SCons" package. Replace it with our own version directory so, if
50 # if they're there, we pick up the right version of the build engine
52 #sys.path = [os.path.join(sys.prefix,
54 # 'scons-%d' % SCons.__version__)] + sys.path[1:]
58 import SCons.Environment
63 from SCons.Optik import OptionParser, SUPPRESS_HELP, OptionValueError
66 import SCons.Taskmaster
71 display = SCons.Util.display
72 progress_display = SCons.Util.DisplayEngine()
76 class BuildTask(SCons.Taskmaster.Task):
77 """An SCons build task."""
78 def display(self, message):
79 display('scons: ' + message)
82 target = self.targets[0]
83 if target.get_state() == SCons.Node.up_to_date:
84 if self.top and target.has_builder():
85 display("scons: `%s' is up to date." % str(self.node))
86 elif target.has_builder() and not hasattr(target.builder, 'status'):
88 start_time = time.time()
89 SCons.Taskmaster.Task.execute(self)
91 finish_time = time.time()
93 command_time = command_time+finish_time-start_time
94 print "Command execution time: %f seconds"%(finish_time-start_time)
96 def do_failed(self, status=2):
99 SCons.Taskmaster.Task.executed(self)
100 elif keep_going_on_error:
101 SCons.Taskmaster.Task.fail_continue(self)
104 SCons.Taskmaster.Task.fail_stop(self)
109 if self.top and not t.has_builder() and not t.side_effect:
111 sys.stderr.write("scons: *** Do not know how to make target `%s'." % t)
112 if not keep_going_on_error:
113 sys.stderr.write(" Stop.")
114 sys.stderr.write("\n")
117 print "scons: Nothing to be done for `%s'." % t
118 SCons.Taskmaster.Task.executed(self)
120 SCons.Taskmaster.Task.executed(self)
123 # Handle the failure of a build task. The primary purpose here
124 # is to display the various types of Errors and Exceptions
127 exc_info = self.exc_info()
134 # The Taskmaster didn't record an exception for this Task;
135 # see if the sys module has one.
136 t, e = sys.exc_info()[:2]
138 if t == SCons.Errors.BuildError:
140 if SCons.Util.is_List(e.node):
141 fname = string.join(map(str, e.node), ', ')
142 sys.stderr.write("scons: *** [%s] %s\n" % (fname, e.errstr))
143 if e.errstr == 'Exception':
144 traceback.print_exception(e.args[0], e.args[1], e.args[2])
145 elif t == SCons.Errors.ExplicitExit:
147 sys.stderr.write("scons: *** [%s] Explicit exit, status %s\n" % (e.node, e.status))
152 if t == SCons.Errors.StopError and not keep_going_on_error:
154 sys.stderr.write("scons: *** %s\n" % s)
156 if tb and print_stacktrace:
157 sys.stderr.write("scons: internal stack trace:\n")
158 traceback.print_tb(tb, file=sys.stderr)
160 self.do_failed(status)
164 def postprocess(self):
169 SCons.Util.print_tree(t, get_all_children)
172 SCons.Util.print_tree(t, get_all_children, showtags=2)
175 SCons.Util.print_tree(t, get_derived_children)
177 tree = t.render_include_tree()
181 SCons.Taskmaster.Task.postprocess(self)
183 def make_ready(self):
184 """Make a task ready for execution"""
185 SCons.Taskmaster.Task.make_ready(self)
186 if self.out_of_date and print_explanations:
187 explanation = self.out_of_date[0].explain()
189 sys.stdout.write("scons: " + explanation)
191 class CleanTask(SCons.Taskmaster.Task):
192 """An SCons clean task."""
194 target = self.targets[0]
195 if (target.has_builder() or target.side_effect) and not target.isdir():
196 display("Removed " + str(target))
197 if SCons.Environment.CleanTargets.has_key(target):
198 files = SCons.Environment.CleanTargets[target]
200 SCons.Util.fs_delete(str(f), 0)
203 target = self.targets[0]
204 if target.has_builder() or target.side_effect:
205 for t in self.targets:
209 print "scons: Could not remove '%s':" % str(t), e.strerror
212 display("Removed " + str(t))
213 if SCons.Environment.CleanTargets.has_key(target):
214 files = SCons.Environment.CleanTargets[target]
216 SCons.Util.fs_delete(str(f))
220 # Have the taskmaster arrange to "execute" all of the targets, because
221 # we'll figure out ourselves (in remove() or show() above) whether
222 # anything really needs to be done.
223 make_ready = SCons.Taskmaster.Task.make_ready_all
228 class QuestionTask(SCons.Taskmaster.Task):
229 """An SCons task for the -q (question) option."""
234 if self.targets[0].get_state() != SCons.Node.up_to_date:
244 keep_going_on_error = 0
246 print_explanations = 0
257 exit_status = 0 # exit status, assume success by default
259 num_jobs = 1 # this is modifed by SConscript.SetJobs()
266 self.append = self.do_nothing
267 self.print_stats = self.do_nothing
268 def enable(self, outfp):
270 self.append = self.do_append
271 self.print_stats = self.do_print
272 def do_nothing(self, *args, **kw):
275 class CountStats(Stats):
276 def do_append(self, label):
277 self.labels.append(label)
278 self.stats.append(SCons.Debug.fetchLoggedInstances())
282 for n in map(lambda t: t[0], s):
283 stats_table[n] = [0, 0, 0, 0]
287 stats_table[n][i] = c
289 keys = stats_table.keys()
291 self.outfp.write("Object counts:\n")
295 fmt1 = string.join(pre + [' %7s']*l + post, '')
296 fmt2 = string.join(pre + [' %7d']*l + post, '')
297 labels = self.labels[:l]
298 labels.append(("", "Class"))
299 self.outfp.write(fmt1 % tuple(map(lambda x: x[0], labels)))
300 self.outfp.write(fmt1 % tuple(map(lambda x: x[1], labels)))
302 r = stats_table[k][:l] + [k]
303 self.outfp.write(fmt2 % tuple(r))
305 count_stats = CountStats()
307 class MemStats(Stats):
308 def do_append(self, label):
309 self.labels.append(label)
310 self.stats.append(SCons.Debug.memory())
312 fmt = 'Memory %-32s %12d\n'
313 for label, stats in map(None, self.labels, self.stats):
314 self.outfp.write(fmt % (label, stats))
316 memory_stats = MemStats()
320 def get_all_children(node): return node.all_children()
322 def get_derived_children(node):
323 children = node.all_children(None)
324 return filter(lambda x: x.has_builder(), children)
326 def _scons_syntax_error(e):
327 """Handle syntax errors. Print out a message and show where the error
330 etype, value, tb = sys.exc_info()
331 lines = traceback.format_exception_only(etype, value)
333 sys.stderr.write(line+'\n')
336 def find_deepest_user_frame(tb):
338 Find the deepest stack frame that is not part of SCons.
340 Input is a "pre-processed" stack trace in the form
341 returned by traceback.extract_tb() or traceback.extract_stack()
346 # find the deepest traceback frame that is not part
350 if string.find(filename, os.sep+'SCons'+os.sep) == -1:
354 def _scons_user_error(e):
355 """Handle user errors. Print out a message and a description of the
356 error, along with the line number and routine where it occured.
357 The file and line number will be the deepest stack frame that is
358 not part of SCons itself.
360 etype, value, tb = sys.exc_info()
361 filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_tb(tb))
362 sys.stderr.write("\nscons: *** %s\n" % value)
363 sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine))
366 def _scons_user_warning(e):
367 """Handle user warnings. Print out a message and a description of
368 the warning, along with the line number and routine where it occured.
369 The file and line number will be the deepest stack frame that is
370 not part of SCons itself.
372 etype, value, tb = sys.exc_info()
373 filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_tb(tb))
374 sys.stderr.write("\nscons: warning: %s\n" % e)
375 sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine))
377 def _scons_internal_warning(e):
378 """Slightly different from _scons_user_warning in that we use the
379 *current call stack* rather than sys.exc_info() to get our stack trace.
380 This is used by the warnings framework to print warnings."""
381 filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_stack())
382 sys.stderr.write("\nscons: warning: %s\n" % e[0])
383 sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine))
385 def _scons_internal_error():
386 """Handle all errors but user errors. Print out a message telling
387 the user what to do in this case and print a normal trace.
389 print 'internal error'
390 traceback.print_exc()
393 def _varargs(option, parser):
396 arg = parser.rargs[0]
402 def _setup_warn(arg):
403 """The --warn option. An argument to this option
404 should be of the form <warning-class> or no-<warning-class>.
405 The warning class is munged in order to get an actual class
406 name from the SCons.Warnings module to enable or disable.
407 The supplied <warning-class> is split on hyphens, each element
408 is captialized, then smushed back together. Then the string
409 "SCons.Warnings." is added to the front and "Warning" is added
410 to the back to get the fully qualified class name.
412 For example, --warn=deprecated will enable the
413 SCons.Warnings.DeprecatedWarning class.
415 --warn=no-dependency will disable the
416 SCons.Warnings.DependencyWarning class.
418 As a special case, --warn=all and --warn=no-all
419 will enable or disable (respectively) the base
420 class of all warnings, which is SCons.Warning.Warning."""
422 elems = string.split(string.lower(arg), '-')
428 if len(elems) == 1 and elems[0] == 'all':
429 class_name = "Warning"
433 return "SCons" + s[5:]
435 return string.capitalize(s)
436 class_name = string.join(map(_capitalize, elems), '') + "Warning"
438 clazz = getattr(SCons.Warnings, class_name)
439 except AttributeError:
440 sys.stderr.write("No warning type: '%s'\n" % arg)
443 SCons.Warnings.enableWarningClass(clazz)
445 SCons.Warnings.suppressWarningClass(clazz)
447 def _SConstruct_exists(dirname=''):
448 """This function checks that an SConstruct file exists in a directory.
449 If so, it returns the path of the file. By default, it checks the
453 for file in ['SConstruct', 'Sconstruct', 'sconstruct']:
454 sfile = os.path.join(dirname, file)
455 if os.path.isfile(sfile):
457 if not os.path.isabs(sfile):
458 for rep in repositories:
459 if os.path.isfile(os.path.join(rep, sfile)):
463 def _set_globals(options):
464 global keep_going_on_error, ignore_errors
465 global count_stats, print_dtree
466 global print_explanations, print_includes, print_memoizer
467 global print_objects, print_stacktrace, print_stree
468 global print_time, print_tree
471 keep_going_on_error = options.keep_going
473 debug_values = options.debug
474 if debug_values is None:
476 except AttributeError:
479 if "count" in debug_values:
480 count_stats.enable(sys.stdout)
481 if "dtree" in debug_values:
483 if "explain" in debug_values:
484 print_explanations = 1
485 if "findlibs" in debug_values:
486 SCons.Scanner.Prog.print_find_libs = "findlibs"
487 if "includes" in debug_values:
489 if "memoizer" in debug_values:
491 if "memory" in debug_values:
492 memory_stats.enable(sys.stdout)
493 if "objects" in debug_values:
495 if "presub" in debug_values:
496 SCons.Action.print_actions_presub = 1
497 if "stacktrace" in debug_values:
499 if "stree" in debug_values:
501 if "time" in debug_values:
503 if "tree" in debug_values:
505 ignore_errors = options.ignore_errors
507 def _create_path(plist):
513 path = path + '/' + d
517 class OptParser(OptionParser):
521 parts = ["SCons by Steven Knight et al.:\n"]
523 parts.append("\tscript: v%s.%s, %s, by %s on %s\n" % (__main__.__version__,
526 __main__.__developer__,
527 __main__.__buildsys__))
528 except KeyboardInterrupt:
531 # On win32 there is no scons.py, so there is no __main__.__version__,
532 # hence there is no script version.
534 parts.append("\tengine: v%s.%s, %s, by %s on %s\n" % (SCons.__version__,
539 parts.append("__COPYRIGHT__")
540 OptionParser.__init__(self, version=string.join(parts, ''),
541 usage="usage: scons [OPTION] [TARGET] ...")
543 # options ignored for compatibility
544 def opt_ignore(option, opt, value, parser):
545 sys.stderr.write("Warning: ignoring %s option\n" % opt)
546 self.add_option("-b", "-m", "-S", "-t", "--no-keep-going", "--stop",
547 "--touch", action="callback", callback=opt_ignore,
548 help="Ignored for compatibility.")
550 self.add_option('-c', '--clean', '--remove', action="store_true",
552 help="Remove specified targets and dependencies.")
554 self.add_option('-C', '--directory', type="string", action = "append",
556 help="Change to DIR before doing anything.")
558 self.add_option('--cache-disable', '--no-cache',
559 action="store_true", dest='cache_disable', default=0,
560 help="Do not retrieve built targets from CacheDir.")
562 self.add_option('--cache-force', '--cache-populate',
563 action="store_true", dest='cache_force', default=0,
564 help="Copy already-built targets into the CacheDir.")
566 self.add_option('--cache-show',
567 action="store_true", dest='cache_show', default=0,
568 help="Print build actions for files from CacheDir.")
570 config_options = ["auto", "force" ,"cache"]
572 def opt_config(option, opt, value, parser, c_options=config_options):
573 if value in c_options:
574 parser.values.config = value
576 raise OptionValueError("Warning: %s is not a valid config type" % value)
577 self.add_option('--config', action="callback", type="string",
578 callback=opt_config, nargs=1, dest="config",
579 metavar="MODE", default="auto",
580 help="Controls Configure subsystem: "
581 "%s." % string.join(config_options, ", "))
583 def opt_not_yet(option, opt, value, parser):
584 sys.stderr.write("Warning: the %s option is not yet implemented\n" % opt)
586 self.add_option('-d', action="callback",
587 callback=opt_not_yet,
588 help = "Print file dependency information.")
590 self.add_option('-D', action="store_const", const=2, dest="climb_up",
591 help="Search up directory tree for SConstruct, "
592 "build all Default() targets.")
594 debug_options = ["count", "dtree", "explain", "findlibs",
595 "includes", "memoizer", "memory",
596 "nomemoizer", "objects",
597 "pdb", "presub", "stacktrace", "stree",
600 def opt_debug(option, opt, value, parser, debug_options=debug_options):
601 if value in debug_options:
603 if parser.values.debug is None:
604 parser.values.debug = []
605 except AttributeError:
606 parser.values.debug = []
607 parser.values.debug.append(value)
609 raise OptionValueError("Warning: %s is not a valid debug type" % value)
610 self.add_option('--debug', action="callback", type="string",
611 callback=opt_debug, nargs=1, dest="debug",
613 help="Print various types of debugging information: "
614 "%s." % string.join(debug_options, ", "))
616 def opt_duplicate(option, opt, value, parser):
617 if not value in SCons.Node.FS.Valid_Duplicates:
618 raise OptionValueError("`%s' is not a valid duplication style." % value)
619 parser.values.duplicate = value
620 # Set the duplicate style right away so it can affect linking
621 # of SConscript files.
622 SCons.Node.FS.set_duplicate(value)
623 self.add_option('--duplicate', action="callback", type="string",
624 callback=opt_duplicate, nargs=1, dest="duplicate",
625 help="Set the preferred duplication methods. Must be one of "
626 + string.join(SCons.Node.FS.Valid_Duplicates, ", "))
628 self.add_option('-f', '--file', '--makefile', '--sconstruct',
629 action="append", nargs=1,
630 help="Read FILE as the top-level SConstruct file.")
632 self.add_option('-h', '--help', action="store_true", default=0,
634 help="Print defined help message, or this one.")
636 self.add_option("-H", "--help-options",
638 help="Print this message and exit.")
640 self.add_option('-i', '--ignore-errors', action="store_true",
641 default=0, dest='ignore_errors',
642 help="Ignore errors from build actions.")
644 self.add_option('-I', '--include-dir', action="append",
645 dest='include_dir', metavar="DIR",
646 help="Search DIR for imported Python modules.")
648 self.add_option('--implicit-cache', action="store_true",
649 dest='implicit_cache',
650 help="Cache implicit dependencies")
652 self.add_option('--implicit-deps-changed', action="store_true",
653 default=0, dest='implicit_deps_changed',
654 help="Ignore cached implicit dependencies.")
655 self.add_option('--implicit-deps-unchanged', action="store_true",
656 default=0, dest='implicit_deps_unchanged',
657 help="Ignore changes in implicit dependencies.")
659 def opt_j(option, opt, value, parser):
661 parser.values.num_jobs = value
662 self.add_option('-j', '--jobs', action="callback", type="int",
663 callback=opt_j, metavar="N",
664 help="Allow N jobs at once.")
666 self.add_option('-k', '--keep-going', action="store_true", default=0,
668 help="Keep going when a target can't be made.")
670 self.add_option('--max-drift', type="int", action="store",
671 dest='max_drift', metavar="N",
672 help="Set maximum system clock drift to N seconds.")
674 self.add_option('-n', '--no-exec', '--just-print', '--dry-run',
675 '--recon', action="store_true", dest='noexec',
676 default=0, help="Don't build; just print commands.")
678 self.add_option('--profile', action="store",
679 dest="profile_file", metavar="FILE",
680 help="Profile SCons and put results in FILE.")
682 self.add_option('-q', '--question', action="store_true", default=0,
683 help="Don't build; exit status says if up to date.")
685 self.add_option('-Q', dest='no_progress', action="store_true",
687 help="Suppress \"Reading/Building\" progress messages.")
689 self.add_option('--random', dest="random", action="store_true",
690 default=0, help="Build dependencies in random order.")
692 self.add_option('-s', '--silent', '--quiet', action="store_true",
693 default=0, help="Don't print commands.")
695 self.add_option('-u', '--up', '--search-up', action="store_const",
696 dest="climb_up", default=0, const=1,
697 help="Search up directory tree for SConstruct, "
698 "build targets at or below current directory.")
699 self.add_option('-U', action="store_const", dest="climb_up",
701 help="Search up directory tree for SConstruct, "
702 "build Default() targets from local SConscript.")
704 self.add_option("-v", "--version",
706 help="Print the SCons version number and exit.")
708 self.add_option('--warn', '--warning', nargs=1, action="store",
709 metavar="WARNING-SPEC",
710 help="Enable or disable warnings.")
712 self.add_option('-Y', '--repository', nargs=1, action="append",
713 help="Search REPOSITORY for source and target files.")
715 self.add_option('-e', '--environment-overrides', action="callback",
716 callback=opt_not_yet,
717 # help="Environment variables override makefiles."
719 self.add_option('-l', '--load-average', '--max-load', action="callback",
720 callback=opt_not_yet, type="int", dest="load_average",
722 # help="Don't start multiple jobs unless load is below "
726 self.add_option('--list-derived', action="callback",
727 callback=opt_not_yet,
728 # help="Don't build; list files that would be built."
730 self.add_option('--list-actions', action="callback",
731 callback=opt_not_yet,
732 # help="Don't build; list files and build actions."
734 self.add_option('--list-where', action="callback",
735 callback=opt_not_yet,
736 # help="Don't build; list files and where defined."
738 self.add_option('-o', '--old-file', '--assume-old', action="callback",
739 callback=opt_not_yet, type="string", dest="old_file",
740 # help = "Consider FILE to be old; don't rebuild it."
742 self.add_option('--override', action="callback", dest="override",
743 callback=opt_not_yet, type="string",
744 # help="Override variables as specified in FILE."
746 self.add_option('-p', action="callback",
747 callback=opt_not_yet,
748 # help="Print internal environments/objects."
750 self.add_option('-r', '-R', '--no-builtin-rules',
751 '--no-builtin-variables', action="callback",
752 callback=opt_not_yet,
753 # help="Clear default environments and variables."
755 self.add_option('-w', '--print-directory', action="callback",
756 callback=opt_not_yet,
757 # help="Print the current directory."
759 self.add_option('--no-print-directory', action="callback",
760 callback=opt_not_yet,
761 # help="Turn off -w, even if it was turned on implicitly."
763 self.add_option('--write-filenames', action="callback",
764 callback=opt_not_yet, type="string", dest="write_filenames",
765 # help="Write all filenames examined into FILE."
767 self.add_option('-W', '--what-if', '--new-file', '--assume-new',
769 action="callback", callback=opt_not_yet, type="string",
770 # help="Consider FILE to be changed."
772 self.add_option('--warn-undefined-variables', action="callback",
773 callback=opt_not_yet,
774 # help="Warn when an undefined variable is referenced."
777 def parse_args(self, args=None, values=None):
778 opt, arglist = OptionParser.parse_args(self, args, values)
779 if opt.implicit_deps_changed or opt.implicit_deps_unchanged:
780 opt.implicit_cache = 1
783 class SConscriptSettableOptions:
784 """This class wraps an OptParser instance and provides
785 uniform access to options that can be either set on the command
786 line or from a SConscript file. A value specified on the command
787 line always overrides a value set in a SConscript file.
788 Not all command line options are SConscript settable, and the ones
789 that are must be explicitly added to settable dictionary and optionally
790 validated and coerced in the set() method."""
792 def __init__(self, options):
793 self.options = options
795 # This dictionary stores the defaults for all the SConscript
796 # settable options, as well as indicating which options
797 # are SConscript settable.
798 self.settable = {'num_jobs':1,
799 'max_drift':SCons.Sig.default_max_drift,
802 'duplicate':'hard-soft-copy'}
805 if not self.settable.has_key(name):
806 raise SCons.Error.UserError, "This option is not settable from a SConscript file: %s"%name
807 if hasattr(self.options, name) and getattr(self.options, name) is not None:
808 return getattr(self.options, name)
810 return self.settable[name]
812 def set(self, name, value):
813 if not self.settable.has_key(name):
814 raise SCons.Error.UserError, "This option is not settable from a SConscript file: %s"%name
816 if name == 'num_jobs':
822 raise SCons.Errors.UserError, "A positive integer is required: %s"%repr(value)
823 elif name == 'max_drift':
827 raise SCons.Errors.UserError, "An integer is required: %s"%repr(value)
828 elif name == 'duplicate':
832 raise SCons.Errors.UserError, "A string is required: %s"%repr(value)
833 if not value in SCons.Node.FS.Valid_Duplicates:
834 raise SCons.Errors.UserError, "Not a valid duplication style: %s" % value
835 # Set the duplicate stye right away so it can affect linking
836 # of SConscript files.
837 SCons.Node.FS.set_duplicate(value)
839 self.settable[name] = value
842 def _main(args, parser):
843 # Here's where everything really happens.
845 # First order of business: set up default warnings and and then
846 # handle the user's warning options, so we can warn about anything
847 # that happens appropriately.
848 default_warnings = [ SCons.Warnings.CorruptSConsignWarning,
849 SCons.Warnings.DeprecatedWarning,
850 SCons.Warnings.DuplicateEnvironmentWarning,
851 SCons.Warnings.MissingSConscriptWarning,
852 SCons.Warnings.NoParallelSupportWarning,
853 SCons.Warnings.MisleadingKeywordsWarning, ]
854 for warning in default_warnings:
855 SCons.Warnings.enableWarningClass(warning)
856 SCons.Warnings._warningOut = _scons_internal_warning
858 _setup_warn(options.warn)
860 # Next, we want to create the FS object that represents the outside
861 # world's file system, as that's central to a lot of initialization.
862 # To do this, however, we need to be in the directory from which we
863 # want to start everything, which means first handling any relevant
864 # options that might cause us to chdir somewhere (-C, -D, -U, -u).
865 if options.directory:
866 cdir = _create_path(options.directory)
870 sys.stderr.write("Could not change directory to %s\n" % cdir)
872 # The SConstruct file may be in a repository, so initialize those
873 # before we start the search up our path for one.
875 if options.repository:
876 repositories.extend(options.repository)
880 target_top = '.' # directory to prepend to targets
881 script_dir = os.getcwd() # location of script
882 while script_dir and not _SConstruct_exists(script_dir):
883 script_dir, last_part = os.path.split(script_dir)
885 target_top = os.path.join(last_part, target_top)
889 display("scons: Entering directory `%s'" % script_dir)
892 # Now that we're in the top-level SConstruct directory, go ahead
893 # and initialize the FS object that represents the file system,
894 # and make it the build engine default.
895 fs = SCons.Node.FS.default_fs = SCons.Node.FS.FS()
897 for rep in repositories:
900 # Now that we have the FS object, the next order of business is to
901 # check for an SConstruct file (or other specified config file).
902 # If there isn't one, we can bail before doing any more work.
905 scripts.extend(options.file)
907 sfile = _SConstruct_exists()
909 scripts.append(sfile)
913 # There's no SConstruct, but they specified -h.
914 # Give them the options usage now, before we fail
915 # trying to read a non-existent SConstruct file.
918 raise SCons.Errors.UserError, "No SConstruct file found."
920 if scripts[0] == "-":
923 d = fs.File(scripts[0]).dir
924 fs.set_SConstruct_dir(d)
926 # Now that we have the FS object and it's intialized, set up (most
927 # of) the rest of the options.
929 ssoptions = SConscriptSettableOptions(options)
931 _set_globals(options)
932 SCons.Node.implicit_cache = options.implicit_cache
933 SCons.Node.implicit_deps_changed = options.implicit_deps_changed
934 SCons.Node.implicit_deps_unchanged = options.implicit_deps_unchanged
936 SCons.SConf.dryrun = 1
937 SCons.Action.execute_actions = None
938 CleanTask.execute = CleanTask.show
940 SCons.SConf.dryrun = 1
941 SCons.SConf.SetCacheMode(options.config)
942 SCons.SConf.SetProgressDisplay(progress_display)
944 if options.no_progress or options.silent:
945 progress_display.set_mode(0)
949 SCons.Action.print_actions = None
950 if options.cache_disable:
951 def disable(self): pass
952 fs.CacheDir = disable
953 if options.cache_force:
955 if options.cache_show:
958 if options.include_dir:
959 sys.path = options.include_dir + sys.path
961 # That should cover (most of) the options. Next, set up the variables
962 # that hold command-line arguments, so the SConscript files that we
963 # read and execute have access to them.
971 SCons.Script._Add_Targets(targets)
972 SCons.Script._Add_Arguments(xmit_args)
975 def __init__(self, file):
977 def write(self, arg):
980 def __getattr__(self, attr):
981 return getattr(self.file, attr)
983 sys.stdout = Unbuffered(sys.stdout)
985 memory_stats.append('before reading SConscript files:')
986 count_stats.append(('pre-', 'read'))
988 progress_display("scons: Reading SConscript files ...")
990 start_time = time.time()
992 for script in scripts:
993 SCons.Script._SConscript._SConscript(fs, script)
994 except SCons.Errors.StopError, e:
995 # We had problems reading an SConscript file, such as it
996 # couldn't be copied in to the BuildDir. Since we're just
997 # reading SConscript files and haven't started building
998 # things yet, stop regardless of whether they used -i or -k
1001 sys.stderr.write("scons: *** %s Stop.\n" % e)
1003 sys.exit(exit_status)
1004 global sconscript_time
1005 sconscript_time = time.time() - start_time
1006 SCons.SConf.CreateConfigHBuilder(SCons.Defaults.DefaultEnvironment())
1007 progress_display("scons: done reading SConscript files.")
1009 # Tell the Node.FS subsystem that we're all done reading the
1010 # SConscript files and calling Repository() and BuildDir() and the
1011 # like, so it can go ahead and start memoizing the string values of
1012 # file system nodes.
1013 SCons.Node.FS.save_strings(1)
1015 memory_stats.append('after reading SConscript files:')
1016 count_stats.append(('post-', 'read'))
1020 if options.help_msg:
1021 help_text = SCons.Script.help_text
1022 if help_text is None:
1023 # They specified -h, but there was no Help() inside the
1024 # SConscript files. Give them the options usage.
1025 parser.print_help(sys.stdout)
1028 print "Use scons -H for help about command-line options."
1031 # Now that we've read the SConscripts we can set the options
1032 # that are SConscript settable:
1033 SCons.Node.implicit_cache = ssoptions.get('implicit_cache')
1034 SCons.Node.FS.set_duplicate(ssoptions.get('duplicate'))
1038 # They specified targets on the command line, so if they
1039 # used -u, -U or -D, we have to look up targets relative
1040 # to the top, but we build whatever they specified.
1042 lookup_top = fs.Dir(target_top)
1045 # There are no targets specified on the command line,
1046 # so if they used -u, -U or -D, we may have to restrict
1047 # what actually gets built.
1050 if options.climb_up == 1:
1051 # -u, local directory and below
1052 target_top = fs.Dir(target_top)
1053 lookup_top = target_top
1054 elif options.climb_up == 2:
1055 # -D, all Default() targets
1058 elif options.climb_up == 3:
1059 # -U, local SConscript Default() targets
1060 target_top = fs.Dir(target_top)
1061 def check_dir(x, target_top=target_top):
1062 if hasattr(x, 'cwd') and not x.cwd is None:
1063 cwd = x.cwd.srcnode()
1064 return cwd == target_top
1066 # x doesn't have a cwd, so it's either not a target,
1067 # or not a file, so go ahead and keep it as a default
1068 # target and let the engine sort it out:
1070 d = filter(check_dir, SCons.Script.DEFAULT_TARGETS)
1071 SCons.Script.DEFAULT_TARGETS[:] = d
1075 targets = SCons.Script._Get_Default_Targets(d, fs)
1078 sys.stderr.write("scons: *** No targets specified and no Default() targets found. Stop.\n")
1081 def Entry(x, ltop=lookup_top, ttop=target_top, fs=fs):
1082 if isinstance(x, SCons.Node.Node):
1086 # Why would ltop be None? Unfortunately this happens.
1087 if ltop == None: ltop = ''
1088 # Curdir becomes important when SCons is called with -u, -C,
1089 # or similar option that changes directory, and so the paths
1090 # of targets given on the command line need to be adjusted.
1091 curdir = os.path.join(os.getcwd(), str(ltop))
1092 for lookup in SCons.Node.arg2nodes_lookups:
1093 node = lookup(x, curdir=curdir)
1097 node = fs.Entry(x, directory=ltop, create=1)
1098 if ttop and not node.is_under(ttop):
1099 if isinstance(node, SCons.Node.FS.Dir) and ttop.is_under(node):
1105 nodes = filter(None, map(Entry, targets))
1107 task_class = BuildTask # default action is to build targets
1108 opening_message = "Building targets ..."
1109 closing_message = "done building targets."
1110 if keep_going_on_error:
1111 failure_message = "done building targets (errors occurred during build)."
1113 failure_message = "building terminated because of errors."
1114 if options.question:
1115 task_class = QuestionTask
1117 if ssoptions.get('clean'):
1118 task_class = CleanTask
1119 opening_message = "Cleaning targets ..."
1120 closing_message = "done cleaning targets."
1121 if keep_going_on_error:
1122 closing_message = "done cleaning targets (errors occurred during clean)."
1124 failure_message = "cleaning terminated because of errors."
1125 except AttributeError:
1128 SCons.Environment.CalculatorArgs['max_drift'] = ssoptions.get('max_drift')
1131 def order(dependencies):
1132 """Randomize the dependencies."""
1133 # This is cribbed from the implementation of
1134 # random.shuffle() in Python 2.X.
1136 for i in xrange(len(d)-1, 0, -1):
1137 j = int(random.random() * (i+1))
1138 d[i], d[j] = d[j], d[i]
1141 def order(dependencies):
1142 """Leave the order of dependencies alone."""
1145 progress_display("scons: " + opening_message)
1146 taskmaster = SCons.Taskmaster.Taskmaster(nodes, task_class, order)
1148 nj = ssoptions.get('num_jobs')
1149 jobs = SCons.Job.Jobs(nj, taskmaster)
1150 if nj > 1 and jobs.num_jobs == 1:
1151 msg = "parallel builds are unsupported by this version of Python;\n" + \
1152 "\tignoring -j or num_jobs option.\n"
1153 SCons.Warnings.warn(SCons.Warnings.NoParallelSupportWarning, msg)
1155 memory_stats.append('before building targets:')
1156 count_stats.append(('pre-', 'build'))
1162 progress_display("scons: " + failure_message)
1164 progress_display("scons: " + closing_message)
1165 if not options.noexec:
1166 SCons.SConsign.write()
1168 memory_stats.append('after building targets:')
1169 count_stats.append(('post-', 'build'))
1172 all_args = sys.argv[1:]
1174 all_args = string.split(os.environ['SCONSFLAGS']) + all_args
1176 # it's OK if there's no SCONSFLAGS
1178 parser = OptParser()
1180 options, args = parser.parse_args(all_args)
1181 if type(options.debug) == type([]) and "pdb" in options.debug:
1183 pdb.Pdb().runcall(_main, args, parser)
1184 elif options.profile_file:
1186 prof = profile.Profile()
1188 prof.runcall(_main, args, parser)
1191 prof.dump_stats(options.profile_file)
1200 except SystemExit, s:
1203 except KeyboardInterrupt:
1204 print "Build interrupted."
1206 except SyntaxError, e:
1207 _scons_syntax_error(e)
1208 except SCons.Errors.InternalError:
1209 _scons_internal_error()
1210 except SCons.Errors.UserError, e:
1211 _scons_user_error(e)
1213 # An exception here is likely a builtin Python exception Python
1214 # code in an SConscript file. Show them precisely what the
1215 # problem was and where it happened.
1216 SCons.Script._SConscript.SConscript_exception()
1219 memory_stats.print_stats()
1220 count_stats.print_stats()
1223 SCons.Debug.listLoggedInstances('*')
1224 #SCons.Debug.dumpLoggedInstances('*')
1227 print "Memoizer (memory cache) hits and misses:"
1228 SCons.Memoize.Dump()
1230 # Dump any development debug info that may have been enabled.
1231 # These are purely for internal debugging during development, so
1232 # there's no need to control them with --debug= options; they're
1233 # controlled by changing the source code.
1234 SCons.Debug.dump_caller_counts()
1235 SCons.Taskmaster.dump_stats()
1238 total_time = time.time()-SCons.Script.start_time
1239 scons_time = total_time-sconscript_time-command_time
1240 print "Total build time: %f seconds"%total_time
1241 print "Total SConscript file execution time: %f seconds"%sconscript_time
1242 print "Total SCons execution time: %f seconds"%scons_time
1243 print "Total command execution time: %f seconds"%command_time
1245 sys.exit(exit_status)