6 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
17 from SCons.Errors import *
22 # Modules and classes that we don't use directly in this script, but
23 # which we want available for use in SConstruct and SConscript files.
25 from SCons.Environment import Environment
26 from SCons.Builder import Builder
29 "XXX: this is here only until the build engine is implemented"
31 def __init__(self, target):
40 "XXX: this is here only until the build engine is implemented"
42 def __init__(self, targets):
43 self.targets = targets
48 if self.num_iterated == len(self.targets):
51 current = self.num_iterated
52 self.num_iterated = self.num_iterated + 1
53 return Task(self.targets[current])
58 def executed(self, task):
61 def failed(self, task):
75 def _scons_syntax_error(e):
76 """Handle syntax errors. Print out a message and show where the error
79 etype, value, tb = sys.exc_info()
80 lines = traceback.format_exception_only(etype, value)
82 sys.stderr.write(line+'\n')
84 def _scons_user_error(e):
85 """Handle user errors. Print out a message and a description of the
86 error, along with the line number and routine where it occured.
88 etype, value, tb = sys.exc_info()
89 while tb.tb_next is not None:
91 lineno = traceback.tb_lineno(tb)
92 filename = tb.tb_frame.f_code.co_filename
93 routine = tb.tb_frame.f_code.co_name
94 sys.stderr.write("\nSCons error: %s\n" % value)
95 sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine))
97 def _scons_user_warning(e):
98 """Handle user warnings. Print out a message and a description of
99 the warning, along with the line number and routine where it occured.
101 etype, value, tb = sys.exc_info()
102 while tb.tb_next is not None:
104 lineno = traceback.tb_lineno(tb)
105 filename = tb.tb_frame.f_code.co_filename
106 routine = tb.tb_frame.f_code.co_name
107 sys.stderr.write("\nSCons warning: %s\n" % e)
108 sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine))
110 def _scons_other_errors():
111 """Handle all errors but user errors. Print out a message telling
112 the user what to do in this case and print a normal trace.
115 traceback.print_exc()
119 def Conscript(filename):
121 scripts.append(filename)
123 def Default(*targets):
125 for s in string.split(t):
126 default_targets.append(s)
130 if help_option == 'h':
132 print "Use scons -H for help about command-line options."
138 # After options are initialized, the following variables are
141 option_list = [] # list of Option objects
142 short_opts = "" # string of short (single-character) options
143 long_opts = [] # array of long (--) options
144 opt_func = {} # mapping of option strings to functions
147 """Initialize command-line options processing.
149 This is in a subroutine mainly so we can easily single-step over
154 """Class for command-line option information.
156 This exists to provide a central location for everything
157 describing a command-line option, so that we can change
158 options without having to update the code to handle the
159 option in one place, the -h help message in another place,
160 etc. There are no methods here, only attributes.
162 You can initialize an Option with the following:
164 func The function that will be called when this
165 option is processed on the command line.
170 If there is no func, then this Option probably
171 stores an optstring to be printed.
174 The string to be printed in -h output. If no
175 helpline is specified but a help string is
176 specified (the usual case), a helpline will be
177 constructed automatically from the short, long,
178 arg, and help attributes. (In practice, then,
179 setting helpline without setting func allows you
180 to print arbitrary lines of text in the -h
183 short The string for short, single-hyphen
184 command-line options.
185 Do not include the hyphen:
187 'a' for -a, 'xy' for -x and -y, etc.
189 long An array of strings for long, double-hyphen
190 command-line options. Do not include
193 ['my-option', 'verbose']
195 arg If this option takes an argument, this string
196 specifies how you want it to appear in the
197 -h output ('DIRECTORY', 'FILE', etc.).
199 help The help string that will be printed for
200 this option in the -h output. Must be
201 49 characters or fewer.
203 future If non-zero, this indicates that this feature
204 will be supported in a future release, not
205 the currently planned one. SCons will
206 recognize the option, but it won't show up
209 The following attribute is derived from the supplied attributes:
212 A string, with hyphens, describing the flags
213 for this option, as constructed from the
214 specified short, long and arg attributes.
216 All Option objects are stored in the global option_list list,
217 in the order in which they're created. This is the list
218 that's used to generate -h output, so the order in which the
219 objects are created is the order in which they're printed.
221 The upshot is that specifying a command-line option and having
222 everything work correctly is a matter of defining a function to
223 process its command-line argument (set the right flag, update
224 the right value), and then creating an appropriate Option object
225 at the correct point in the code below.
228 def __init__(self, func = None, helpline = None,
229 short = None, long = None, arg = None,
230 help = None, future = None):
241 opts = opts + ['-' + c]
245 l = map(lambda x,a=arg: x + "=" + a, self.long)
246 opts = opts + map(lambda x: '--' + x, l)
247 self.optstring = string.join(opts, ', ')
249 self.helpline = helpline
250 elif help and not future:
251 if len(self.optstring) <= 26:
252 sep = " " * (28 - len(self.optstring))
254 sep = self.helpstring = "\n" + " " * 30
255 self.helpline = " " + self.optstring + sep + self.help
259 option_list.append(self)
261 # Generic routine for to-be-written options, used by multiple
264 def opt_not_yet(opt, arg):
265 sys.stderr.write("Warning: the %s option is not yet implemented\n"
268 # In the following instantiations, the help string should be no
269 # longer than 49 characters. Use the following as a guide:
270 # help = "1234567890123456789012345678901234567890123456789"
272 def opt_ignore(opt, arg):
273 sys.stderr.write("Warning: ignoring %s option\n" % opt)
275 Option(func = opt_ignore,
276 short = 'bmSt', long = ['no-keep-going', 'stop', 'touch'],
277 help = "Ignored for compatibility.")
279 Option(func = opt_not_yet,
280 short = 'c', long = ['clean', 'remove'],
281 help = "Remove specified targets and dependencies.")
283 Option(func = opt_not_yet, future = 1,
284 long = ['cache-disable', 'no-cache'],
285 help = "Do not retrieve built targets from Cache.")
287 Option(func = opt_not_yet, future = 1,
288 long = ['cache-force', 'cache-populate'],
289 help = "Copy already-built targets into the Cache.")
291 Option(func = opt_not_yet, future = 1,
292 long = ['cache-show'],
293 help = "Print what would have built Cached targets.")
299 sys.stderr.write("Could not change directory to 'arg'\n")
302 short = 'C', long = ['directory'], arg = 'DIRECTORY',
303 help = "Change to DIRECTORY before doing anything.")
305 Option(func = opt_not_yet,
307 help = "Print file dependency information.")
309 Option(func = opt_not_yet, future = 1,
310 long = ['debug'], arg = 'FLAGS',
311 help = "Print various types of debugging information.")
313 Option(func = opt_not_yet, future = 1,
314 short = 'e', long = ['environment-overrides'],
315 help = "Environment variables override makefiles.")
322 short = 'f', long = ['file', 'makefile', 'sconstruct'], arg = 'FILE',
323 help = "Read FILE as the top-level SConstruct file.")
325 def opt_help(opt, arg):
329 Option(func = opt_help,
330 short = 'h', long = ['help'],
331 help = "Print defined help message, or this one.")
333 def opt_help_options(opt, arg):
337 Option(func = opt_help_options,
338 short = 'H', long = ['help-options'],
339 help = "Print this message and exit.")
341 Option(func = opt_not_yet,
342 short = 'i', long = ['ignore-errors'],
343 help = "Ignore errors from build actions.")
347 include_dirs = include_dirs + [arg]
350 short = 'I', long = ['include-dir'], arg = 'DIRECTORY',
351 help = "Search DIRECTORY for imported Python modules.")
366 short = 'j', long = ['jobs'], arg = 'N',
367 help = "Allow N jobs at once.")
369 Option(func = opt_not_yet,
370 short = 'k', long = ['keep-going'],
371 help = "Keep going when a target can't be made.")
373 Option(func = opt_not_yet, future = 1,
374 short = 'l', long = ['load-average', 'max-load'], arg = 'N',
375 help = "Don't start multiple jobs unless load is below N.")
377 Option(func = opt_not_yet, future = 1,
378 long = ['list-derived'],
379 help = "Don't build; list files that would be built.")
381 Option(func = opt_not_yet, future = 1,
382 long = ['list-actions'],
383 help = "Don't build; list files and build actions.")
385 Option(func = opt_not_yet, future = 1,
386 long = ['list-where'],
387 help = "Don't build; list files and where defined.")
390 SCons.Builder.execute_actions = None
393 short = 'n', long = ['no-exec', 'just-print', 'dry-run', 'recon'],
394 help = "Don't build; just print commands.")
396 Option(func = opt_not_yet, future = 1,
397 short = 'o', long = ['old-file', 'assume-old'], arg = 'FILE',
398 help = "Consider FILE to be old; don't rebuild it.")
400 Option(func = opt_not_yet, future = 1,
401 long = ['override'], arg = 'FILE',
402 help = "Override variables as specified in FILE.")
404 Option(func = opt_not_yet, future = 1,
406 help = "Print internal environments/objects.")
408 Option(func = opt_not_yet, future = 1,
409 short = 'q', long = ['question'],
410 help = "Don't build; exit status says if up to date.")
412 Option(func = opt_not_yet, future = 1,
413 short = 'rR', long = ['no-builtin-rules', 'no-builtin-variables'],
414 help = "Clear default environments and variables.")
416 Option(func = opt_not_yet, future = 1,
418 help = "Build dependencies in random order.")
421 SCons.Builder.print_actions = None
424 short = 's', long = ['silent', 'quiet'],
425 help = "Don't print commands.")
427 Option(func = opt_not_yet, future = 1,
428 short = 'u', long = ['up', 'search-up'],
429 help = "Search up directory tree for SConstruct.")
431 def option_v(opt, arg):
432 print "SCons version __VERSION__, by Steven Knight et al."
433 print "Copyright 2001 Steven Knight"
436 Option(func = option_v,
437 short = 'v', long = ['version'],
438 help = "Print the SCons version number and exit.")
440 Option(func = opt_not_yet, future = 1,
441 short = 'w', long = ['print-directory'],
442 help = "Print the current directory.")
444 Option(func = opt_not_yet, future = 1,
445 long = ['no-print-directory'],
446 help = "Turn off -w, even if it was turned on implicitly.")
448 Option(func = opt_not_yet, future = 1,
449 long = ['write-filenames'], arg = 'FILE',
450 help = "Write all filenames examined into FILE.")
452 Option(func = opt_not_yet, future = 1,
453 short = 'W', long = ['what-if', 'new-file', 'assume-new'], arg = 'FILE',
454 help = "Consider FILE to be changed.")
456 Option(func = opt_not_yet, future = 1,
457 long = ['warn-undefined-variables'],
458 help = "Warn when an undefined variable is referenced.")
460 Option(func = opt_not_yet, future = 1,
461 short = 'Y', long = ['repository'], arg = 'REPOSITORY',
462 help = "Search REPOSITORY for source and target files.")
467 for o in option_list:
471 opt_func['-' + c] = o.func
472 short_opts = short_opts + o.short
474 short_opts = short_opts + ":"
478 opt_func['--' + l] = o.func
480 long_opts = long_opts + map(lambda a: a + "=", o.long)
482 long_opts = long_opts + o.long
489 help_opts = filter(lambda x: x.helpline, option_list)
490 s = "Usage: scons [OPTION] [TARGET] ...\n" + "Options:\n" + \
491 string.join(map(lambda x: x.helpline, help_opts), "\n") + "\n"
497 global scripts, help_option, num_jobs
501 # It looks like 2.0 changed the name of the exception class
504 getopt_err = getopt.GetoptError
506 getopt_err = getopt.error
509 cmd_opts, t = getopt.getopt(string.split(os.environ['SCONSFLAGS']),
510 short_opts, long_opts)
512 # It's all right if there's no SCONSFLAGS environment variable.
514 except getopt_err, x:
515 _scons_user_warning("SCONSFLAGS " + x)
517 for opt, arg in cmd_opts:
518 opt_func[opt](opt, arg)
521 cmd_opts, targets = getopt.getopt(sys.argv[1:], short_opts, long_opts)
522 except getopt_err, x:
525 for opt, arg in cmd_opts:
526 opt_func[opt](opt, arg)
529 for file in ['SConstruct', 'Sconstruct', 'sconstruct']:
530 if os.path.isfile(file):
534 if help_option == 'H':
539 if help_option == 'h':
540 # There's no SConstruct, but they specified either -h or
541 # -H. Give them the options usage now, before we fail
542 # trying to read a non-existent SConstruct file.
546 raise UserError, "No SConstruct file found."
548 # XXX The commented-out code here adds any "scons" subdirs in anything
549 # along sys.path to sys.path. This was an attempt at setting up things
550 # so we can import "node.FS" instead of "SCons.Node.FS". This doesn't
551 # quite fit our testing methodology, though, so save it for now until
552 # the right solutions pops up.
555 #for dir in sys.path:
556 # scons = os.path.join(dir, 'scons')
557 # if os.path.isdir(scons):
558 # dirlist = dirlist + [scons]
559 # dirlist = dirlist + [dir]
563 sys.path = include_dirs + sys.path
566 file, scripts = scripts[0], scripts[1:]
568 exec sys.stdin in globals()
573 sys.stderr.write("Ignoring missing script '%s'\n" % file)
577 if help_option == 'h':
578 # They specified -h, but there was no Help() inside the
579 # SConscript files. Give them the options usage.
584 targets = default_targets
586 # XXX Right now, this next block prints all "up to date" messages
587 # first, and then goes through and builds the other nodes:
589 # $ scons aaa bbb ccc ddd
590 # scons: "aaa" is up to date.
591 # scons: "ccc" is up to date.
595 # When we get the real Task and Taskmaster classes, this should
596 # be changed to interact with the engine to deal with targets in
597 # the same order as specified:
599 # $ scons aaa bbb ccc ddd
600 # scons: "aaa" is up to date.
602 # scons: "ccc" is up to date.
605 calc = SCons.Sig.Calculator(SCons.Sig.MD5)
607 for t in map(lambda x: SCons.Node.FS.default_fs.File(x), targets):
609 print 'scons: "%s" is up to date.' % t.path
613 taskmaster = Taskmaster(nodes)
615 jobs = SCons.Job.Jobs(num_jobs, taskmaster)
621 if __name__ == "__main__":
626 except KeyboardInterrupt:
627 print "Build interrupted."
628 except SyntaxError, e:
629 _scons_syntax_error(e)
633 _scons_other_errors()