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 raise SCons.Errors.UserError, "No SConstruct file found."
874 fs.set_toplevel_dir(os.getcwd())
878 scripts.extend(options.file)
880 sfile = _SConstruct_exists()
882 scripts.append(sfile)
886 # There's no SConstruct, but they specified -h.
887 # Give them the options usage now, before we fail
888 # trying to read a non-existent SConstruct file.
893 raise SCons.Errors.UserError, "No SConstruct file found."
895 if scripts[0] == "-":
898 d = fs.File(scripts[0]).dir
899 fs.set_SConstruct_dir(d)
902 def __init__(self, file):
904 def write(self, arg):
907 def __getattr__(self, attr):
908 return getattr(self.file, attr)
910 sys.stdout = Unbuffered(sys.stdout)
912 if options.include_dir:
913 sys.path = options.include_dir + sys.path
916 for rep in repositories:
919 if not memory_stats is None: memory_stats.append(SCons.Debug.memory())
920 if not count_stats is None: count_stats.append(SCons.Debug.fetchLoggedInstances())
922 progress_display("scons: Reading SConscript files ...")
924 start_time = time.time()
926 for script in scripts:
927 SCons.Script._SConscript._SConscript(fs, script)
928 except SCons.Errors.StopError, e:
929 # We had problems reading an SConscript file, such as it
930 # couldn't be copied in to the BuildDir. Since we're just
931 # reading SConscript files and haven't started building
932 # things yet, stop regardless of whether they used -i or -k
935 sys.stderr.write("scons: *** %s Stop.\n" % e)
937 sys.exit(exit_status)
938 global sconscript_time
939 sconscript_time = time.time() - start_time
940 SCons.SConf.CreateConfigHBuilder(SCons.Defaults.DefaultEnvironment())
941 progress_display("scons: done reading SConscript files.")
943 # Tell the Node.FS subsystem that we're all done reading the
944 # SConscript files and calling Repository() and BuildDir() and the
945 # like, so it can go ahead and start memoizing the string values of
947 SCons.Node.FS.save_strings(1)
949 if not memory_stats is None: memory_stats.append(SCons.Debug.memory())
950 if not count_stats is None: count_stats.append(SCons.Debug.fetchLoggedInstances())
955 help_text = SCons.Script.help_text
956 if help_text is None:
957 # They specified -h, but there was no Help() inside the
958 # SConscript files. Give them the options usage.
959 parser.print_help(sys.stdout)
962 print "Use scons -H for help about command-line options."
965 # Now that we've read the SConscripts we can set the options
966 # that are SConscript settable:
967 SCons.Node.implicit_cache = ssoptions.get('implicit_cache')
968 SCons.Node.FS.set_duplicate(ssoptions.get('duplicate'))
972 # They specified targets on the command line, so if they
973 # used -u, -U or -D, we have to look up targets relative
974 # to the top, but we build whatever they specified.
976 lookup_top = fs.Dir(target_top)
979 # There are no targets specified on the command line,
980 # so if they used -u, -U or -D, we may have to restrict
981 # what actually gets built.
984 if options.climb_up == 1:
985 # -u, local directory and below
986 target_top = fs.Dir(target_top)
987 lookup_top = target_top
988 elif options.climb_up == 2:
989 # -D, all Default() targets
992 elif options.climb_up == 3:
993 # -U, local SConscript Default() targets
994 target_top = fs.Dir(target_top)
995 def check_dir(x, target_top=target_top):
996 if hasattr(x, 'cwd') and not x.cwd is None:
997 cwd = x.cwd.srcnode()
998 return cwd == target_top
1000 # x doesn't have a cwd, so it's either not a target,
1001 # or not a file, so go ahead and keep it as a default
1002 # target and let the engine sort it out:
1004 d = filter(check_dir, SCons.Script.DEFAULT_TARGETS)
1005 SCons.Script.DEFAULT_TARGETS[:] = d
1009 targets = SCons.Script._Get_Default_Targets(d, fs)
1012 sys.stderr.write("scons: *** No targets specified and no Default() targets found. Stop.\n")
1015 def Entry(x, ltop=lookup_top, ttop=target_top, fs=fs):
1016 if isinstance(x, SCons.Node.Node):
1019 node = SCons.Node.Alias.default_ans.lookup(x)
1021 node = fs.Entry(x, directory=ltop, create=1)
1022 if ttop and not node.is_under(ttop):
1023 if isinstance(node, SCons.Node.FS.Dir) and ttop.is_under(node):
1029 nodes = filter(lambda x: x is not None, map(Entry, targets))
1031 task_class = BuildTask # default action is to build targets
1032 opening_message = "Building targets ..."
1033 closing_message = "done building targets."
1034 if keep_going_on_error:
1035 failure_message = "done building targets (errors occurred during build)."
1037 failure_message = "building terminated because of errors."
1038 if options.question:
1039 task_class = QuestionTask
1041 if ssoptions.get('clean'):
1042 task_class = CleanTask
1043 opening_message = "Cleaning targets ..."
1044 closing_message = "done cleaning targets."
1045 if keep_going_on_error:
1046 closing_message = "done cleaning targets (errors occurred during clean)."
1048 failure_message = "cleaning terminated because of errors."
1049 except AttributeError:
1052 SCons.Environment.CalculatorArgs['max_drift'] = ssoptions.get('max_drift')
1055 def order(dependencies):
1056 """Randomize the dependencies."""
1057 # This is cribbed from the implementation of
1058 # random.shuffle() in Python 2.X.
1060 for i in xrange(len(d)-1, 0, -1):
1061 j = int(random.random() * (i+1))
1062 d[i], d[j] = d[j], d[i]
1065 def order(dependencies):
1066 """Leave the order of dependencies alone."""
1069 progress_display("scons: " + opening_message)
1070 taskmaster = SCons.Taskmaster.Taskmaster(nodes, task_class, order)
1072 nj = ssoptions.get('num_jobs')
1073 jobs = SCons.Job.Jobs(nj, taskmaster)
1074 if nj > 1 and jobs.num_jobs == 1:
1075 msg = "parallel builds are unsupported by this version of Python;\n" + \
1076 "\tignoring -j or num_jobs option.\n"
1077 SCons.Warnings.warn(SCons.Warnings.NoParallelSupportWarning, msg)
1079 if not memory_stats is None: memory_stats.append(SCons.Debug.memory())
1080 if not count_stats is None: count_stats.append(SCons.Debug.fetchLoggedInstances())
1086 progress_display("scons: " + failure_message)
1088 progress_display("scons: " + closing_message)
1089 if not options.noexec:
1090 SCons.SConsign.write()
1092 if not memory_stats is None:
1093 memory_stats.append(SCons.Debug.memory())
1095 'before reading SConscript files',
1096 'after reading SConscript files',
1097 'before building targets',
1098 'after building targets',
1100 for i in xrange(len(when)):
1101 memory_outf.write('Memory %-32s %12d\n' % (when[i]+':', memory_stats[i]))
1103 if not count_stats is None:
1104 count_stats.append(SCons.Debug.fetchLoggedInstances())
1106 for cs in count_stats:
1107 for n in map(lambda t: t[0], cs):
1108 stats_table[n] = [0, 0, 0, 0]
1110 for cs in count_stats:
1112 stats_table[n][i] = c
1114 keys = stats_table.keys()
1116 print "Object counts:"
1117 fmt = " %7s %7s %7s %7s %s"
1118 print fmt % ("pre-", "post-", "pre-", "post-", "")
1119 print fmt % ("read", "read", "build", "build", "Class")
1122 print " %7d %7d %7d %7d %s" % (r[0], r[1], r[2], r[3], k)
1125 SCons.Debug.listLoggedInstances('*')
1126 #SCons.Debug.dumpLoggedInstances('*')
1129 print "Memoizer (memory cache) hits and misses:"
1130 SCons.Memoize.Dump()
1133 all_args = sys.argv[1:]
1135 all_args = string.split(os.environ['SCONSFLAGS']) + all_args
1137 # it's OK if there's no SCONSFLAGS
1139 parser = OptParser()
1141 options, args = parser.parse_args(all_args)
1142 if type(options.debug) == type([]) and "pdb" in options.debug:
1144 pdb.Pdb().runcall(_main, args, parser)
1153 except SystemExit, s:
1156 except KeyboardInterrupt:
1157 print "Build interrupted."
1159 except SyntaxError, e:
1160 _scons_syntax_error(e)
1161 except SCons.Errors.InternalError:
1162 _scons_internal_error()
1163 except SCons.Errors.UserError, e:
1164 _scons_user_error(e)
1166 # An exception here is likely a builtin Python exception Python
1167 # code in an SConscript file. Show them precisely what the
1168 # problem was and where it happened.
1169 SCons.Script._SConscript.SConscript_exception()
1173 total_time = time.time()-SCons.Script.start_time
1174 scons_time = total_time-sconscript_time-command_time
1175 print "Total build time: %f seconds"%total_time
1176 print "Total SConscript file execution time: %f seconds"%sconscript_time
1177 print "Total SCons execution time: %f seconds"%scons_time
1178 print "Total command execution time: %f seconds"%command_time
1180 sys.exit(exit_status)