~ubuntu-branches/ubuntu/trusty/blender/trusty-proposed

« back to all changes in this revision

Viewing changes to scons/scons-local/SCons/Script/Main.py

  • Committer: Package Import Robot
  • Author(s): Matteo F. Vescovi
  • Date: 2013-08-14 10:43:49 UTC
  • mfrom: (14.2.19 sid)
  • Revision ID: package-import@ubuntu.com-20130814104349-t1d5mtwkphp12dyj
Tags: 2.68a-3
* Upload to unstable
* debian/: python3.3 Depends simplified
  - debian/control: python3.3 Depends dropped
    for blender-data package
  - 0001-blender_thumbnailer.patch refreshed
* debian/control: libavcodec b-dep versioning dropped

Show diffs side-by-side

added added

removed removed

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