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__"
40 start_time = time.time()
49 # Strip the script directory from sys.path() so on case-insensitive
50 # (WIN32) systems Python doesn't think that the "scons" script is the
51 # "SCons" package. Replace it with our own version directory so, if
52 # if they're there, we pick up the right version of the build engine
54 #sys.path = [os.path.join(sys.prefix,
56 # 'scons-%d' % SCons.__version__)] + sys.path[1:]
60 import SCons.Environment
65 from SCons.Optik import OptionParser, SUPPRESS_HELP, OptionValueError
66 import SCons.Script.SConscript
68 import SCons.Taskmaster
76 except AttributeError:
79 for i in xrange(len(l1)):
80 result.append((l1[i], l2[i]))
85 display = SCons.Util.display
86 progress_display = SCons.Util.DisplayEngine()
90 class BuildTask(SCons.Taskmaster.Task):
91 """An SCons build task."""
92 def display(self, message):
93 display('scons: ' + message)
96 target = self.targets[0]
97 if target.get_state() == SCons.Node.up_to_date:
98 if self.top and target.has_builder():
99 display("scons: `%s' is up to date." % str(self.node))
100 elif target.has_builder() and not hasattr(target.builder, 'status'):
102 start_time = time.time()
103 SCons.Taskmaster.Task.execute(self)
105 finish_time = time.time()
107 command_time = command_time+finish_time-start_time
108 print "Command execution time: %f seconds"%(finish_time-start_time)
110 def do_failed(self, status=2):
113 SCons.Taskmaster.Task.executed(self)
114 elif keep_going_on_error:
115 SCons.Taskmaster.Task.fail_continue(self)
118 SCons.Taskmaster.Task.fail_stop(self)
123 if self.top and not t.has_builder() and not t.side_effect:
125 sys.stderr.write("scons: *** Do not know how to make target `%s'." % t)
126 if not keep_going_on_error:
127 sys.stderr.write(" Stop.")
128 sys.stderr.write("\n")
131 print "scons: Nothing to be done for `%s'." % t
132 SCons.Taskmaster.Task.executed(self)
134 SCons.Taskmaster.Task.executed(self)
136 # print the tree here instead of in execute() because
137 # this method is serialized, but execute isn't:
138 if print_tree and self.top:
140 print SCons.Util.render_tree(self.targets[0], get_all_children)
141 if print_dtree and self.top:
143 print SCons.Util.render_tree(self.targets[0], get_derived_children)
144 if print_includes and self.top:
146 tree = t.render_include_tree()
152 # Handle the failure of a build task. The primary purpose here
153 # is to display the various types of Errors and Exceptions
159 if t is SCons.Errors.TaskmasterException:
160 # The Taskmaster received an Error or Exception while trying
161 # to process or build the Nodes and dependencies, which it
162 # wrapped up for us in the object recorded as the value of
163 # the Exception, so process the wrapped info instead of the
164 # TaskmasterException itself.
169 if t == SCons.Errors.BuildError:
170 sys.stderr.write("scons: *** [%s] %s\n" % (e.node, e.errstr))
171 if e.errstr == 'Exception':
172 traceback.print_exception(e.args[0], e.args[1], e.args[2])
173 elif t == SCons.Errors.ExplicitExit:
175 sys.stderr.write("scons: *** [%s] Explicit exit, status %s\n" % (e.node, e.status))
180 if t == SCons.Errors.StopError and not keep_going_on_error:
182 sys.stderr.write("scons: *** %s\n" % s)
185 sys.stderr.write("scons: internal stack trace:\n")
186 traceback.print_tb(tb, file=sys.stderr)
188 self.do_failed(status)
190 def make_ready(self):
191 """Make a task ready for execution"""
192 SCons.Taskmaster.Task.make_ready(self)
193 if self.out_of_date and print_explanations:
194 explanation = self.out_of_date[0].explain()
196 sys.stdout.write("scons: " + explanation)
198 class CleanTask(SCons.Taskmaster.Task):
199 """An SCons clean task."""
201 if (self.targets[0].has_builder() or self.targets[0].side_effect) \
202 and not os.path.isdir(str(self.targets[0])):
203 display("Removed " + str(self.targets[0]))
204 if SCons.Environment.CleanTargets.has_key(self.targets[0]):
205 files = SCons.Environment.CleanTargets[self.targets[0]]
207 SCons.Util.fs_delete(str(f), 0)
210 if self.targets[0].has_builder() or self.targets[0].side_effect:
211 for t in self.targets:
215 print "scons: Could not remove '%s':" % str(t), e.strerror
218 display("Removed " + str(t))
219 if SCons.Environment.CleanTargets.has_key(self.targets[0]):
220 files = SCons.Environment.CleanTargets[self.targets[0]]
222 SCons.Util.fs_delete(str(f))
226 # Have the taskmaster arrange to "execute" all of the targets, because
227 # we'll figure out ourselves (in remove() or show() above) whether
228 # anything really needs to be done.
229 make_ready = SCons.Taskmaster.Task.make_ready_all
234 class QuestionTask(SCons.Taskmaster.Task):
235 """An SCons task for the -q (question) option."""
240 if self.targets[0].get_state() != SCons.Node.up_to_date:
250 keep_going_on_error = 0
253 print_explanations = 0
262 exit_status = 0 # exit status, assume success by default
265 num_jobs = 1 # this is modifed by SConscript.SetJobs()
267 # Exceptions for this module
268 class PrintHelp(Exception):
273 def get_all_children(node): return node.all_children(None)
275 def get_derived_children(node):
276 children = node.all_children(None)
277 return filter(lambda x: x.has_builder(), children)
279 def _scons_syntax_error(e):
280 """Handle syntax errors. Print out a message and show where the error
283 etype, value, tb = sys.exc_info()
284 lines = traceback.format_exception_only(etype, value)
286 sys.stderr.write(line+'\n')
289 def find_deepest_user_frame(tb):
291 Find the deepest stack frame that is not part of SCons.
293 Input is a "pre-processed" stack trace in the form
294 returned by traceback.extract_tb() or traceback.extract_stack()
299 # find the deepest traceback frame that is not part
303 if string.find(filename, os.sep+'SCons'+os.sep) == -1:
307 def _scons_user_error(e):
308 """Handle user errors. Print out a message and a description of the
309 error, along with the line number and routine where it occured.
310 The file and line number will be the deepest stack frame that is
311 not part of SCons itself.
313 etype, value, tb = sys.exc_info()
314 filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_tb(tb))
315 sys.stderr.write("\nscons: *** %s\n" % value)
316 sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine))
319 def _scons_user_warning(e):
320 """Handle user warnings. Print out a message and a description of
321 the warning, along with the line number and routine where it occured.
322 The file and line number will be the deepest stack frame that is
323 not part of SCons itself.
325 etype, value, tb = sys.exc_info()
326 filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_tb(tb))
327 sys.stderr.write("\nscons: warning: %s\n" % e)
328 sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine))
330 def _scons_internal_warning(e):
331 """Slightly different from _scons_user_warning in that we use the
332 *current call stack* rather than sys.exc_info() to get our stack trace.
333 This is used by the warnings framework to print warnings."""
334 filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_stack())
335 sys.stderr.write("\nscons: warning: %s\n" % e[0])
336 sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine))
338 def _scons_internal_error():
339 """Handle all errors but user errors. Print out a message telling
340 the user what to do in this case and print a normal trace.
342 print 'internal error'
343 traceback.print_exc()
346 def _varargs(option, parser):
349 arg = parser.rargs[0]
355 def _setup_warn(arg):
356 """The --warn option. An argument to this option
357 should be of the form <warning-class> or no-<warning-class>.
358 The warning class is munged in order to get an actual class
359 name from the SCons.Warnings module to enable or disable.
360 The supplied <warning-class> is split on hyphens, each element
361 is captialized, then smushed back together. Then the string
362 "SCons.Warnings." is added to the front and "Warning" is added
363 to the back to get the fully qualified class name.
365 For example, --warn=deprecated will enable the
366 SCons.Warnings.DeprecatedWarning class.
368 --warn=no-dependency will disable the
369 SCons.Warnings.DependencyWarning class.
371 As a special case, --warn=all and --warn=no-all
372 will enable or disable (respectively) the base
373 class of all warnings, which is SCons.Warning.Warning."""
375 elems = string.split(string.lower(arg), '-')
381 if len(elems) == 1 and elems[0] == 'all':
382 class_name = "Warning"
386 return "SCons" + s[5:]
388 return string.capitalize(s)
389 class_name = string.join(map(_capitalize, elems), '') + "Warning"
391 clazz = getattr(SCons.Warnings, class_name)
392 except AttributeError:
393 sys.stderr.write("No warning type: '%s'\n" % arg)
396 SCons.Warnings.enableWarningClass(clazz)
398 SCons.Warnings.suppressWarningClass(clazz)
400 def _SConstruct_exists(dirname=''):
401 """This function checks that an SConstruct file exists in a directory.
402 If so, it returns the path of the file. By default, it checks the
406 for file in ['SConstruct', 'Sconstruct', 'sconstruct']:
407 sfile = os.path.join(dirname, file)
408 if os.path.isfile(sfile):
410 if not os.path.isabs(sfile):
411 for rep in repositories:
412 if os.path.isfile(os.path.join(rep, sfile)):
416 def _set_globals(options):
417 global repositories, keep_going_on_error, ignore_errors
418 global print_count, print_dtree
419 global print_explanations, print_includes
420 global print_objects, print_time, print_tree
421 global memory_outf, memory_stats
423 if options.repository:
424 repositories.extend(options.repository)
425 keep_going_on_error = options.keep_going
428 if options.debug == "count":
430 elif options.debug == "dtree":
432 elif options.debug == "explain":
433 print_explanations = 1
434 elif options.debug == "includes":
436 elif options.debug == "memory":
438 memory_outf = sys.stdout
439 elif options.debug == "objects":
441 elif options.debug == "presub":
442 SCons.Action.print_actions_presub = 1
443 elif options.debug == "time":
445 elif options.debug == "tree":
447 except AttributeError:
449 ignore_errors = options.ignore_errors
451 def _create_path(plist):
457 path = path + '/' + d
461 class OptParser(OptionParser):
465 parts = ["SCons by Steven Knight et al.:\n"]
467 parts.append("\tscript: v%s.%s, %s, by %s on %s\n" % (__main__.__version__,
470 __main__.__developer__,
471 __main__.__buildsys__))
472 except KeyboardInterrupt:
475 # On win32 there is no scons.py, so there is no __main__.__version__,
476 # hence there is no script version.
478 parts.append("\tengine: v%s.%s, %s, by %s on %s\n" % (SCons.__version__,
483 parts.append("__COPYRIGHT__")
484 OptionParser.__init__(self, version=string.join(parts, ''),
485 usage="usage: scons [OPTION] [TARGET] ...")
487 # options ignored for compatibility
488 def opt_ignore(option, opt, value, parser):
489 sys.stderr.write("Warning: ignoring %s option\n" % opt)
490 self.add_option("-b", "-m", "-S", "-t", "--no-keep-going", "--stop",
491 "--touch", action="callback", callback=opt_ignore,
492 help="Ignored for compatibility.")
494 self.add_option('-c', '--clean', '--remove', action="store_true",
496 help="Remove specified targets and dependencies.")
498 self.add_option('-C', '--directory', type="string", action = "append",
500 help="Change to DIR before doing anything.")
502 self.add_option('--cache-disable', '--no-cache',
503 action="store_true", dest='cache_disable', default=0,
504 help="Do not retrieve built targets from CacheDir.")
506 self.add_option('--cache-force', '--cache-populate',
507 action="store_true", dest='cache_force', default=0,
508 help="Copy already-built targets into the CacheDir.")
510 self.add_option('--cache-show',
511 action="store_true", dest='cache_show', default=0,
512 help="Print build actions for files from CacheDir.")
514 def opt_not_yet(option, opt, value, parser):
515 sys.stderr.write("Warning: the %s option is not yet implemented\n" % opt)
517 self.add_option('-d', action="callback",
518 callback=opt_not_yet,
519 help = "Print file dependency information.")
521 self.add_option('-D', action="store_const", const=2, dest="climb_up",
522 help="Search up directory tree for SConstruct, "
523 "build all Default() targets.")
525 debug_options = ["count", "dtree", "explain",
526 "includes", "memory", "objects",
527 "pdb", "presub", "time", "tree"]
529 def opt_debug(option, opt, value, parser, debug_options=debug_options):
530 if value in debug_options:
531 parser.values.debug = value
533 raise OptionValueError("Warning: %s is not a valid debug type" % value)
534 self.add_option('--debug', action="callback", type="string",
535 callback=opt_debug, nargs=1, dest="debug",
537 help="Print various types of debugging information: "
538 "%s." % string.join(debug_options, ", "))
540 def opt_duplicate(option, opt, value, parser):
541 if not value in SCons.Node.FS.Valid_Duplicates:
542 raise OptionValueError("`%s' is not a valid duplication style." % value)
543 parser.values.duplicate = value
544 # Set the duplicate style right away so it can affect linking
545 # of SConscript files.
546 SCons.Node.FS.set_duplicate(value)
547 self.add_option('--duplicate', action="callback", type="string",
548 callback=opt_duplicate, nargs=1, dest="duplicate",
549 help="Set the preferred duplication methods. Must be one of "
550 + string.join(SCons.Node.FS.Valid_Duplicates, ", "))
552 self.add_option('-f', '--file', '--makefile', '--sconstruct',
553 action="append", nargs=1,
554 help="Read FILE as the top-level SConstruct file.")
556 self.add_option('-h', '--help', action="store_true", default=0,
558 help="Print defined help message, or this one.")
560 self.add_option("-H", "--help-options",
562 help="Print this message and exit.")
564 self.add_option('-i', '--ignore-errors', action="store_true",
565 default=0, dest='ignore_errors',
566 help="Ignore errors from build actions.")
568 self.add_option('-I', '--include-dir', action="append",
569 dest='include_dir', metavar="DIR",
570 help="Search DIR for imported Python modules.")
572 self.add_option('--implicit-cache', action="store_true",
573 dest='implicit_cache',
574 help="Cache implicit dependencies")
576 self.add_option('--implicit-deps-changed', action="store_true",
577 default=0, dest='implicit_deps_changed',
578 help="Ignore cached implicit dependencies.")
579 self.add_option('--implicit-deps-unchanged', action="store_true",
580 default=0, dest='implicit_deps_unchanged',
581 help="Ignore changes in implicit dependencies.")
583 def opt_j(option, opt, value, parser):
585 parser.values.num_jobs = value
586 self.add_option('-j', '--jobs', action="callback", type="int",
587 callback=opt_j, metavar="N",
588 help="Allow N jobs at once.")
590 self.add_option('-k', '--keep-going', action="store_true", default=0,
592 help="Keep going when a target can't be made.")
594 self.add_option('--max-drift', type="int", action="store",
595 dest='max_drift', metavar="N",
596 help="Set maximum system clock drift to N seconds.")
598 self.add_option('-n', '--no-exec', '--just-print', '--dry-run',
599 '--recon', action="store_true", dest='noexec',
600 default=0, help="Don't build; just print commands.")
602 def opt_profile(option, opt, value, parser):
607 profile.run('SCons.Script.main()', value)
608 sys.exit(exit_status)
609 self.add_option('--profile', nargs=1, action="callback",
610 callback=opt_profile, type="string", dest="profile",
612 help="Profile SCons and put results in FILE.")
614 self.add_option('-q', '--question', action="store_true", default=0,
615 help="Don't build; exit status says if up to date.")
617 self.add_option('-Q', dest='no_progress', action="store_true",
619 help="Suppress \"Reading/Building\" progress messages.")
621 self.add_option('--random', dest="random", action="store_true",
622 default=0, help="Build dependencies in random order.")
624 self.add_option('-s', '--silent', '--quiet', action="store_true",
625 default=0, help="Don't print commands.")
627 self.add_option('--save-explain-info', type="int", action="store",
628 dest='save_explain_info', metavar='0|1',
629 help="(Don't) save --debug=explain information")
631 self.add_option('-u', '--up', '--search-up', action="store_const",
632 dest="climb_up", default=0, const=1,
633 help="Search up directory tree for SConstruct, "
634 "build targets at or below current directory.")
635 self.add_option('-U', action="store_const", dest="climb_up",
637 help="Search up directory tree for SConstruct, "
638 "build Default() targets from local SConscript.")
640 self.add_option("-v", "--version",
642 help="Print the SCons version number and exit.")
644 self.add_option('--warn', '--warning', nargs=1, action="store",
645 metavar="WARNING-SPEC",
646 help="Enable or disable warnings.")
648 self.add_option('-Y', '--repository', nargs=1, action="append",
649 help="Search REPOSITORY for source and target files.")
651 self.add_option('-e', '--environment-overrides', action="callback",
652 callback=opt_not_yet,
653 # help="Environment variables override makefiles."
655 self.add_option('-l', '--load-average', '--max-load', action="callback",
656 callback=opt_not_yet, type="int", dest="load_average",
658 # help="Don't start multiple jobs unless load is below "
662 self.add_option('--list-derived', action="callback",
663 callback=opt_not_yet,
664 # help="Don't build; list files that would be built."
666 self.add_option('--list-actions', action="callback",
667 callback=opt_not_yet,
668 # help="Don't build; list files and build actions."
670 self.add_option('--list-where', action="callback",
671 callback=opt_not_yet,
672 # help="Don't build; list files and where defined."
674 self.add_option('-o', '--old-file', '--assume-old', action="callback",
675 callback=opt_not_yet, type="string", dest="old_file",
676 # help = "Consider FILE to be old; don't rebuild it."
678 self.add_option('--override', action="callback", dest="override",
679 callback=opt_not_yet, type="string",
680 # help="Override variables as specified in FILE."
682 self.add_option('-p', action="callback",
683 callback=opt_not_yet,
684 # help="Print internal environments/objects."
686 self.add_option('-r', '-R', '--no-builtin-rules',
687 '--no-builtin-variables', action="callback",
688 callback=opt_not_yet,
689 # help="Clear default environments and variables."
691 self.add_option('-w', '--print-directory', action="callback",
692 callback=opt_not_yet,
693 # help="Print the current directory."
695 self.add_option('--no-print-directory', action="callback",
696 callback=opt_not_yet,
697 # help="Turn off -w, even if it was turned on implicitly."
699 self.add_option('--write-filenames', action="callback",
700 callback=opt_not_yet, type="string", dest="write_filenames",
701 # help="Write all filenames examined into FILE."
703 self.add_option('-W', '--what-if', '--new-file', '--assume-new',
705 action="callback", callback=opt_not_yet, type="string",
706 # help="Consider FILE to be changed."
708 self.add_option('--warn-undefined-variables', action="callback",
709 callback=opt_not_yet,
710 # help="Warn when an undefined variable is referenced."
713 def parse_args(self, args=None, values=None):
714 opt, arglist = OptionParser.parse_args(self, args, values)
715 if opt.implicit_deps_changed or opt.implicit_deps_unchanged:
716 opt.implicit_cache = 1
719 class SConscriptSettableOptions:
720 """This class wraps an OptParser instance and provides
721 uniform access to options that can be either set on the command
722 line or from a SConscript file. A value specified on the command
723 line always overrides a value set in a SConscript file.
724 Not all command line options are SConscript settable, and the ones
725 that are must be explicitly added to settable dictionary and optionally
726 validated and coerced in the set() method."""
728 def __init__(self, options):
729 self.options = options
731 # This dictionary stores the defaults for all the SConscript
732 # settable options, as well as indicating which options
733 # are SConscript settable.
734 self.settable = {'num_jobs':1,
735 'max_drift':SCons.Sig.default_max_drift,
738 'duplicate':'hard-soft-copy',
739 'save_explain_info':1}
742 if not self.settable.has_key(name):
743 raise SCons.Error.UserError, "This option is not settable from a SConscript file: %s"%name
744 if hasattr(self.options, name) and getattr(self.options, name) is not None:
745 return getattr(self.options, name)
747 return self.settable[name]
749 def set(self, name, value):
750 if not self.settable.has_key(name):
751 raise SCons.Error.UserError, "This option is not settable from a SConscript file: %s"%name
753 if name == 'num_jobs':
759 raise SCons.Errors.UserError, "A positive integer is required: %s"%repr(value)
760 elif name == 'max_drift':
764 raise SCons.Errors.UserError, "An integer is required: %s"%repr(value)
765 elif name == 'duplicate':
769 raise SCons.Errors.UserError, "A string is required: %s"%repr(value)
770 if not value in SCons.Node.FS.Valid_Duplicates:
771 raise SCons.Errors.UserError, "Not a valid duplication style: %s" % value
772 # Set the duplicate stye right away so it can affect linking
773 # of SConscript files.
774 SCons.Node.FS.set_duplicate(value)
776 self.settable[name] = value
779 def _main(args, parser):
781 fs = SCons.Node.FS.default_fs
783 # Enable deprecated warnings by default.
784 SCons.Warnings._warningOut = _scons_internal_warning
785 SCons.Warnings.enableWarningClass(SCons.Warnings.CorruptSConsignWarning)
786 SCons.Warnings.enableWarningClass(SCons.Warnings.DeprecatedWarning)
787 SCons.Warnings.enableWarningClass(SCons.Warnings.DuplicateEnvironmentWarning)
788 SCons.Warnings.enableWarningClass(SCons.Warnings.MissingSConscriptWarning)
789 SCons.Warnings.enableWarningClass(SCons.Warnings.NoParallelSupportWarning)
790 # This is good for newbies, and hopefully most everyone else too.
791 SCons.Warnings.enableWarningClass(SCons.Warnings.MisleadingKeywordsWarning)
794 ssoptions = SConscriptSettableOptions(options)
797 def raisePrintHelp(text):
798 raise PrintHelp, text
799 SCons.Script.SConscript.HelpFunction = raisePrintHelp
801 _set_globals(options)
802 SCons.Node.implicit_cache = options.implicit_cache
803 SCons.Node.implicit_deps_changed = options.implicit_deps_changed
804 SCons.Node.implicit_deps_unchanged = options.implicit_deps_unchanged
806 _setup_warn(options.warn)
808 SCons.SConf.dryrun = 1
809 SCons.Action.execute_actions = None
810 CleanTask.execute = CleanTask.show
812 SCons.SConf.dryrun = 1
814 if options.no_progress or options.silent:
815 progress_display.set_mode(0)
819 SCons.Action.print_actions = None
820 if options.cache_disable:
821 def disable(self): pass
822 fs.CacheDir = disable
823 if options.cache_force:
825 if options.cache_show:
827 if options.directory:
828 cdir = _create_path(options.directory)
832 sys.stderr.write("Could not change directory to %s\n" % cdir)
840 SCons.Script.SConscript._scons_add_args(xmit_args)
841 SCons.Script.SConscript._scons_add_targets(targets)
845 target_top = '.' # directory to prepend to targets
846 script_dir = os.getcwd() # location of script
847 while script_dir and not _SConstruct_exists(script_dir):
848 script_dir, last_part = os.path.split(script_dir)
850 target_top = os.path.join(last_part, target_top)
854 display("scons: Entering directory `%s'" % script_dir)
857 raise SCons.Errors.UserError, "No SConstruct file found."
859 fs.set_toplevel_dir(os.getcwd())
863 scripts.extend(options.file)
865 sfile = _SConstruct_exists()
867 scripts.append(sfile)
871 # There's no SConstruct, but they specified -h.
872 # Give them the options usage now, before we fail
873 # trying to read a non-existent SConstruct file.
876 SCons.Script.SConscript.print_help = 1
879 raise SCons.Errors.UserError, "No SConstruct file found."
881 if scripts[0] == "-":
884 d = fs.File(scripts[0]).dir
885 fs.set_SConstruct_dir(d)
888 def __init__(self, file):
890 def write(self, arg):
893 def __getattr__(self, attr):
894 return getattr(self.file, attr)
896 sys.stdout = Unbuffered(sys.stdout)
898 if options.include_dir:
899 sys.path = options.include_dir + sys.path
902 for rep in repositories:
905 if not memory_stats is None: memory_stats.append(SCons.Debug.memory())
907 progress_display("scons: Reading SConscript files ...")
909 start_time = time.time()
911 for script in scripts:
912 SCons.Script.SConscript._SConscript(fs, script)
913 except SCons.Errors.StopError, e:
914 # We had problems reading an SConscript file, such as it
915 # couldn't be copied in to the BuildDir. Since we're just
916 # reading SConscript files and haven't started building
917 # things yet, stop regardless of whether they used -i or -k
918 # or anything else, but don't say "Stop." on the message.
920 sys.stderr.write("scons: *** %s\n" % e)
922 sys.exit(exit_status)
923 global sconscript_time
924 sconscript_time = time.time() - start_time
925 except PrintHelp, text:
926 progress_display("scons: done reading SConscript files.")
928 print "Use scons -H for help about command-line options."
930 progress_display("scons: done reading SConscript files.")
932 # Tell the Node.FS subsystem that we're all done reading the
933 # SConscript files and calling Repository() and BuildDir() and the
934 # like, so it can go ahead and start memoizing the string values of
936 SCons.Node.FS.save_strings(1)
938 if not memory_stats is None: memory_stats.append(SCons.Debug.memory())
943 # They specified -h, but there was no Help() inside the
944 # SConscript files. Give them the options usage.
945 parser.print_help(sys.stdout)
948 # Now that we've read the SConscripts we can set the options
949 # that are SConscript settable:
950 SCons.Node.implicit_cache = ssoptions.get('implicit_cache')
951 SCons.Node.FS.set_duplicate(ssoptions.get('duplicate'))
952 SCons.Node.Save_Explain_Info = ssoptions.get('save_explain_info') or print_explanations
956 # They specified targets on the command line, so if they
957 # used -u, -U or -D, we have to look up targets relative
958 # to the top, but we build whatever they specified.
960 lookup_top = fs.Dir(target_top)
963 # There are no targets specified on the command line,
964 # so if they used -u, -U or -D, we may have to restrict
965 # what actually gets built.
968 if options.climb_up == 1:
969 # -u, local directory and below
970 target_top = fs.Dir(target_top)
971 lookup_top = target_top
972 elif options.climb_up == 2:
973 # -D, all Default() targets
976 elif options.climb_up == 3:
977 # -U, local SConscript Default() targets
978 target_top = fs.Dir(target_top)
979 def check_dir(x, target_top=target_top):
980 if hasattr(x, 'cwd') and not x.cwd is None:
981 cwd = x.cwd.srcnode()
982 return cwd == target_top
984 # x doesn't have a cwd, so it's either not a target,
985 # or not a file, so go ahead and keep it as a default
986 # target and let the engine sort it out:
988 d = filter(check_dir, SCons.Script.SConscript.DefaultTargets)
989 SCons.Script.SConscript.DefaultTargets[:] = d
993 if SCons.Script.SConscript.DefaultCalled:
994 targets = SCons.Script.SConscript.DefaultTargets
1002 sys.stderr.write("scons: *** No targets specified and no Default() targets found. Stop.\n")
1005 def Entry(x, ltop=lookup_top, ttop=target_top, fs=fs):
1006 if isinstance(x, SCons.Node.Node):
1009 node = SCons.Node.Alias.default_ans.lookup(x)
1011 node = fs.Entry(x, directory=ltop, create=1)
1012 if ttop and not node.is_under(ttop):
1013 if isinstance(node, SCons.Node.FS.Dir) and ttop.is_under(node):
1019 nodes = filter(lambda x: x is not None, map(Entry, targets))
1021 task_class = BuildTask # default action is to build targets
1022 opening_message = "Building targets ..."
1023 closing_message = "done building targets."
1024 failure_message = "building terminated because of errors."
1025 if options.question:
1026 task_class = QuestionTask
1028 if ssoptions.get('clean'):
1029 task_class = CleanTask
1030 opening_message = "Cleaning targets ..."
1031 closing_message = "done cleaning targets."
1032 failure_message = "cleaning terminated because of errors."
1033 except AttributeError:
1036 SCons.Environment.CalculatorArgs['max_drift'] = ssoptions.get('max_drift')
1039 def order(dependencies):
1040 """Randomize the dependencies."""
1041 # This is cribbed from the implementation of
1042 # random.shuffle() in Python 2.X.
1044 for i in xrange(len(d)-1, 0, -1):
1045 j = int(random.random() * (i+1))
1046 d[i], d[j] = d[j], d[i]
1049 def order(dependencies):
1050 """Leave the order of dependencies alone."""
1053 progress_display("scons: " + opening_message)
1054 taskmaster = SCons.Taskmaster.Taskmaster(nodes, task_class, order)
1056 nj = ssoptions.get('num_jobs')
1057 jobs = SCons.Job.Jobs(nj, taskmaster)
1058 if nj > 1 and jobs.num_jobs == 1:
1059 msg = "parallel builds are unsupported by this version of Python;\n" + \
1060 "\tignoring -j or num_jobs option.\n"
1061 SCons.Warnings.warn(SCons.Warnings.NoParallelSupportWarning, msg)
1063 if not memory_stats is None: memory_stats.append(SCons.Debug.memory())
1069 progress_display("scons: " + failure_message)
1071 progress_display("scons: " + closing_message)
1072 if not options.noexec:
1073 SCons.SConsign.write()
1075 if not memory_stats is None:
1076 memory_stats.append(SCons.Debug.memory())
1078 'before SConscript files',
1079 'after SConscript files',
1083 for i in xrange(len(when)):
1084 memory_outf.write('Memory %s: %d\n' % (when[i], memory_stats[i]))
1087 SCons.Debug.countLoggedInstances('*')
1090 SCons.Debug.listLoggedInstances('*')
1091 #SCons.Debug.dumpLoggedInstances('*')
1094 all_args = sys.argv[1:]
1096 all_args = string.split(os.environ['SCONSFLAGS']) + all_args
1098 # it's OK if there's no SCONSFLAGS
1100 parser = OptParser()
1102 options, args = parser.parse_args(all_args)
1103 if options.debug == "pdb":
1105 pdb.Pdb().runcall(_main, args, parser)
1114 except SystemExit, s:
1117 except KeyboardInterrupt:
1118 print "Build interrupted."
1120 except SyntaxError, e:
1121 _scons_syntax_error(e)
1122 except SCons.Errors.InternalError:
1123 _scons_internal_error()
1124 except SCons.Errors.UserError, e:
1125 _scons_user_error(e)
1126 except SCons.Errors.ConfigureDryRunError, e:
1127 _scons_configure_dryrun_error(e)
1129 # An exception here is likely a builtin Python exception Python
1130 # code in an SConscript file. Show them precisely what the
1131 # problem was and where it happened.
1132 SCons.Script.SConscript.SConscript_exception()
1136 total_time = time.time()-start_time
1137 scons_time = total_time-sconscript_time-command_time
1138 print "Total build time: %f seconds"%total_time
1139 print "Total SConscript file execution time: %f seconds"%sconscript_time
1140 print "Total SCons execution time: %f seconds"%scons_time
1141 print "Total command execution time: %f seconds"%command_time
1143 sys.exit(exit_status)