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 if (self.targets[0].has_builder() or self.targets[0].side_effect) \
195 and not os.path.isdir(str(self.targets[0])):
196 display("Removed " + str(self.targets[0]))
197 if SCons.Environment.CleanTargets.has_key(self.targets[0]):
198 files = SCons.Environment.CleanTargets[self.targets[0]]
200 SCons.Util.fs_delete(str(f), 0)
203 if self.targets[0].has_builder() or self.targets[0].side_effect:
204 for t in self.targets:
208 print "scons: Could not remove '%s':" % str(t), e.strerror
211 display("Removed " + str(t))
212 if SCons.Environment.CleanTargets.has_key(self.targets[0]):
213 files = SCons.Environment.CleanTargets[self.targets[0]]
215 SCons.Util.fs_delete(str(f))
219 # Have the taskmaster arrange to "execute" all of the targets, because
220 # we'll figure out ourselves (in remove() or show() above) whether
221 # anything really needs to be done.
222 make_ready = SCons.Taskmaster.Task.make_ready_all
227 class QuestionTask(SCons.Taskmaster.Task):
228 """An SCons task for the -q (question) option."""
233 if self.targets[0].get_state() != SCons.Node.up_to_date:
243 keep_going_on_error = 0
246 print_explanations = 0
258 exit_status = 0 # exit status, assume success by default
261 num_jobs = 1 # this is modifed by SConscript.SetJobs()
265 def get_all_children(node): return node.all_children()
267 def get_derived_children(node):
268 children = node.all_children(None)
269 return filter(lambda x: x.has_builder(), children)
271 def _scons_syntax_error(e):
272 """Handle syntax errors. Print out a message and show where the error
275 etype, value, tb = sys.exc_info()
276 lines = traceback.format_exception_only(etype, value)
278 sys.stderr.write(line+'\n')
281 def find_deepest_user_frame(tb):
283 Find the deepest stack frame that is not part of SCons.
285 Input is a "pre-processed" stack trace in the form
286 returned by traceback.extract_tb() or traceback.extract_stack()
291 # find the deepest traceback frame that is not part
295 if string.find(filename, os.sep+'SCons'+os.sep) == -1:
299 def _scons_user_error(e):
300 """Handle user errors. Print out a message and a description of the
301 error, along with the line number and routine where it occured.
302 The file and line number will be the deepest stack frame that is
303 not part of SCons itself.
305 etype, value, tb = sys.exc_info()
306 filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_tb(tb))
307 sys.stderr.write("\nscons: *** %s\n" % value)
308 sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine))
311 def _scons_user_warning(e):
312 """Handle user warnings. Print out a message and a description of
313 the warning, along with the line number and routine where it occured.
314 The file and line number will be the deepest stack frame that is
315 not part of SCons itself.
317 etype, value, tb = sys.exc_info()
318 filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_tb(tb))
319 sys.stderr.write("\nscons: warning: %s\n" % e)
320 sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine))
322 def _scons_internal_warning(e):
323 """Slightly different from _scons_user_warning in that we use the
324 *current call stack* rather than sys.exc_info() to get our stack trace.
325 This is used by the warnings framework to print warnings."""
326 filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_stack())
327 sys.stderr.write("\nscons: warning: %s\n" % e[0])
328 sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine))
330 def _scons_internal_error():
331 """Handle all errors but user errors. Print out a message telling
332 the user what to do in this case and print a normal trace.
334 print 'internal error'
335 traceback.print_exc()
338 def _varargs(option, parser):
341 arg = parser.rargs[0]
347 def _setup_warn(arg):
348 """The --warn option. An argument to this option
349 should be of the form <warning-class> or no-<warning-class>.
350 The warning class is munged in order to get an actual class
351 name from the SCons.Warnings module to enable or disable.
352 The supplied <warning-class> is split on hyphens, each element
353 is captialized, then smushed back together. Then the string
354 "SCons.Warnings." is added to the front and "Warning" is added
355 to the back to get the fully qualified class name.
357 For example, --warn=deprecated will enable the
358 SCons.Warnings.DeprecatedWarning class.
360 --warn=no-dependency will disable the
361 SCons.Warnings.DependencyWarning class.
363 As a special case, --warn=all and --warn=no-all
364 will enable or disable (respectively) the base
365 class of all warnings, which is SCons.Warning.Warning."""
367 elems = string.split(string.lower(arg), '-')
373 if len(elems) == 1 and elems[0] == 'all':
374 class_name = "Warning"
378 return "SCons" + s[5:]
380 return string.capitalize(s)
381 class_name = string.join(map(_capitalize, elems), '') + "Warning"
383 clazz = getattr(SCons.Warnings, class_name)
384 except AttributeError:
385 sys.stderr.write("No warning type: '%s'\n" % arg)
388 SCons.Warnings.enableWarningClass(clazz)
390 SCons.Warnings.suppressWarningClass(clazz)
392 def _SConstruct_exists(dirname=''):
393 """This function checks that an SConstruct file exists in a directory.
394 If so, it returns the path of the file. By default, it checks the
398 for file in ['SConstruct', 'Sconstruct', 'sconstruct']:
399 sfile = os.path.join(dirname, file)
400 if os.path.isfile(sfile):
402 if not os.path.isabs(sfile):
403 for rep in repositories:
404 if os.path.isfile(os.path.join(rep, sfile)):
408 def _set_globals(options):
409 global repositories, keep_going_on_error, ignore_errors
410 global count_stats, print_dtree
411 global print_explanations, print_includes, print_memoizer
412 global print_objects, print_stacktrace, print_stree
413 global print_time, print_tree
414 global memory_outf, memory_stats
416 if options.repository:
417 repositories.extend(options.repository)
418 keep_going_on_error = options.keep_going
420 debug_values = options.debug
421 if debug_values is None:
423 except AttributeError:
426 if "count" in debug_values:
428 if "dtree" in debug_values:
430 if "explain" in debug_values:
431 print_explanations = 1
432 if "findlibs" in debug_values:
433 SCons.Scanner.Prog.print_find_libs = "findlibs"
434 if "includes" in debug_values:
436 if "memoizer" in debug_values:
438 if "memory" in debug_values:
440 memory_outf = sys.stdout
441 if "objects" in debug_values:
443 if "presub" in debug_values:
444 SCons.Action.print_actions_presub = 1
445 if "stacktrace" in debug_values:
447 if "stree" in debug_values:
449 if "time" in debug_values:
451 if "tree" in debug_values:
453 ignore_errors = options.ignore_errors
455 def _create_path(plist):
461 path = path + '/' + d
465 class OptParser(OptionParser):
469 parts = ["SCons by Steven Knight et al.:\n"]
471 parts.append("\tscript: v%s.%s, %s, by %s on %s\n" % (__main__.__version__,
474 __main__.__developer__,
475 __main__.__buildsys__))
476 except KeyboardInterrupt:
479 # On win32 there is no scons.py, so there is no __main__.__version__,
480 # hence there is no script version.
482 parts.append("\tengine: v%s.%s, %s, by %s on %s\n" % (SCons.__version__,
487 parts.append("__COPYRIGHT__")
488 OptionParser.__init__(self, version=string.join(parts, ''),
489 usage="usage: scons [OPTION] [TARGET] ...")
491 # options ignored for compatibility
492 def opt_ignore(option, opt, value, parser):
493 sys.stderr.write("Warning: ignoring %s option\n" % opt)
494 self.add_option("-b", "-m", "-S", "-t", "--no-keep-going", "--stop",
495 "--touch", action="callback", callback=opt_ignore,
496 help="Ignored for compatibility.")
498 self.add_option('-c', '--clean', '--remove', action="store_true",
500 help="Remove specified targets and dependencies.")
502 self.add_option('-C', '--directory', type="string", action = "append",
504 help="Change to DIR before doing anything.")
506 self.add_option('--cache-disable', '--no-cache',
507 action="store_true", dest='cache_disable', default=0,
508 help="Do not retrieve built targets from CacheDir.")
510 self.add_option('--cache-force', '--cache-populate',
511 action="store_true", dest='cache_force', default=0,
512 help="Copy already-built targets into the CacheDir.")
514 self.add_option('--cache-show',
515 action="store_true", dest='cache_show', default=0,
516 help="Print build actions for files from CacheDir.")
518 config_options = ["auto", "force" ,"cache"]
520 def opt_config(option, opt, value, parser, c_options=config_options):
521 if value in c_options:
522 parser.values.config = value
524 raise OptionValueError("Warning: %s is not a valid config type" % value)
525 self.add_option('--config', action="callback", type="string",
526 callback=opt_config, nargs=1, dest="config",
527 metavar="MODE", default="auto",
528 help="Controls Configure subsystem: "
529 "%s." % string.join(config_options, ", "))
531 def opt_not_yet(option, opt, value, parser):
532 sys.stderr.write("Warning: the %s option is not yet implemented\n" % opt)
534 self.add_option('-d', action="callback",
535 callback=opt_not_yet,
536 help = "Print file dependency information.")
538 self.add_option('-D', action="store_const", const=2, dest="climb_up",
539 help="Search up directory tree for SConstruct, "
540 "build all Default() targets.")
542 debug_options = ["count", "dtree", "explain", "findlibs",
543 "includes", "memoizer", "memory", "objects",
544 "pdb", "presub", "stacktrace", "stree",
547 def opt_debug(option, opt, value, parser, debug_options=debug_options):
548 if value in debug_options:
550 if parser.values.debug is None:
551 parser.values.debug = []
552 except AttributeError:
553 parser.values.debug = []
554 parser.values.debug.append(value)
556 raise OptionValueError("Warning: %s is not a valid debug type" % value)
557 self.add_option('--debug', action="callback", type="string",
558 callback=opt_debug, nargs=1, dest="debug",
560 help="Print various types of debugging information: "
561 "%s." % string.join(debug_options, ", "))
563 def opt_duplicate(option, opt, value, parser):
564 if not value in SCons.Node.FS.Valid_Duplicates:
565 raise OptionValueError("`%s' is not a valid duplication style." % value)
566 parser.values.duplicate = value
567 # Set the duplicate style right away so it can affect linking
568 # of SConscript files.
569 SCons.Node.FS.set_duplicate(value)
570 self.add_option('--duplicate', action="callback", type="string",
571 callback=opt_duplicate, nargs=1, dest="duplicate",
572 help="Set the preferred duplication methods. Must be one of "
573 + string.join(SCons.Node.FS.Valid_Duplicates, ", "))
575 self.add_option('-f', '--file', '--makefile', '--sconstruct',
576 action="append", nargs=1,
577 help="Read FILE as the top-level SConstruct file.")
579 self.add_option('-h', '--help', action="store_true", default=0,
581 help="Print defined help message, or this one.")
583 self.add_option("-H", "--help-options",
585 help="Print this message and exit.")
587 self.add_option('-i', '--ignore-errors', action="store_true",
588 default=0, dest='ignore_errors',
589 help="Ignore errors from build actions.")
591 self.add_option('-I', '--include-dir', action="append",
592 dest='include_dir', metavar="DIR",
593 help="Search DIR for imported Python modules.")
595 self.add_option('--implicit-cache', action="store_true",
596 dest='implicit_cache',
597 help="Cache implicit dependencies")
599 self.add_option('--implicit-deps-changed', action="store_true",
600 default=0, dest='implicit_deps_changed',
601 help="Ignore cached implicit dependencies.")
602 self.add_option('--implicit-deps-unchanged', action="store_true",
603 default=0, dest='implicit_deps_unchanged',
604 help="Ignore changes in implicit dependencies.")
606 def opt_j(option, opt, value, parser):
608 parser.values.num_jobs = value
609 self.add_option('-j', '--jobs', action="callback", type="int",
610 callback=opt_j, metavar="N",
611 help="Allow N jobs at once.")
613 self.add_option('-k', '--keep-going', action="store_true", default=0,
615 help="Keep going when a target can't be made.")
617 self.add_option('--max-drift', type="int", action="store",
618 dest='max_drift', metavar="N",
619 help="Set maximum system clock drift to N seconds.")
621 self.add_option('-n', '--no-exec', '--just-print', '--dry-run',
622 '--recon', action="store_true", dest='noexec',
623 default=0, help="Don't build; just print commands.")
625 def opt_profile(option, opt, value, parser):
630 profile.run('SCons.Script.Main.main()', value)
631 sys.exit(exit_status)
632 self.add_option('--profile', nargs=1, action="callback",
633 callback=opt_profile, type="string", dest="profile",
635 help="Profile SCons and put results in FILE.")
637 self.add_option('-q', '--question', action="store_true", default=0,
638 help="Don't build; exit status says if up to date.")
640 self.add_option('-Q', dest='no_progress', action="store_true",
642 help="Suppress \"Reading/Building\" progress messages.")
644 self.add_option('--random', dest="random", action="store_true",
645 default=0, help="Build dependencies in random order.")
647 self.add_option('-s', '--silent', '--quiet', action="store_true",
648 default=0, help="Don't print commands.")
650 self.add_option('-u', '--up', '--search-up', action="store_const",
651 dest="climb_up", default=0, const=1,
652 help="Search up directory tree for SConstruct, "
653 "build targets at or below current directory.")
654 self.add_option('-U', action="store_const", dest="climb_up",
656 help="Search up directory tree for SConstruct, "
657 "build Default() targets from local SConscript.")
659 self.add_option("-v", "--version",
661 help="Print the SCons version number and exit.")
663 self.add_option('--warn', '--warning', nargs=1, action="store",
664 metavar="WARNING-SPEC",
665 help="Enable or disable warnings.")
667 self.add_option('-Y', '--repository', nargs=1, action="append",
668 help="Search REPOSITORY for source and target files.")
670 self.add_option('-e', '--environment-overrides', action="callback",
671 callback=opt_not_yet,
672 # help="Environment variables override makefiles."
674 self.add_option('-l', '--load-average', '--max-load', action="callback",
675 callback=opt_not_yet, type="int", dest="load_average",
677 # help="Don't start multiple jobs unless load is below "
681 self.add_option('--list-derived', action="callback",
682 callback=opt_not_yet,
683 # help="Don't build; list files that would be built."
685 self.add_option('--list-actions', action="callback",
686 callback=opt_not_yet,
687 # help="Don't build; list files and build actions."
689 self.add_option('--list-where', action="callback",
690 callback=opt_not_yet,
691 # help="Don't build; list files and where defined."
693 self.add_option('-o', '--old-file', '--assume-old', action="callback",
694 callback=opt_not_yet, type="string", dest="old_file",
695 # help = "Consider FILE to be old; don't rebuild it."
697 self.add_option('--override', action="callback", dest="override",
698 callback=opt_not_yet, type="string",
699 # help="Override variables as specified in FILE."
701 self.add_option('-p', action="callback",
702 callback=opt_not_yet,
703 # help="Print internal environments/objects."
705 self.add_option('-r', '-R', '--no-builtin-rules',
706 '--no-builtin-variables', action="callback",
707 callback=opt_not_yet,
708 # help="Clear default environments and variables."
710 self.add_option('-w', '--print-directory', action="callback",
711 callback=opt_not_yet,
712 # help="Print the current directory."
714 self.add_option('--no-print-directory', action="callback",
715 callback=opt_not_yet,
716 # help="Turn off -w, even if it was turned on implicitly."
718 self.add_option('--write-filenames', action="callback",
719 callback=opt_not_yet, type="string", dest="write_filenames",
720 # help="Write all filenames examined into FILE."
722 self.add_option('-W', '--what-if', '--new-file', '--assume-new',
724 action="callback", callback=opt_not_yet, type="string",
725 # help="Consider FILE to be changed."
727 self.add_option('--warn-undefined-variables', action="callback",
728 callback=opt_not_yet,
729 # help="Warn when an undefined variable is referenced."
732 def parse_args(self, args=None, values=None):
733 opt, arglist = OptionParser.parse_args(self, args, values)
734 if opt.implicit_deps_changed or opt.implicit_deps_unchanged:
735 opt.implicit_cache = 1
738 class SConscriptSettableOptions:
739 """This class wraps an OptParser instance and provides
740 uniform access to options that can be either set on the command
741 line or from a SConscript file. A value specified on the command
742 line always overrides a value set in a SConscript file.
743 Not all command line options are SConscript settable, and the ones
744 that are must be explicitly added to settable dictionary and optionally
745 validated and coerced in the set() method."""
747 def __init__(self, options):
748 self.options = options
750 # This dictionary stores the defaults for all the SConscript
751 # settable options, as well as indicating which options
752 # are SConscript settable.
753 self.settable = {'num_jobs':1,
754 'max_drift':SCons.Sig.default_max_drift,
757 'duplicate':'hard-soft-copy'}
760 if not self.settable.has_key(name):
761 raise SCons.Error.UserError, "This option is not settable from a SConscript file: %s"%name
762 if hasattr(self.options, name) and getattr(self.options, name) is not None:
763 return getattr(self.options, name)
765 return self.settable[name]
767 def set(self, name, value):
768 if not self.settable.has_key(name):
769 raise SCons.Error.UserError, "This option is not settable from a SConscript file: %s"%name
771 if name == 'num_jobs':
777 raise SCons.Errors.UserError, "A positive integer is required: %s"%repr(value)
778 elif name == 'max_drift':
782 raise SCons.Errors.UserError, "An integer is required: %s"%repr(value)
783 elif name == 'duplicate':
787 raise SCons.Errors.UserError, "A string is required: %s"%repr(value)
788 if not value in SCons.Node.FS.Valid_Duplicates:
789 raise SCons.Errors.UserError, "Not a valid duplication style: %s" % value
790 # Set the duplicate stye right away so it can affect linking
791 # of SConscript files.
792 SCons.Node.FS.set_duplicate(value)
794 self.settable[name] = value
797 def _main(args, parser):
799 fs = SCons.Node.FS.default_fs
801 # Enable deprecated warnings by default.
802 SCons.Warnings._warningOut = _scons_internal_warning
803 SCons.Warnings.enableWarningClass(SCons.Warnings.CorruptSConsignWarning)
804 SCons.Warnings.enableWarningClass(SCons.Warnings.DeprecatedWarning)
805 SCons.Warnings.enableWarningClass(SCons.Warnings.DuplicateEnvironmentWarning)
806 SCons.Warnings.enableWarningClass(SCons.Warnings.MissingSConscriptWarning)
807 SCons.Warnings.enableWarningClass(SCons.Warnings.NoParallelSupportWarning)
808 # This is good for newbies, and hopefully most everyone else too.
809 SCons.Warnings.enableWarningClass(SCons.Warnings.MisleadingKeywordsWarning)
812 ssoptions = SConscriptSettableOptions(options)
814 _set_globals(options)
815 SCons.Node.implicit_cache = options.implicit_cache
816 SCons.Node.implicit_deps_changed = options.implicit_deps_changed
817 SCons.Node.implicit_deps_unchanged = options.implicit_deps_unchanged
819 _setup_warn(options.warn)
821 SCons.SConf.dryrun = 1
822 SCons.Action.execute_actions = None
823 CleanTask.execute = CleanTask.show
825 SCons.SConf.dryrun = 1
826 SCons.SConf.SetCacheMode(options.config)
827 SCons.SConf.SetProgressDisplay(progress_display)
829 if options.no_progress or options.silent:
830 progress_display.set_mode(0)
834 SCons.Action.print_actions = None
835 if options.cache_disable:
836 def disable(self): pass
837 fs.CacheDir = disable
838 if options.cache_force:
840 if options.cache_show:
842 if options.directory:
843 cdir = _create_path(options.directory)
847 sys.stderr.write("Could not change directory to %s\n" % cdir)
855 SCons.Script._Add_Arguments(xmit_args)
856 SCons.Script._Add_Targets(targets)
860 target_top = '.' # directory to prepend to targets
861 script_dir = os.getcwd() # location of script
862 while script_dir and not _SConstruct_exists(script_dir):
863 script_dir, last_part = os.path.split(script_dir)
865 target_top = os.path.join(last_part, target_top)
869 display("scons: Entering directory `%s'" % script_dir)
872 fs.set_toplevel_dir(os.getcwd())
876 scripts.extend(options.file)
878 sfile = _SConstruct_exists()
880 scripts.append(sfile)
884 # There's no SConstruct, but they specified -h.
885 # Give them the options usage now, before we fail
886 # trying to read a non-existent SConstruct file.
891 raise SCons.Errors.UserError, "No SConstruct file found."
893 if scripts[0] == "-":
896 d = fs.File(scripts[0]).dir
897 fs.set_SConstruct_dir(d)
900 def __init__(self, file):
902 def write(self, arg):
905 def __getattr__(self, attr):
906 return getattr(self.file, attr)
908 sys.stdout = Unbuffered(sys.stdout)
910 if options.include_dir:
911 sys.path = options.include_dir + sys.path
914 for rep in repositories:
917 if not memory_stats is None: memory_stats.append(SCons.Debug.memory())
918 if not count_stats is None: count_stats.append(SCons.Debug.fetchLoggedInstances())
920 progress_display("scons: Reading SConscript files ...")
922 start_time = time.time()
924 for script in scripts:
925 SCons.Script._SConscript._SConscript(fs, script)
926 except SCons.Errors.StopError, e:
927 # We had problems reading an SConscript file, such as it
928 # couldn't be copied in to the BuildDir. Since we're just
929 # reading SConscript files and haven't started building
930 # things yet, stop regardless of whether they used -i or -k
933 sys.stderr.write("scons: *** %s Stop.\n" % e)
935 sys.exit(exit_status)
936 global sconscript_time
937 sconscript_time = time.time() - start_time
938 SCons.SConf.CreateConfigHBuilder(SCons.Defaults.DefaultEnvironment())
939 progress_display("scons: done reading SConscript files.")
941 # Tell the Node.FS subsystem that we're all done reading the
942 # SConscript files and calling Repository() and BuildDir() and the
943 # like, so it can go ahead and start memoizing the string values of
945 SCons.Node.FS.save_strings(1)
947 if not memory_stats is None: memory_stats.append(SCons.Debug.memory())
948 if not count_stats is None: count_stats.append(SCons.Debug.fetchLoggedInstances())
953 help_text = SCons.Script.help_text
954 if help_text is None:
955 # They specified -h, but there was no Help() inside the
956 # SConscript files. Give them the options usage.
957 parser.print_help(sys.stdout)
960 print "Use scons -H for help about command-line options."
963 # Now that we've read the SConscripts we can set the options
964 # that are SConscript settable:
965 SCons.Node.implicit_cache = ssoptions.get('implicit_cache')
966 SCons.Node.FS.set_duplicate(ssoptions.get('duplicate'))
970 # They specified targets on the command line, so if they
971 # used -u, -U or -D, we have to look up targets relative
972 # to the top, but we build whatever they specified.
974 lookup_top = fs.Dir(target_top)
977 # There are no targets specified on the command line,
978 # so if they used -u, -U or -D, we may have to restrict
979 # what actually gets built.
982 if options.climb_up == 1:
983 # -u, local directory and below
984 target_top = fs.Dir(target_top)
985 lookup_top = target_top
986 elif options.climb_up == 2:
987 # -D, all Default() targets
990 elif options.climb_up == 3:
991 # -U, local SConscript Default() targets
992 target_top = fs.Dir(target_top)
993 def check_dir(x, target_top=target_top):
994 if hasattr(x, 'cwd') and not x.cwd is None:
995 cwd = x.cwd.srcnode()
996 return cwd == target_top
998 # x doesn't have a cwd, so it's either not a target,
999 # or not a file, so go ahead and keep it as a default
1000 # target and let the engine sort it out:
1002 d = filter(check_dir, SCons.Script.DEFAULT_TARGETS)
1003 SCons.Script.DEFAULT_TARGETS[:] = d
1007 targets = SCons.Script._Get_Default_Targets(d, fs)
1010 sys.stderr.write("scons: *** No targets specified and no Default() targets found. Stop.\n")
1013 def Entry(x, ltop=lookup_top, ttop=target_top, fs=fs):
1014 if isinstance(x, SCons.Node.Node):
1017 node = SCons.Node.Alias.default_ans.lookup(x)
1019 node = fs.Entry(x, directory=ltop, create=1)
1020 if ttop and not node.is_under(ttop):
1021 if isinstance(node, SCons.Node.FS.Dir) and ttop.is_under(node):
1027 nodes = filter(lambda x: x is not None, map(Entry, targets))
1029 task_class = BuildTask # default action is to build targets
1030 opening_message = "Building targets ..."
1031 closing_message = "done building targets."
1032 if keep_going_on_error:
1033 failure_message = "done building targets (errors occurred during build)."
1035 failure_message = "building terminated because of errors."
1036 if options.question:
1037 task_class = QuestionTask
1039 if ssoptions.get('clean'):
1040 task_class = CleanTask
1041 opening_message = "Cleaning targets ..."
1042 closing_message = "done cleaning targets."
1043 if keep_going_on_error:
1044 closing_message = "done cleaning targets (errors occurred during clean)."
1046 failure_message = "cleaning terminated because of errors."
1047 except AttributeError:
1050 SCons.Environment.CalculatorArgs['max_drift'] = ssoptions.get('max_drift')
1053 def order(dependencies):
1054 """Randomize the dependencies."""
1055 # This is cribbed from the implementation of
1056 # random.shuffle() in Python 2.X.
1058 for i in xrange(len(d)-1, 0, -1):
1059 j = int(random.random() * (i+1))
1060 d[i], d[j] = d[j], d[i]
1063 def order(dependencies):
1064 """Leave the order of dependencies alone."""
1067 progress_display("scons: " + opening_message)
1068 taskmaster = SCons.Taskmaster.Taskmaster(nodes, task_class, order)
1070 nj = ssoptions.get('num_jobs')
1071 jobs = SCons.Job.Jobs(nj, taskmaster)
1072 if nj > 1 and jobs.num_jobs == 1:
1073 msg = "parallel builds are unsupported by this version of Python;\n" + \
1074 "\tignoring -j or num_jobs option.\n"
1075 SCons.Warnings.warn(SCons.Warnings.NoParallelSupportWarning, msg)
1077 if not memory_stats is None: memory_stats.append(SCons.Debug.memory())
1078 if not count_stats is None: count_stats.append(SCons.Debug.fetchLoggedInstances())
1084 progress_display("scons: " + failure_message)
1086 progress_display("scons: " + closing_message)
1087 if not options.noexec:
1088 SCons.SConsign.write()
1090 if not memory_stats is None:
1091 memory_stats.append(SCons.Debug.memory())
1093 'before reading SConscript files',
1094 'after reading SConscript files',
1095 'before building targets',
1096 'after building targets',
1098 for i in xrange(len(when)):
1099 memory_outf.write('Memory %-32s %12d\n' % (when[i]+':', memory_stats[i]))
1101 if not count_stats is None:
1102 count_stats.append(SCons.Debug.fetchLoggedInstances())
1104 for cs in count_stats:
1105 for n in map(lambda t: t[0], cs):
1106 stats_table[n] = [0, 0, 0, 0]
1108 for cs in count_stats:
1110 stats_table[n][i] = c
1112 keys = stats_table.keys()
1114 print "Object counts:"
1115 fmt = " %7s %7s %7s %7s %s"
1116 print fmt % ("pre-", "post-", "pre-", "post-", "")
1117 print fmt % ("read", "read", "build", "build", "Class")
1120 print " %7d %7d %7d %7d %s" % (r[0], r[1], r[2], r[3], k)
1123 SCons.Debug.listLoggedInstances('*')
1124 #SCons.Debug.dumpLoggedInstances('*')
1127 print "Memoizer (memory cache) hits and misses:"
1128 SCons.Memoize.Dump()
1131 all_args = sys.argv[1:]
1133 all_args = string.split(os.environ['SCONSFLAGS']) + all_args
1135 # it's OK if there's no SCONSFLAGS
1137 parser = OptParser()
1139 options, args = parser.parse_args(all_args)
1140 if type(options.debug) == type([]) and "pdb" in options.debug:
1142 pdb.Pdb().runcall(_main, args, parser)
1151 except SystemExit, s:
1154 except KeyboardInterrupt:
1155 print "Build interrupted."
1157 except SyntaxError, e:
1158 _scons_syntax_error(e)
1159 except SCons.Errors.InternalError:
1160 _scons_internal_error()
1161 except SCons.Errors.UserError, e:
1162 _scons_user_error(e)
1164 # An exception here is likely a builtin Python exception Python
1165 # code in an SConscript file. Show them precisely what the
1166 # problem was and where it happened.
1167 SCons.Script._SConscript.SConscript_exception()
1171 total_time = time.time()-SCons.Script.start_time
1172 scons_time = total_time-sconscript_time-command_time
1173 print "Total build time: %f seconds"%total_time
1174 print "Total SConscript file execution time: %f seconds"%sconscript_time
1175 print "Total SCons execution time: %f seconds"%scons_time
1176 print "Total command execution time: %f seconds"%command_time
1178 sys.exit(exit_status)