1 """SCons.Script
2
3 This file implements the main() function used by the scons script.
4
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,
10 it goes here.
11 """
12
13 from __future__ import print_function
14
15
16 unsupported_python_version = (2, 6, 0)
17 deprecated_python_version = (2, 7, 0)
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41 __revision__ = "src/engine/SCons/Script/Main.py 3a41ed6b288cee8d085373ad7fa02894e1903864 2019-01-23 17:30:35 bdeegan"
42
43
44 import SCons.compat
45
46 import os
47 import sys
48 import time
49 import traceback
50 import sysconfig
51 import platform
52
53 import SCons.CacheDir
54 import SCons.Debug
55 import SCons.Defaults
56 import SCons.Environment
57 import SCons.Errors
58 import SCons.Job
59 import SCons.Node
60 import SCons.Node.FS
61 import SCons.Platform
62 import SCons.Platform.virtualenv
63 import SCons.SConf
64 import SCons.Script
65 import SCons.Taskmaster
66 import SCons.Util
67 import SCons.Warnings
68
69 import SCons.Script.Interactive
70
71
81
82
84
85
86
87 sys.stderr = sys.__stderr__
88 sys.stdout = sys.__stdout__
89
92
93 display = SCons.Util.display
94 progress_display = SCons.Util.DisplayEngine()
95
96 first_command_start = None
97 last_command_end = None
98
99
101 prev = ''
102 count = 0
103 target_string = '$TARGET'
104
105 - def __init__(self, obj, interval=1, file=None, overwrite=False):
106 if file is None:
107 file = sys.stdout
108
109 self.obj = obj
110 self.file = file
111 self.interval = interval
112 self.overwrite = overwrite
113
114 if callable(obj):
115 self.func = obj
116 elif SCons.Util.is_List(obj):
117 self.func = self.spinner
118 elif obj.find(self.target_string) != -1:
119 self.func = self.replace_string
120 else:
121 self.func = self.string
122
127
129 if self.prev:
130 length = len(self.prev)
131 if self.prev[-1] in ('\n', '\r'):
132 length = length - 1
133 self.write(' ' * length + '\r')
134 self.prev = ''
135
137 self.write(self.obj[self.count % len(self.obj)])
138
141
144
151
152 ProgressObject = SCons.Util.Null()
153
157
158
159
160
161 _BuildFailures = []
162
163
166
167
168 -class BuildTask(SCons.Taskmaster.OutOfDateTask):
169 """An SCons build task."""
170 progress = ProgressObject
171
174
178
185
200
215
240
242
243
244
245 exc_info = self.exc_info()
246 try:
247 t, e, tb = exc_info
248 except ValueError:
249 t, e = exc_info
250 tb = None
251
252 if t is None:
253
254
255 try:
256 t, e, tb = sys.exc_info()[:]
257 except ValueError:
258 t, e = exc_info
259 tb = None
260
261
262
263 if e is None:
264 e = t
265
266 buildError = SCons.Errors.convert_to_BuildError(e)
267 if not buildError.node:
268 buildError.node = self.node
269
270 node = buildError.node
271 if not SCons.Util.is_List(node):
272 node = [ node ]
273 nodename = ', '.join(map(str, node))
274
275 errfmt = "scons: *** [%s] %s\n"
276 sys.stderr.write(errfmt % (nodename, buildError))
277
278 if (buildError.exc_info[2] and buildError.exc_info[1] and
279 not isinstance(
280 buildError.exc_info[1],
281 (EnvironmentError, SCons.Errors.StopError,
282 SCons.Errors.UserError))):
283 type, value, trace = buildError.exc_info
284 if tb and print_stacktrace:
285 sys.stderr.write("scons: internal stack trace:\n")
286 traceback.print_tb(tb, file=sys.stderr)
287 traceback.print_exception(type, value, trace)
288 elif tb and print_stacktrace:
289 sys.stderr.write("scons: internal stack trace:\n")
290 traceback.print_tb(tb, file=sys.stderr)
291
292 self.exception = (e, buildError, tb)
293 self.do_failed(buildError.exitstatus)
294
295 self.exc_clear()
296
297 - def postprocess(self):
298 if self.top:
299 t = self.targets[0]
300 for tp in self.options.tree_printers:
301 tp.display(t)
302 if self.options.debug_includes:
303 tree = t.render_include_tree()
304 if tree:
305 print()
306 print(tree)
307 SCons.Taskmaster.OutOfDateTask.postprocess(self)
308
310 """Make a task ready for execution"""
311 SCons.Taskmaster.OutOfDateTask.make_ready(self)
312 if self.out_of_date and self.options.debug_explain:
313 explanation = self.out_of_date[0].explain()
314 if explanation:
315 sys.stdout.write("scons: " + explanation)
316
317
318 -class CleanTask(SCons.Taskmaster.AlwaysTask):
398
400 """An SCons task for the -q (question) option."""
403
412
415
416
418 - def __init__(self, derived=False, prune=False, status=False):
419 self.derived = derived
420 self.prune = prune
421 self.status = status
434
435
437 return sys.version.split()[0]
438
441
444
445
446
447
448 print_objects = 0
449 print_memoizer = 0
450 print_stacktrace = 0
451 print_time = 0
452 sconscript_time = 0
453 cumulative_command_time = 0
454 exit_status = 0
455 this_build_status = 0
456 num_jobs = None
457 delayed_warnings = []
458
460 """
461 A do-nothing option parser, used for the initial OptionsParser variable.
462
463 During normal SCons operation, the OptionsParser is created right
464 away by the main() function. Certain tests scripts however, can
465 introspect on different Tool modules, the initialization of which
466 can try to add a new, local option to an otherwise uninitialized
467 OptionsParser object. This allows that introspection to happen
468 without blowing up.
469
470 """
474 values = FakeOptionValues()
477
478 OptionsParser = FakeOptionParser()
479
485
488
491
494
507
513 stats_table = {}
514 for s in self.stats:
515 for n in [t[0] for t in s]:
516 stats_table[n] = [0, 0, 0, 0]
517 i = 0
518 for s in self.stats:
519 for n, c in s:
520 stats_table[n][i] = c
521 i = i + 1
522 self.outfp.write("Object counts:\n")
523 pre = [" "]
524 post = [" %s\n"]
525 l = len(self.stats)
526 fmt1 = ''.join(pre + [' %7s']*l + post)
527 fmt2 = ''.join(pre + [' %7d']*l + post)
528 labels = self.labels[:l]
529 labels.append(("", "Class"))
530 self.outfp.write(fmt1 % tuple([x[0] for x in labels]))
531 self.outfp.write(fmt1 % tuple([x[1] for x in labels]))
532 for k in sorted(stats_table.keys()):
533 r = stats_table[k][:l] + [k]
534 self.outfp.write(fmt2 % tuple(r))
535
536 count_stats = CountStats()
537
543 fmt = 'Memory %-32s %12d\n'
544 for label, stats in zip(self.labels, self.stats):
545 self.outfp.write(fmt % (label, stats))
546
547 memory_stats = MemStats()
548
549
550
552 """Handle syntax errors. Print out a message and show where the error
553 occurred.
554 """
555 etype, value, tb = sys.exc_info()
556 lines = traceback.format_exception_only(etype, value)
557 for line in lines:
558 sys.stderr.write(line+'\n')
559 sys.exit(2)
560
562 """
563 Find the deepest stack frame that is not part of SCons.
564
565 Input is a "pre-processed" stack trace in the form
566 returned by traceback.extract_tb() or traceback.extract_stack()
567 """
568
569 tb.reverse()
570
571
572
573 for frame in tb:
574 filename = frame[0]
575 if filename.find(os.sep+'SCons'+os.sep) == -1:
576 return frame
577 return tb[0]
578
580 """Handle user errors. Print out a message and a description of the
581 error, along with the line number and routine where it occured.
582 The file and line number will be the deepest stack frame that is
583 not part of SCons itself.
584 """
585 global print_stacktrace
586 etype, value, tb = sys.exc_info()
587 if print_stacktrace:
588 traceback.print_exception(etype, value, tb)
589 filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_tb(tb))
590 sys.stderr.write("\nscons: *** %s\n" % value)
591 sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine))
592 sys.exit(2)
593
595 """Handle user warnings. Print out a message and a description of
596 the warning, along with the line number and routine where it occured.
597 The file and line number will be the deepest stack frame that is
598 not part of SCons itself.
599 """
600 etype, value, tb = sys.exc_info()
601 filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_tb(tb))
602 sys.stderr.write("\nscons: warning: %s\n" % e)
603 sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine))
604
606 """Slightly different from _scons_user_warning in that we use the
607 *current call stack* rather than sys.exc_info() to get our stack trace.
608 This is used by the warnings framework to print warnings."""
609 filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_stack())
610 sys.stderr.write("\nscons: warning: %s\n" % e.args[0])
611 sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine))
612
614 """Handle all errors but user errors. Print out a message telling
615 the user what to do in this case and print a normal trace.
616 """
617 print('internal error')
618 traceback.print_exc()
619 sys.exit(2)
620
622 """This function checks that an SConstruct file exists in a directory.
623 If so, it returns the path of the file. By default, it checks the
624 current directory.
625 """
626 if not filelist:
627 filelist = ['SConstruct', 'Sconstruct', 'sconstruct', 'SConstruct.py', 'Sconstruct.py', 'sconstruct.py']
628 for file in filelist:
629 sfile = os.path.join(dirname, file)
630 if os.path.isfile(sfile):
631 return sfile
632 if not os.path.isabs(sfile):
633 for rep in repositories:
634 if os.path.isfile(os.path.join(rep, sfile)):
635 return sfile
636 return None
637
683
692
694 """Load the site_scons dir under topdir.
695 Prepends site_scons to sys.path, imports site_scons/site_init.py,
696 and prepends site_scons/site_tools to default toolpath."""
697 if site_dir_name:
698 err_if_not_found = True
699 else:
700 site_dir_name = "site_scons"
701 err_if_not_found = False
702
703 site_dir = os.path.join(topdir, site_dir_name)
704 if not os.path.exists(site_dir):
705 if err_if_not_found:
706 raise SCons.Errors.UserError("site dir %s not found."%site_dir)
707 return
708
709 site_init_filename = "site_init.py"
710 site_init_modname = "site_init"
711 site_tools_dirname = "site_tools"
712
713 sys.path = [os.path.abspath(site_dir)] + sys.path
714 site_init_file = os.path.join(site_dir, site_init_filename)
715 site_tools_dir = os.path.join(site_dir, site_tools_dirname)
716 if os.path.exists(site_init_file):
717 import imp, re
718 try:
719 try:
720 fp, pathname, description = imp.find_module(site_init_modname,
721 [site_dir])
722
723
724
725
726
727
728
729 try:
730 m = sys.modules['SCons.Script']
731 except Exception as e:
732 fmt = 'cannot import site_init.py: missing SCons.Script module %s'
733 raise SCons.Errors.InternalError(fmt % repr(e))
734 try:
735 sfx = description[0]
736 modname = os.path.basename(pathname)[:-len(sfx)]
737 site_m = {"__file__": pathname, "__name__": modname, "__doc__": None}
738 re_special = re.compile("__[^_]+__")
739 for k in list(m.__dict__.keys()):
740 if not re_special.match(k):
741 site_m[k] = m.__dict__[k]
742
743
744 exec(compile(fp.read(), fp.name, 'exec'), site_m)
745 except KeyboardInterrupt:
746 raise
747 except Exception as e:
748 fmt = '*** Error loading site_init file %s:\n'
749 sys.stderr.write(fmt % repr(site_init_file))
750 raise
751 else:
752 for k in site_m:
753 if not re_special.match(k):
754 m.__dict__[k] = site_m[k]
755 except KeyboardInterrupt:
756 raise
757 except ImportError as e:
758 fmt = '*** cannot import site init file %s:\n'
759 sys.stderr.write(fmt % repr(site_init_file))
760 raise
761 finally:
762 if fp:
763 fp.close()
764 if os.path.exists(site_tools_dir):
765
766 SCons.Tool.DefaultToolpath.insert(0, os.path.abspath(site_tools_dir))
767
769 """Load all of the predefined site_scons dir.
770 Order is significant; we load them in order from most generic
771 (machine-wide) to most specific (topdir).
772 The verbose argument is only for testing.
773 """
774 platform = SCons.Platform.platform_default()
775
776 def homedir(d):
777 return os.path.expanduser('~/'+d)
778
779 if platform == 'win32' or platform == 'cygwin':
780
781
782
783 sysdirs=[
784 os.path.expandvars('$ALLUSERSPROFILE\\Application Data\\scons'),
785 os.path.expandvars('$USERPROFILE\\Local Settings\\Application Data\\scons')]
786 appdatadir = os.path.expandvars('$APPDATA\\scons')
787 if appdatadir not in sysdirs:
788 sysdirs.append(appdatadir)
789 sysdirs.append(homedir('.scons'))
790
791 elif platform == 'darwin':
792 sysdirs=['/Library/Application Support/SCons',
793 '/opt/local/share/scons',
794 '/sw/share/scons',
795 homedir('Library/Application Support/SCons'),
796 homedir('.scons')]
797 elif platform == 'sunos':
798 sysdirs=['/opt/sfw/scons',
799 '/usr/share/scons',
800 homedir('.scons')]
801 else:
802
803 sysdirs=['/usr/share/scons',
804 homedir('.scons')]
805
806 dirs=sysdirs + [topdir]
807 for d in dirs:
808 if verbose:
809 print("Loading site dir ", d)
810 _load_site_scons_dir(d)
811
814
828
830 path = module.__path__
831 return "\t%s path: %s\n"%(label,path)
832
834 global exit_status
835 global this_build_status
836
837 options = parser.values
838
839
840
841
842
843
844
845
846 default_warnings = [ SCons.Warnings.WarningOnByDefault,
847 SCons.Warnings.DeprecatedWarning,
848 ]
849
850 for warning in default_warnings:
851 SCons.Warnings.enableWarningClass(warning)
852 SCons.Warnings._warningOut = _scons_internal_warning
853 SCons.Warnings.process_warn_strings(options.warn)
854
855
856
857
858 try:
859 dw = options.delayed_warnings
860 except AttributeError:
861 pass
862 else:
863 delayed_warnings.extend(dw)
864 for warning_type, message in delayed_warnings:
865 SCons.Warnings.warn(warning_type, message)
866
867 if not SCons.Platform.virtualenv.virtualenv_enabled_by_default:
868 if options.enable_virtualenv:
869 SCons.Platform.virtualenv.enable_virtualenv = True
870
871 if options.ignore_virtualenv:
872 SCons.Platform.virtualenv.ignore_virtualenv = True
873
874 if options.diskcheck:
875 SCons.Node.FS.set_diskcheck(options.diskcheck)
876
877
878
879
880
881
882 if options.directory:
883 script_dir = os.path.abspath(_create_path(options.directory))
884 else:
885 script_dir = os.getcwd()
886
887 target_top = None
888 if options.climb_up:
889 target_top = '.'
890 while script_dir and not _SConstruct_exists(script_dir,
891 options.repository,
892 options.file):
893 script_dir, last_part = os.path.split(script_dir)
894 if last_part:
895 target_top = os.path.join(last_part, target_top)
896 else:
897 script_dir = ''
898
899 if script_dir and script_dir != os.getcwd():
900 if not options.silent:
901 display("scons: Entering directory `%s'" % script_dir)
902 try:
903 os.chdir(script_dir)
904 except OSError:
905 sys.stderr.write("Could not change directory to %s\n" % script_dir)
906
907
908
909
910 fs = SCons.Node.FS.get_default_fs()
911
912 for rep in options.repository:
913 fs.Repository(rep)
914
915
916
917
918 scripts = []
919 if options.file:
920 scripts.extend(options.file)
921 if not scripts:
922 sfile = _SConstruct_exists(repositories=options.repository,
923 filelist=options.file)
924 if sfile:
925 scripts.append(sfile)
926
927 if not scripts:
928 if options.help:
929
930
931
932 raise SConsPrintHelpException
933 raise SCons.Errors.UserError("No SConstruct file found.")
934
935 if scripts[0] == "-":
936 d = fs.getcwd()
937 else:
938 d = fs.File(scripts[0]).dir
939 fs.set_SConstruct_dir(d)
940
941 _set_debug_values(options)
942 SCons.Node.implicit_cache = options.implicit_cache
943 SCons.Node.implicit_deps_changed = options.implicit_deps_changed
944 SCons.Node.implicit_deps_unchanged = options.implicit_deps_unchanged
945
946 if options.no_exec:
947 SCons.SConf.dryrun = 1
948 SCons.Action.execute_actions = None
949 if options.question:
950 SCons.SConf.dryrun = 1
951 if options.clean:
952 SCons.SConf.SetBuildType('clean')
953 if options.help:
954 SCons.SConf.SetBuildType('help')
955 SCons.SConf.SetCacheMode(options.config)
956 SCons.SConf.SetProgressDisplay(progress_display)
957
958 if options.no_progress or options.silent:
959 progress_display.set_mode(0)
960
961 if options.site_dir:
962 _load_site_scons_dir(d.get_internal_path(), options.site_dir)
963 elif not options.no_site_dir:
964 _load_all_site_scons_dirs(d.get_internal_path())
965
966 if options.include_dir:
967 sys.path = options.include_dir + sys.path
968
969
970
971
972
973
974 if options.interactive:
975 SCons.Node.interactive = True
976
977
978
979
980 targets = []
981 xmit_args = []
982 for a in parser.largs:
983 if a[:1] == '-':
984 continue
985 if '=' in a:
986 xmit_args.append(a)
987 else:
988 targets.append(a)
989 SCons.Script._Add_Targets(targets + parser.rargs)
990 SCons.Script._Add_Arguments(xmit_args)
991
992
993
994
995
996
997
998
999
1000 if not hasattr(sys.stdout, 'isatty') or not sys.stdout.isatty():
1001 sys.stdout = SCons.Util.Unbuffered(sys.stdout)
1002 if not hasattr(sys.stderr, 'isatty') or not sys.stderr.isatty():
1003 sys.stderr = SCons.Util.Unbuffered(sys.stderr)
1004
1005 memory_stats.append('before reading SConscript files:')
1006 count_stats.append(('pre-', 'read'))
1007
1008
1009
1010 progress_display("scons: Reading SConscript files ...")
1011
1012 start_time = time.time()
1013 try:
1014 for script in scripts:
1015 SCons.Script._SConscript._SConscript(fs, script)
1016 except SCons.Errors.StopError as e:
1017
1018
1019
1020
1021
1022 revert_io()
1023 sys.stderr.write("scons: *** %s Stop.\n" % e)
1024 sys.exit(2)
1025 global sconscript_time
1026 sconscript_time = time.time() - start_time
1027
1028 progress_display("scons: done reading SConscript files.")
1029
1030 memory_stats.append('after reading SConscript files:')
1031 count_stats.append(('post-', 'read'))
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042 SCons.Warnings.process_warn_strings(options.warn)
1043
1044
1045
1046
1047 if python_version_deprecated():
1048 msg = "Support for pre-%s Python version (%s) is deprecated.\n" + \
1049 " If this will cause hardship, contact scons-dev@scons.org"
1050 deprecated_version_string = ".".join(map(str, deprecated_python_version))
1051 SCons.Warnings.warn(SCons.Warnings.PythonVersionWarning,
1052 msg % (deprecated_version_string, python_version_string()))
1053
1054 if not options.help:
1055
1056
1057
1058 if SCons.SConf.NeedConfigHBuilder():
1059 SCons.SConf.CreateConfigHBuilder(SCons.Defaults.DefaultEnvironment())
1060
1061
1062
1063
1064
1065
1066
1067 parser.preserve_unknown_options = False
1068 parser.parse_args(parser.largs, options)
1069
1070 if options.help:
1071 help_text = SCons.Script.help_text
1072 if help_text is None:
1073
1074
1075 raise SConsPrintHelpException
1076 else:
1077 print(help_text)
1078 print("Use scons -H for help about command-line options.")
1079 exit_status = 0
1080 return
1081
1082
1083
1084
1085
1086
1087
1088 fs.chdir(fs.Top)
1089
1090 SCons.Node.FS.save_strings(1)
1091
1092
1093
1094 SCons.Node.implicit_cache = options.implicit_cache
1095 SCons.Node.FS.set_duplicate(options.duplicate)
1096 fs.set_max_drift(options.max_drift)
1097
1098 SCons.Job.explicit_stack_size = options.stack_size
1099
1100 if options.md5_chunksize:
1101 SCons.Node.FS.File.md5_chunksize = options.md5_chunksize
1102
1103 platform = SCons.Platform.platform_module()
1104
1105 if options.interactive:
1106 SCons.Script.Interactive.interact(fs, OptionsParser, options,
1107 targets, target_top)
1108
1109 else:
1110
1111
1112 nodes = _build_targets(fs, options, targets, target_top)
1113 if not nodes:
1114 revert_io()
1115 print('Found nothing to build')
1116 exit_status = 2
1117
1181 d = [tgt for tgt in SCons.Script.DEFAULT_TARGETS if check_dir(tgt)]
1182 SCons.Script.DEFAULT_TARGETS[:] = d
1183 target_top = None
1184 lookup_top = None
1185
1186 targets = SCons.Script._Get_Default_Targets(d, fs)
1187
1188 if not targets:
1189 sys.stderr.write("scons: *** No targets specified and no Default() targets found. Stop.\n")
1190 return None
1191
1192 def Entry(x, ltop=lookup_top, ttop=target_top, fs=fs):
1193 if isinstance(x, SCons.Node.Node):
1194 node = x
1195 else:
1196 node = None
1197
1198 if ltop is None: ltop = ''
1199
1200
1201
1202 curdir = os.path.join(os.getcwd(), str(ltop))
1203 for lookup in SCons.Node.arg2nodes_lookups:
1204 node = lookup(x, curdir=curdir)
1205 if node is not None:
1206 break
1207 if node is None:
1208 node = fs.Entry(x, directory=ltop, create=1)
1209 if ttop and not node.is_under(ttop):
1210 if isinstance(node, SCons.Node.FS.Dir) and ttop.is_under(node):
1211 node = ttop
1212 else:
1213 node = None
1214 return node
1215
1216 nodes = [_f for _f in map(Entry, targets) if _f]
1217
1218 task_class = BuildTask
1219 opening_message = "Building targets ..."
1220 closing_message = "done building targets."
1221 if options.keep_going:
1222 failure_message = "done building targets (errors occurred during build)."
1223 else:
1224 failure_message = "building terminated because of errors."
1225 if options.question:
1226 task_class = QuestionTask
1227 try:
1228 if options.clean:
1229 task_class = CleanTask
1230 opening_message = "Cleaning targets ..."
1231 closing_message = "done cleaning targets."
1232 if options.keep_going:
1233 failure_message = "done cleaning targets (errors occurred during clean)."
1234 else:
1235 failure_message = "cleaning terminated because of errors."
1236 except AttributeError:
1237 pass
1238
1239 task_class.progress = ProgressObject
1240
1241 if options.random:
1242 def order(dependencies):
1243 """Randomize the dependencies."""
1244 import random
1245 random.shuffle(dependencies)
1246 return dependencies
1247 else:
1248 def order(dependencies):
1249 """Leave the order of dependencies alone."""
1250 return dependencies
1251
1252 if options.taskmastertrace_file == '-':
1253 tmtrace = sys.stdout
1254 elif options.taskmastertrace_file:
1255 tmtrace = open(options.taskmastertrace_file, 'w')
1256 else:
1257 tmtrace = None
1258 taskmaster = SCons.Taskmaster.Taskmaster(nodes, task_class, order, tmtrace)
1259
1260
1261
1262 BuildTask.options = options
1263
1264
1265 is_pypy = platform.python_implementation() == 'PyPy'
1266
1267
1268 is_37_or_later = sys.version_info >= (3, 7)
1269 python_has_threads = sysconfig.get_config_var('WITH_THREAD') or is_pypy or is_37_or_later
1270
1271 global num_jobs
1272 num_jobs = options.num_jobs
1273 jobs = SCons.Job.Jobs(num_jobs, taskmaster)
1274 if num_jobs > 1:
1275 msg = None
1276 if sys.platform == 'win32':
1277 msg = fetch_win32_parallel_msg()
1278 elif jobs.num_jobs == 1 or not python_has_threads:
1279 msg = "parallel builds are unsupported by this version of Python;\n" + \
1280 "\tignoring -j or num_jobs option.\n"
1281 if msg:
1282 SCons.Warnings.warn(SCons.Warnings.NoParallelSupportWarning, msg)
1283
1284 memory_stats.append('before building targets:')
1285 count_stats.append(('pre-', 'build'))
1286
1287 def jobs_postfunc(
1288 jobs=jobs,
1289 options=options,
1290 closing_message=closing_message,
1291 failure_message=failure_message
1292 ):
1293 if jobs.were_interrupted():
1294 if not options.no_progress and not options.silent:
1295 sys.stderr.write("scons: Build interrupted.\n")
1296 global exit_status
1297 global this_build_status
1298 exit_status = 2
1299 this_build_status = 2
1300
1301 if this_build_status:
1302 progress_display("scons: " + failure_message)
1303 else:
1304 progress_display("scons: " + closing_message)
1305 if not options.no_exec:
1306 if jobs.were_interrupted():
1307 progress_display("scons: writing .sconsign file.")
1308 SCons.SConsign.write()
1309
1310 progress_display("scons: " + opening_message)
1311 jobs.run(postfunc = jobs_postfunc)
1312
1313 memory_stats.append('after building targets:')
1314 count_stats.append(('post-', 'build'))
1315
1316 return nodes
1317
1318 -def _exec_main(parser, values):
1319 sconsflags = os.environ.get('SCONSFLAGS', '')
1320 all_args = sconsflags.split() + sys.argv[1:]
1321
1322 options, args = parser.parse_args(all_args, values)
1323
1324 if isinstance(options.debug, list) and "pdb" in options.debug:
1325 import pdb
1326 pdb.Pdb().runcall(_main, parser)
1327 elif options.profile_file:
1328
1329 from profile import Profile
1330
1331 prof = Profile()
1332 try:
1333 prof.runcall(_main, parser)
1334 finally:
1335 prof.dump_stats(options.profile_file)
1336 else:
1337 _main(parser)
1338
1340 global OptionsParser
1341 global exit_status
1342 global first_command_start
1343
1344
1345
1346
1347
1348 if python_version_unsupported():
1349 msg = "scons: *** SCons version %s does not run under Python version %s.\n"
1350 sys.stderr.write(msg % (SCons.__version__, python_version_string()))
1351 sys.exit(1)
1352
1353 parts = ["SCons by Steven Knight et al.:\n"]
1354 try:
1355 import __main__
1356 parts.append(version_string("script", __main__))
1357 except (ImportError, AttributeError):
1358
1359
1360 pass
1361 parts.append(version_string("engine", SCons))
1362 parts.append(path_string("engine", SCons))
1363 parts.append("Copyright (c) 2001 - 2019 The SCons Foundation")
1364 version = ''.join(parts)
1365
1366 from . import SConsOptions
1367 parser = SConsOptions.Parser(version)
1368 values = SConsOptions.SConsValues(parser.get_default_values())
1369
1370 OptionsParser = parser
1371
1372 try:
1373 try:
1374 _exec_main(parser, values)
1375 finally:
1376 revert_io()
1377 except SystemExit as s:
1378 if s:
1379 exit_status = s
1380 except KeyboardInterrupt:
1381 print("scons: Build interrupted.")
1382 sys.exit(2)
1383 except SyntaxError as e:
1384 _scons_syntax_error(e)
1385 except SCons.Errors.InternalError:
1386 _scons_internal_error()
1387 except SCons.Errors.UserError as e:
1388 _scons_user_error(e)
1389 except SConsPrintHelpException:
1390 parser.print_help()
1391 exit_status = 0
1392 except SCons.Errors.BuildError as e:
1393 print(e)
1394 exit_status = e.exitstatus
1395 except:
1396
1397
1398
1399 SCons.Script._SConscript.SConscript_exception()
1400 sys.exit(2)
1401
1402 memory_stats.print_stats()
1403 count_stats.print_stats()
1404
1405 if print_objects:
1406 SCons.Debug.listLoggedInstances('*')
1407
1408
1409 if print_memoizer:
1410 SCons.Memoize.Dump("Memoizer (memory cache) hits and misses:")
1411
1412
1413
1414
1415
1416 SCons.Debug.dump_caller_counts()
1417 SCons.Taskmaster.dump_stats()
1418
1419 if print_time:
1420 total_time = time.time() - SCons.Script.start_time
1421 if num_jobs == 1:
1422 ct = cumulative_command_time
1423 else:
1424 if last_command_end is None or first_command_start is None:
1425 ct = 0.0
1426 else:
1427 ct = last_command_end - first_command_start
1428 scons_time = total_time - sconscript_time - ct
1429 print("Total build time: %f seconds"%total_time)
1430 print("Total SConscript file execution time: %f seconds"%sconscript_time)
1431 print("Total SCons execution time: %f seconds"%scons_time)
1432 print("Total command execution time: %f seconds"%ct)
1433
1434 sys.exit(exit_status)
1435
1436
1437
1438
1439
1440
1441