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 - 2017 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 74b2c53bc42290e911b334a6b44f187da698a668 2017/11/14 13:16:53 bdbaddog" 
  42   
  43   
  44  import SCons.compat 
  45   
  46  import os 
  47  import sys 
  48  import time 
  49  import traceback 
  50  import sysconfig 
  51   
  52  import SCons.CacheDir 
  53  import SCons.Debug 
  54  import SCons.Defaults 
  55  import SCons.Environment 
  56  import SCons.Errors 
  57  import SCons.Job 
  58  import SCons.Node 
  59  import SCons.Node.FS 
  60  import SCons.Platform 
  61  import SCons.SConf 
  62  import SCons.Script 
  63  import SCons.Taskmaster 
  64  import SCons.Util 
  65  import SCons.Warnings 
  66   
  67  import SCons.Script.Interactive 
  68   
  69   
70 -def fetch_win32_parallel_msg():
71 # A subsidiary function that exists solely to isolate this import 72 # so we don't have to pull it in on all platforms, and so that an 73 # in-line "import" statement in the _main() function below doesn't 74 # cause warnings about local names shadowing use of the 'SCons' 75 # global in nest scopes and UnboundLocalErrors and the like in some 76 # versions (2.1) of Python. 77 import SCons.Platform.win32 78 return SCons.Platform.win32.parallel_msg
79 80
81 -def revert_io():
82 # This call is added to revert stderr and stdout to the original 83 # ones just in case some build rule or something else in the system 84 # has redirected them elsewhere. 85 sys.stderr = sys.__stderr__ 86 sys.stdout = sys.__stdout__
87
88 -class SConsPrintHelpException(Exception):
89 pass
90 91 display = SCons.Util.display 92 progress_display = SCons.Util.DisplayEngine() 93 94 first_command_start = None 95 last_command_end = None 96 97
98 -class Progressor(object):
99 prev = '' 100 count = 0 101 target_string = '$TARGET' 102
103 - def __init__(self, obj, interval=1, file=None, overwrite=False):
104 if file is None: 105 file = sys.stdout 106 107 self.obj = obj 108 self.file = file 109 self.interval = interval 110 self.overwrite = overwrite 111 112 if callable(obj): 113 self.func = obj 114 elif SCons.Util.is_List(obj): 115 self.func = self.spinner 116 elif obj.find(self.target_string) != -1: 117 self.func = self.replace_string 118 else: 119 self.func = self.string
120
121 - def write(self, s):
122 self.file.write(s) 123 self.file.flush() 124 self.prev = s
125
126 - def erase_previous(self):
127 if self.prev: 128 length = len(self.prev) 129 if self.prev[-1] in ('\n', '\r'): 130 length = length - 1 131 self.write(' ' * length + '\r') 132 self.prev = ''
133
134 - def spinner(self, node):
135 self.write(self.obj[self.count % len(self.obj)])
136
137 - def string(self, node):
138 self.write(self.obj)
139
140 - def replace_string(self, node):
141 self.write(self.obj.replace(self.target_string, str(node)))
142
143 - def __call__(self, node):
144 self.count = self.count + 1 145 if (self.count % self.interval) == 0: 146 if self.overwrite: 147 self.erase_previous() 148 self.func(node)
149 150 ProgressObject = SCons.Util.Null() 151
152 -def Progress(*args, **kw):
153 global ProgressObject 154 ProgressObject = Progressor(*args, **kw)
155 156 # Task control. 157 # 158 159 _BuildFailures = [] 160 161
162 -def GetBuildFailures():
163 return _BuildFailures
164 165
166 -class BuildTask(SCons.Taskmaster.OutOfDateTask):
167 """An SCons build task.""" 168 progress = ProgressObject 169
170 - def display(self, message):
171 display('scons: ' + message)
172
173 - def prepare(self):
174 self.progress(self.targets[0]) 175 return SCons.Taskmaster.OutOfDateTask.prepare(self)
176
177 - def needs_execute(self):
178 if SCons.Taskmaster.OutOfDateTask.needs_execute(self): 179 return True 180 if self.top and self.targets[0].has_builder(): 181 display("scons: `%s' is up to date." % str(self.node)) 182 return False
183
184 - def execute(self):
185 if print_time: 186 start_time = time.time() 187 global first_command_start 188 if first_command_start is None: 189 first_command_start = start_time 190 SCons.Taskmaster.OutOfDateTask.execute(self) 191 if print_time: 192 global cumulative_command_time 193 global last_command_end 194 finish_time = time.time() 195 last_command_end = finish_time 196 cumulative_command_time = cumulative_command_time+finish_time-start_time 197 sys.stdout.write("Command execution time: %s: %f seconds\n"%(str(self.node), finish_time-start_time))
198
199 - def do_failed(self, status=2):
200 _BuildFailures.append(self.exception[1]) 201 global exit_status 202 global this_build_status 203 if self.options.ignore_errors: 204 SCons.Taskmaster.OutOfDateTask.executed(self) 205 elif self.options.keep_going: 206 SCons.Taskmaster.OutOfDateTask.fail_continue(self) 207 exit_status = status 208 this_build_status = status 209 else: 210 SCons.Taskmaster.OutOfDateTask.fail_stop(self) 211 exit_status = status 212 this_build_status = status
213
214 - def executed(self):
215 t = self.targets[0] 216 if self.top and not t.has_builder() and not t.side_effect: 217 if not t.exists(): 218 if t.__class__.__name__ in ('File', 'Dir', 'Entry'): 219 errstr="Do not know how to make %s target `%s' (%s)." % (t.__class__.__name__, t, t.get_abspath()) 220 else: # Alias or Python or ... 221 errstr="Do not know how to make %s target `%s'." % (t.__class__.__name__, t) 222 sys.stderr.write("scons: *** " + errstr) 223 if not self.options.keep_going: 224 sys.stderr.write(" Stop.") 225 sys.stderr.write("\n") 226 try: 227 raise SCons.Errors.BuildError(t, errstr) 228 except KeyboardInterrupt: 229 raise 230 except: 231 self.exception_set() 232 self.do_failed() 233 else: 234 print("scons: Nothing to be done for `%s'." % t) 235 SCons.Taskmaster.OutOfDateTask.executed(self) 236 else: 237 SCons.Taskmaster.OutOfDateTask.executed(self)
238
239 - def failed(self):
240 # Handle the failure of a build task. The primary purpose here 241 # is to display the various types of Errors and Exceptions 242 # appropriately. 243 exc_info = self.exc_info() 244 try: 245 t, e, tb = exc_info 246 except ValueError: 247 t, e = exc_info 248 tb = None 249 250 if t is None: 251 # The Taskmaster didn't record an exception for this Task; 252 # see if the sys module has one. 253 try: 254 t, e, tb = sys.exc_info()[:] 255 except ValueError: 256 t, e = exc_info 257 tb = None 258 259 # Deprecated string exceptions will have their string stored 260 # in the first entry of the tuple. 261 if e is None: 262 e = t 263 264 buildError = SCons.Errors.convert_to_BuildError(e) 265 if not buildError.node: 266 buildError.node = self.node 267 268 node = buildError.node 269 if not SCons.Util.is_List(node): 270 node = [ node ] 271 nodename = ', '.join(map(str, node)) 272 273 errfmt = "scons: *** [%s] %s\n" 274 sys.stderr.write(errfmt % (nodename, buildError)) 275 276 if (buildError.exc_info[2] and buildError.exc_info[1] and 277 not isinstance( 278 buildError.exc_info[1], 279 (EnvironmentError, SCons.Errors.StopError, 280 SCons.Errors.UserError))): 281 type, value, trace = buildError.exc_info 282 if tb and print_stacktrace: 283 sys.stderr.write("scons: internal stack trace:\n") 284 traceback.print_tb(tb, file=sys.stderr) 285 traceback.print_exception(type, value, trace) 286 elif tb and print_stacktrace: 287 sys.stderr.write("scons: internal stack trace:\n") 288 traceback.print_tb(tb, file=sys.stderr) 289 290 self.exception = (e, buildError, tb) # type, value, traceback 291 self.do_failed(buildError.exitstatus) 292 293 self.exc_clear()
294
295 - def postprocess(self):
296 if self.top: 297 t = self.targets[0] 298 for tp in self.options.tree_printers: 299 tp.display(t) 300 if self.options.debug_includes: 301 tree = t.render_include_tree() 302 if tree: 303 print() 304 print(tree) 305 SCons.Taskmaster.OutOfDateTask.postprocess(self)
306
307 - def make_ready(self):
308 """Make a task ready for execution""" 309 SCons.Taskmaster.OutOfDateTask.make_ready(self) 310 if self.out_of_date and self.options.debug_explain: 311 explanation = self.out_of_date[0].explain() 312 if explanation: 313 sys.stdout.write("scons: " + explanation)
314 315
316 -class CleanTask(SCons.Taskmaster.AlwaysTask):
317 """An SCons clean task."""
318 - def fs_delete(self, path, pathstr, remove=True):
319 try: 320 if os.path.lexists(path): 321 if os.path.isfile(path) or os.path.islink(path): 322 if remove: os.unlink(path) 323 display("Removed " + pathstr) 324 elif os.path.isdir(path) and not os.path.islink(path): 325 # delete everything in the dir 326 for e in sorted(os.listdir(path)): 327 p = os.path.join(path, e) 328 s = os.path.join(pathstr, e) 329 if os.path.isfile(p): 330 if remove: os.unlink(p) 331 display("Removed " + s) 332 else: 333 self.fs_delete(p, s, remove) 334 # then delete dir itself 335 if remove: os.rmdir(path) 336 display("Removed directory " + pathstr) 337 else: 338 errstr = "Path '%s' exists but isn't a file or directory." 339 raise SCons.Errors.UserError(errstr % (pathstr)) 340 except SCons.Errors.UserError as e: 341 print(e) 342 except (IOError, OSError) as e: 343 print("scons: Could not remove '%s':" % pathstr, e.strerror)
344
345 - def _get_files_to_clean(self):
346 result = [] 347 target = self.targets[0] 348 if target.has_builder() or target.side_effect: 349 result = [t for t in self.targets if not t.noclean] 350 return result
351
352 - def _clean_targets(self, remove=True):
353 target = self.targets[0] 354 if target in SCons.Environment.CleanTargets: 355 files = SCons.Environment.CleanTargets[target] 356 for f in files: 357 self.fs_delete(f.get_abspath(), str(f), remove)
358
359 - def show(self):
360 for t in self._get_files_to_clean(): 361 if not t.isdir(): 362 display("Removed " + str(t)) 363 self._clean_targets(remove=False)
364
365 - def remove(self):
366 for t in self._get_files_to_clean(): 367 try: 368 removed = t.remove() 369 except OSError as e: 370 # An OSError may indicate something like a permissions 371 # issue, an IOError would indicate something like 372 # the file not existing. In either case, print a 373 # message and keep going to try to remove as many 374 # targets as possible. 375 print("scons: Could not remove '{0}'".format(str(t)), e.strerror) 376 else: 377 if removed: 378 display("Removed " + str(t)) 379 self._clean_targets(remove=True)
380 381 execute = remove 382 383 # We want the Taskmaster to update the Node states (and therefore 384 # handle reference counts, etc.), but we don't want to call 385 # back to the Node's post-build methods, which would do things 386 # we don't want, like store .sconsign information. 387 executed = SCons.Taskmaster.Task.executed_without_callbacks 388 389 # Have the Taskmaster arrange to "execute" all of the targets, because 390 # we'll figure out ourselves (in remove() or show() above) whether 391 # anything really needs to be done. 392 make_ready = SCons.Taskmaster.Task.make_ready_all 393
394 - def prepare(self):
395 pass
396
397 -class QuestionTask(SCons.Taskmaster.AlwaysTask):
398 """An SCons task for the -q (question) option."""
399 - def prepare(self):
400 pass
401
402 - def execute(self):
403 if self.targets[0].get_state() != SCons.Node.up_to_date or \ 404 (self.top and not self.targets[0].exists()): 405 global exit_status 406 global this_build_status 407 exit_status = 1 408 this_build_status = 1 409 self.tm.stop()
410
411 - def executed(self):
412 pass
413 414
415 -class TreePrinter(object):
416 - def __init__(self, derived=False, prune=False, status=False):
417 self.derived = derived 418 self.prune = prune 419 self.status = status
420 - def get_all_children(self, node):
421 return node.all_children()
422 - def get_derived_children(self, node):
423 children = node.all_children(None) 424 return [x for x in children if x.has_builder()]
425 - def display(self, t):
426 if self.derived: 427 func = self.get_derived_children 428 else: 429 func = self.get_all_children 430 s = self.status and 2 or 0 431 SCons.Util.print_tree(t, func, prune=self.prune, showtags=s)
432 433
434 -def python_version_string():
435 return sys.version.split()[0]
436
437 -def python_version_unsupported(version=sys.version_info):
438 return version < unsupported_python_version
439
440 -def python_version_deprecated(version=sys.version_info):
441 return version < deprecated_python_version
442 443 444 # Global variables 445 446 print_objects = 0 447 print_memoizer = 0 448 print_stacktrace = 0 449 print_time = 0 450 sconscript_time = 0 451 cumulative_command_time = 0 452 exit_status = 0 # final exit status, assume success by default 453 this_build_status = 0 # "exit status" of an individual build 454 num_jobs = None 455 delayed_warnings = [] 456
457 -class FakeOptionParser(object):
458 """ 459 A do-nothing option parser, used for the initial OptionsParser variable. 460 461 During normal SCons operation, the OptionsParser is created right 462 away by the main() function. Certain tests scripts however, can 463 introspect on different Tool modules, the initialization of which 464 can try to add a new, local option to an otherwise uninitialized 465 OptionsParser object. This allows that introspection to happen 466 without blowing up. 467 468 """
469 - class FakeOptionValues(object):
470 - def __getattr__(self, attr):
471 return None
472 values = FakeOptionValues()
473 - def add_local_option(self, *args, **kw):
474 pass
475 476 OptionsParser = FakeOptionParser() 477
478 -def AddOption(*args, **kw):
479 if 'default' not in kw: 480 kw['default'] = None 481 result = OptionsParser.add_local_option(*args, **kw) 482 return result
483
484 -def GetOption(name):
485 return getattr(OptionsParser.values, name)
486
487 -def SetOption(name, value):
488 return OptionsParser.values.set_option(name, value)
489
490 -def PrintHelp(file=None):
491 OptionsParser.print_help(file=file)
492
493 -class Stats(object):
494 - def __init__(self):
495 self.stats = [] 496 self.labels = [] 497 self.append = self.do_nothing 498 self.print_stats = self.do_nothing
499 - def enable(self, outfp):
500 self.outfp = outfp 501 self.append = self.do_append 502 self.print_stats = self.do_print
503 - def do_nothing(self, *args, **kw):
504 pass
505
506 -class CountStats(Stats):
507 - def do_append(self, label):
508 self.labels.append(label) 509 self.stats.append(SCons.Debug.fetchLoggedInstances())
510 - def do_print(self):
511 stats_table = {} 512 for s in self.stats: 513 for n in [t[0] for t in s]: 514 stats_table[n] = [0, 0, 0, 0] 515 i = 0 516 for s in self.stats: 517 for n, c in s: 518 stats_table[n][i] = c 519 i = i + 1 520 self.outfp.write("Object counts:\n") 521 pre = [" "] 522 post = [" %s\n"] 523 l = len(self.stats) 524 fmt1 = ''.join(pre + [' %7s']*l + post) 525 fmt2 = ''.join(pre + [' %7d']*l + post) 526 labels = self.labels[:l] 527 labels.append(("", "Class")) 528 self.outfp.write(fmt1 % tuple([x[0] for x in labels])) 529 self.outfp.write(fmt1 % tuple([x[1] for x in labels])) 530 for k in sorted(stats_table.keys()): 531 r = stats_table[k][:l] + [k] 532 self.outfp.write(fmt2 % tuple(r))
533 534 count_stats = CountStats() 535
536 -class MemStats(Stats):
537 - def do_append(self, label):
538 self.labels.append(label) 539 self.stats.append(SCons.Debug.memory())
540 - def do_print(self):
541 fmt = 'Memory %-32s %12d\n' 542 for label, stats in zip(self.labels, self.stats): 543 self.outfp.write(fmt % (label, stats))
544 545 memory_stats = MemStats() 546 547 # utility functions 548
549 -def _scons_syntax_error(e):
550 """Handle syntax errors. Print out a message and show where the error 551 occurred. 552 """ 553 etype, value, tb = sys.exc_info() 554 lines = traceback.format_exception_only(etype, value) 555 for line in lines: 556 sys.stderr.write(line+'\n') 557 sys.exit(2)
558
559 -def find_deepest_user_frame(tb):
560 """ 561 Find the deepest stack frame that is not part of SCons. 562 563 Input is a "pre-processed" stack trace in the form 564 returned by traceback.extract_tb() or traceback.extract_stack() 565 """ 566 567 tb.reverse() 568 569 # find the deepest traceback frame that is not part 570 # of SCons: 571 for frame in tb: 572 filename = frame[0] 573 if filename.find(os.sep+'SCons'+os.sep) == -1: 574 return frame 575 return tb[0]
576
577 -def _scons_user_error(e):
578 """Handle user errors. Print out a message and a description of the 579 error, along with the line number and routine where it occured. 580 The file and line number will be the deepest stack frame that is 581 not part of SCons itself. 582 """ 583 global print_stacktrace 584 etype, value, tb = sys.exc_info() 585 if print_stacktrace: 586 traceback.print_exception(etype, value, tb) 587 filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_tb(tb)) 588 sys.stderr.write("\nscons: *** %s\n" % value) 589 sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine)) 590 sys.exit(2)
591
592 -def _scons_user_warning(e):
593 """Handle user warnings. Print out a message and a description of 594 the warning, along with the line number and routine where it occured. 595 The file and line number will be the deepest stack frame that is 596 not part of SCons itself. 597 """ 598 etype, value, tb = sys.exc_info() 599 filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_tb(tb)) 600 sys.stderr.write("\nscons: warning: %s\n" % e) 601 sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine))
602
603 -def _scons_internal_warning(e):
604 """Slightly different from _scons_user_warning in that we use the 605 *current call stack* rather than sys.exc_info() to get our stack trace. 606 This is used by the warnings framework to print warnings.""" 607 filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_stack()) 608 sys.stderr.write("\nscons: warning: %s\n" % e.args[0]) 609 sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine))
610
611 -def _scons_internal_error():
612 """Handle all errors but user errors. Print out a message telling 613 the user what to do in this case and print a normal trace. 614 """ 615 print('internal error') 616 traceback.print_exc() 617 sys.exit(2)
618
619 -def _SConstruct_exists(dirname='', repositories=[], filelist=None):
620 """This function checks that an SConstruct file exists in a directory. 621 If so, it returns the path of the file. By default, it checks the 622 current directory. 623 """ 624 if not filelist: 625 filelist = ['SConstruct', 'Sconstruct', 'sconstruct'] 626 for file in filelist: 627 sfile = os.path.join(dirname, file) 628 if os.path.isfile(sfile): 629 return sfile 630 if not os.path.isabs(sfile): 631 for rep in repositories: 632 if os.path.isfile(os.path.join(rep, sfile)): 633 return sfile 634 return None
635
636 -def _set_debug_values(options):
637 global print_memoizer, print_objects, print_stacktrace, print_time 638 639 debug_values = options.debug 640 641 if "count" in debug_values: 642 # All of the object counts are within "if track_instances:" blocks, 643 # which get stripped when running optimized (with python -O or 644 # from compiled *.pyo files). Provide a warning if __debug__ is 645 # stripped, so it doesn't just look like --debug=count is broken. 646 enable_count = False 647 if __debug__: enable_count = True 648 if enable_count: 649 count_stats.enable(sys.stdout) 650 SCons.Debug.track_instances = True 651 else: 652 msg = "--debug=count is not supported when running SCons\n" + \ 653 "\twith the python -O option or optimized (.pyo) modules." 654 SCons.Warnings.warn(SCons.Warnings.NoObjectCountWarning, msg) 655 if "dtree" in debug_values: 656 options.tree_printers.append(TreePrinter(derived=True)) 657 options.debug_explain = ("explain" in debug_values) 658 if "findlibs" in debug_values: 659 SCons.Scanner.Prog.print_find_libs = "findlibs" 660 options.debug_includes = ("includes" in debug_values) 661 print_memoizer = ("memoizer" in debug_values) 662 if "memory" in debug_values: 663 memory_stats.enable(sys.stdout) 664 print_objects = ("objects" in debug_values) 665 if print_objects: 666 SCons.Debug.track_instances = True 667 if "presub" in debug_values: 668 SCons.Action.print_actions_presub = 1 669 if "stacktrace" in debug_values: 670 print_stacktrace = 1 671 if "stree" in debug_values: 672 options.tree_printers.append(TreePrinter(status=True)) 673 if "time" in debug_values: 674 print_time = 1 675 if "tree" in debug_values: 676 options.tree_printers.append(TreePrinter()) 677 if "prepare" in debug_values: 678 SCons.Taskmaster.print_prepare = 1 679 if "duplicate" in debug_values: 680 SCons.Node.print_duplicate = 1
681
682 -def _create_path(plist):
683 path = '.' 684 for d in plist: 685 if os.path.isabs(d): 686 path = d 687 else: 688 path = path + '/' + d 689 return path
690
691 -def _load_site_scons_dir(topdir, site_dir_name=None):
692 """Load the site_scons dir under topdir. 693 Prepends site_scons to sys.path, imports site_scons/site_init.py, 694 and prepends site_scons/site_tools to default toolpath.""" 695 if site_dir_name: 696 err_if_not_found = True # user specified: err if missing 697 else: 698 site_dir_name = "site_scons" 699 err_if_not_found = False 700 701 site_dir = os.path.join(topdir, site_dir_name) 702 if not os.path.exists(site_dir): 703 if err_if_not_found: 704 raise SCons.Errors.UserError("site dir %s not found."%site_dir) 705 return 706 707 site_init_filename = "site_init.py" 708 site_init_modname = "site_init" 709 site_tools_dirname = "site_tools" 710 # prepend to sys.path 711 sys.path = [os.path.abspath(site_dir)] + sys.path 712 site_init_file = os.path.join(site_dir, site_init_filename) 713 site_tools_dir = os.path.join(site_dir, site_tools_dirname) 714 if os.path.exists(site_init_file): 715 import imp, re 716 try: 717 try: 718 fp, pathname, description = imp.find_module(site_init_modname, 719 [site_dir]) 720 # Load the file into SCons.Script namespace. This is 721 # opaque and clever; m is the module object for the 722 # SCons.Script module, and the exec ... in call executes a 723 # file (or string containing code) in the context of the 724 # module's dictionary, so anything that code defines ends 725 # up adding to that module. This is really short, but all 726 # the error checking makes it longer. 727 try: 728 m = sys.modules['SCons.Script'] 729 except Exception as e: 730 fmt = 'cannot import site_init.py: missing SCons.Script module %s' 731 raise SCons.Errors.InternalError(fmt % repr(e)) 732 try: 733 sfx = description[0] 734 modname = os.path.basename(pathname)[:-len(sfx)] 735 site_m = {"__file__": pathname, "__name__": modname, "__doc__": None} 736 re_special = re.compile("__[^_]+__") 737 for k in list(m.__dict__.keys()): 738 if not re_special.match(k): 739 site_m[k] = m.__dict__[k] 740 741 # This is the magic. 742 exec(compile(fp.read(), fp.name, 'exec'), site_m) 743 except KeyboardInterrupt: 744 raise 745 except Exception as e: 746 fmt = '*** Error loading site_init file %s:\n' 747 sys.stderr.write(fmt % repr(site_init_file)) 748 raise 749 else: 750 for k in site_m: 751 if not re_special.match(k): 752 m.__dict__[k] = site_m[k] 753 except KeyboardInterrupt: 754 raise 755 except ImportError as e: 756 fmt = '*** cannot import site init file %s:\n' 757 sys.stderr.write(fmt % repr(site_init_file)) 758 raise 759 finally: 760 if fp: 761 fp.close() 762 if os.path.exists(site_tools_dir): 763 # prepend to DefaultToolpath 764 SCons.Tool.DefaultToolpath.insert(0, os.path.abspath(site_tools_dir))
765
766 -def _load_all_site_scons_dirs(topdir, verbose=None):
767 """Load all of the predefined site_scons dir. 768 Order is significant; we load them in order from most generic 769 (machine-wide) to most specific (topdir). 770 The verbose argument is only for testing. 771 """ 772 platform = SCons.Platform.platform_default() 773 774 def homedir(d): 775 return os.path.expanduser('~/'+d)
776 777 if platform == 'win32' or platform == 'cygwin': 778 # Note we use $ here instead of %...% because older 779 # pythons (prior to 2.6?) didn't expand %...% on Windows. 780 # This set of dirs should work on XP, Vista, 7 and later. 781 sysdirs=[ 782 os.path.expandvars('$ALLUSERSPROFILE\\Application Data\\scons'), 783 os.path.expandvars('$USERPROFILE\\Local Settings\\Application Data\\scons')] 784 appdatadir = os.path.expandvars('$APPDATA\\scons') 785 if appdatadir not in sysdirs: 786 sysdirs.append(appdatadir) 787 sysdirs.append(homedir('.scons')) 788 789 elif platform == 'darwin': # MacOS X 790 sysdirs=['/Library/Application Support/SCons', 791 '/opt/local/share/scons', # (for MacPorts) 792 '/sw/share/scons', # (for Fink) 793 homedir('Library/Application Support/SCons'), 794 homedir('.scons')] 795 elif platform == 'sunos': # Solaris 796 sysdirs=['/opt/sfw/scons', 797 '/usr/share/scons', 798 homedir('.scons')] 799 else: # Linux, HPUX, etc. 800 # assume posix-like, i.e. platform == 'posix' 801 sysdirs=['/usr/share/scons', 802 homedir('.scons')] 803 804 dirs=sysdirs + [topdir] 805 for d in dirs: 806 if verbose: # this is used by unit tests. 807 print("Loading site dir ", d) 808 _load_site_scons_dir(d) 809
810 -def test_load_all_site_scons_dirs(d):
811 _load_all_site_scons_dirs(d, True)
812
813 -def version_string(label, module):
814 version = module.__version__ 815 build = module.__build__ 816 if build: 817 if build[0] != '.': 818 build = '.' + build 819 version = version + build 820 fmt = "\t%s: v%s, %s, by %s on %s\n" 821 return fmt % (label, 822 version, 823 module.__date__, 824 module.__developer__, 825 module.__buildsys__)
826
827 -def path_string(label, module):
828 path = module.__path__ 829 return "\t%s path: %s\n"%(label,path)
830
831 -def _main(parser):
832 global exit_status 833 global this_build_status 834 835 options = parser.values 836 837 # Here's where everything really happens. 838 839 # First order of business: set up default warnings and then 840 # handle the user's warning options, so that we can issue (or 841 # suppress) appropriate warnings about anything that might happen, 842 # as configured by the user. 843 844 default_warnings = [ SCons.Warnings.WarningOnByDefault, 845 SCons.Warnings.DeprecatedWarning, 846 ] 847 848 for warning in default_warnings: 849 SCons.Warnings.enableWarningClass(warning) 850 SCons.Warnings._warningOut = _scons_internal_warning 851 SCons.Warnings.process_warn_strings(options.warn) 852 853 # Now that we have the warnings configuration set up, we can actually 854 # issue (or suppress) any warnings about warning-worthy things that 855 # occurred while the command-line options were getting parsed. 856 try: 857 dw = options.delayed_warnings 858 except AttributeError: 859 pass 860 else: 861 delayed_warnings.extend(dw) 862 for warning_type, message in delayed_warnings: 863 SCons.Warnings.warn(warning_type, message) 864 865 if options.diskcheck: 866 SCons.Node.FS.set_diskcheck(options.diskcheck) 867 868 # Next, we want to create the FS object that represents the outside 869 # world's file system, as that's central to a lot of initialization. 870 # To do this, however, we need to be in the directory from which we 871 # want to start everything, which means first handling any relevant 872 # options that might cause us to chdir somewhere (-C, -D, -U, -u). 873 if options.directory: 874 script_dir = os.path.abspath(_create_path(options.directory)) 875 else: 876 script_dir = os.getcwd() 877 878 target_top = None 879 if options.climb_up: 880 target_top = '.' # directory to prepend to targets 881 while script_dir and not _SConstruct_exists(script_dir, 882 options.repository, 883 options.file): 884 script_dir, last_part = os.path.split(script_dir) 885 if last_part: 886 target_top = os.path.join(last_part, target_top) 887 else: 888 script_dir = '' 889 890 if script_dir and script_dir != os.getcwd(): 891 if not options.silent: 892 display("scons: Entering directory `%s'" % script_dir) 893 try: 894 os.chdir(script_dir) 895 except OSError: 896 sys.stderr.write("Could not change directory to %s\n" % script_dir) 897 898 # Now that we're in the top-level SConstruct directory, go ahead 899 # and initialize the FS object that represents the file system, 900 # and make it the build engine default. 901 fs = SCons.Node.FS.get_default_fs() 902 903 for rep in options.repository: 904 fs.Repository(rep) 905 906 # Now that we have the FS object, the next order of business is to 907 # check for an SConstruct file (or other specified config file). 908 # If there isn't one, we can bail before doing any more work. 909 scripts = [] 910 if options.file: 911 scripts.extend(options.file) 912 if not scripts: 913 sfile = _SConstruct_exists(repositories=options.repository, 914 filelist=options.file) 915 if sfile: 916 scripts.append(sfile) 917 918 if not scripts: 919 if options.help: 920 # There's no SConstruct, but they specified -h. 921 # Give them the options usage now, before we fail 922 # trying to read a non-existent SConstruct file. 923 raise SConsPrintHelpException 924 raise SCons.Errors.UserError("No SConstruct file found.") 925 926 if scripts[0] == "-": 927 d = fs.getcwd() 928 else: 929 d = fs.File(scripts[0]).dir 930 fs.set_SConstruct_dir(d) 931 932 _set_debug_values(options) 933 SCons.Node.implicit_cache = options.implicit_cache 934 SCons.Node.implicit_deps_changed = options.implicit_deps_changed 935 SCons.Node.implicit_deps_unchanged = options.implicit_deps_unchanged 936 937 if options.no_exec: 938 SCons.SConf.dryrun = 1 939 SCons.Action.execute_actions = None 940 if options.question: 941 SCons.SConf.dryrun = 1 942 if options.clean: 943 SCons.SConf.SetBuildType('clean') 944 if options.help: 945 SCons.SConf.SetBuildType('help') 946 SCons.SConf.SetCacheMode(options.config) 947 SCons.SConf.SetProgressDisplay(progress_display) 948 949 if options.no_progress or options.silent: 950 progress_display.set_mode(0) 951 952 if options.site_dir: 953 _load_site_scons_dir(d.get_internal_path(), options.site_dir) 954 elif not options.no_site_dir: 955 _load_all_site_scons_dirs(d.get_internal_path()) 956 957 if options.include_dir: 958 sys.path = options.include_dir + sys.path 959 960 # If we're about to start SCons in the interactive mode, 961 # inform the FS about this right here. Else, the release_target_info 962 # method could get called on some nodes, like the used "gcc" compiler, 963 # when using the Configure methods within the SConscripts. 964 # This would then cause subtle bugs, as already happened in #2971. 965 if options.interactive: 966 SCons.Node.interactive = True 967 968 # That should cover (most of) the options. Next, set up the variables 969 # that hold command-line arguments, so the SConscript files that we 970 # read and execute have access to them. 971 targets = [] 972 xmit_args = [] 973 for a in parser.largs: 974 if a[:1] == '-': 975 continue 976 if '=' in a: 977 xmit_args.append(a) 978 else: 979 targets.append(a) 980 SCons.Script._Add_Targets(targets + parser.rargs) 981 SCons.Script._Add_Arguments(xmit_args) 982 983 # If stdout is not a tty, replace it with a wrapper object to call flush 984 # after every write. 985 # 986 # Tty devices automatically flush after every newline, so the replacement 987 # isn't necessary. Furthermore, if we replace sys.stdout, the readline 988 # module will no longer work. This affects the behavior during 989 # --interactive mode. --interactive should only be used when stdin and 990 # stdout refer to a tty. 991 if not hasattr(sys.stdout, 'isatty') or not sys.stdout.isatty(): 992 sys.stdout = SCons.Util.Unbuffered(sys.stdout) 993 if not hasattr(sys.stderr, 'isatty') or not sys.stderr.isatty(): 994 sys.stderr = SCons.Util.Unbuffered(sys.stderr) 995 996 memory_stats.append('before reading SConscript files:') 997 count_stats.append(('pre-', 'read')) 998 999 # And here's where we (finally) read the SConscript files. 1000 1001 progress_display("scons: Reading SConscript files ...") 1002 1003 start_time = time.time() 1004 try: 1005 for script in scripts: 1006 SCons.Script._SConscript._SConscript(fs, script) 1007 except SCons.Errors.StopError as e: 1008 # We had problems reading an SConscript file, such as it 1009 # couldn't be copied in to the VariantDir. Since we're just 1010 # reading SConscript files and haven't started building 1011 # things yet, stop regardless of whether they used -i or -k 1012 # or anything else. 1013 revert_io() 1014 sys.stderr.write("scons: *** %s Stop.\n" % e) 1015 sys.exit(2) 1016 global sconscript_time 1017 sconscript_time = time.time() - start_time 1018 1019 progress_display("scons: done reading SConscript files.") 1020 1021 memory_stats.append('after reading SConscript files:') 1022 count_stats.append(('post-', 'read')) 1023 1024 # Re-{enable,disable} warnings in case they disabled some in 1025 # the SConscript file. 1026 # 1027 # We delay enabling the PythonVersionWarning class until here so that, 1028 # if they explicitly disabled it in either in the command line or in 1029 # $SCONSFLAGS, or in the SConscript file, then the search through 1030 # the list of deprecated warning classes will find that disabling 1031 # first and not issue the warning. 1032 #SCons.Warnings.enableWarningClass(SCons.Warnings.PythonVersionWarning) 1033 SCons.Warnings.process_warn_strings(options.warn) 1034 1035 # Now that we've read the SConscript files, we can check for the 1036 # warning about deprecated Python versions--delayed until here 1037 # in case they disabled the warning in the SConscript files. 1038 if python_version_deprecated(): 1039 msg = "Support for pre-%s Python version (%s) is deprecated.\n" + \ 1040 " If this will cause hardship, contact scons-dev@scons.org" 1041 deprecated_version_string = ".".join(map(str, deprecated_python_version)) 1042 SCons.Warnings.warn(SCons.Warnings.PythonVersionWarning, 1043 msg % (deprecated_version_string, python_version_string())) 1044 1045 if not options.help: 1046 # [ ] Clarify why we need to create Builder here at all, and 1047 # why it is created in DefaultEnvironment 1048 # https://bitbucket.org/scons/scons/commits/d27a548aeee8ad5e67ea75c2d19a7d305f784e30 1049 if SCons.SConf.NeedConfigHBuilder(): 1050 SCons.SConf.CreateConfigHBuilder(SCons.Defaults.DefaultEnvironment()) 1051 1052 # Now re-parse the command-line options (any to the left of a '--' 1053 # argument, that is) with any user-defined command-line options that 1054 # the SConscript files may have added to the parser object. This will 1055 # emit the appropriate error message and exit if any unknown option 1056 # was specified on the command line. 1057 1058 parser.preserve_unknown_options = False 1059 parser.parse_args(parser.largs, options) 1060 1061 if options.help: 1062 help_text = SCons.Script.help_text 1063 if help_text is None: 1064 # They specified -h, but there was no Help() inside the 1065 # SConscript files. Give them the options usage. 1066 raise SConsPrintHelpException 1067 else: 1068 print(help_text) 1069 print("Use scons -H for help about command-line options.") 1070 exit_status = 0 1071 return 1072 1073 # Change directory to the top-level SConstruct directory, then tell 1074 # the Node.FS subsystem that we're all done reading the SConscript 1075 # files and calling Repository() and VariantDir() and changing 1076 # directories and the like, so it can go ahead and start memoizing 1077 # the string values of file system nodes. 1078 1079 fs.chdir(fs.Top) 1080 1081 SCons.Node.FS.save_strings(1) 1082 1083 # Now that we've read the SConscripts we can set the options 1084 # that are SConscript settable: 1085 SCons.Node.implicit_cache = options.implicit_cache 1086 SCons.Node.FS.set_duplicate(options.duplicate) 1087 fs.set_max_drift(options.max_drift) 1088 1089 SCons.Job.explicit_stack_size = options.stack_size 1090 1091 if options.md5_chunksize: 1092 SCons.Node.FS.File.md5_chunksize = options.md5_chunksize 1093 1094 platform = SCons.Platform.platform_module() 1095 1096 if options.interactive: 1097 SCons.Script.Interactive.interact(fs, OptionsParser, options, 1098 targets, target_top) 1099 1100 else: 1101 1102 # Build the targets 1103 nodes = _build_targets(fs, options, targets, target_top) 1104 if not nodes: 1105 revert_io() 1106 print('Found nothing to build') 1107 exit_status = 2
1108
1109 -def _build_targets(fs, options, targets, target_top):
1110 1111 global this_build_status 1112 this_build_status = 0 1113 1114 progress_display.set_mode(not (options.no_progress or options.silent)) 1115 display.set_mode(not options.silent) 1116 SCons.Action.print_actions = not options.silent 1117 SCons.Action.execute_actions = not options.no_exec 1118 SCons.Node.do_store_info = not options.no_exec 1119 SCons.SConf.dryrun = options.no_exec 1120 1121 if options.diskcheck: 1122 SCons.Node.FS.set_diskcheck(options.diskcheck) 1123 1124 SCons.CacheDir.cache_enabled = not options.cache_disable 1125 SCons.CacheDir.cache_readonly = options.cache_readonly 1126 SCons.CacheDir.cache_debug = options.cache_debug 1127 SCons.CacheDir.cache_force = options.cache_force 1128 SCons.CacheDir.cache_show = options.cache_show 1129 1130 if options.no_exec: 1131 CleanTask.execute = CleanTask.show 1132 else: 1133 CleanTask.execute = CleanTask.remove 1134 1135 lookup_top = None 1136 if targets or SCons.Script.BUILD_TARGETS != SCons.Script._build_plus_default: 1137 # They specified targets on the command line or modified 1138 # BUILD_TARGETS in the SConscript file(s), so if they used -u, 1139 # -U or -D, we have to look up targets relative to the top, 1140 # but we build whatever they specified. 1141 if target_top: 1142 lookup_top = fs.Dir(target_top) 1143 target_top = None 1144 1145 targets = SCons.Script.BUILD_TARGETS 1146 else: 1147 # There are no targets specified on the command line, 1148 # so if they used -u, -U or -D, we may have to restrict 1149 # what actually gets built. 1150 d = None 1151 if target_top: 1152 if options.climb_up == 1: 1153 # -u, local directory and below 1154 target_top = fs.Dir(target_top) 1155 lookup_top = target_top 1156 elif options.climb_up == 2: 1157 # -D, all Default() targets 1158 target_top = None 1159 lookup_top = None 1160 elif options.climb_up == 3: 1161 # -U, local SConscript Default() targets 1162 target_top = fs.Dir(target_top) 1163 def check_dir(x, target_top=target_top): 1164 if hasattr(x, 'cwd') and not x.cwd is None: 1165 cwd = x.cwd.srcnode() 1166 return cwd == target_top 1167 else: 1168 # x doesn't have a cwd, so it's either not a target, 1169 # or not a file, so go ahead and keep it as a default 1170 # target and let the engine sort it out: 1171 return 1
1172 d = [tgt for tgt in SCons.Script.DEFAULT_TARGETS if check_dir(tgt)] 1173 SCons.Script.DEFAULT_TARGETS[:] = d 1174 target_top = None 1175 lookup_top = None 1176 1177 targets = SCons.Script._Get_Default_Targets(d, fs) 1178 1179 if not targets: 1180 sys.stderr.write("scons: *** No targets specified and no Default() targets found. Stop.\n") 1181 return None 1182 1183 def Entry(x, ltop=lookup_top, ttop=target_top, fs=fs): 1184 if isinstance(x, SCons.Node.Node): 1185 node = x 1186 else: 1187 node = None 1188 # Why would ltop be None? Unfortunately this happens. 1189 if ltop is None: ltop = '' 1190 # Curdir becomes important when SCons is called with -u, -C, 1191 # or similar option that changes directory, and so the paths 1192 # of targets given on the command line need to be adjusted. 1193 curdir = os.path.join(os.getcwd(), str(ltop)) 1194 for lookup in SCons.Node.arg2nodes_lookups: 1195 node = lookup(x, curdir=curdir) 1196 if node is not None: 1197 break 1198 if node is None: 1199 node = fs.Entry(x, directory=ltop, create=1) 1200 if ttop and not node.is_under(ttop): 1201 if isinstance(node, SCons.Node.FS.Dir) and ttop.is_under(node): 1202 node = ttop 1203 else: 1204 node = None 1205 return node 1206 1207 nodes = [_f for _f in map(Entry, targets) if _f] 1208 1209 task_class = BuildTask # default action is to build targets 1210 opening_message = "Building targets ..." 1211 closing_message = "done building targets." 1212 if options.keep_going: 1213 failure_message = "done building targets (errors occurred during build)." 1214 else: 1215 failure_message = "building terminated because of errors." 1216 if options.question: 1217 task_class = QuestionTask 1218 try: 1219 if options.clean: 1220 task_class = CleanTask 1221 opening_message = "Cleaning targets ..." 1222 closing_message = "done cleaning targets." 1223 if options.keep_going: 1224 failure_message = "done cleaning targets (errors occurred during clean)." 1225 else: 1226 failure_message = "cleaning terminated because of errors." 1227 except AttributeError: 1228 pass 1229 1230 task_class.progress = ProgressObject 1231 1232 if options.random: 1233 def order(dependencies): 1234 """Randomize the dependencies.""" 1235 import random 1236 random.shuffle(dependencies) 1237 return dependencies 1238 else: 1239 def order(dependencies): 1240 """Leave the order of dependencies alone.""" 1241 return dependencies 1242 1243 if options.taskmastertrace_file == '-': 1244 tmtrace = sys.stdout 1245 elif options.taskmastertrace_file: 1246 tmtrace = open(options.taskmastertrace_file, 'w') 1247 else: 1248 tmtrace = None 1249 taskmaster = SCons.Taskmaster.Taskmaster(nodes, task_class, order, tmtrace) 1250 1251 # Let the BuildTask objects get at the options to respond to the 1252 # various print_* settings, tree_printer list, etc. 1253 BuildTask.options = options 1254 1255 1256 python_has_threads = sysconfig.get_config_var('WITH_THREAD') 1257 # to check if python configured with threads. 1258 global num_jobs 1259 num_jobs = options.num_jobs 1260 jobs = SCons.Job.Jobs(num_jobs, taskmaster) 1261 if num_jobs > 1: 1262 msg = None 1263 if sys.platform == 'win32': 1264 msg = fetch_win32_parallel_msg() 1265 elif jobs.num_jobs == 1 or not python_has_threads: 1266 msg = "parallel builds are unsupported by this version of Python;\n" + \ 1267 "\tignoring -j or num_jobs option.\n" 1268 if msg: 1269 SCons.Warnings.warn(SCons.Warnings.NoParallelSupportWarning, msg) 1270 1271 memory_stats.append('before building targets:') 1272 count_stats.append(('pre-', 'build')) 1273 1274 def jobs_postfunc( 1275 jobs=jobs, 1276 options=options, 1277 closing_message=closing_message, 1278 failure_message=failure_message 1279 ): 1280 if jobs.were_interrupted(): 1281 if not options.no_progress and not options.silent: 1282 sys.stderr.write("scons: Build interrupted.\n") 1283 global exit_status 1284 global this_build_status 1285 exit_status = 2 1286 this_build_status = 2 1287 1288 if this_build_status: 1289 progress_display("scons: " + failure_message) 1290 else: 1291 progress_display("scons: " + closing_message) 1292 if not options.no_exec: 1293 if jobs.were_interrupted(): 1294 progress_display("scons: writing .sconsign file.") 1295 SCons.SConsign.write() 1296 1297 progress_display("scons: " + opening_message) 1298 jobs.run(postfunc = jobs_postfunc) 1299 1300 memory_stats.append('after building targets:') 1301 count_stats.append(('post-', 'build')) 1302 1303 return nodes 1304
1305 -def _exec_main(parser, values):
1306 sconsflags = os.environ.get('SCONSFLAGS', '') 1307 all_args = sconsflags.split() + sys.argv[1:] 1308 1309 options, args = parser.parse_args(all_args, values) 1310 1311 if isinstance(options.debug, list) and "pdb" in options.debug: 1312 import pdb 1313 pdb.Pdb().runcall(_main, parser) 1314 elif options.profile_file: 1315 # compat layer imports "cProfile" for us if it's available. 1316 from profile import Profile 1317 1318 prof = Profile() 1319 try: 1320 prof.runcall(_main, parser) 1321 finally: 1322 prof.dump_stats(options.profile_file) 1323 else: 1324 _main(parser)
1325
1326 -def main():
1327 global OptionsParser 1328 global exit_status 1329 global first_command_start 1330 1331 # Check up front for a Python version we do not support. We 1332 # delay the check for deprecated Python versions until later, 1333 # after the SConscript files have been read, in case they 1334 # disable that warning. 1335 if python_version_unsupported(): 1336 msg = "scons: *** SCons version %s does not run under Python version %s.\n" 1337 sys.stderr.write(msg % (SCons.__version__, python_version_string())) 1338 sys.exit(1) 1339 1340 parts = ["SCons by Steven Knight et al.:\n"] 1341 try: 1342 import __main__ 1343 parts.append(version_string("script", __main__)) 1344 except (ImportError, AttributeError): 1345 # On Windows there is no scons.py, so there is no 1346 # __main__.__version__, hence there is no script version. 1347 pass 1348 parts.append(version_string("engine", SCons)) 1349 parts.append(path_string("engine", SCons)) 1350 parts.append("Copyright (c) 2001 - 2017 The SCons Foundation") 1351 version = ''.join(parts) 1352 1353 from . import SConsOptions 1354 parser = SConsOptions.Parser(version) 1355 values = SConsOptions.SConsValues(parser.get_default_values()) 1356 1357 OptionsParser = parser 1358 1359 try: 1360 try: 1361 _exec_main(parser, values) 1362 finally: 1363 revert_io() 1364 except SystemExit as s: 1365 if s: 1366 exit_status = s 1367 except KeyboardInterrupt: 1368 print("scons: Build interrupted.") 1369 sys.exit(2) 1370 except SyntaxError as e: 1371 _scons_syntax_error(e) 1372 except SCons.Errors.InternalError: 1373 _scons_internal_error() 1374 except SCons.Errors.UserError as e: 1375 _scons_user_error(e) 1376 except SConsPrintHelpException: 1377 parser.print_help() 1378 exit_status = 0 1379 except SCons.Errors.BuildError as e: 1380 print(e) 1381 exit_status = e.exitstatus 1382 except: 1383 # An exception here is likely a builtin Python exception Python 1384 # code in an SConscript file. Show them precisely what the 1385 # problem was and where it happened. 1386 SCons.Script._SConscript.SConscript_exception() 1387 sys.exit(2) 1388 1389 memory_stats.print_stats() 1390 count_stats.print_stats() 1391 1392 if print_objects: 1393 SCons.Debug.listLoggedInstances('*') 1394 #SCons.Debug.dumpLoggedInstances('*') 1395 1396 if print_memoizer: 1397 SCons.Memoize.Dump("Memoizer (memory cache) hits and misses:") 1398 1399 # Dump any development debug info that may have been enabled. 1400 # These are purely for internal debugging during development, so 1401 # there's no need to control them with --debug= options; they're 1402 # controlled by changing the source code. 1403 SCons.Debug.dump_caller_counts() 1404 SCons.Taskmaster.dump_stats() 1405 1406 if print_time: 1407 total_time = time.time() - SCons.Script.start_time 1408 if num_jobs == 1: 1409 ct = cumulative_command_time 1410 else: 1411 if last_command_end is None or first_command_start is None: 1412 ct = 0.0 1413 else: 1414 ct = last_command_end - first_command_start 1415 scons_time = total_time - sconscript_time - ct 1416 print("Total build time: %f seconds"%total_time) 1417 print("Total SConscript file execution time: %f seconds"%sconscript_time) 1418 print("Total SCons execution time: %f seconds"%scons_time) 1419 print("Total command execution time: %f seconds"%ct) 1420 1421 sys.exit(exit_status)
1422 1423 # Local Variables: 1424 # tab-width:4 1425 # indent-tabs-mode:nil 1426 # End: 1427 # vim: set expandtab tabstop=4 shiftwidth=4: 1428