Package SCons :: Package Script :: Module Main
[hide private]
[frames] | no frames]

Source Code for Module SCons.Script.Main

   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  # Copyright (c) 2001 - 2019 The SCons Foundation 
  21  # 
  22  # Permission is hereby granted, free of charge, to any person obtaining 
  23  # a copy of this software and associated documentation files (the 
  24  # "Software"), to deal in the Software without restriction, including 
  25  # without limitation the rights to use, copy, modify, merge, publish, 
  26  # distribute, sublicense, and/or sell copies of the Software, and to 
  27  # permit persons to whom the Software is furnished to do so, subject to 
  28  # the following conditions: 
  29  # 
  30  # The above copyright notice and this permission notice shall be included 
  31  # in all copies or substantial portions of the Software. 
  32  # 
  33  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY 
  34  # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 
  35  # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
  36  # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 
  37  # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 
  38  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 
  39  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 
  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   
72 -def fetch_win32_parallel_msg():
73 # A subsidiary function that exists solely to isolate this import 74 # so we don't have to pull it in on all platforms, and so that an 75 # in-line "import" statement in the _main() function below doesn't 76 # cause warnings about local names shadowing use of the 'SCons' 77 # global in nest scopes and UnboundLocalErrors and the like in some 78 # versions (2.1) of Python. 79 import SCons.Platform.win32 80 return SCons.Platform.win32.parallel_msg
81 82
83 -def revert_io():
84 # This call is added to revert stderr and stdout to the original 85 # ones just in case some build rule or something else in the system 86 # has redirected them elsewhere. 87 sys.stderr = sys.__stderr__ 88 sys.stdout = sys.__stdout__
89
90 -class SConsPrintHelpException(Exception):
91 pass
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
100 -class Progressor(object):
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
123 - def write(self, s):
124 self.file.write(s) 125 self.file.flush() 126 self.prev = s
127
128 - def erase_previous(self):
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
136 - def spinner(self, node):
137 self.write(self.obj[self.count % len(self.obj)])
138
139 - def string(self, node):
140 self.write(self.obj)
141
142 - def replace_string(self, node):
143 self.write(self.obj.replace(self.target_string, str(node)))
144
145 - def __call__(self, node):
146 self.count = self.count + 1 147 if (self.count % self.interval) == 0: 148 if self.overwrite: 149 self.erase_previous() 150 self.func(node)
151 152 ProgressObject = SCons.Util.Null() 153
154 -def Progress(*args, **kw):
155 global ProgressObject 156 ProgressObject = Progressor(*args, **kw)
157 158 # Task control. 159 # 160 161 _BuildFailures = [] 162 163
164 -def GetBuildFailures():
165 return _BuildFailures
166 167
168 -class BuildTask(SCons.Taskmaster.OutOfDateTask):
169 """An SCons build task.""" 170 progress = ProgressObject 171
172 - def display(self, message):
173 display('scons: ' + message)
174
175 - def prepare(self):
176 self.progress(self.targets[0]) 177 return SCons.Taskmaster.OutOfDateTask.prepare(self)
178
179 - def needs_execute(self):
180 if SCons.Taskmaster.OutOfDateTask.needs_execute(self): 181 return True 182 if self.top and self.targets[0].has_builder(): 183 display("scons: `%s' is up to date." % str(self.node)) 184 return False
185
186 - def execute(self):
187 if print_time: 188 start_time = time.time() 189 global first_command_start 190 if first_command_start is None: 191 first_command_start = start_time 192 SCons.Taskmaster.OutOfDateTask.execute(self) 193 if print_time: 194 global cumulative_command_time 195 global last_command_end 196 finish_time = time.time() 197 last_command_end = finish_time 198 cumulative_command_time = cumulative_command_time+finish_time-start_time 199 sys.stdout.write("Command execution time: %s: %f seconds\n"%(str(self.node), finish_time-start_time))
200
201 - def do_failed(self, status=2):
202 _BuildFailures.append(self.exception[1]) 203 global exit_status 204 global this_build_status 205 if self.options.ignore_errors: 206 SCons.Taskmaster.OutOfDateTask.executed(self) 207 elif self.options.keep_going: 208 SCons.Taskmaster.OutOfDateTask.fail_continue(self) 209 exit_status = status 210 this_build_status = status 211 else: 212 SCons.Taskmaster.OutOfDateTask.fail_stop(self) 213 exit_status = status 214 this_build_status = status
215
216 - def executed(self):
217 t = self.targets[0] 218 if self.top and not t.has_builder() and not t.side_effect: 219 if not t.exists(): 220 if t.__class__.__name__ in ('File', 'Dir', 'Entry'): 221 errstr="Do not know how to make %s target `%s' (%s)." % (t.__class__.__name__, t, t.get_abspath()) 222 else: # Alias or Python or ... 223 errstr="Do not know how to make %s target `%s'." % (t.__class__.__name__, t) 224 sys.stderr.write("scons: *** " + errstr) 225 if not self.options.keep_going: 226 sys.stderr.write(" Stop.") 227 sys.stderr.write("\n") 228 try: 229 raise SCons.Errors.BuildError(t, errstr) 230 except KeyboardInterrupt: 231 raise 232 except: 233 self.exception_set() 234 self.do_failed() 235 else: 236 print("scons: Nothing to be done for `%s'." % t) 237 SCons.Taskmaster.OutOfDateTask.executed(self) 238 else: 239 SCons.Taskmaster.OutOfDateTask.executed(self)
240
241 - def failed(self):
242 # Handle the failure of a build task. The primary purpose here 243 # is to display the various types of Errors and Exceptions 244 # appropriately. 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 # The Taskmaster didn't record an exception for this Task; 254 # see if the sys module has one. 255 try: 256 t, e, tb = sys.exc_info()[:] 257 except ValueError: 258 t, e = exc_info 259 tb = None 260 261 # Deprecated string exceptions will have their string stored 262 # in the first entry of the tuple. 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) # type, value, traceback 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
309 - def make_ready(self):
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):
319 """An SCons clean task."""
320 - def fs_delete(self, path, pathstr, remove=True):
321 try: 322 if os.path.lexists(path): 323 if os.path.isfile(path) or os.path.islink(path): 324 if remove: os.unlink(path) 325 display("Removed " + pathstr) 326 elif os.path.isdir(path) and not os.path.islink(path): 327 # delete everything in the dir 328 for e in sorted(os.listdir(path)): 329 p = os.path.join(path, e) 330 s = os.path.join(pathstr, e) 331 if os.path.isfile(p): 332 if remove: os.unlink(p) 333 display("Removed " + s) 334 else: 335 self.fs_delete(p, s, remove) 336 # then delete dir itself 337 if remove: os.rmdir(path) 338 display("Removed directory " + pathstr) 339 else: 340 errstr = "Path '%s' exists but isn't a file or directory." 341 raise SCons.Errors.UserError(errstr % (pathstr)) 342 except SCons.Errors.UserError as e: 343 print(e) 344 except (IOError, OSError) as e: 345 print("scons: Could not remove '%s':" % pathstr, e.strerror)
346
347 - def _get_files_to_clean(self):
348 result = [] 349 target = self.targets[0] 350 if target.has_builder() or target.side_effect: 351 result = [t for t in self.targets if not t.noclean] 352 return result
353
354 - def _clean_targets(self, remove=True):
355 target = self.targets[0] 356 if target in SCons.Environment.CleanTargets: 357 files = SCons.Environment.CleanTargets[target] 358 for f in files: 359 self.fs_delete(f.get_abspath(), str(f), remove)
360
361 - def show(self):
362 for t in self._get_files_to_clean(): 363 if not t.isdir(): 364 display("Removed " + str(t)) 365 self._clean_targets(remove=False)
366
367 - def remove(self):
368 for t in self._get_files_to_clean(): 369 try: 370 removed = t.remove() 371 except OSError as e: 372 # An OSError may indicate something like a permissions 373 # issue, an IOError would indicate something like 374 # the file not existing. In either case, print a 375 # message and keep going to try to remove as many 376 # targets as possible. 377 print("scons: Could not remove '{0}'".format(str(t)), e.strerror) 378 else: 379 if removed: 380 display("Removed " + str(t)) 381 self._clean_targets(remove=True)
382 383 execute = remove 384 385 # We want the Taskmaster to update the Node states (and therefore 386 # handle reference counts, etc.), but we don't want to call 387 # back to the Node's post-build methods, which would do things 388 # we don't want, like store .sconsign information. 389 executed = SCons.Taskmaster.Task.executed_without_callbacks 390 391 # Have the Taskmaster arrange to "execute" all of the targets, because 392 # we'll figure out ourselves (in remove() or show() above) whether 393 # anything really needs to be done. 394 make_ready = SCons.Taskmaster.Task.make_ready_all 395
396 - def prepare(self):
397 pass
398
399 -class QuestionTask(SCons.Taskmaster.AlwaysTask):
400 """An SCons task for the -q (question) option."""
401 - def prepare(self):
402 pass
403
404 - def execute(self):
405 if self.targets[0].get_state() != SCons.Node.up_to_date or \ 406 (self.top and not self.targets[0].exists()): 407 global exit_status 408 global this_build_status 409 exit_status = 1 410 this_build_status = 1 411 self.tm.stop()
412
413 - def executed(self):
414 pass
415 416
417 -class TreePrinter(object):
418 - def __init__(self, derived=False, prune=False, status=False):
419 self.derived = derived 420 self.prune = prune 421 self.status = status
422 - def get_all_children(self, node):
423 return node.all_children()
424 - def get_derived_children(self, node):
425 children = node.all_children(None) 426 return [x for x in children if x.has_builder()]
427 - def display(self, t):
428 if self.derived: 429 func = self.get_derived_children 430 else: 431 func = self.get_all_children 432 s = self.status and 2 or 0 433 SCons.Util.print_tree(t, func, prune=self.prune, showtags=s)
434 435
436 -def python_version_string():
437 return sys.version.split()[0]
438
439 -def python_version_unsupported(version=sys.version_info):
440 return version < unsupported_python_version
441
442 -def python_version_deprecated(version=sys.version_info):
443 return version < deprecated_python_version
444 445 446 # Global variables 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 # final exit status, assume success by default 455 this_build_status = 0 # "exit status" of an individual build 456 num_jobs = None 457 delayed_warnings = [] 458
459 -class FakeOptionParser(object):
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 """
471 - class FakeOptionValues(object):
472 - def __getattr__(self, attr):
473 return None
474 values = FakeOptionValues()
475 - def add_local_option(self, *args, **kw):
476 pass
477 478 OptionsParser = FakeOptionParser() 479
480 -def AddOption(*args, **kw):
481 if 'default' not in kw: 482 kw['default'] = None 483 result = OptionsParser.add_local_option(*args, **kw) 484 return result
485
486 -def GetOption(name):
487 return getattr(OptionsParser.values, name)
488
489 -def SetOption(name, value):
490 return OptionsParser.values.set_option(name, value)
491
492 -def PrintHelp(file=None):
493 OptionsParser.print_help(file=file)
494
495 -class Stats(object):
496 - def __init__(self):
497 self.stats = [] 498 self.labels = [] 499 self.append = self.do_nothing 500 self.print_stats = self.do_nothing
501 - def enable(self, outfp):
502 self.outfp = outfp 503 self.append = self.do_append 504 self.print_stats = self.do_print
505 - def do_nothing(self, *args, **kw):
506 pass
507
508 -class CountStats(Stats):
509 - def do_append(self, label):
510 self.labels.append(label) 511 self.stats.append(SCons.Debug.fetchLoggedInstances())
512 - def do_print(self):
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
538 -class MemStats(Stats):
539 - def do_append(self, label):
540 self.labels.append(label) 541 self.stats.append(SCons.Debug.memory())
542 - def do_print(self):
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 # utility functions 550
551 -def _scons_syntax_error(e):
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
561 -def find_deepest_user_frame(tb):
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 # find the deepest traceback frame that is not part 572 # of SCons: 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
579 -def _scons_user_error(e):
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
594 -def _scons_user_warning(e):
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
605 -def _scons_internal_warning(e):
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
613 -def _scons_internal_error():
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
621 -def _SConstruct_exists(dirname='', repositories=[], filelist=None):
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
638 -def _set_debug_values(options):
639 global print_memoizer, print_objects, print_stacktrace, print_time 640 641 debug_values = options.debug 642 643 if "count" in debug_values: 644 # All of the object counts are within "if track_instances:" blocks, 645 # which get stripped when running optimized (with python -O or 646 # from compiled *.pyo files). Provide a warning if __debug__ is 647 # stripped, so it doesn't just look like --debug=count is broken. 648 enable_count = False 649 if __debug__: enable_count = True 650 if enable_count: 651 count_stats.enable(sys.stdout) 652 SCons.Debug.track_instances = True 653 else: 654 msg = "--debug=count is not supported when running SCons\n" + \ 655 "\twith the python -O option or optimized (.pyo) modules." 656 SCons.Warnings.warn(SCons.Warnings.NoObjectCountWarning, msg) 657 if "dtree" in debug_values: 658 options.tree_printers.append(TreePrinter(derived=True)) 659 options.debug_explain = ("explain" in debug_values) 660 if "findlibs" in debug_values: 661 SCons.Scanner.Prog.print_find_libs = "findlibs" 662 options.debug_includes = ("includes" in debug_values) 663 print_memoizer = ("memoizer" in debug_values) 664 if "memory" in debug_values: 665 memory_stats.enable(sys.stdout) 666 print_objects = ("objects" in debug_values) 667 if print_objects: 668 SCons.Debug.track_instances = True 669 if "presub" in debug_values: 670 SCons.Action.print_actions_presub = 1 671 if "stacktrace" in debug_values: 672 print_stacktrace = 1 673 if "stree" in debug_values: 674 options.tree_printers.append(TreePrinter(status=True)) 675 if "time" in debug_values: 676 print_time = 1 677 if "tree" in debug_values: 678 options.tree_printers.append(TreePrinter()) 679 if "prepare" in debug_values: 680 SCons.Taskmaster.print_prepare = 1 681 if "duplicate" in debug_values: 682 SCons.Node.print_duplicate = 1
683
684 -def _create_path(plist):
685 path = '.' 686 for d in plist: 687 if os.path.isabs(d): 688 path = d 689 else: 690 path = path + '/' + d 691 return path
692
693 -def _load_site_scons_dir(topdir, site_dir_name=None):
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 # user specified: err if missing 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 # prepend to sys.path 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 # Load the file into SCons.Script namespace. This is 723 # opaque and clever; m is the module object for the 724 # SCons.Script module, and the exec ... in call executes a 725 # file (or string containing code) in the context of the 726 # module's dictionary, so anything that code defines ends 727 # up adding to that module. This is really short, but all 728 # the error checking makes it longer. 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 # This is the magic. 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 # prepend to DefaultToolpath 766 SCons.Tool.DefaultToolpath.insert(0, os.path.abspath(site_tools_dir))
767
768 -def _load_all_site_scons_dirs(topdir, verbose=None):
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 # Note we use $ here instead of %...% because older 781 # pythons (prior to 2.6?) didn't expand %...% on Windows. 782 # This set of dirs should work on XP, Vista, 7 and later. 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': # MacOS X 792 sysdirs=['/Library/Application Support/SCons', 793 '/opt/local/share/scons', # (for MacPorts) 794 '/sw/share/scons', # (for Fink) 795 homedir('Library/Application Support/SCons'), 796 homedir('.scons')] 797 elif platform == 'sunos': # Solaris 798 sysdirs=['/opt/sfw/scons', 799 '/usr/share/scons', 800 homedir('.scons')] 801 else: # Linux, HPUX, etc. 802 # assume posix-like, i.e. platform == 'posix' 803 sysdirs=['/usr/share/scons', 804 homedir('.scons')] 805 806 dirs=sysdirs + [topdir] 807 for d in dirs: 808 if verbose: # this is used by unit tests. 809 print("Loading site dir ", d) 810 _load_site_scons_dir(d) 811
812 -def test_load_all_site_scons_dirs(d):
813 _load_all_site_scons_dirs(d, True)
814
815 -def version_string(label, module):
816 version = module.__version__ 817 build = module.__build__ 818 if build: 819 if build[0] != '.': 820 build = '.' + build 821 version = version + build 822 fmt = "\t%s: v%s, %s, by %s on %s\n" 823 return fmt % (label, 824 version, 825 module.__date__, 826 module.__developer__, 827 module.__buildsys__)
828
829 -def path_string(label, module):
830 path = module.__path__ 831 return "\t%s path: %s\n"%(label,path)
832
833 -def _main(parser):
834 global exit_status 835 global this_build_status 836 837 options = parser.values 838 839 # Here's where everything really happens. 840 841 # First order of business: set up default warnings and then 842 # handle the user's warning options, so that we can issue (or 843 # suppress) appropriate warnings about anything that might happen, 844 # as configured by the user. 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 # Now that we have the warnings configuration set up, we can actually 856 # issue (or suppress) any warnings about warning-worthy things that 857 # occurred while the command-line options were getting parsed. 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 # Next, we want to create the FS object that represents the outside 878 # world's file system, as that's central to a lot of initialization. 879 # To do this, however, we need to be in the directory from which we 880 # want to start everything, which means first handling any relevant 881 # options that might cause us to chdir somewhere (-C, -D, -U, -u). 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 = '.' # directory to prepend to targets 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 # Now that we're in the top-level SConstruct directory, go ahead 908 # and initialize the FS object that represents the file system, 909 # and make it the build engine default. 910 fs = SCons.Node.FS.get_default_fs() 911 912 for rep in options.repository: 913 fs.Repository(rep) 914 915 # Now that we have the FS object, the next order of business is to 916 # check for an SConstruct file (or other specified config file). 917 # If there isn't one, we can bail before doing any more work. 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 # There's no SConstruct, but they specified -h. 930 # Give them the options usage now, before we fail 931 # trying to read a non-existent SConstruct file. 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 # If we're about to start SCons in the interactive mode, 970 # inform the FS about this right here. Else, the release_target_info 971 # method could get called on some nodes, like the used "gcc" compiler, 972 # when using the Configure methods within the SConscripts. 973 # This would then cause subtle bugs, as already happened in #2971. 974 if options.interactive: 975 SCons.Node.interactive = True 976 977 # That should cover (most of) the options. Next, set up the variables 978 # that hold command-line arguments, so the SConscript files that we 979 # read and execute have access to them. 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 # If stdout is not a tty, replace it with a wrapper object to call flush 993 # after every write. 994 # 995 # Tty devices automatically flush after every newline, so the replacement 996 # isn't necessary. Furthermore, if we replace sys.stdout, the readline 997 # module will no longer work. This affects the behavior during 998 # --interactive mode. --interactive should only be used when stdin and 999 # stdout refer to a tty. 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 # And here's where we (finally) read the SConscript files. 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 # We had problems reading an SConscript file, such as it 1018 # couldn't be copied in to the VariantDir. Since we're just 1019 # reading SConscript files and haven't started building 1020 # things yet, stop regardless of whether they used -i or -k 1021 # or anything else. 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 # Re-{enable,disable} warnings in case they disabled some in 1034 # the SConscript file. 1035 # 1036 # We delay enabling the PythonVersionWarning class until here so that, 1037 # if they explicitly disabled it in either in the command line or in 1038 # $SCONSFLAGS, or in the SConscript file, then the search through 1039 # the list of deprecated warning classes will find that disabling 1040 # first and not issue the warning. 1041 #SCons.Warnings.enableWarningClass(SCons.Warnings.PythonVersionWarning) 1042 SCons.Warnings.process_warn_strings(options.warn) 1043 1044 # Now that we've read the SConscript files, we can check for the 1045 # warning about deprecated Python versions--delayed until here 1046 # in case they disabled the warning in the SConscript files. 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 # [ ] Clarify why we need to create Builder here at all, and 1056 # why it is created in DefaultEnvironment 1057 # https://bitbucket.org/scons/scons/commits/d27a548aeee8ad5e67ea75c2d19a7d305f784e30 1058 if SCons.SConf.NeedConfigHBuilder(): 1059 SCons.SConf.CreateConfigHBuilder(SCons.Defaults.DefaultEnvironment()) 1060 1061 # Now re-parse the command-line options (any to the left of a '--' 1062 # argument, that is) with any user-defined command-line options that 1063 # the SConscript files may have added to the parser object. This will 1064 # emit the appropriate error message and exit if any unknown option 1065 # was specified on the command line. 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 # They specified -h, but there was no Help() inside the 1074 # SConscript files. Give them the options usage. 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 # Change directory to the top-level SConstruct directory, then tell 1083 # the Node.FS subsystem that we're all done reading the SConscript 1084 # files and calling Repository() and VariantDir() and changing 1085 # directories and the like, so it can go ahead and start memoizing 1086 # the string values of file system nodes. 1087 1088 fs.chdir(fs.Top) 1089 1090 SCons.Node.FS.save_strings(1) 1091 1092 # Now that we've read the SConscripts we can set the options 1093 # that are SConscript settable: 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 # Build the targets 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
1118 -def _build_targets(fs, options, targets, target_top):
1119 1120 global this_build_status 1121 this_build_status = 0 1122 1123 progress_display.set_mode(not (options.no_progress or options.silent)) 1124 display.set_mode(not options.silent) 1125 SCons.Action.print_actions = not options.silent 1126 SCons.Action.execute_actions = not options.no_exec 1127 SCons.Node.do_store_info = not options.no_exec 1128 SCons.SConf.dryrun = options.no_exec 1129 1130 if options.diskcheck: 1131 SCons.Node.FS.set_diskcheck(options.diskcheck) 1132 1133 SCons.CacheDir.cache_enabled = not options.cache_disable 1134 SCons.CacheDir.cache_readonly = options.cache_readonly 1135 SCons.CacheDir.cache_debug = options.cache_debug 1136 SCons.CacheDir.cache_force = options.cache_force 1137 SCons.CacheDir.cache_show = options.cache_show 1138 1139 if options.no_exec: 1140 CleanTask.execute = CleanTask.show 1141 else: 1142 CleanTask.execute = CleanTask.remove 1143 1144 lookup_top = None 1145 if targets or SCons.Script.BUILD_TARGETS != SCons.Script._build_plus_default: 1146 # They specified targets on the command line or modified 1147 # BUILD_TARGETS in the SConscript file(s), so if they used -u, 1148 # -U or -D, we have to look up targets relative to the top, 1149 # but we build whatever they specified. 1150 if target_top: 1151 lookup_top = fs.Dir(target_top) 1152 target_top = None 1153 1154 targets = SCons.Script.BUILD_TARGETS 1155 else: 1156 # There are no targets specified on the command line, 1157 # so if they used -u, -U or -D, we may have to restrict 1158 # what actually gets built. 1159 d = None 1160 if target_top: 1161 if options.climb_up == 1: 1162 # -u, local directory and below 1163 target_top = fs.Dir(target_top) 1164 lookup_top = target_top 1165 elif options.climb_up == 2: 1166 # -D, all Default() targets 1167 target_top = None 1168 lookup_top = None 1169 elif options.climb_up == 3: 1170 # -U, local SConscript Default() targets 1171 target_top = fs.Dir(target_top) 1172 def check_dir(x, target_top=target_top): 1173 if hasattr(x, 'cwd') and not x.cwd is None: 1174 cwd = x.cwd.srcnode() 1175 return cwd == target_top 1176 else: 1177 # x doesn't have a cwd, so it's either not a target, 1178 # or not a file, so go ahead and keep it as a default 1179 # target and let the engine sort it out: 1180 return 1
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 # Why would ltop be None? Unfortunately this happens. 1198 if ltop is None: ltop = '' 1199 # Curdir becomes important when SCons is called with -u, -C, 1200 # or similar option that changes directory, and so the paths 1201 # of targets given on the command line need to be adjusted. 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 # default action is to build targets 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 # Let the BuildTask objects get at the options to respond to the 1261 # various print_* settings, tree_printer list, etc. 1262 BuildTask.options = options 1263 1264 1265 is_pypy = platform.python_implementation() == 'PyPy' 1266 # As of 3.7, python removed support for threadless platforms. 1267 # See https://www.python.org/dev/peps/pep-0011/ 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 # to check if python configured with threads. 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 # compat layer imports "cProfile" for us if it's available. 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
1339 -def main():
1340 global OptionsParser 1341 global exit_status 1342 global first_command_start 1343 1344 # Check up front for a Python version we do not support. We 1345 # delay the check for deprecated Python versions until later, 1346 # after the SConscript files have been read, in case they 1347 # disable that warning. 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 # On Windows there is no scons.py, so there is no 1359 # __main__.__version__, hence there is no script version. 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 # An exception here is likely a builtin Python exception Python 1397 # code in an SConscript file. Show them precisely what the 1398 # problem was and where it happened. 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 #SCons.Debug.dumpLoggedInstances('*') 1408 1409 if print_memoizer: 1410 SCons.Memoize.Dump("Memoizer (memory cache) hits and misses:") 1411 1412 # Dump any development debug info that may have been enabled. 1413 # These are purely for internal debugging during development, so 1414 # there's no need to control them with --debug= options; they're 1415 # controlled by changing the source code. 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 # Local Variables: 1437 # tab-width:4 1438 # indent-tabs-mode:nil 1439 # End: 1440 # vim: set expandtab tabstop=4 shiftwidth=4: 1441