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