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
257 exit_status = 0 # exit status, assume success by default
260 num_jobs = 1 # this is modifed by SConscript.SetJobs()
264 def get_all_children(node): return node.all_children()
266 def get_derived_children(node):
267 children = node.all_children(None)
268 return filter(lambda x: x.has_builder(), children)
270 def _scons_syntax_error(e):
271 """Handle syntax errors. Print out a message and show where the error
274 etype, value, tb = sys.exc_info()
275 lines = traceback.format_exception_only(etype, value)
277 sys.stderr.write(line+'\n')
280 def find_deepest_user_frame(tb):
282 Find the deepest stack frame that is not part of SCons.
284 Input is a "pre-processed" stack trace in the form
285 returned by traceback.extract_tb() or traceback.extract_stack()
290 # find the deepest traceback frame that is not part
294 if string.find(filename, os.sep+'SCons'+os.sep) == -1:
298 def _scons_user_error(e):
299 """Handle user errors. Print out a message and a description of the
300 error, along with the line number and routine where it occured.
301 The file and line number will be the deepest stack frame that is
302 not part of SCons itself.
304 etype, value, tb = sys.exc_info()
305 filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_tb(tb))
306 sys.stderr.write("\nscons: *** %s\n" % value)
307 sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine))
310 def _scons_user_warning(e):
311 """Handle user warnings. Print out a message and a description of
312 the warning, along with the line number and routine where it occured.
313 The file and line number will be the deepest stack frame that is
314 not part of SCons itself.
316 etype, value, tb = sys.exc_info()
317 filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_tb(tb))
318 sys.stderr.write("\nscons: warning: %s\n" % e)
319 sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine))
321 def _scons_internal_warning(e):
322 """Slightly different from _scons_user_warning in that we use the
323 *current call stack* rather than sys.exc_info() to get our stack trace.
324 This is used by the warnings framework to print warnings."""
325 filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_stack())
326 sys.stderr.write("\nscons: warning: %s\n" % e[0])
327 sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine))
329 def _scons_internal_error():
330 """Handle all errors but user errors. Print out a message telling
331 the user what to do in this case and print a normal trace.
333 print 'internal error'
334 traceback.print_exc()
337 def _varargs(option, parser):
340 arg = parser.rargs[0]
346 def _setup_warn(arg):
347 """The --warn option. An argument to this option
348 should be of the form <warning-class> or no-<warning-class>.
349 The warning class is munged in order to get an actual class
350 name from the SCons.Warnings module to enable or disable.
351 The supplied <warning-class> is split on hyphens, each element
352 is captialized, then smushed back together. Then the string
353 "SCons.Warnings." is added to the front and "Warning" is added
354 to the back to get the fully qualified class name.
356 For example, --warn=deprecated will enable the
357 SCons.Warnings.DeprecatedWarning class.
359 --warn=no-dependency will disable the
360 SCons.Warnings.DependencyWarning class.
362 As a special case, --warn=all and --warn=no-all
363 will enable or disable (respectively) the base
364 class of all warnings, which is SCons.Warning.Warning."""
366 elems = string.split(string.lower(arg), '-')
372 if len(elems) == 1 and elems[0] == 'all':
373 class_name = "Warning"
377 return "SCons" + s[5:]
379 return string.capitalize(s)
380 class_name = string.join(map(_capitalize, elems), '') + "Warning"
382 clazz = getattr(SCons.Warnings, class_name)
383 except AttributeError:
384 sys.stderr.write("No warning type: '%s'\n" % arg)
387 SCons.Warnings.enableWarningClass(clazz)
389 SCons.Warnings.suppressWarningClass(clazz)
391 def _SConstruct_exists(dirname=''):
392 """This function checks that an SConstruct file exists in a directory.
393 If so, it returns the path of the file. By default, it checks the
397 for file in ['SConstruct', 'Sconstruct', 'sconstruct']:
398 sfile = os.path.join(dirname, file)
399 if os.path.isfile(sfile):
401 if not os.path.isabs(sfile):
402 for rep in repositories:
403 if os.path.isfile(os.path.join(rep, sfile)):
407 def _set_globals(options):
408 global repositories, keep_going_on_error, ignore_errors
409 global print_count, print_dtree
410 global print_explanations, print_includes
411 global print_objects, print_stacktrace, print_stree
412 global print_time, print_tree
413 global memory_outf, memory_stats
415 if options.repository:
416 repositories.extend(options.repository)
417 keep_going_on_error = options.keep_going
420 if options.debug == "count":
422 elif options.debug == "dtree":
424 elif options.debug == "explain":
425 print_explanations = 1
426 elif options.debug == "findlibs":
427 SCons.Scanner.Prog.print_find_libs = "findlibs"
428 elif options.debug == "includes":
430 elif options.debug == "memory":
432 memory_outf = sys.stdout
433 elif options.debug == "objects":
435 elif options.debug == "presub":
436 SCons.Action.print_actions_presub = 1
437 elif options.debug == "stacktrace":
439 elif options.debug == "stree":
441 elif options.debug == "time":
443 elif options.debug == "tree":
445 except AttributeError:
447 ignore_errors = options.ignore_errors
449 def _create_path(plist):
455 path = path + '/' + d
459 class OptParser(OptionParser):
463 parts = ["SCons by Steven Knight et al.:\n"]
465 parts.append("\tscript: v%s.%s, %s, by %s on %s\n" % (__main__.__version__,
468 __main__.__developer__,
469 __main__.__buildsys__))
470 except KeyboardInterrupt:
473 # On win32 there is no scons.py, so there is no __main__.__version__,
474 # hence there is no script version.
476 parts.append("\tengine: v%s.%s, %s, by %s on %s\n" % (SCons.__version__,
481 parts.append("__COPYRIGHT__")
482 OptionParser.__init__(self, version=string.join(parts, ''),
483 usage="usage: scons [OPTION] [TARGET] ...")
485 # options ignored for compatibility
486 def opt_ignore(option, opt, value, parser):
487 sys.stderr.write("Warning: ignoring %s option\n" % opt)
488 self.add_option("-b", "-m", "-S", "-t", "--no-keep-going", "--stop",
489 "--touch", action="callback", callback=opt_ignore,
490 help="Ignored for compatibility.")
492 self.add_option('-c', '--clean', '--remove', action="store_true",
494 help="Remove specified targets and dependencies.")
496 self.add_option('-C', '--directory', type="string", action = "append",
498 help="Change to DIR before doing anything.")
500 self.add_option('--cache-disable', '--no-cache',
501 action="store_true", dest='cache_disable', default=0,
502 help="Do not retrieve built targets from CacheDir.")
504 self.add_option('--cache-force', '--cache-populate',
505 action="store_true", dest='cache_force', default=0,
506 help="Copy already-built targets into the CacheDir.")
508 self.add_option('--cache-show',
509 action="store_true", dest='cache_show', default=0,
510 help="Print build actions for files from CacheDir.")
512 config_options = ["auto", "force" ,"cache"]
514 def opt_config(option, opt, value, parser, c_options=config_options):
515 if value in c_options:
516 parser.values.config = value
518 raise OptionValueError("Warning: %s is not a valid config type" % value)
519 self.add_option('--config', action="callback", type="string",
520 callback=opt_config, nargs=1, dest="config",
521 metavar="MODE", default="auto",
522 help="Controls Configure subsystem: "
523 "%s." % string.join(config_options, ", "))
525 def opt_not_yet(option, opt, value, parser):
526 sys.stderr.write("Warning: the %s option is not yet implemented\n" % opt)
528 self.add_option('-d', action="callback",
529 callback=opt_not_yet,
530 help = "Print file dependency information.")
532 self.add_option('-D', action="store_const", const=2, dest="climb_up",
533 help="Search up directory tree for SConstruct, "
534 "build all Default() targets.")
536 debug_options = ["count", "dtree", "explain", "findlibs",
537 "includes", "memory", "objects",
538 "pdb", "presub", "stacktrace", "stree",
541 def opt_debug(option, opt, value, parser, debug_options=debug_options):
542 if value in debug_options:
543 parser.values.debug = value
545 raise OptionValueError("Warning: %s is not a valid debug type" % value)
546 self.add_option('--debug', action="callback", type="string",
547 callback=opt_debug, nargs=1, dest="debug",
549 help="Print various types of debugging information: "
550 "%s." % string.join(debug_options, ", "))
552 def opt_duplicate(option, opt, value, parser):
553 if not value in SCons.Node.FS.Valid_Duplicates:
554 raise OptionValueError("`%s' is not a valid duplication style." % value)
555 parser.values.duplicate = value
556 # Set the duplicate style right away so it can affect linking
557 # of SConscript files.
558 SCons.Node.FS.set_duplicate(value)
559 self.add_option('--duplicate', action="callback", type="string",
560 callback=opt_duplicate, nargs=1, dest="duplicate",
561 help="Set the preferred duplication methods. Must be one of "
562 + string.join(SCons.Node.FS.Valid_Duplicates, ", "))
564 self.add_option('-f', '--file', '--makefile', '--sconstruct',
565 action="append", nargs=1,
566 help="Read FILE as the top-level SConstruct file.")
568 self.add_option('-h', '--help', action="store_true", default=0,
570 help="Print defined help message, or this one.")
572 self.add_option("-H", "--help-options",
574 help="Print this message and exit.")
576 self.add_option('-i', '--ignore-errors', action="store_true",
577 default=0, dest='ignore_errors',
578 help="Ignore errors from build actions.")
580 self.add_option('-I', '--include-dir', action="append",
581 dest='include_dir', metavar="DIR",
582 help="Search DIR for imported Python modules.")
584 self.add_option('--implicit-cache', action="store_true",
585 dest='implicit_cache',
586 help="Cache implicit dependencies")
588 self.add_option('--implicit-deps-changed', action="store_true",
589 default=0, dest='implicit_deps_changed',
590 help="Ignore cached implicit dependencies.")
591 self.add_option('--implicit-deps-unchanged', action="store_true",
592 default=0, dest='implicit_deps_unchanged',
593 help="Ignore changes in implicit dependencies.")
595 def opt_j(option, opt, value, parser):
597 parser.values.num_jobs = value
598 self.add_option('-j', '--jobs', action="callback", type="int",
599 callback=opt_j, metavar="N",
600 help="Allow N jobs at once.")
602 self.add_option('-k', '--keep-going', action="store_true", default=0,
604 help="Keep going when a target can't be made.")
606 self.add_option('--max-drift', type="int", action="store",
607 dest='max_drift', metavar="N",
608 help="Set maximum system clock drift to N seconds.")
610 self.add_option('-n', '--no-exec', '--just-print', '--dry-run',
611 '--recon', action="store_true", dest='noexec',
612 default=0, help="Don't build; just print commands.")
614 def opt_profile(option, opt, value, parser):
619 profile.run('SCons.Script.Main.main()', value)
620 sys.exit(exit_status)
621 self.add_option('--profile', nargs=1, action="callback",
622 callback=opt_profile, type="string", dest="profile",
624 help="Profile SCons and put results in FILE.")
626 self.add_option('-q', '--question', action="store_true", default=0,
627 help="Don't build; exit status says if up to date.")
629 self.add_option('-Q', dest='no_progress', action="store_true",
631 help="Suppress \"Reading/Building\" progress messages.")
633 self.add_option('--random', dest="random", action="store_true",
634 default=0, help="Build dependencies in random order.")
636 self.add_option('-s', '--silent', '--quiet', action="store_true",
637 default=0, help="Don't print commands.")
639 self.add_option('-u', '--up', '--search-up', action="store_const",
640 dest="climb_up", default=0, const=1,
641 help="Search up directory tree for SConstruct, "
642 "build targets at or below current directory.")
643 self.add_option('-U', action="store_const", dest="climb_up",
645 help="Search up directory tree for SConstruct, "
646 "build Default() targets from local SConscript.")
648 self.add_option("-v", "--version",
650 help="Print the SCons version number and exit.")
652 self.add_option('--warn', '--warning', nargs=1, action="store",
653 metavar="WARNING-SPEC",
654 help="Enable or disable warnings.")
656 self.add_option('-Y', '--repository', nargs=1, action="append",
657 help="Search REPOSITORY for source and target files.")
659 self.add_option('-e', '--environment-overrides', action="callback",
660 callback=opt_not_yet,
661 # help="Environment variables override makefiles."
663 self.add_option('-l', '--load-average', '--max-load', action="callback",
664 callback=opt_not_yet, type="int", dest="load_average",
666 # help="Don't start multiple jobs unless load is below "
670 self.add_option('--list-derived', action="callback",
671 callback=opt_not_yet,
672 # help="Don't build; list files that would be built."
674 self.add_option('--list-actions', action="callback",
675 callback=opt_not_yet,
676 # help="Don't build; list files and build actions."
678 self.add_option('--list-where', action="callback",
679 callback=opt_not_yet,
680 # help="Don't build; list files and where defined."
682 self.add_option('-o', '--old-file', '--assume-old', action="callback",
683 callback=opt_not_yet, type="string", dest="old_file",
684 # help = "Consider FILE to be old; don't rebuild it."
686 self.add_option('--override', action="callback", dest="override",
687 callback=opt_not_yet, type="string",
688 # help="Override variables as specified in FILE."
690 self.add_option('-p', action="callback",
691 callback=opt_not_yet,
692 # help="Print internal environments/objects."
694 self.add_option('-r', '-R', '--no-builtin-rules',
695 '--no-builtin-variables', action="callback",
696 callback=opt_not_yet,
697 # help="Clear default environments and variables."
699 self.add_option('-w', '--print-directory', action="callback",
700 callback=opt_not_yet,
701 # help="Print the current directory."
703 self.add_option('--no-print-directory', action="callback",
704 callback=opt_not_yet,
705 # help="Turn off -w, even if it was turned on implicitly."
707 self.add_option('--write-filenames', action="callback",
708 callback=opt_not_yet, type="string", dest="write_filenames",
709 # help="Write all filenames examined into FILE."
711 self.add_option('-W', '--what-if', '--new-file', '--assume-new',
713 action="callback", callback=opt_not_yet, type="string",
714 # help="Consider FILE to be changed."
716 self.add_option('--warn-undefined-variables', action="callback",
717 callback=opt_not_yet,
718 # help="Warn when an undefined variable is referenced."
721 def parse_args(self, args=None, values=None):
722 opt, arglist = OptionParser.parse_args(self, args, values)
723 if opt.implicit_deps_changed or opt.implicit_deps_unchanged:
724 opt.implicit_cache = 1
727 class SConscriptSettableOptions:
728 """This class wraps an OptParser instance and provides
729 uniform access to options that can be either set on the command
730 line or from a SConscript file. A value specified on the command
731 line always overrides a value set in a SConscript file.
732 Not all command line options are SConscript settable, and the ones
733 that are must be explicitly added to settable dictionary and optionally
734 validated and coerced in the set() method."""
736 def __init__(self, options):
737 self.options = options
739 # This dictionary stores the defaults for all the SConscript
740 # settable options, as well as indicating which options
741 # are SConscript settable.
742 self.settable = {'num_jobs':1,
743 'max_drift':SCons.Sig.default_max_drift,
746 'duplicate':'hard-soft-copy'}
749 if not self.settable.has_key(name):
750 raise SCons.Error.UserError, "This option is not settable from a SConscript file: %s"%name
751 if hasattr(self.options, name) and getattr(self.options, name) is not None:
752 return getattr(self.options, name)
754 return self.settable[name]
756 def set(self, name, value):
757 if not self.settable.has_key(name):
758 raise SCons.Error.UserError, "This option is not settable from a SConscript file: %s"%name
760 if name == 'num_jobs':
766 raise SCons.Errors.UserError, "A positive integer is required: %s"%repr(value)
767 elif name == 'max_drift':
771 raise SCons.Errors.UserError, "An integer is required: %s"%repr(value)
772 elif name == 'duplicate':
776 raise SCons.Errors.UserError, "A string is required: %s"%repr(value)
777 if not value in SCons.Node.FS.Valid_Duplicates:
778 raise SCons.Errors.UserError, "Not a valid duplication style: %s" % value
779 # Set the duplicate stye right away so it can affect linking
780 # of SConscript files.
781 SCons.Node.FS.set_duplicate(value)
783 self.settable[name] = value
786 def _main(args, parser):
788 fs = SCons.Node.FS.default_fs
790 # Enable deprecated warnings by default.
791 SCons.Warnings._warningOut = _scons_internal_warning
792 SCons.Warnings.enableWarningClass(SCons.Warnings.CorruptSConsignWarning)
793 SCons.Warnings.enableWarningClass(SCons.Warnings.DeprecatedWarning)
794 SCons.Warnings.enableWarningClass(SCons.Warnings.DuplicateEnvironmentWarning)
795 SCons.Warnings.enableWarningClass(SCons.Warnings.MissingSConscriptWarning)
796 SCons.Warnings.enableWarningClass(SCons.Warnings.NoParallelSupportWarning)
797 # This is good for newbies, and hopefully most everyone else too.
798 SCons.Warnings.enableWarningClass(SCons.Warnings.MisleadingKeywordsWarning)
801 ssoptions = SConscriptSettableOptions(options)
803 _set_globals(options)
804 SCons.Node.implicit_cache = options.implicit_cache
805 SCons.Node.implicit_deps_changed = options.implicit_deps_changed
806 SCons.Node.implicit_deps_unchanged = options.implicit_deps_unchanged
808 _setup_warn(options.warn)
810 SCons.SConf.dryrun = 1
811 SCons.Action.execute_actions = None
812 CleanTask.execute = CleanTask.show
814 SCons.SConf.dryrun = 1
815 SCons.SConf.SetCacheMode(options.config)
816 SCons.SConf.SetProgressDisplay(progress_display)
818 if options.no_progress or options.silent:
819 progress_display.set_mode(0)
823 SCons.Action.print_actions = None
824 if options.cache_disable:
825 def disable(self): pass
826 fs.CacheDir = disable
827 if options.cache_force:
829 if options.cache_show:
831 if options.directory:
832 cdir = _create_path(options.directory)
836 sys.stderr.write("Could not change directory to %s\n" % cdir)
844 SCons.Script._Add_Arguments(xmit_args)
845 SCons.Script._Add_Targets(targets)
849 target_top = '.' # directory to prepend to targets
850 script_dir = os.getcwd() # location of script
851 while script_dir and not _SConstruct_exists(script_dir):
852 script_dir, last_part = os.path.split(script_dir)
854 target_top = os.path.join(last_part, target_top)
858 display("scons: Entering directory `%s'" % script_dir)
861 raise SCons.Errors.UserError, "No SConstruct file found."
863 fs.set_toplevel_dir(os.getcwd())
867 scripts.extend(options.file)
869 sfile = _SConstruct_exists()
871 scripts.append(sfile)
875 # There's no SConstruct, but they specified -h.
876 # Give them the options usage now, before we fail
877 # trying to read a non-existent SConstruct file.
882 raise SCons.Errors.UserError, "No SConstruct file found."
884 if scripts[0] == "-":
887 d = fs.File(scripts[0]).dir
888 fs.set_SConstruct_dir(d)
891 def __init__(self, file):
893 def write(self, arg):
896 def __getattr__(self, attr):
897 return getattr(self.file, attr)
899 sys.stdout = Unbuffered(sys.stdout)
901 if options.include_dir:
902 sys.path = options.include_dir + sys.path
905 for rep in repositories:
908 if not memory_stats is None: memory_stats.append(SCons.Debug.memory())
910 progress_display("scons: Reading SConscript files ...")
912 start_time = time.time()
914 for script in scripts:
915 SCons.Script._SConscript._SConscript(fs, script)
916 except SCons.Errors.StopError, e:
917 # We had problems reading an SConscript file, such as it
918 # couldn't be copied in to the BuildDir. Since we're just
919 # reading SConscript files and haven't started building
920 # things yet, stop regardless of whether they used -i or -k
923 sys.stderr.write("scons: *** %s Stop.\n" % e)
925 sys.exit(exit_status)
926 global sconscript_time
927 sconscript_time = time.time() - start_time
928 SCons.SConf.CreateConfigHBuilder(SCons.Defaults.DefaultEnvironment())
929 progress_display("scons: done reading SConscript files.")
931 # Tell the Node.FS subsystem that we're all done reading the
932 # SConscript files and calling Repository() and BuildDir() and the
933 # like, so it can go ahead and start memoizing the string values of
935 SCons.Node.FS.save_strings(1)
937 if not memory_stats is None: memory_stats.append(SCons.Debug.memory())
942 help_text = SCons.Script.help_text
943 if help_text is None:
944 # They specified -h, but there was no Help() inside the
945 # SConscript files. Give them the options usage.
946 parser.print_help(sys.stdout)
949 print "Use scons -H for help about command-line options."
952 # Now that we've read the SConscripts we can set the options
953 # that are SConscript settable:
954 SCons.Node.implicit_cache = ssoptions.get('implicit_cache')
955 SCons.Node.FS.set_duplicate(ssoptions.get('duplicate'))
959 # They specified targets on the command line, so if they
960 # used -u, -U or -D, we have to look up targets relative
961 # to the top, but we build whatever they specified.
963 lookup_top = fs.Dir(target_top)
966 # There are no targets specified on the command line,
967 # so if they used -u, -U or -D, we may have to restrict
968 # what actually gets built.
971 if options.climb_up == 1:
972 # -u, local directory and below
973 target_top = fs.Dir(target_top)
974 lookup_top = target_top
975 elif options.climb_up == 2:
976 # -D, all Default() targets
979 elif options.climb_up == 3:
980 # -U, local SConscript Default() targets
981 target_top = fs.Dir(target_top)
982 def check_dir(x, target_top=target_top):
983 if hasattr(x, 'cwd') and not x.cwd is None:
984 cwd = x.cwd.srcnode()
985 return cwd == target_top
987 # x doesn't have a cwd, so it's either not a target,
988 # or not a file, so go ahead and keep it as a default
989 # target and let the engine sort it out:
991 d = filter(check_dir, SCons.Script.DEFAULT_TARGETS)
992 SCons.Script.DEFAULT_TARGETS[:] = d
996 targets = SCons.Script._Get_Default_Targets(d, fs)
999 sys.stderr.write("scons: *** No targets specified and no Default() targets found. Stop.\n")
1002 def Entry(x, ltop=lookup_top, ttop=target_top, fs=fs):
1003 if isinstance(x, SCons.Node.Node):
1006 node = SCons.Node.Alias.default_ans.lookup(x)
1008 node = fs.Entry(x, directory=ltop, create=1)
1009 if ttop and not node.is_under(ttop):
1010 if isinstance(node, SCons.Node.FS.Dir) and ttop.is_under(node):
1016 nodes = filter(lambda x: x is not None, map(Entry, targets))
1018 task_class = BuildTask # default action is to build targets
1019 opening_message = "Building targets ..."
1020 closing_message = "done building targets."
1021 if keep_going_on_error:
1022 failure_message = "done building targets (errors occurred during build)."
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 if keep_going_on_error:
1033 closing_message = "done cleaning targets (errors occurred during clean)."
1035 failure_message = "cleaning terminated because of errors."
1036 except AttributeError:
1039 SCons.Environment.CalculatorArgs['max_drift'] = ssoptions.get('max_drift')
1042 def order(dependencies):
1043 """Randomize the dependencies."""
1044 # This is cribbed from the implementation of
1045 # random.shuffle() in Python 2.X.
1047 for i in xrange(len(d)-1, 0, -1):
1048 j = int(random.random() * (i+1))
1049 d[i], d[j] = d[j], d[i]
1052 def order(dependencies):
1053 """Leave the order of dependencies alone."""
1056 progress_display("scons: " + opening_message)
1057 taskmaster = SCons.Taskmaster.Taskmaster(nodes, task_class, order)
1059 nj = ssoptions.get('num_jobs')
1060 jobs = SCons.Job.Jobs(nj, taskmaster)
1061 if nj > 1 and jobs.num_jobs == 1:
1062 msg = "parallel builds are unsupported by this version of Python;\n" + \
1063 "\tignoring -j or num_jobs option.\n"
1064 SCons.Warnings.warn(SCons.Warnings.NoParallelSupportWarning, msg)
1066 if not memory_stats is None: memory_stats.append(SCons.Debug.memory())
1072 progress_display("scons: " + failure_message)
1074 progress_display("scons: " + closing_message)
1075 if not options.noexec:
1076 SCons.SConsign.write()
1078 if not memory_stats is None:
1079 memory_stats.append(SCons.Debug.memory())
1081 'before SConscript files',
1082 'after SConscript files',
1086 for i in xrange(len(when)):
1087 memory_outf.write('Memory %s: %d\n' % (when[i], memory_stats[i]))
1090 SCons.Debug.countLoggedInstances('*')
1093 SCons.Debug.listLoggedInstances('*')
1094 #SCons.Debug.dumpLoggedInstances('*')
1097 all_args = sys.argv[1:]
1099 all_args = string.split(os.environ['SCONSFLAGS']) + all_args
1101 # it's OK if there's no SCONSFLAGS
1103 parser = OptParser()
1105 options, args = parser.parse_args(all_args)
1106 if options.debug == "pdb":
1108 pdb.Pdb().runcall(_main, args, parser)
1117 except SystemExit, s:
1120 except KeyboardInterrupt:
1121 print "Build interrupted."
1123 except SyntaxError, e:
1124 _scons_syntax_error(e)
1125 except SCons.Errors.InternalError:
1126 _scons_internal_error()
1127 except SCons.Errors.UserError, e:
1128 _scons_user_error(e)
1130 # An exception here is likely a builtin Python exception Python
1131 # code in an SConscript file. Show them precisely what the
1132 # problem was and where it happened.
1133 SCons.Script._SConscript.SConscript_exception()
1137 total_time = time.time()-SCons.Script.start_time
1138 scons_time = total_time-sconscript_time-command_time
1139 print "Total build time: %f seconds"%total_time
1140 print "Total SConscript file execution time: %f seconds"%sconscript_time
1141 print "Total SCons execution time: %f seconds"%scons_time
1142 print "Total command execution time: %f seconds"%command_time
1144 sys.exit(exit_status)