Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(18)

Side by Side Diff: scons-2.0.1/engine/SCons/Script/Main.py

Issue 6711079: Added an unmodified copy of SCons to third_party. (Closed) Base URL: svn://svn.chromium.org/native_client/trunk/src/third_party/
Patch Set: '' Created 9 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(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:
OLDNEW
« no previous file with comments | « scons-2.0.1/engine/SCons/Script/Interactive.py ('k') | scons-2.0.1/engine/SCons/Script/SConsOptions.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698