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