~ubuntu-branches/ubuntu/utopic/scons/utopic-proposed

« back to all changes in this revision

Viewing changes to engine/SCons/Script/__init__.py

  • Committer: Bazaar Package Importer
  • Author(s): Mark Brown
  • Date: 2004-08-24 08:57:22 UTC
  • mfrom: (0.2.1 upstream) (1.1.1 warty)
  • Revision ID: james.westby@ubuntu.com-20040824085722-hfk4f0pjbyu0ebxv
Tags: 0.96.1-1
New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
"""engine.SCons.script
 
1
"""SCons.Script
2
2
 
3
3
This file implements the main() function used by the scons script.
4
4
 
12
12
"""
13
13
 
14
14
#
15
 
# Copyright (c) 2001, 2002 Steven Knight
 
15
# Copyright (c) 2001, 2002, 2003, 2004 The SCons Foundation
16
16
#
17
17
# Permission is hereby granted, free of charge, to any person obtaining
18
18
# a copy of this software and associated documentation files (the
34
34
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35
35
#
36
36
 
37
 
__revision__ = "src/engine/SCons/Script/__init__.py 0.D006 2002/03/28 02:47:47 software"
38
 
 
39
 
import getopt
 
37
__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Script/__init__.py 0.96.1.D001 2004/08/23 09:55:29 knight"
 
38
 
 
39
import time
 
40
start_time = time.time()
 
41
 
40
42
import os
41
43
import os.path
 
44
import random
42
45
import string
43
46
import sys
44
47
import traceback
45
 
import copy
46
48
 
47
49
# Strip the script directory from sys.path() so on case-insensitive
48
50
# (WIN32) systems Python doesn't think that the "scons" script is the
49
51
# "SCons" package.  Replace it with our own version directory so, if
50
52
# if they're there, we pick up the right version of the build engine
51
53
# modules.
52
 
sys.path = [os.path.join(sys.prefix, 'lib', 'scons-0.06')] + sys.path[1:]
 
54
#sys.path = [os.path.join(sys.prefix,
 
55
#                         'lib',
 
56
#                         'scons-%d' % SCons.__version__)] + sys.path[1:]
53
57
 
 
58
import SCons.Debug
 
59
import SCons.Defaults
 
60
import SCons.Environment
 
61
import SCons.Errors
 
62
import SCons.Job
54
63
import SCons.Node
55
64
import SCons.Node.FS
56
 
import SCons.Job
57
 
from SCons.Errors import *
 
65
from SCons.Optik import OptionParser, SUPPRESS_HELP, OptionValueError
 
66
import SCons.Script.SConscript
58
67
import SCons.Sig
59
 
import SCons.Sig.MD5
60
 
from SCons.Taskmaster import Taskmaster
61
 
import SCons.Builder
62
 
import SCons.Script.SConscript
63
 
 
64
 
 
65
 
#
 
68
import SCons.Taskmaster
 
69
import SCons.Util
 
70
import SCons.Warnings
 
71
 
 
72
#
 
73
import __builtin__
 
74
try:
 
75
    __builtin__.zip
 
76
except AttributeError:
 
77
    def zip(l1, l2):
 
78
        result = []
 
79
        for i in xrange(len(l1)):
 
80
           result.append((l1[i], l2[i]))
 
81
        return result
 
82
    __builtin__.zip = zip
 
83
 
 
84
#
 
85
display = SCons.Util.display
 
86
progress_display = SCons.Util.DisplayEngine()
 
87
 
66
88
# Task control.
67
89
#
68
90
class BuildTask(SCons.Taskmaster.Task):
69
91
    """An SCons build task."""
 
92
    def display(self, message):
 
93
        display('scons: ' + message)
 
94
 
70
95
    def execute(self):
71
 
        if self.targets[0].get_state() == SCons.Node.up_to_date:
72
 
            if self.top:
73
 
                print 'scons: "%s" is up to date.' % str(self.targets[0])
74
 
        else:
75
 
            try:
76
 
                self.targets[0].prepare()
77
 
                self.targets[0].build()
78
 
            except BuildError, e:
79
 
                sys.stderr.write("scons: *** [%s] %s\n" % (e.node, e.errstr))
80
 
                if e.errstr == 'Exception':
81
 
                    traceback.print_exception(e.args[0], e.args[1],
82
 
                                              e.args[2])
83
 
                raise
84
 
 
85
 
    def executed(self):
86
 
        SCons.Taskmaster.Task.executed(self)
87
 
        # print the tree here instead of in execute() because
88
 
        # this method is serialized, but execute isn't:
89
 
        if print_tree and self.top:
90
 
            print
91
 
            print SCons.Util.render_tree(self.targets[0], get_children)
92
 
 
93
 
    def failed(self):
 
96
        target = self.targets[0]
 
97
        if target.get_state() == SCons.Node.up_to_date:
 
98
            if self.top and target.has_builder():
 
99
                display("scons: `%s' is up to date." % str(self.node))
 
100
        elif target.has_builder() and not hasattr(target.builder, 'status'):
 
101
            if print_time:
 
102
                start_time = time.time()
 
103
            SCons.Taskmaster.Task.execute(self)
 
104
            if print_time:
 
105
                finish_time = time.time()
 
106
                global command_time
 
107
                command_time = command_time+finish_time-start_time
 
108
                print "Command execution time: %f seconds"%(finish_time-start_time)
 
109
 
 
110
    def do_failed(self, status=2):
94
111
        global exit_status
95
112
        if ignore_errors:
96
113
            SCons.Taskmaster.Task.executed(self)
97
114
        elif keep_going_on_error:
98
115
            SCons.Taskmaster.Task.fail_continue(self)
99
 
            exit_status = 2
 
116
            exit_status = status
100
117
        else:
101
118
            SCons.Taskmaster.Task.fail_stop(self)
102
 
            exit_status = 2
 
119
            exit_status = status
 
120
            
 
121
    def executed(self):
 
122
        t = self.targets[0]
 
123
        if self.top and not t.has_builder() and not t.side_effect:
 
124
            if not t.exists():
 
125
                sys.stderr.write("scons: *** Do not know how to make target `%s'." % t)
 
126
                if not keep_going_on_error:
 
127
                    sys.stderr.write("  Stop.")
 
128
                sys.stderr.write("\n")
 
129
                self.do_failed()
 
130
            else:
 
131
                print "scons: Nothing to be done for `%s'." % t
 
132
                SCons.Taskmaster.Task.executed(self)
 
133
        else:
 
134
            SCons.Taskmaster.Task.executed(self)
 
135
 
 
136
        # print the tree here instead of in execute() because
 
137
        # this method is serialized, but execute isn't:
 
138
        if print_tree and self.top:
 
139
            print
 
140
            print SCons.Util.render_tree(self.targets[0], get_all_children)
 
141
        if print_dtree and self.top:
 
142
            print
 
143
            print SCons.Util.render_tree(self.targets[0], get_derived_children)
 
144
        if print_includes and self.top:
 
145
            t = self.targets[0]
 
146
            tree = t.render_include_tree()
 
147
            if tree:
 
148
                print
 
149
                print tree
 
150
 
 
151
    def failed(self):
 
152
        # Handle the failure of a build task.  The primary purpose here
 
153
        # is to display the various types of Errors and Exceptions
 
154
        # appropriately.
 
155
        status = 2
 
156
        t, e = self.exc_info()[:2]
 
157
        tb = None
 
158
        if t is None:
 
159
            # The Taskmaster didn't record an exception for this Task;
 
160
            # see if the sys module has one.
 
161
            t, e = sys.exc_info()[:2]
 
162
 
 
163
        if t == SCons.Errors.BuildError:
 
164
            sys.stderr.write("scons: *** [%s] %s\n" % (e.node, e.errstr))
 
165
            if e.errstr == 'Exception':
 
166
                traceback.print_exception(e.args[0], e.args[1], e.args[2])
 
167
        elif t == SCons.Errors.ExplicitExit:
 
168
            status = e.status
 
169
            sys.stderr.write("scons: *** [%s] Explicit exit, status %s\n" % (e.node, e.status))
 
170
        else:
 
171
            if e is None:
 
172
                e = t
 
173
            s = str(e)
 
174
            if t == SCons.Errors.StopError and not keep_going_on_error:
 
175
                s = s + '  Stop.'
 
176
            sys.stderr.write("scons: *** %s\n" % s)
 
177
 
 
178
            if tb:
 
179
                sys.stderr.write("scons: internal stack trace:\n")
 
180
                traceback.print_tb(tb, file=sys.stderr)
 
181
 
 
182
        self.do_failed(status)
 
183
 
 
184
        self.exc_clear()
 
185
 
 
186
    def make_ready(self):
 
187
        """Make a task ready for execution"""
 
188
        SCons.Taskmaster.Task.make_ready(self)
 
189
        if self.out_of_date and print_explanations:
 
190
            explanation = self.out_of_date[0].explain()
 
191
            if explanation:
 
192
                sys.stdout.write("scons: " + explanation)
103
193
 
104
194
class CleanTask(SCons.Taskmaster.Task):
105
195
    """An SCons clean task."""
106
 
    def execute(self):
107
 
        if self.targets[0].builder:
108
 
            try:
109
 
                os.unlink(self.targets[0].path)
110
 
            except OSError:
111
 
                pass
112
 
            else:
113
 
                print "Removed " + self.targets[0].path
114
 
            try:
115
 
                for t in self.targets[1:]:
116
 
                    try:
117
 
                        os.unlink(t.path)
118
 
                    except OSError:
119
 
                        pass
120
 
            except IndexError:
121
 
                pass
 
196
    def show(self):
 
197
        if (self.targets[0].has_builder() or self.targets[0].side_effect) \
 
198
           and not os.path.isdir(str(self.targets[0])):
 
199
            display("Removed " + str(self.targets[0]))
 
200
        if SCons.Environment.CleanTargets.has_key(self.targets[0]):
 
201
            files = SCons.Environment.CleanTargets[self.targets[0]]
 
202
            for f in files:
 
203
                SCons.Util.fs_delete(str(f), 0)
 
204
 
 
205
    def remove(self):
 
206
        if self.targets[0].has_builder() or self.targets[0].side_effect:
 
207
            for t in self.targets:
 
208
                try:
 
209
                    removed = t.remove()
 
210
                except OSError, e:
 
211
                    print "scons: Could not remove '%s':" % str(t), e.strerror
 
212
                else:
 
213
                    if removed:
 
214
                        display("Removed " + str(t))
 
215
        if SCons.Environment.CleanTargets.has_key(self.targets[0]):
 
216
            files = SCons.Environment.CleanTargets[self.targets[0]]
 
217
            for f in files:
 
218
                SCons.Util.fs_delete(str(f))
 
219
 
 
220
    execute = remove
 
221
 
 
222
    # Have the taskmaster arrange to "execute" all of the targets, because
 
223
    # we'll figure out ourselves (in remove() or show() above) whether
 
224
    # anything really needs to be done.
 
225
    make_ready = SCons.Taskmaster.Task.make_ready_all
 
226
 
 
227
    def prepare(self):
 
228
        pass
122
229
 
123
230
class QuestionTask(SCons.Taskmaster.Task):
124
231
    """An SCons task for the -q (question) option."""
 
232
    def prepare(self):
 
233
        pass
 
234
    
125
235
    def execute(self):
126
236
        if self.targets[0].get_state() != SCons.Node.up_to_date:
127
237
            global exit_status
133
243
 
134
244
# Global variables
135
245
 
136
 
include_dirs = []
137
 
num_jobs = 1
138
 
scripts = []
139
 
task_class = BuildTask  # default action is to build targets
140
 
current_func = None
141
 
calc = None
142
 
ignore_errors = 0
143
246
keep_going_on_error = 0
144
 
help_option = None
 
247
print_count = 0
 
248
print_dtree = 0
 
249
print_explanations = 0
 
250
print_includes = 0
 
251
print_objects = 0
 
252
print_time = 0
145
253
print_tree = 0
146
 
climb_up = 0
147
 
target_top = None
 
254
memory_stats = None
 
255
ignore_errors = 0
 
256
sconscript_time = 0
 
257
command_time = 0
148
258
exit_status = 0 # exit status, assume success by default
 
259
profiling = 0
 
260
repositories = []
 
261
num_jobs = 1 # this is modifed by SConscript.SetJobs()
 
262
 
 
263
# Exceptions for this module
 
264
class PrintHelp(Exception):
 
265
    pass
149
266
 
150
267
# utility functions
151
268
 
152
 
def get_children(node): return node.all_children(None)
 
269
def get_all_children(node): return node.all_children(None)
 
270
 
 
271
def get_derived_children(node):
 
272
    children = node.all_children(None)
 
273
    return filter(lambda x: x.has_builder(), children)
153
274
 
154
275
def _scons_syntax_error(e):
155
276
    """Handle syntax errors. Print out a message and show where the error
161
282
        sys.stderr.write(line+'\n')
162
283
    sys.exit(2)
163
284
 
 
285
def find_deepest_user_frame(tb):
 
286
    """
 
287
    Find the deepest stack frame that is not part of SCons.
 
288
 
 
289
    Input is a "pre-processed" stack trace in the form
 
290
    returned by traceback.extract_tb() or traceback.extract_stack()
 
291
    """
 
292
    
 
293
    tb.reverse()
 
294
 
 
295
    # find the deepest traceback frame that is not part
 
296
    # of SCons:
 
297
    for frame in tb:
 
298
        filename = frame[0]
 
299
        if string.find(filename, os.sep+'SCons'+os.sep) == -1:
 
300
            return frame
 
301
    return tb[0]
 
302
 
164
303
def _scons_user_error(e):
165
304
    """Handle user errors. Print out a message and a description of the
166
 
    error, along with the line number and routine where it occured.
 
305
    error, along with the line number and routine where it occured. 
 
306
    The file and line number will be the deepest stack frame that is
 
307
    not part of SCons itself.
167
308
    """
168
309
    etype, value, tb = sys.exc_info()
169
 
    while tb.tb_next is not None:
170
 
        tb = tb.tb_next
171
 
    lineno = traceback.tb_lineno(tb)
172
 
    filename = tb.tb_frame.f_code.co_filename
173
 
    routine = tb.tb_frame.f_code.co_name
174
 
    sys.stderr.write("\nSCons error: %s\n" % value)
 
310
    filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_tb(tb))
 
311
    sys.stderr.write("\nscons: *** %s\n" % value)
175
312
    sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine))
176
313
    sys.exit(2)
177
314
 
178
315
def _scons_user_warning(e):
179
316
    """Handle user warnings. Print out a message and a description of
180
317
    the warning, along with the line number and routine where it occured.
 
318
    The file and line number will be the deepest stack frame that is
 
319
    not part of SCons itself.
181
320
    """
182
321
    etype, value, tb = sys.exc_info()
183
 
    while tb.tb_next is not None:
184
 
        tb = tb.tb_next
185
 
    lineno = traceback.tb_lineno(tb)
186
 
    filename = tb.tb_frame.f_code.co_filename
187
 
    routine = tb.tb_frame.f_code.co_name
188
 
    sys.stderr.write("\nSCons warning: %s\n" % e)
189
 
    sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine))
190
 
 
191
 
def _scons_other_errors():
 
322
    filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_tb(tb))
 
323
    sys.stderr.write("\nscons: warning: %s\n" % e)
 
324
    sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine))
 
325
 
 
326
def _scons_internal_warning(e):
 
327
    """Slightly different from _scons_user_warning in that we use the
 
328
    *current call stack* rather than sys.exc_info() to get our stack trace.
 
329
    This is used by the warnings framework to print warnings."""
 
330
    filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_stack())
 
331
    sys.stderr.write("\nscons: warning: %s\n" % e[0])
 
332
    sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine))
 
333
 
 
334
def _scons_internal_error():
192
335
    """Handle all errors but user errors. Print out a message telling
193
336
    the user what to do in this case and print a normal trace.
194
337
    """
195
 
    print 'other errors'
 
338
    print 'internal error'
196
339
    traceback.print_exc()
197
340
    sys.exit(2)
198
341
 
199
 
 
200
 
#
201
 
# After options are initialized, the following variables are
202
 
# filled in:
203
 
#
204
 
option_list = []        # list of Option objects
205
 
short_opts = ""         # string of short (single-character) options
206
 
long_opts = []          # array of long (--) options
207
 
opt_func = {}           # mapping of option strings to functions
208
 
 
209
 
def options_init():
210
 
    """Initialize command-line options processing.
211
 
    
212
 
    This is in a subroutine mainly so we can easily single-step over
213
 
    it in the debugger.
214
 
    """
215
 
 
216
 
    class Option:
217
 
        """Class for command-line option information.
218
 
 
219
 
        This exists to provide a central location for everything
220
 
        describing a command-line option, so that we can change
221
 
        options without having to update the code to handle the
222
 
        option in one place, the -h help message in another place,
223
 
        etc.  There are no methods here, only attributes.
224
 
 
225
 
        You can initialize an Option with the following:
226
 
 
227
 
        func    The function that will be called when this
228
 
                option is processed on the command line.
229
 
                Calling sequence is:
230
 
 
231
 
                        func(opt, arg)
232
 
 
233
 
                If there is no func, then this Option probably
234
 
                stores an optstring to be printed.
235
 
 
236
 
        helpline
237
 
                The string to be printed in -h output.  If no
238
 
                helpline is specified but a help string is
239
 
                specified (the usual case), a helpline will be
240
 
                constructed automatically from the short, long,
241
 
                arg, and help attributes.  (In practice, then,
242
 
                setting helpline without setting func allows you
243
 
                to print arbitrary lines of text in the -h
244
 
                output.)
245
 
 
246
 
        short   The string for short, single-hyphen
247
 
                command-line options.
248
 
                Do not include the hyphen:
249
 
 
250
 
                        'a' for -a, 'xy' for -x and -y, etc.
251
 
 
252
 
        long    An array of strings for long, double-hyphen
253
 
                command-line options.  Do not include
254
 
                the hyphens:
255
 
 
256
 
                        ['my-option', 'verbose']
257
 
 
258
 
        arg     If this option takes an argument, this string
259
 
                specifies how you want it to appear in the
260
 
                -h output ('DIRECTORY', 'FILE', etc.).
261
 
 
262
 
        help    The help string that will be printed for
263
 
                this option in the -h output.  Must be
264
 
                49 characters or fewer.
265
 
 
266
 
        future  If non-zero, this indicates that this feature
267
 
                will be supported in a future release, not
268
 
                the currently planned one.  SCons will
269
 
                recognize the option, but it won't show up
270
 
                in the -h output.
271
 
 
272
 
        The following attribute is derived from the supplied attributes:
273
 
 
274
 
        optstring
275
 
                A string, with hyphens, describing the flags
276
 
                for this option, as constructed from the
277
 
                specified short, long and arg attributes.
278
 
 
279
 
        All Option objects are stored in the global option_list list,
280
 
        in the order in which they're created.  This is the list
281
 
        that's used to generate -h output, so the order in which the
282
 
        objects are created is the order in which they're printed.
283
 
 
284
 
        The upshot is that specifying a command-line option and having
285
 
        everything work correctly is a matter of defining a function to
286
 
        process its command-line argument (set the right flag, update
287
 
        the right value), and then creating an appropriate Option object
288
 
        at the correct point in the code below.
289
 
        """
290
 
 
291
 
        def __init__(self, func = None, helpline = None,
292
 
                 short = None, long = None, arg = None,
293
 
                 help = None, future = None):
294
 
            self.func = func
295
 
            self.short = short
296
 
            self.long = long
297
 
            self.arg = arg
298
 
            self.help = help
299
 
            opts = []
300
 
            if self.short:
301
 
                for c in self.short:
302
 
                    if arg:
303
 
                        c = c + " " + arg
304
 
                    opts = opts + ['-' + c]
305
 
            if self.long:
306
 
                l = self.long
307
 
                if arg:
308
 
                    l = map(lambda x,a=arg: x + "=" + a, self.long)
309
 
                opts = opts + map(lambda x: '--' + x, l)
310
 
            self.optstring = string.join(opts, ', ')
311
 
            if helpline:
312
 
                self.helpline = helpline
313
 
            elif help and not future:
314
 
                if len(self.optstring) <= 26:
315
 
                    sep = " " * (28 - len(self.optstring))
316
 
                else:
317
 
                    sep = self.helpstring = "\n" + " " * 30
318
 
                self.helpline = "  " + self.optstring + sep + self.help
319
 
            else:
320
 
                self.helpline = None
321
 
            global option_list
322
 
            option_list.append(self)
323
 
 
324
 
    # Generic routine for to-be-written options, used by multiple
325
 
    # options below.
326
 
 
327
 
    def opt_not_yet(opt, arg):
328
 
        sys.stderr.write("Warning:  the %s option is not yet implemented\n"
329
 
                          % opt)
330
 
 
331
 
    # In the following instantiations, the help string should be no
332
 
    # longer than 49 characters.  Use the following as a guide:
333
 
    #   help = "1234567890123456789012345678901234567890123456789"
334
 
 
335
 
    def opt_ignore(opt, arg):
336
 
        sys.stderr.write("Warning:  ignoring %s option\n" % opt)
337
 
 
338
 
    Option(func = opt_ignore,
339
 
        short = 'bmSt', long = ['no-keep-going', 'stop', 'touch'],
340
 
        help = "Ignored for compatibility.")
341
 
 
342
 
    def opt_c(opt, arg):
343
 
        global task_class, calc
344
 
        task_class = CleanTask
345
 
        class CleanCalculator:
346
 
            def bsig(self, node):
347
 
                return None
348
 
            def csig(self, node):
349
 
                return None
350
 
            def current(self, node, sig):
351
 
                return 0
352
 
            def write(self):
353
 
                pass
354
 
        calc = CleanCalculator()
355
 
 
356
 
    Option(func = opt_c,
357
 
        short = 'c', long = ['clean', 'remove'],
358
 
        help = "Remove specified targets and dependencies.")
359
 
 
360
 
    Option(func = opt_not_yet, future = 1,
361
 
        long = ['cache-disable', 'no-cache'],
362
 
        help = "Do not retrieve built targets from Cache.")
363
 
 
364
 
    Option(func = opt_not_yet, future = 1,
365
 
        long = ['cache-force', 'cache-populate'],
366
 
        help = "Copy already-built targets into the Cache.")
367
 
 
368
 
    Option(func = opt_not_yet, future = 1,
369
 
        long = ['cache-show'],
370
 
        help = "Print what would have built Cached targets.")
371
 
 
372
 
    def opt_C(opt, arg):
373
 
        try:
374
 
            os.chdir(arg)
375
 
        except:
376
 
            sys.stderr.write("Could not change directory to 'arg'\n")
377
 
 
378
 
    Option(func = opt_C,
379
 
        short = 'C', long = ['directory'], arg = 'DIRECTORY',
380
 
        help = "Change to DIRECTORY before doing anything.")
381
 
 
382
 
    Option(func = opt_not_yet,
383
 
        short = 'd',
384
 
        help = "Print file dependency information.")
385
 
 
386
 
    def opt_debug(opt, arg):
387
 
        global print_tree
388
 
        if arg == "pdb":
389
 
            args = [ sys.executable, "pdb.py" ] + \
390
 
                     filter(lambda x: x != "--debug=pdb", sys.argv)
391
 
            if sys.platform == 'win32':
392
 
                args[1] = os.path.join(sys.exec_prefix, "lib", "pdb.py")
393
 
                sys.exit(os.spawnve(os.P_WAIT, args[0], args, os.environ))
 
342
def _varargs(option, parser):
 
343
    value = None
 
344
    if parser.rargs:
 
345
        arg = parser.rargs[0]
 
346
        if arg[0] != "-":
 
347
            value = arg
 
348
            del parser.rargs[0]
 
349
    return value
 
350
 
 
351
def _setup_warn(arg):
 
352
    """The --warn option.  An argument to this option
 
353
    should be of the form <warning-class> or no-<warning-class>.
 
354
    The warning class is munged in order to get an actual class
 
355
    name from the SCons.Warnings module to enable or disable.
 
356
    The supplied <warning-class> is split on hyphens, each element
 
357
    is captialized, then smushed back together.  Then the string
 
358
    "SCons.Warnings." is added to the front and "Warning" is added
 
359
    to the back to get the fully qualified class name.
 
360
 
 
361
    For example, --warn=deprecated will enable the
 
362
    SCons.Warnings.DeprecatedWarning class.
 
363
 
 
364
    --warn=no-dependency will disable the
 
365
    SCons.Warnings.DependencyWarning class.
 
366
 
 
367
    As a special case, --warn=all and --warn=no-all
 
368
    will enable or disable (respectively) the base
 
369
    class of all warnings, which is SCons.Warning.Warning."""
 
370
 
 
371
    elems = string.split(string.lower(arg), '-')
 
372
    enable = 1
 
373
    if elems[0] == 'no':
 
374
        enable = 0
 
375
        del elems[0]
 
376
 
 
377
    if len(elems) == 1 and elems[0] == 'all':
 
378
        class_name = "Warning"
 
379
    else:
 
380
        def _capitalize(s):
 
381
            if s[:5] == "scons":
 
382
                return "SCons" + s[5:]
394
383
            else:
395
 
                args[1] = os.path.join(sys.exec_prefix,
396
 
                                       "lib",
397
 
                                       "python" + sys.version[0:3],
398
 
                                       "pdb.py")
399
 
                os.execvpe(args[0], args, os.environ)
400
 
        elif arg == "tree":
401
 
            print_tree = 1
 
384
                return string.capitalize(s)
 
385
        class_name = string.join(map(_capitalize, elems), '') + "Warning"
 
386
    try:
 
387
        clazz = getattr(SCons.Warnings, class_name)
 
388
    except AttributeError:
 
389
        sys.stderr.write("No warning type: '%s'\n" % arg)
 
390
    else:
 
391
        if enable:
 
392
            SCons.Warnings.enableWarningClass(clazz)
402
393
        else:
403
 
            sys.stderr.write("Warning:  %s is not a valid debug type\n"
404
 
                             % arg)
405
 
 
406
 
    Option(func = opt_debug,
407
 
           long = ['debug'], arg='TYPE',
408
 
           help = "Print various types of debugging information.")
409
 
 
410
 
    Option(func = opt_not_yet, future = 1,
411
 
        short = 'e', long = ['environment-overrides'],
412
 
        help = "Environment variables override makefiles.")
413
 
 
414
 
    def opt_f(opt, arg):
415
 
        global scripts
416
 
        scripts.append(arg)
417
 
 
418
 
    Option(func = opt_f,
419
 
        short = 'f', long = ['file', 'makefile', 'sconstruct'], arg = 'FILE',
420
 
        help = "Read FILE as the top-level SConstruct file.")
421
 
 
422
 
    def opt_help(opt, arg):
423
 
        global help_option
424
 
        help_option = 'h'
425
 
        SCons.Script.SConscript.print_help = 1
426
 
 
427
 
    Option(func = opt_help,
428
 
        short = 'h', long = ['help'],
429
 
        help = "Print defined help message, or this one.")
430
 
 
431
 
    def opt_help_options(opt, arg):
432
 
        global help_option
433
 
        help_option = 'H'
434
 
 
435
 
    Option(func = opt_help_options,
436
 
        short = 'H', long = ['help-options'],
437
 
        help = "Print this message and exit.")
438
 
 
439
 
    def opt_i(opt, arg):
440
 
        global ignore_errors
441
 
        ignore_errors = 1
442
 
 
443
 
    Option(func = opt_i,
444
 
        short = 'i', long = ['ignore-errors'],
445
 
        help = "Ignore errors from build actions.")
446
 
 
447
 
    def opt_I(opt, arg):
448
 
        global include_dirs
449
 
        include_dirs = include_dirs + [arg]
450
 
 
451
 
    Option(func = opt_I,
452
 
        short = 'I', long = ['include-dir'], arg = 'DIRECTORY',
453
 
        help = "Search DIRECTORY for imported Python modules.")
454
 
 
455
 
    def opt_j(opt, arg):
456
 
        global num_jobs
457
 
        try:
458
 
            num_jobs = int(arg)
459
 
        except:
460
 
            print UsageString()
461
 
            sys.exit(1)
462
 
 
463
 
        if num_jobs <= 0:
464
 
            print UsageString()
465
 
            sys.exit(1)
466
 
 
467
 
    Option(func = opt_j,
468
 
        short = 'j', long = ['jobs'], arg = 'N',
469
 
        help = "Allow N jobs at once.")
470
 
 
471
 
    def opt_k(opt, arg):
472
 
        global keep_going_on_error
473
 
        keep_going_on_error = 1
474
 
 
475
 
    Option(func = opt_k,
476
 
        short = 'k', long = ['keep-going'],
477
 
        help = "Keep going when a target can't be made.")
478
 
 
479
 
    Option(func = opt_not_yet, future = 1,
480
 
        short = 'l', long = ['load-average', 'max-load'], arg = 'N',
481
 
        help = "Don't start multiple jobs unless load is below N.")
482
 
 
483
 
    Option(func = opt_not_yet, future = 1,
484
 
        long = ['list-derived'],
485
 
        help = "Don't build; list files that would be built.")
486
 
 
487
 
    Option(func = opt_not_yet, future = 1,
488
 
        long = ['list-actions'],
489
 
        help = "Don't build; list files and build actions.")
490
 
 
491
 
    Option(func = opt_not_yet, future = 1,
492
 
        long = ['list-where'],
493
 
        help = "Don't build; list files and where defined.")
494
 
 
495
 
    def opt_n(opt, arg):
496
 
        SCons.Action.execute_actions = None
497
 
 
498
 
    Option(func = opt_n,
499
 
        short = 'n', long = ['no-exec', 'just-print', 'dry-run', 'recon'],
500
 
        help = "Don't build; just print commands.")
501
 
 
502
 
    Option(func = opt_not_yet, future = 1,
503
 
        short = 'o', long = ['old-file', 'assume-old'], arg = 'FILE',
504
 
        help = "Consider FILE to be old; don't rebuild it.")
505
 
 
506
 
    Option(func = opt_not_yet, future = 1,
507
 
        long = ['override'], arg = 'FILE',
508
 
        help = "Override variables as specified in FILE.")
509
 
 
510
 
    Option(func = opt_not_yet, future = 1,
511
 
        short = 'p',
512
 
        help = "Print internal environments/objects.")
513
 
 
514
 
    def opt_profile(opt, arg):
515
 
        sys.argv = filter(lambda x: x[0:10] != "--profile=", sys.argv)
516
 
        import profile
517
 
        profile.run('SCons.Script.main()', arg)
518
 
 
519
 
    Option(func = opt_profile,
520
 
        long = ['profile'], arg = 'FILE',
521
 
        help = "Profile SCons and put results in FILE.")
522
 
 
523
 
    def opt_q(opt, arg):
524
 
        global task_class
525
 
        task_class = QuestionTask
526
 
 
527
 
    Option(func = opt_q, future = 1,
528
 
        short = 'q', long = ['question'],
529
 
        help = "Don't build; exit status says if up to date.")
530
 
 
531
 
    Option(func = opt_not_yet, future = 1,
532
 
        short = 'rR', long = ['no-builtin-rules', 'no-builtin-variables'],
533
 
        help = "Clear default environments and variables.")
534
 
 
535
 
    Option(func = opt_not_yet, future = 1,
536
 
        long = ['random'],
537
 
        help = "Build dependencies in random order.")
538
 
 
539
 
    def opt_s(opt, arg):
540
 
        SCons.Action.print_actions = None
541
 
 
542
 
    Option(func = opt_s,
543
 
        short = 's', long = ['silent', 'quiet'],
544
 
        help = "Don't print commands.")
545
 
 
546
 
    def opt_u(opt, arg):
547
 
        global climb_up
548
 
        climb_up = 1
549
 
 
550
 
    Option(func = opt_u,
551
 
        short = 'u', long = ['up', 'search-up'],
552
 
        help = "Search up directory tree for SConstruct.")
553
 
 
554
 
    def opt_U(opt, arg):
555
 
        global climb_up
556
 
        climb_up = 2
557
 
 
558
 
    Option(func = opt_U,
559
 
        short = 'U',
560
 
        help = "Search up directory tree for SConstruct.")
561
 
 
562
 
    def option_v(opt, arg):
563
 
        import SCons
564
 
        print "SCons by Steven Knight et al.:"
565
 
        print "\tscript version 0.06"
566
 
        print "\tbuild engine version %s" % SCons.__version__
567
 
        print "Copyright 2001, 2002 Steven Knight"
568
 
        sys.exit(0)
569
 
 
570
 
    Option(func = option_v,
571
 
        short = 'v', long = ['version'],
572
 
        help = "Print the SCons version number and exit.")
573
 
 
574
 
    Option(func = opt_not_yet, future = 1,
575
 
        short = 'w', long = ['print-directory'],
576
 
        help = "Print the current directory.")
577
 
 
578
 
    Option(func = opt_not_yet, future = 1,
579
 
        long = ['no-print-directory'],
580
 
        help = "Turn off -w, even if it was turned on implicitly.")
581
 
 
582
 
    Option(func = opt_not_yet, future = 1,
583
 
        long = ['write-filenames'], arg = 'FILE',
584
 
        help = "Write all filenames examined into FILE.")
585
 
 
586
 
    Option(func = opt_not_yet, future = 1,
587
 
        short = 'W', long = ['what-if', 'new-file', 'assume-new'], arg = 'FILE',
588
 
        help = "Consider FILE to be changed.")
589
 
 
590
 
    Option(func = opt_not_yet, future = 1,
591
 
        long = ['warn-undefined-variables'],
592
 
        help = "Warn when an undefined variable is referenced.")
593
 
 
594
 
    Option(func = opt_not_yet, future = 1,
595
 
        short = 'Y', long = ['repository'], arg = 'REPOSITORY',
596
 
        help = "Search REPOSITORY for source and target files.")
597
 
 
598
 
    global short_opts
599
 
    global long_opts
600
 
    global opt_func
601
 
    for o in option_list:
602
 
        if o.short:
603
 
            if o.func:
604
 
                for c in o.short:
605
 
                    opt_func['-' + c] = o.func
606
 
            short_opts = short_opts + o.short
607
 
            if o.arg:
608
 
                short_opts = short_opts + ":"
609
 
        if o.long:
610
 
            if o.func:
611
 
                for l in o.long:
612
 
                    opt_func['--' + l] = o.func
613
 
            if o.arg:
614
 
                long_opts = long_opts + map(lambda a: a + "=", o.long)
615
 
            else:
616
 
                long_opts = long_opts + o.long
617
 
 
618
 
options_init()
619
 
 
620
 
 
 
394
            SCons.Warnings.suppressWarningClass(clazz)
621
395
 
622
396
def _SConstruct_exists(dirname=''):
623
397
    """This function checks that an SConstruct file exists in a directory.
624
398
    If so, it returns the path of the file. By default, it checks the
625
399
    current directory.
626
400
    """
 
401
    global repositories
627
402
    for file in ['SConstruct', 'Sconstruct', 'sconstruct']:
628
403
        sfile = os.path.join(dirname, file)
629
404
        if os.path.isfile(sfile):
630
405
            return sfile
 
406
        if not os.path.isabs(sfile):
 
407
            for rep in repositories:
 
408
                if os.path.isfile(os.path.join(rep, sfile)):
 
409
                    return sfile
631
410
    return None
632
411
 
633
 
 
634
 
def UsageString():
635
 
    help_opts = filter(lambda x: x.helpline, option_list)
636
 
    s = "Usage: scons [OPTION] [TARGET] ...\n" + "Options:\n" + \
637
 
        string.join(map(lambda x: x.helpline, help_opts), "\n") + "\n"
638
 
    return s
639
 
 
640
 
 
641
 
 
642
 
def _main():
643
 
    global scripts, num_jobs, task_class, calc, target_top
644
 
 
 
412
def _set_globals(options):
 
413
    global repositories, keep_going_on_error, ignore_errors
 
414
    global print_count, print_dtree
 
415
    global print_explanations, print_includes
 
416
    global print_objects, print_time, print_tree
 
417
    global memory_outf, memory_stats
 
418
 
 
419
    if options.repository:
 
420
        repositories.extend(options.repository)
 
421
    keep_going_on_error = options.keep_going
 
422
    try:
 
423
        if options.debug:
 
424
            if options.debug == "count":
 
425
                print_count = 1
 
426
            elif options.debug == "dtree":
 
427
                print_dtree = 1
 
428
            elif options.debug == "explain":
 
429
                print_explanations = 1
 
430
            elif options.debug == "includes":
 
431
                print_includes = 1
 
432
            elif options.debug == "memory":
 
433
                memory_stats = []
 
434
                memory_outf = sys.stdout
 
435
            elif options.debug == "objects":
 
436
                print_objects = 1
 
437
            elif options.debug == "presub":
 
438
                SCons.Action.print_actions_presub = 1
 
439
            elif options.debug == "time":
 
440
                print_time = 1
 
441
            elif options.debug == "tree":
 
442
                print_tree = 1
 
443
    except AttributeError:
 
444
        pass
 
445
    ignore_errors = options.ignore_errors
 
446
 
 
447
def _create_path(plist):
 
448
    path = '.'
 
449
    for d in plist:
 
450
        if os.path.isabs(d):
 
451
            path = d
 
452
        else:
 
453
            path = path + '/' + d
 
454
    return path
 
455
 
 
456
 
 
457
class OptParser(OptionParser):
 
458
    def __init__(self):
 
459
        import __main__
 
460
        import SCons
 
461
        parts = ["SCons by Steven Knight et al.:\n"]
 
462
        try:
 
463
            parts.append("\tscript: v%s.%s, %s, by %s on %s\n" % (__main__.__version__,
 
464
                                                                  __main__.__build__,
 
465
                                                                  __main__.__date__,
 
466
                                                                  __main__.__developer__,
 
467
                                                                  __main__.__buildsys__))
 
468
        except KeyboardInterrupt:
 
469
            raise
 
470
        except:
 
471
            # On win32 there is no scons.py, so there is no __main__.__version__,
 
472
            # hence there is no script version.
 
473
            pass 
 
474
        parts.append("\tengine: v%s.%s, %s, by %s on %s\n" % (SCons.__version__,
 
475
                                                              SCons.__build__,
 
476
                                                              SCons.__date__,
 
477
                                                              SCons.__developer__,
 
478
                                                              SCons.__buildsys__))
 
479
        parts.append("Copyright (c) 2001, 2002, 2003, 2004 The SCons Foundation")
 
480
        OptionParser.__init__(self, version=string.join(parts, ''),
 
481
                              usage="usage: scons [OPTION] [TARGET] ...")
 
482
 
 
483
        # options ignored for compatibility
 
484
        def opt_ignore(option, opt, value, parser):
 
485
            sys.stderr.write("Warning:  ignoring %s option\n" % opt)
 
486
        self.add_option("-b", "-m", "-S", "-t", "--no-keep-going", "--stop",
 
487
                        "--touch", action="callback", callback=opt_ignore,
 
488
                        help="Ignored for compatibility.")
 
489
 
 
490
        self.add_option('-c', '--clean', '--remove', action="store_true",
 
491
                        dest="clean",
 
492
                        help="Remove specified targets and dependencies.")
 
493
 
 
494
        self.add_option('-C', '--directory', type="string", action = "append",
 
495
                        metavar="DIR",
 
496
                        help="Change to DIR before doing anything.")
 
497
 
 
498
        self.add_option('--cache-disable', '--no-cache',
 
499
                        action="store_true", dest='cache_disable', default=0,
 
500
                        help="Do not retrieve built targets from CacheDir.")
 
501
 
 
502
        self.add_option('--cache-force', '--cache-populate',
 
503
                        action="store_true", dest='cache_force', default=0,
 
504
                        help="Copy already-built targets into the CacheDir.")
 
505
 
 
506
        self.add_option('--cache-show',
 
507
                        action="store_true", dest='cache_show', default=0,
 
508
                        help="Print build actions for files from CacheDir.")
 
509
 
 
510
        def opt_not_yet(option, opt, value, parser):
 
511
            sys.stderr.write("Warning:  the %s option is not yet implemented\n" % opt)
 
512
            sys.exit(0)
 
513
        self.add_option('-d', action="callback",
 
514
                        callback=opt_not_yet,
 
515
                        help = "Print file dependency information.")
 
516
        
 
517
        self.add_option('-D', action="store_const", const=2, dest="climb_up",
 
518
                        help="Search up directory tree for SConstruct,       "
 
519
                             "build all Default() targets.")
 
520
 
 
521
        debug_options = ["count", "dtree", "explain",
 
522
                         "includes", "memory", "objects",
 
523
                         "pdb", "presub", "time", "tree"]
 
524
 
 
525
        def opt_debug(option, opt, value, parser, debug_options=debug_options):
 
526
            if value in debug_options:
 
527
                parser.values.debug = value
 
528
            else:
 
529
                raise OptionValueError("Warning:  %s is not a valid debug type" % value)
 
530
        self.add_option('--debug', action="callback", type="string",
 
531
                        callback=opt_debug, nargs=1, dest="debug",
 
532
                        metavar="TYPE",
 
533
                        help="Print various types of debugging information: "
 
534
                             "%s." % string.join(debug_options, ", "))
 
535
 
 
536
        def opt_duplicate(option, opt, value, parser):
 
537
            if not value in SCons.Node.FS.Valid_Duplicates:
 
538
                raise OptionValueError("`%s' is not a valid duplication style." % value)
 
539
            parser.values.duplicate = value
 
540
            # Set the duplicate style right away so it can affect linking
 
541
            # of SConscript files.
 
542
            SCons.Node.FS.set_duplicate(value)
 
543
        self.add_option('--duplicate', action="callback", type="string",
 
544
                        callback=opt_duplicate, nargs=1, dest="duplicate",
 
545
                        help="Set the preferred duplication methods. Must be one of "
 
546
                        + string.join(SCons.Node.FS.Valid_Duplicates, ", "))
 
547
 
 
548
        self.add_option('-f', '--file', '--makefile', '--sconstruct',
 
549
                        action="append", nargs=1,
 
550
                        help="Read FILE as the top-level SConstruct file.")
 
551
 
 
552
        self.add_option('-h', '--help', action="store_true", default=0,
 
553
                        dest="help_msg",
 
554
                        help="Print defined help message, or this one.")
 
555
 
 
556
        self.add_option("-H", "--help-options",
 
557
                        action="help",
 
558
                        help="Print this message and exit.")
 
559
 
 
560
        self.add_option('-i', '--ignore-errors', action="store_true",
 
561
                        default=0, dest='ignore_errors',
 
562
                        help="Ignore errors from build actions.")
 
563
 
 
564
        self.add_option('-I', '--include-dir', action="append",
 
565
                        dest='include_dir', metavar="DIR",
 
566
                        help="Search DIR for imported Python modules.")
 
567
 
 
568
        self.add_option('--implicit-cache', action="store_true",
 
569
                        dest='implicit_cache',
 
570
                        help="Cache implicit dependencies")
 
571
 
 
572
        self.add_option('--implicit-deps-changed', action="store_true",
 
573
                        default=0, dest='implicit_deps_changed',
 
574
                        help="Ignore cached implicit dependencies.")
 
575
        self.add_option('--implicit-deps-unchanged', action="store_true",
 
576
                        default=0, dest='implicit_deps_unchanged',
 
577
                        help="Ignore changes in implicit dependencies.")
 
578
 
 
579
        def opt_j(option, opt, value, parser):
 
580
            value = int(value)
 
581
            parser.values.num_jobs = value
 
582
        self.add_option('-j', '--jobs', action="callback", type="int",
 
583
                        callback=opt_j, metavar="N",
 
584
                        help="Allow N jobs at once.")
 
585
 
 
586
        self.add_option('-k', '--keep-going', action="store_true", default=0,
 
587
                        dest='keep_going',
 
588
                        help="Keep going when a target can't be made.")
 
589
 
 
590
        self.add_option('--max-drift', type="int", action="store",
 
591
                        dest='max_drift', metavar="N",
 
592
                        help="Set maximum system clock drift to N seconds.")
 
593
 
 
594
        self.add_option('-n', '--no-exec', '--just-print', '--dry-run',
 
595
                        '--recon', action="store_true", dest='noexec',
 
596
                        default=0, help="Don't build; just print commands.")
 
597
 
 
598
        def opt_profile(option, opt, value, parser):
 
599
            global profiling
 
600
            if not profiling:
 
601
                profiling = 1
 
602
                import profile
 
603
                profile.run('SCons.Script.main()', value)
 
604
                sys.exit(exit_status)
 
605
        self.add_option('--profile', nargs=1, action="callback",
 
606
                        callback=opt_profile, type="string", dest="profile",
 
607
                        metavar="FILE",
 
608
                        help="Profile SCons and put results in FILE.")
 
609
 
 
610
        self.add_option('-q', '--question', action="store_true", default=0,
 
611
                        help="Don't build; exit status says if up to date.")
 
612
 
 
613
        self.add_option('-Q', dest='no_progress', action="store_true",
 
614
                        default=0,
 
615
                        help="Suppress \"Reading/Building\" progress messages.")
 
616
 
 
617
        self.add_option('--random', dest="random", action="store_true",
 
618
                        default=0, help="Build dependencies in random order.")
 
619
 
 
620
        self.add_option('-s', '--silent', '--quiet', action="store_true",
 
621
                        default=0, help="Don't print commands.")
 
622
 
 
623
        self.add_option('-u', '--up', '--search-up', action="store_const",
 
624
                        dest="climb_up", default=0, const=1,
 
625
                        help="Search up directory tree for SConstruct,       "
 
626
                             "build targets at or below current directory.")
 
627
        self.add_option('-U', action="store_const", dest="climb_up",
 
628
                        default=0, const=3,
 
629
                        help="Search up directory tree for SConstruct,       "
 
630
                             "build Default() targets from local SConscript.")
 
631
 
 
632
        self.add_option("-v", "--version",
 
633
                        action="version",
 
634
                        help="Print the SCons version number and exit.")
 
635
 
 
636
        self.add_option('--warn', '--warning', nargs=1, action="store",
 
637
                        metavar="WARNING-SPEC",
 
638
                        help="Enable or disable warnings.")
 
639
 
 
640
        self.add_option('-Y', '--repository', nargs=1, action="append",
 
641
                        help="Search REPOSITORY for source and target files.")
 
642
 
 
643
        self.add_option('-e', '--environment-overrides', action="callback",
 
644
                        callback=opt_not_yet,
 
645
                        # help="Environment variables override makefiles."
 
646
                        help=SUPPRESS_HELP)
 
647
        self.add_option('-l', '--load-average', '--max-load', action="callback",
 
648
                        callback=opt_not_yet, type="int", dest="load_average",
 
649
                        # action="store",
 
650
                        # help="Don't start multiple jobs unless load is below "
 
651
                        #      "LOAD-AVERAGE."
 
652
                        # type="int",
 
653
                        help=SUPPRESS_HELP)
 
654
        self.add_option('--list-derived', action="callback",
 
655
                        callback=opt_not_yet,
 
656
                        # help="Don't build; list files that would be built."
 
657
                        help=SUPPRESS_HELP)
 
658
        self.add_option('--list-actions', action="callback",
 
659
                        callback=opt_not_yet,
 
660
                        # help="Don't build; list files and build actions."
 
661
                        help=SUPPRESS_HELP)
 
662
        self.add_option('--list-where', action="callback",
 
663
                        callback=opt_not_yet,
 
664
                        # help="Don't build; list files and where defined."
 
665
                        help=SUPPRESS_HELP)
 
666
        self.add_option('-o', '--old-file', '--assume-old', action="callback",
 
667
                        callback=opt_not_yet, type="string", dest="old_file",
 
668
                        # help = "Consider FILE to be old; don't rebuild it."
 
669
                        help=SUPPRESS_HELP)
 
670
        self.add_option('--override', action="callback", dest="override",
 
671
                        callback=opt_not_yet, type="string",
 
672
                        # help="Override variables as specified in FILE."
 
673
                        help=SUPPRESS_HELP)
 
674
        self.add_option('-p', action="callback",
 
675
                        callback=opt_not_yet,
 
676
                        # help="Print internal environments/objects."
 
677
                        help=SUPPRESS_HELP)
 
678
        self.add_option('-r', '-R', '--no-builtin-rules',
 
679
                        '--no-builtin-variables', action="callback",
 
680
                        callback=opt_not_yet,
 
681
                        # help="Clear default environments and variables."
 
682
                        help=SUPPRESS_HELP)
 
683
        self.add_option('-w', '--print-directory', action="callback",
 
684
                        callback=opt_not_yet,
 
685
                        # help="Print the current directory."
 
686
                        help=SUPPRESS_HELP)
 
687
        self.add_option('--no-print-directory', action="callback",
 
688
                        callback=opt_not_yet,
 
689
                        # help="Turn off -w, even if it was turned on implicitly."
 
690
                        help=SUPPRESS_HELP)
 
691
        self.add_option('--write-filenames', action="callback",
 
692
                        callback=opt_not_yet, type="string", dest="write_filenames",
 
693
                        # help="Write all filenames examined into FILE."
 
694
                        help=SUPPRESS_HELP)
 
695
        self.add_option('-W', '--what-if', '--new-file', '--assume-new',
 
696
                        dest="new_file",
 
697
                        action="callback", callback=opt_not_yet, type="string",
 
698
                        # help="Consider FILE to be changed."
 
699
                        help=SUPPRESS_HELP)
 
700
        self.add_option('--warn-undefined-variables', action="callback",
 
701
                        callback=opt_not_yet,
 
702
                        # help="Warn when an undefined variable is referenced."
 
703
                        help=SUPPRESS_HELP)
 
704
 
 
705
    def parse_args(self, args=None, values=None):
 
706
        opt, arglist = OptionParser.parse_args(self, args, values)
 
707
        if opt.implicit_deps_changed or opt.implicit_deps_unchanged:
 
708
            opt.implicit_cache = 1
 
709
        return opt, arglist
 
710
 
 
711
class SConscriptSettableOptions:
 
712
    """This class wraps an OptParser instance and provides
 
713
    uniform access to options that can be either set on the command
 
714
    line or from a SConscript file. A value specified on the command
 
715
    line always overrides a value set in a SConscript file.
 
716
    Not all command line options are SConscript settable, and the ones
 
717
    that are must be explicitly added to settable dictionary and optionally
 
718
    validated and coerced in the set() method."""
 
719
    
 
720
    def __init__(self, options):
 
721
        self.options = options
 
722
 
 
723
        # This dictionary stores the defaults for all the SConscript
 
724
        # settable options, as well as indicating which options
 
725
        # are SConscript settable. 
 
726
        self.settable = {'num_jobs':1,
 
727
                         'max_drift':SCons.Sig.default_max_drift,
 
728
                         'implicit_cache':0,
 
729
                         'clean':0,
 
730
                         'duplicate':'hard-soft-copy'}
 
731
 
 
732
    def get(self, name):
 
733
        if not self.settable.has_key(name):
 
734
            raise SCons.Error.UserError, "This option is not settable from a SConscript file: %s"%name
 
735
        if hasattr(self.options, name) and getattr(self.options, name) is not None:
 
736
            return getattr(self.options, name)
 
737
        else:
 
738
            return self.settable[name]
 
739
 
 
740
    def set(self, name, value):
 
741
        if not self.settable.has_key(name):
 
742
            raise SCons.Error.UserError, "This option is not settable from a SConscript file: %s"%name
 
743
 
 
744
        if name == 'num_jobs':
 
745
            try:
 
746
                value = int(value)
 
747
                if value < 1:
 
748
                    raise ValueError
 
749
            except ValueError:
 
750
                raise SCons.Errors.UserError, "A positive integer is required: %s"%repr(value)
 
751
        elif name == 'max_drift':
 
752
            try:
 
753
                value = int(value)
 
754
            except ValueError:
 
755
                raise SCons.Errors.UserError, "An integer is required: %s"%repr(value)
 
756
        elif name == 'duplicate':
 
757
            try:
 
758
                value = str(value)
 
759
            except ValueError:
 
760
                raise SCons.Errors.UserError, "A string is required: %s"%repr(value)
 
761
            if not value in SCons.Node.FS.Valid_Duplicates:
 
762
                raise SCons.Errors.UserError, "Not a valid duplication style: %s" % value
 
763
            # Set the duplicate stye right away so it can affect linking
 
764
            # of SConscript files.
 
765
            SCons.Node.FS.set_duplicate(value)
 
766
 
 
767
        self.settable[name] = value
 
768
    
 
769
 
 
770
def _main(args, parser):
645
771
    targets = []
646
 
 
647
 
    # It looks like 2.0 changed the name of the exception class
648
 
    # raised by getopt.
649
 
    try:
650
 
        getopt_err = getopt.GetoptError
651
 
    except:
652
 
        getopt_err = getopt.error
653
 
 
654
 
    try:
655
 
        cmd_opts, t = getopt.getopt(string.split(os.environ['SCONSFLAGS']),
656
 
                                          short_opts, long_opts)
657
 
    except KeyError:
658
 
        # It's all right if there's no SCONSFLAGS environment variable.
659
 
        pass
660
 
    except getopt_err, x:
661
 
        _scons_user_warning("SCONSFLAGS " + str(x))
662
 
    else:
663
 
        for opt, arg in cmd_opts:
664
 
            opt_func[opt](opt, arg)
665
 
 
666
 
    try:
667
 
        cmd_opts, args = getopt.getopt(sys.argv[1:], short_opts, long_opts)
668
 
    except getopt_err, x:
669
 
        _scons_user_error(x)
670
 
    else:
671
 
        for opt, arg in cmd_opts:
672
 
            opt_func[opt](opt, arg)
673
 
        xmit_args = []
674
 
        for a in args:
675
 
            if '=' in a:
676
 
                xmit_args.append(a)
677
 
            else:
678
 
                targets.append(a)
679
 
        SCons.Script.SConscript._scons_add_args(xmit_args)
680
 
 
681
 
    if climb_up:
682
 
        target_top = ''  # directory to prepend to targets
 
772
    fs = SCons.Node.FS.default_fs
 
773
 
 
774
    # Enable deprecated warnings by default.
 
775
    SCons.Warnings._warningOut = _scons_internal_warning
 
776
    SCons.Warnings.enableWarningClass(SCons.Warnings.CorruptSConsignWarning)
 
777
    SCons.Warnings.enableWarningClass(SCons.Warnings.DeprecatedWarning)
 
778
    SCons.Warnings.enableWarningClass(SCons.Warnings.DuplicateEnvironmentWarning)
 
779
    SCons.Warnings.enableWarningClass(SCons.Warnings.MissingSConscriptWarning)
 
780
    SCons.Warnings.enableWarningClass(SCons.Warnings.NoParallelSupportWarning)
 
781
    # This is good for newbies, and hopefully most everyone else too.
 
782
    SCons.Warnings.enableWarningClass(SCons.Warnings.MisleadingKeywordsWarning)
 
783
 
 
784
    global ssoptions
 
785
    ssoptions = SConscriptSettableOptions(options)
 
786
 
 
787
    if options.help_msg:
 
788
        def raisePrintHelp(text):
 
789
            raise PrintHelp, text
 
790
        SCons.Script.SConscript.HelpFunction = raisePrintHelp
 
791
 
 
792
    _set_globals(options)
 
793
    SCons.Node.implicit_cache = options.implicit_cache
 
794
    SCons.Node.implicit_deps_changed = options.implicit_deps_changed
 
795
    SCons.Node.implicit_deps_unchanged = options.implicit_deps_unchanged
 
796
    if options.warn:
 
797
        _setup_warn(options.warn)
 
798
    if options.noexec:
 
799
        SCons.SConf.dryrun = 1
 
800
        SCons.Action.execute_actions = None
 
801
        CleanTask.execute = CleanTask.show
 
802
    if options.question:
 
803
        SCons.SConf.dryrun = 1
 
804
 
 
805
    if options.no_progress or options.silent:
 
806
        progress_display.set_mode(0)
 
807
    if options.silent:
 
808
        display.set_mode(0)
 
809
    if options.silent:
 
810
        SCons.Action.print_actions = None
 
811
    if options.cache_disable:
 
812
        def disable(self): pass
 
813
        fs.CacheDir = disable
 
814
    if options.cache_force:
 
815
        fs.cache_force = 1
 
816
    if options.cache_show:
 
817
        fs.cache_show = 1
 
818
    if options.directory:
 
819
        cdir = _create_path(options.directory)
 
820
        try:
 
821
            os.chdir(cdir)
 
822
        except OSError:
 
823
            sys.stderr.write("Could not change directory to %s\n" % cdir)
 
824
 
 
825
    xmit_args = []
 
826
    for a in args:
 
827
        if '=' in a:
 
828
            xmit_args.append(a)
 
829
        else:
 
830
            targets.append(a)
 
831
    SCons.Script.SConscript._scons_add_args(xmit_args)
 
832
    SCons.Script.SConscript._scons_add_targets(targets)
 
833
 
 
834
    target_top = None
 
835
    if options.climb_up:
 
836
        target_top = '.'  # directory to prepend to targets
683
837
        script_dir = os.getcwd()  # location of script
684
838
        while script_dir and not _SConstruct_exists(script_dir):
685
839
            script_dir, last_part = os.path.split(script_dir)
688
842
            else:
689
843
                script_dir = ''
690
844
        if script_dir:
691
 
            print "scons: Entering directory %s" % script_dir
 
845
            display("scons: Entering directory `%s'" % script_dir)
692
846
            os.chdir(script_dir)
693
847
        else:
694
 
            raise UserError, "No SConstruct file found."
695
 
        
696
 
    SCons.Node.FS.default_fs.set_toplevel_dir(os.getcwd())
697
 
 
 
848
            raise SCons.Errors.UserError, "No SConstruct file found."
 
849
 
 
850
    fs.set_toplevel_dir(os.getcwd())
 
851
 
 
852
    scripts = []
 
853
    if options.file:
 
854
        scripts.extend(options.file)
698
855
    if not scripts:
699
856
        sfile = _SConstruct_exists()
700
857
        if sfile:
701
858
            scripts.append(sfile)
702
859
 
703
 
    if help_option == 'H':
704
 
        print UsageString()
705
 
        sys.exit(0)
706
 
 
707
 
    if not scripts:
708
 
        if help_option == 'h':
 
860
    if options.help_msg:
 
861
        if not scripts:
709
862
            # There's no SConstruct, but they specified -h.
710
863
            # Give them the options usage now, before we fail
711
864
            # trying to read a non-existent SConstruct file.
712
 
            print UsageString()
713
 
            sys.exit(0)
714
 
        else:
715
 
            raise UserError, "No SConstruct file found."
 
865
            parser.print_help()
 
866
            sys.exit(0)
 
867
        SCons.Script.SConscript.print_help = 1
 
868
 
 
869
    if not scripts:
 
870
        raise SCons.Errors.UserError, "No SConstruct file found."
 
871
 
 
872
    if scripts[0] == "-":
 
873
        d = fs.getcwd()
 
874
    else:
 
875
        d = fs.File(scripts[0]).dir
 
876
    fs.set_SConstruct_dir(d)
716
877
 
717
878
    class Unbuffered:
718
879
        def __init__(self, file):
725
886
 
726
887
    sys.stdout = Unbuffered(sys.stdout)
727
888
 
728
 
    sys.path = include_dirs + sys.path
729
 
 
730
 
    for script in scripts:
731
 
        SCons.Script.SConscript.SConscript(script)
732
 
 
733
 
    SCons.Node.FS.default_fs.chdir(SCons.Node.FS.default_fs.Top)
734
 
 
735
 
    if help_option == 'h':
736
 
        # They specified -h, but there was no Help() inside the
737
 
        # SConscript files.  Give them the options usage.
738
 
        print UsageString()
739
 
        sys.exit(0)
740
 
 
741
 
    if target_top:
742
 
        if climb_up == 2 and not targets:
743
 
            # -U with default targets
 
889
    if options.include_dir:
 
890
        sys.path = options.include_dir + sys.path
 
891
 
 
892
    global repositories
 
893
    for rep in repositories:
 
894
        fs.Repository(rep)
 
895
 
 
896
    if not memory_stats is None: memory_stats.append(SCons.Debug.memory())
 
897
 
 
898
    progress_display("scons: Reading SConscript files ...")
 
899
    try:
 
900
        start_time = time.time()
 
901
        try:
 
902
            for script in scripts:
 
903
                SCons.Script.SConscript._SConscript(fs, script)
 
904
        except SCons.Errors.StopError, e:
 
905
            # We had problems reading an SConscript file, such as it
 
906
            # couldn't be copied in to the BuildDir.  Since we're just
 
907
            # reading SConscript files and haven't started building
 
908
            # things yet, stop regardless of whether they used -i or -k
 
909
            # or anything else, but don't say "Stop." on the message.
 
910
            global exit_status
 
911
            sys.stderr.write("scons: *** %s\n" % e)
 
912
            exit_status = 2
 
913
            sys.exit(exit_status)
 
914
        global sconscript_time
 
915
        sconscript_time = time.time() - start_time
 
916
    except PrintHelp, text:
 
917
        progress_display("scons: done reading SConscript files.")
 
918
        print text
 
919
        print "Use scons -H for help about command-line options."
 
920
        sys.exit(0)
 
921
    progress_display("scons: done reading SConscript files.")
 
922
 
 
923
    # Tell the Node.FS subsystem that we're all done reading the
 
924
    # SConscript files and calling Repository() and BuildDir() and the
 
925
    # like, so it can go ahead and start memoizing the string values of
 
926
    # file system nodes.
 
927
    SCons.Node.FS.save_strings(1)
 
928
 
 
929
    if not memory_stats is None: memory_stats.append(SCons.Debug.memory())
 
930
 
 
931
    fs.chdir(fs.Top)
 
932
 
 
933
    if options.help_msg:
 
934
        # They specified -h, but there was no Help() inside the
 
935
        # SConscript files.  Give them the options usage.
 
936
        parser.print_help(sys.stdout)
 
937
        sys.exit(0)
 
938
 
 
939
    # Now that we've read the SConscripts we can set the options
 
940
    # that are SConscript settable:
 
941
    SCons.Node.implicit_cache = ssoptions.get('implicit_cache')
 
942
    SCons.Node.FS.set_duplicate(ssoptions.get('duplicate'))
 
943
 
 
944
    lookup_top = None
 
945
    if targets:
 
946
        # They specified targets on the command line, so if they
 
947
        # used -u, -U or -D, we have to look up targets relative
 
948
        # to the top, but we build whatever they specified.
 
949
        if target_top:
 
950
            lookup_top = fs.Dir(target_top)
744
951
            target_top = None
 
952
    else:
 
953
        # There are no targets specified on the command line,
 
954
        # so if they used -u, -U or -D, we may have to restrict
 
955
        # what actually gets built.
 
956
        d = None
 
957
        if target_top:
 
958
            if options.climb_up == 1:
 
959
                # -u, local directory and below
 
960
                target_top = fs.Dir(target_top)
 
961
                lookup_top = target_top
 
962
            elif options.climb_up == 2:
 
963
                # -D, all Default() targets
 
964
                target_top = None
 
965
                lookup_top = None
 
966
            elif options.climb_up == 3:
 
967
                # -U, local SConscript Default() targets
 
968
                target_top = fs.Dir(target_top)
 
969
                def check_dir(x, target_top=target_top):
 
970
                    if hasattr(x, 'cwd') and not x.cwd is None:
 
971
                        cwd = x.cwd.srcnode()
 
972
                        return cwd == target_top
 
973
                    else:
 
974
                        # x doesn't have a cwd, so it's either not a target,
 
975
                        # or not a file, so go ahead and keep it as a default
 
976
                        # target and let the engine sort it out:
 
977
                        return 1                
 
978
                d = filter(check_dir, SCons.Script.SConscript.DefaultTargets)
 
979
                SCons.Script.SConscript.DefaultTargets[:] = d
 
980
                target_top = None
 
981
                lookup_top = None
 
982
 
 
983
        if SCons.Script.SConscript.DefaultCalled:
 
984
            targets = SCons.Script.SConscript.DefaultTargets
745
985
        else:
746
 
            target_top = SCons.Node.FS.default_fs.Dir(target_top)
 
986
            if d is None:
 
987
                d = [fs.Dir('.')]
 
988
            targets = d
 
989
 
747
990
 
748
991
    if not targets:
749
 
        targets = SCons.Script.SConscript.default_targets
 
992
        sys.stderr.write("scons: *** No targets specified and no Default() targets found.  Stop.\n")
 
993
        sys.exit(2)
750
994
 
751
 
    def Entry(x, top = target_top):
 
995
    def Entry(x, ltop=lookup_top, ttop=target_top, fs=fs):
752
996
        if isinstance(x, SCons.Node.Node):
753
997
            node = x
754
998
        else:
755
 
            try:
756
 
                node = SCons.Node.Alias.default_ans.lookup(x)
757
 
                if node is None:
758
 
                    node = SCons.Node.FS.default_fs.Entry(x,
759
 
                                                          directory = top,
760
 
                                                          create = 0)
761
 
            except UserError:
762
 
                string = "scons: *** Do not know how to make target `%s'." % x
763
 
                if not keep_going_on_error:
764
 
                    sys.stderr.write(string + "  Stop.\n")
765
 
                    sys.exit(2)
766
 
                sys.stderr.write(string + "\n")
767
 
                node = None
768
 
        if top and not node.is_under(top):
769
 
            if isinstance(node, SCons.Node.FS.Dir) and top.is_under(node):
770
 
                node = top
 
999
            node = SCons.Node.Alias.default_ans.lookup(x)
 
1000
            if node is None:
 
1001
                node = fs.Entry(x, directory=ltop, create=1)
 
1002
        if ttop and not node.is_under(ttop):
 
1003
            if isinstance(node, SCons.Node.FS.Dir) and ttop.is_under(node):
 
1004
                node = ttop
771
1005
            else:
772
1006
                node = None
773
1007
        return node
774
1008
 
775
1009
    nodes = filter(lambda x: x is not None, map(Entry, targets))
776
1010
 
777
 
    if not calc:
778
 
        calc = SCons.Sig.Calculator(SCons.Sig.MD5)
779
 
 
780
 
    taskmaster = SCons.Taskmaster.Taskmaster(nodes, task_class, calc)
781
 
 
782
 
    jobs = SCons.Job.Jobs(num_jobs, taskmaster)
783
 
    jobs.start()
784
 
    jobs.wait()
785
 
 
786
 
    SCons.Sig.write()
 
1011
    task_class = BuildTask      # default action is to build targets
 
1012
    opening_message = "Building targets ..."
 
1013
    closing_message = "done building targets."
 
1014
    failure_message = "building terminated because of errors."
 
1015
    if options.question:
 
1016
        task_class = QuestionTask
 
1017
    try:
 
1018
        if ssoptions.get('clean'):
 
1019
            task_class = CleanTask
 
1020
            opening_message = "Cleaning targets ..."
 
1021
            closing_message = "done cleaning targets."
 
1022
            failure_message = "cleaning terminated because of errors."
 
1023
    except AttributeError:
 
1024
        pass
 
1025
 
 
1026
    SCons.Environment.CalculatorArgs['max_drift'] = ssoptions.get('max_drift')
 
1027
 
 
1028
    if options.random:
 
1029
        def order(dependencies):
 
1030
            """Randomize the dependencies."""
 
1031
            # This is cribbed from the implementation of
 
1032
            # random.shuffle() in Python 2.X.
 
1033
            d = dependencies
 
1034
            for i in xrange(len(d)-1, 0, -1):
 
1035
                j = int(random.random() * (i+1))
 
1036
                d[i], d[j] = d[j], d[i]
 
1037
            return d
 
1038
    else:
 
1039
        def order(dependencies):
 
1040
            """Leave the order of dependencies alone."""
 
1041
            return dependencies
 
1042
 
 
1043
    progress_display("scons: " + opening_message)
 
1044
    taskmaster = SCons.Taskmaster.Taskmaster(nodes, task_class, order)
 
1045
 
 
1046
    nj = ssoptions.get('num_jobs')
 
1047
    jobs = SCons.Job.Jobs(nj, taskmaster)
 
1048
    if nj > 1 and jobs.num_jobs == 1:
 
1049
        msg = "parallel builds are unsupported by this version of Python;\n" + \
 
1050
              "\tignoring -j or num_jobs option.\n"
 
1051
        SCons.Warnings.warn(SCons.Warnings.NoParallelSupportWarning, msg)
 
1052
 
 
1053
    if not memory_stats is None: memory_stats.append(SCons.Debug.memory())
 
1054
 
 
1055
    try:
 
1056
        jobs.run()
 
1057
    finally:
 
1058
        if exit_status:
 
1059
            progress_display("scons: " + failure_message)
 
1060
        else:
 
1061
            progress_display("scons: " + closing_message)
 
1062
        if not options.noexec:
 
1063
            SCons.SConsign.write()
 
1064
 
 
1065
    if not memory_stats is None:
 
1066
        memory_stats.append(SCons.Debug.memory())
 
1067
        when = [
 
1068
            'before SConscript files',
 
1069
            'after SConscript files',
 
1070
            'before building',
 
1071
            'after building',
 
1072
        ]
 
1073
        for i in xrange(len(when)):
 
1074
            memory_outf.write('Memory %s:  %d\n' % (when[i], memory_stats[i]))
 
1075
 
 
1076
    if print_count:
 
1077
        SCons.Debug.countLoggedInstances('*')
 
1078
 
 
1079
    if print_objects:
 
1080
        SCons.Debug.listLoggedInstances('*')
 
1081
        #SCons.Debug.dumpLoggedInstances('*')
 
1082
 
 
1083
def _exec_main():
 
1084
    all_args = sys.argv[1:]
 
1085
    try:
 
1086
        all_args = string.split(os.environ['SCONSFLAGS']) + all_args
 
1087
    except KeyError:
 
1088
            # it's OK if there's no SCONSFLAGS
 
1089
            pass
 
1090
    parser = OptParser()
 
1091
    global options
 
1092
    options, args = parser.parse_args(all_args)
 
1093
    if options.debug == "pdb":
 
1094
        import pdb
 
1095
        pdb.Pdb().runcall(_main, args, parser)
 
1096
    else:
 
1097
        _main(args, parser)
787
1098
 
788
1099
def main():
 
1100
    global exit_status
 
1101
    
789
1102
    try:
790
 
        _main()
791
 
    except SystemExit:
792
 
        pass
 
1103
        _exec_main()
 
1104
    except SystemExit, s:
 
1105
        if s:
 
1106
            exit_status = s
793
1107
    except KeyboardInterrupt:
794
1108
        print "Build interrupted."
795
 
        sys.exit(1)
 
1109
        sys.exit(2)
796
1110
    except SyntaxError, e:
797
1111
        _scons_syntax_error(e)
798
 
    except UserError, e:
 
1112
    except SCons.Errors.InternalError:
 
1113
        _scons_internal_error()
 
1114
    except SCons.Errors.UserError, e:
799
1115
        _scons_user_error(e)
 
1116
    except SCons.Errors.ConfigureDryRunError, e:
 
1117
        _scons_configure_dryrun_error(e)
800
1118
    except:
801
 
        _scons_other_errors()
 
1119
        # An exception here is likely a builtin Python exception Python
 
1120
        # code in an SConscript file.  Show them precisely what the
 
1121
        # problem was and where it happened.
 
1122
        SCons.Script.SConscript.SConscript_exception()
 
1123
        sys.exit(2)
 
1124
 
 
1125
    if print_time:
 
1126
        total_time = time.time()-start_time
 
1127
        scons_time = total_time-sconscript_time-command_time
 
1128
        print "Total build time: %f seconds"%total_time
 
1129
        print "Total SConscript file execution time: %f seconds"%sconscript_time
 
1130
        print "Total SCons execution time: %f seconds"%scons_time
 
1131
        print "Total command execution time: %f seconds"%command_time
802
1132
 
803
1133
    sys.exit(exit_status)