http://scons.tigris.org/issues/show_bug.cgi?id=2345
[scons.git] / src / engine / SCons / Script / SConsOptions.py
1 #
2 # __COPYRIGHT__
3 #
4 # Permission is hereby granted, free of charge, to any person obtaining
5 # a copy of this software and associated documentation files (the
6 # "Software"), to deal in the Software without restriction, including
7 # without limitation the rights to use, copy, modify, merge, publish,
8 # distribute, sublicense, and/or sell copies of the Software, and to
9 # permit persons to whom the Software is furnished to do so, subject to
10 # the following conditions:
11 #
12 # The above copyright notice and this permission notice shall be included
13 # in all copies or substantial portions of the Software.
14 #
15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
16 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
17 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 #
23
24 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
25
26 import optparse
27 import re
28 import sys
29 import textwrap
30
31 try:
32     no_hyphen_re = re.compile(r'(\s+|(?<=[\w\!\"\'\&\.\,\?])-{2,}(?=\w))')
33 except re.error:
34     # Pre-2.0 Python versions don't have the (?<= negative
35     # look-behind assertion.
36     no_hyphen_re = re.compile(r'(\s+|-*\w{2,}-(?=\w{2,}))')
37
38 try:
39     from gettext import gettext
40 except ImportError:
41     def gettext(message):
42         return message
43 _ = gettext
44
45 import SCons.Node.FS
46 import SCons.Warnings
47
48 OptionValueError        = optparse.OptionValueError
49 SUPPRESS_HELP           = optparse.SUPPRESS_HELP
50
51 diskcheck_all = SCons.Node.FS.diskcheck_types()
52
53 def diskcheck_convert(value):
54     if value is None:
55         return []
56     if not SCons.Util.is_List(value):
57         value = value.split(',')
58     result = []
59     for v in value:
60         v = v.lower()
61         if v == 'all':
62             result = diskcheck_all
63         elif v == 'none':
64             result = []
65         elif v in diskcheck_all:
66             result.append(v)
67         else:
68             raise ValueError(v)
69     return result
70
71 class SConsValues(optparse.Values):
72     """
73     Holder class for uniform access to SCons options, regardless
74     of whether or not they can be set on the command line or in the
75     SConscript files (using the SetOption() function).
76
77     A SCons option value can originate three different ways:
78
79         1)  set on the command line;
80         2)  set in an SConscript file;
81         3)  the default setting (from the the op.add_option()
82             calls in the Parser() function, below).
83
84     The command line always overrides a value set in a SConscript file,
85     which in turn always overrides default settings.  Because we want
86     to support user-specified options in the SConscript file itself,
87     though, we may not know about all of the options when the command
88     line is first parsed, so we can't make all the necessary precedence
89     decisions at the time the option is configured.
90
91     The solution implemented in this class is to keep these different sets
92     of settings separate (command line, SConscript file, and default)
93     and to override the __getattr__() method to check them in turn.
94     This should allow the rest of the code to just fetch values as
95     attributes of an instance of this class, without having to worry
96     about where they came from.
97
98     Note that not all command line options are settable from SConscript
99     files, and the ones that are must be explicitly added to the
100     "settable" list in this class, and optionally validated and coerced
101     in the set_option() method.
102     """
103
104     def __init__(self, defaults):
105         self.__dict__['__defaults__'] = defaults
106         self.__dict__['__SConscript_settings__'] = {}
107
108     def __getattr__(self, attr):
109         """
110         Fetches an options value, checking first for explicit settings
111         from the command line (which are direct attributes), then the
112         SConscript file settings, then the default values.
113         """
114         try:
115             return self.__dict__[attr]
116         except KeyError:
117             try:
118                 return self.__dict__['__SConscript_settings__'][attr]
119             except KeyError:
120                 return getattr(self.__dict__['__defaults__'], attr)
121
122     settable = [
123         'clean',
124         'diskcheck',
125         'duplicate',
126         'help',
127         'implicit_cache',
128         'max_drift',
129         'md5_chunksize',
130         'no_exec',
131         'num_jobs',
132         'random',
133         'stack_size',
134         'warn',
135     ]
136
137     def set_option(self, name, value):
138         """
139         Sets an option from an SConscript file.
140         """
141         if not name in self.settable:
142             raise SCons.Errors.UserError("This option is not settable from a SConscript file: %s"%name)
143
144         if name == 'num_jobs':
145             try:
146                 value = int(value)
147                 if value < 1:
148                     raise ValueError
149             except ValueError:
150                 raise SCons.Errors.UserError("A positive integer is required: %s"%repr(value))
151         elif name == 'max_drift':
152             try:
153                 value = int(value)
154             except ValueError:
155                 raise SCons.Errors.UserError("An integer is required: %s"%repr(value))
156         elif name == 'duplicate':
157             try:
158                 value = str(value)
159             except ValueError:
160                 raise SCons.Errors.UserError("A string is required: %s"%repr(value))
161             if not value in SCons.Node.FS.Valid_Duplicates:
162                 raise SCons.Errors.UserError("Not a valid duplication style: %s" % value)
163             # Set the duplicate style right away so it can affect linking
164             # of SConscript files.
165             SCons.Node.FS.set_duplicate(value)
166         elif name == 'diskcheck':
167             try:
168                 value = diskcheck_convert(value)
169             except ValueError, v:
170                 raise SCons.Errors.UserError("Not a valid diskcheck value: %s"%v)
171             if 'diskcheck' not in self.__dict__:
172                 # No --diskcheck= option was specified on the command line.
173                 # Set this right away so it can affect the rest of the
174                 # file/Node lookups while processing the SConscript files.
175                 SCons.Node.FS.set_diskcheck(value)
176         elif name == 'stack_size':
177             try:
178                 value = int(value)
179             except ValueError:
180                 raise SCons.Errors.UserError("An integer is required: %s"%repr(value))
181         elif name == 'md5_chunksize':
182             try:
183                 value = int(value)
184             except ValueError:
185                 raise SCons.Errors.UserError("An integer is required: %s"%repr(value))
186         elif name == 'warn':
187             if SCons.Util.is_String(value):
188                 value = [value]
189             value = self.__SConscript_settings__.get(name, []) + value
190             SCons.Warnings.process_warn_strings(value)
191
192         self.__SConscript_settings__[name] = value
193
194 class SConsOption(optparse.Option):
195     def convert_value(self, opt, value):
196         if value is not None:
197             if self.nargs in (1, '?'):
198                 return self.check_value(opt, value)
199             else:
200                 return tuple([self.check_value(opt, v) for v in value])
201
202     def process(self, opt, value, values, parser):
203
204         # First, convert the value(s) to the right type.  Howl if any
205         # value(s) are bogus.
206         value = self.convert_value(opt, value)
207
208         # And then take whatever action is expected of us.
209         # This is a separate method to make life easier for
210         # subclasses to add new actions.
211         return self.take_action(
212             self.action, self.dest, opt, value, values, parser)
213
214     def _check_nargs_optional(self):
215         if self.nargs == '?' and self._short_opts:
216             fmt = "option %s: nargs='?' is incompatible with short options"
217             raise SCons.Errors.UserError(fmt % self._short_opts[0])
218
219     try:
220         _orig_CONST_ACTIONS = optparse.Option.CONST_ACTIONS
221
222         _orig_CHECK_METHODS = optparse.Option.CHECK_METHODS
223
224     except AttributeError:
225         # optparse.Option had no CONST_ACTIONS before Python 2.5.
226
227         _orig_CONST_ACTIONS = ("store_const",)
228
229         def _check_const(self):
230             if self.action not in self.CONST_ACTIONS and self.const is not None:
231                 raise OptionError(
232                     "'const' must not be supplied for action %r" % self.action,
233                     self)
234
235         # optparse.Option collects its list of unbound check functions
236         # up front.  This sucks because it means we can't just override
237         # the _check_const() function like a normal method, we have to
238         # actually replace it in the list.  This seems to be the most
239         # straightforward way to do that.
240
241         _orig_CHECK_METHODS = [optparse.Option._check_action,
242                      optparse.Option._check_type,
243                      optparse.Option._check_choice,
244                      optparse.Option._check_dest,
245                      _check_const,
246                      optparse.Option._check_nargs,
247                      optparse.Option._check_callback]
248
249     CHECK_METHODS = _orig_CHECK_METHODS + [_check_nargs_optional]
250
251     CONST_ACTIONS = _orig_CONST_ACTIONS + optparse.Option.TYPED_ACTIONS
252
253 class SConsOptionGroup(optparse.OptionGroup):
254     """
255     A subclass for SCons-specific option groups.
256     
257     The only difference between this and the base class is that we print
258     the group's help text flush left, underneath their own title but
259     lined up with the normal "SCons Options".
260     """
261     def format_help(self, formatter):
262         """
263         Format an option group's help text, outdenting the title so it's
264         flush with the "SCons Options" title we print at the top.
265         """
266         formatter.dedent()
267         result = formatter.format_heading(self.title)
268         formatter.indent()
269         result = result + optparse.OptionContainer.format_help(self, formatter)
270         return result
271
272 class SConsOptionParser(optparse.OptionParser):
273     preserve_unknown_options = False
274
275     def error(self, msg):
276         self.print_usage(sys.stderr)
277         sys.stderr.write("SCons error: %s\n" % msg)
278         sys.exit(2)
279
280     def _process_long_opt(self, rargs, values):
281         """
282         SCons-specific processing of long options.
283
284         This is copied directly from the normal
285         optparse._process_long_opt() method, except that, if configured
286         to do so, we catch the exception thrown when an unknown option
287         is encountered and just stick it back on the "leftover" arguments
288         for later (re-)processing.
289         """
290         arg = rargs.pop(0)
291
292         # Value explicitly attached to arg?  Pretend it's the next
293         # argument.
294         if "=" in arg:
295             (opt, next_arg) = arg.split("=", 1)
296             rargs.insert(0, next_arg)
297             had_explicit_value = True
298         else:
299             opt = arg
300             had_explicit_value = False
301
302         try:
303             opt = self._match_long_opt(opt)
304         except optparse.BadOptionError:
305             if self.preserve_unknown_options:
306                 # SCons-specific:  if requested, add unknown options to
307                 # the "leftover arguments" list for later processing.
308                 self.largs.append(arg)
309                 if had_explicit_value:
310                     # The unknown option will be re-processed later,
311                     # so undo the insertion of the explicit value.
312                     rargs.pop(0)
313                 return
314             raise
315
316         option = self._long_opt[opt]
317         if option.takes_value():
318             nargs = option.nargs
319             if nargs == '?':
320                 if had_explicit_value:
321                     value = rargs.pop(0)
322                 else:
323                     value = option.const
324             elif len(rargs) < nargs:
325                 if nargs == 1:
326                     self.error(_("%s option requires an argument") % opt)
327                 else:
328                     self.error(_("%s option requires %d arguments")
329                                % (opt, nargs))
330             elif nargs == 1:
331                 value = rargs.pop(0)
332             else:
333                 value = tuple(rargs[0:nargs])
334                 del rargs[0:nargs]
335
336         elif had_explicit_value:
337             self.error(_("%s option does not take a value") % opt)
338
339         else:
340             value = None
341
342         option.process(opt, value, values, self)
343
344     def add_local_option(self, *args, **kw):
345         """
346         Adds a local option to the parser.
347         
348         This is initiated by a SetOption() call to add a user-defined
349         command-line option.  We add the option to a separate option
350         group for the local options, creating the group if necessary.
351         """
352         try:
353             group = self.local_option_group
354         except AttributeError:
355             group = SConsOptionGroup(self, 'Local Options')
356             group = self.add_option_group(group)
357             self.local_option_group = group
358
359         result = group.add_option(*args, **kw)
360
361         if result:
362             # The option was added succesfully.  We now have to add the
363             # default value to our object that holds the default values
364             # (so that an attempt to fetch the option's attribute will
365             # yield the default value when not overridden) and then
366             # we re-parse the leftover command-line options, so that
367             # any value overridden on the command line is immediately
368             # available if the user turns around and does a GetOption()
369             # right away.
370             setattr(self.values.__defaults__, result.dest, result.default)
371             self.parse_args(self.largs, self.values)
372
373         return result
374
375 class SConsIndentedHelpFormatter(optparse.IndentedHelpFormatter):
376     def format_usage(self, usage):
377         return "usage: %s\n" % usage
378
379     def format_heading(self, heading):
380         """
381         This translates any heading of "options" or "Options" into
382         "SCons Options."  Unfortunately, we have to do this here,
383         because those titles are hard-coded in the optparse calls.
384         """
385         if heading == 'options':
386             # The versions of optparse.py shipped with Pythons 2.3 and
387             # 2.4 pass this in uncapitalized; override that so we get
388             # consistent output on all versions.
389             heading = "Options"
390         if heading == 'Options':
391             heading = "SCons Options"
392         return optparse.IndentedHelpFormatter.format_heading(self, heading)
393
394     def format_option(self, option):
395         """
396         A copy of the normal optparse.IndentedHelpFormatter.format_option()
397         method.  This has been snarfed so we can modify text wrapping to
398         out liking:
399
400         --  add our own regular expression that doesn't break on hyphens
401             (so things like --no-print-directory don't get broken); 
402
403         --  wrap the list of options themselves when it's too long
404             (the wrapper.fill(opts) call below);
405  
406         --  set the subsequent_indent when wrapping the help_text.
407         """
408         # The help for each option consists of two parts:
409         #   * the opt strings and metavars
410         #     eg. ("-x", or "-fFILENAME, --file=FILENAME")
411         #   * the user-supplied help string
412         #     eg. ("turn on expert mode", "read data from FILENAME")
413         #
414         # If possible, we write both of these on the same line:
415         #   -x      turn on expert mode
416         #
417         # But if the opt string list is too long, we put the help
418         # string on a second line, indented to the same column it would
419         # start in if it fit on the first line.
420         #   -fFILENAME, --file=FILENAME
421         #           read data from FILENAME
422         result = []
423
424         try:
425             opts = self.option_strings[option]
426         except AttributeError:
427             # The Python 2.3 version of optparse attaches this to
428             # to the option argument, not to this object.
429             opts = option.option_strings
430
431         opt_width = self.help_position - self.current_indent - 2
432         if len(opts) > opt_width:
433             wrapper = textwrap.TextWrapper(width=self.width,
434                                            initial_indent = '  ',
435                                            subsequent_indent = '  ')
436             wrapper.wordsep_re = no_hyphen_re
437             opts = wrapper.fill(opts) + '\n'
438             indent_first = self.help_position
439         else:                       # start help on same line as opts
440             opts = "%*s%-*s  " % (self.current_indent, "", opt_width, opts)
441             indent_first = 0
442         result.append(opts)
443         if option.help:
444
445             try:
446                 expand_default = self.expand_default
447             except AttributeError:
448                 # The HelpFormatter base class in the Python 2.3 version
449                 # of optparse has no expand_default() method.
450                 help_text = option.help
451             else:
452                 help_text = expand_default(option)
453
454             # SCons:  indent every line of the help text but the first.
455             wrapper = textwrap.TextWrapper(width=self.help_width,
456                                            subsequent_indent = '  ')
457             wrapper.wordsep_re = no_hyphen_re
458             help_lines = wrapper.wrap(help_text)
459             result.append("%*s%s\n" % (indent_first, "", help_lines[0]))
460             for line in help_lines[1:]:
461                 result.append("%*s%s\n" % (self.help_position, "", line))
462         elif opts[-1] != "\n":
463             result.append("\n")
464         return "".join(result)
465
466     # For consistent help output across Python versions, we provide a
467     # subclass copy of format_option_strings() and these two variables.
468     # This is necessary (?) for Python2.3, which otherwise concatenates
469     # a short option with its metavar.
470     _short_opt_fmt = "%s %s"
471     _long_opt_fmt = "%s=%s"
472
473     def format_option_strings(self, option):
474         """Return a comma-separated list of option strings & metavariables."""
475         if option.takes_value():
476             metavar = option.metavar or option.dest.upper()
477             short_opts = []
478             for sopt in option._short_opts:
479                 short_opts.append(self._short_opt_fmt % (sopt, metavar))
480             long_opts = []
481             for lopt in option._long_opts:
482                 long_opts.append(self._long_opt_fmt % (lopt, metavar))
483         else:
484             short_opts = option._short_opts
485             long_opts = option._long_opts
486
487         if self.short_first:
488             opts = short_opts + long_opts
489         else:
490             opts = long_opts + short_opts
491
492         return ", ".join(opts)
493
494 def Parser(version):
495     """
496     Returns an options parser object initialized with the standard
497     SCons options.
498     """
499
500     formatter = SConsIndentedHelpFormatter(max_help_position=30)
501
502     op = SConsOptionParser(option_class=SConsOption,
503                            add_help_option=False,
504                            formatter=formatter,
505                            usage="usage: scons [OPTION] [TARGET] ...",)
506
507     op.preserve_unknown_options = True
508     op.version = version
509
510     # Add the options to the parser we just created.
511     #
512     # These are in the order we want them to show up in the -H help
513     # text, basically alphabetical.  Each op.add_option() call below
514     # should have a consistent format:
515     #
516     #   op.add_option("-L", "--long-option-name",
517     #                 nargs=1, type="string",
518     #                 dest="long_option_name", default='foo',
519     #                 action="callback", callback=opt_long_option,
520     #                 help="help text goes here",
521     #                 metavar="VAR")
522     #
523     # Even though the optparse module constructs reasonable default
524     # destination names from the long option names, we're going to be
525     # explicit about each one for easier readability and so this code
526     # will at least show up when grepping the source for option attribute
527     # names, or otherwise browsing the source code.
528
529     # options ignored for compatibility
530     def opt_ignore(option, opt, value, parser):
531         sys.stderr.write("Warning:  ignoring %s option\n" % opt)
532     op.add_option("-b", "-d", "-e", "-m", "-S", "-t", "-w",
533                   "--environment-overrides",
534                   "--no-keep-going",
535                   "--no-print-directory",
536                   "--print-directory",
537                   "--stop",
538                   "--touch",
539                   action="callback", callback=opt_ignore,
540                   help="Ignored for compatibility.")
541
542     op.add_option('-c', '--clean', '--remove',
543                   dest="clean", default=False,
544                   action="store_true",
545                   help="Remove specified targets and dependencies.")
546
547     op.add_option('-C', '--directory',
548                   nargs=1, type="string",
549                   dest="directory", default=[],
550                   action="append",
551                   help="Change to DIR before doing anything.",
552                   metavar="DIR")
553
554     op.add_option('--cache-debug',
555                   nargs=1,
556                   dest="cache_debug", default=None,
557                   action="store",
558                   help="Print CacheDir debug info to FILE.",
559                   metavar="FILE")
560
561     op.add_option('--cache-disable', '--no-cache',
562                   dest='cache_disable', default=False,
563                   action="store_true",
564                   help="Do not retrieve built targets from CacheDir.")
565
566     op.add_option('--cache-force', '--cache-populate',
567                   dest='cache_force', default=False,
568                   action="store_true",
569                   help="Copy already-built targets into the CacheDir.")
570
571     op.add_option('--cache-show',
572                   dest='cache_show', default=False,
573                   action="store_true",
574                   help="Print build actions for files from CacheDir.")
575
576     config_options = ["auto", "force" ,"cache"]
577
578     def opt_config(option, opt, value, parser, c_options=config_options):
579         if not value in c_options:
580             raise OptionValueError("Warning:  %s is not a valid config type" % value)
581         setattr(parser.values, option.dest, value)
582     opt_config_help = "Controls Configure subsystem: %s." \
583                       % ", ".join(config_options)
584     op.add_option('--config',
585                   nargs=1, type="string",
586                   dest="config", default="auto",
587                   action="callback", callback=opt_config,
588                   help = opt_config_help,
589                   metavar="MODE")
590
591     op.add_option('-D',
592                   dest="climb_up", default=None,
593                   action="store_const", const=2,
594                   help="Search up directory tree for SConstruct,       "
595                        "build all Default() targets.")
596
597     deprecated_debug_options = {
598         "dtree"         : '; please use --tree=derived instead',
599         "nomemoizer"    : ' and has no effect',
600         "stree"         : '; please use --tree=all,status instead',
601         "tree"          : '; please use --tree=all instead',
602     }
603
604     debug_options = ["count", "explain", "findlibs",
605                      "includes", "memoizer", "memory", "objects",
606                      "pdb", "presub", "stacktrace",
607                      "time"] + deprecated_debug_options.keys()
608
609     def opt_debug(option, opt, value, parser,
610                   debug_options=debug_options,
611                   deprecated_debug_options=deprecated_debug_options):
612         if value in debug_options:
613             parser.values.debug.append(value)
614             if value in deprecated_debug_options.keys():
615                 try:
616                     parser.values.delayed_warnings
617                 except AttributeError:
618                     parser.values.delayed_warnings = []
619                 msg = deprecated_debug_options[value]
620                 w = "The --debug=%s option is deprecated%s." % (value, msg)
621                 t = (SCons.Warnings.DeprecatedWarning, w)
622                 parser.values.delayed_warnings.append(t)
623         else:
624             raise OptionValueError("Warning:  %s is not a valid debug type" % value)
625     opt_debug_help = "Print various types of debugging information: %s." \
626                      % ", ".join(debug_options)
627     op.add_option('--debug',
628                   nargs=1, type="string",
629                   dest="debug", default=[],
630                   action="callback", callback=opt_debug,
631                   help=opt_debug_help,
632                   metavar="TYPE")
633
634     def opt_diskcheck(option, opt, value, parser):
635         try:
636             diskcheck_value = diskcheck_convert(value)
637         except ValueError, e:
638             raise OptionValueError("Warning: `%s' is not a valid diskcheck type" % e)
639         setattr(parser.values, option.dest, diskcheck_value)
640
641     op.add_option('--diskcheck',
642                   nargs=1, type="string",
643                   dest='diskcheck', default=None,
644                   action="callback", callback=opt_diskcheck,
645                   help="Enable specific on-disk checks.",
646                   metavar="TYPE")
647
648     def opt_duplicate(option, opt, value, parser):
649         if not value in SCons.Node.FS.Valid_Duplicates:
650             raise OptionValueError("`%s' is not a valid duplication style." % value)
651         setattr(parser.values, option.dest, value)
652         # Set the duplicate style right away so it can affect linking
653         # of SConscript files.
654         SCons.Node.FS.set_duplicate(value)
655
656     opt_duplicate_help = "Set the preferred duplication methods. Must be one of " \
657                          + ", ".join(SCons.Node.FS.Valid_Duplicates)
658
659     op.add_option('--duplicate',
660                   nargs=1, type="string",
661                   dest="duplicate", default='hard-soft-copy',
662                   action="callback", callback=opt_duplicate,
663                   help=opt_duplicate_help)
664
665     op.add_option('-f', '--file', '--makefile', '--sconstruct',
666                   nargs=1, type="string",
667                   dest="file", default=[],
668                   action="append",
669                   help="Read FILE as the top-level SConstruct file.")
670
671     op.add_option('-h', '--help',
672                   dest="help", default=False,
673                   action="store_true",
674                   help="Print defined help message, or this one.")
675
676     op.add_option("-H", "--help-options",
677                   action="help",
678                   help="Print this message and exit.")
679
680     op.add_option('-i', '--ignore-errors',
681                   dest='ignore_errors', default=False,
682                   action="store_true",
683                   help="Ignore errors from build actions.")
684
685     op.add_option('-I', '--include-dir',
686                   nargs=1,
687                   dest='include_dir', default=[],
688                   action="append",
689                   help="Search DIR for imported Python modules.",
690                   metavar="DIR")
691
692     op.add_option('--implicit-cache',
693                   dest='implicit_cache', default=False,
694                   action="store_true",
695                   help="Cache implicit dependencies")
696
697     def opt_implicit_deps(option, opt, value, parser):
698         setattr(parser.values, 'implicit_cache', True)
699         setattr(parser.values, option.dest, True)
700
701     op.add_option('--implicit-deps-changed',
702                   dest="implicit_deps_changed", default=False,
703                   action="callback", callback=opt_implicit_deps,
704                   help="Ignore cached implicit dependencies.")
705
706     op.add_option('--implicit-deps-unchanged',
707                   dest="implicit_deps_unchanged", default=False,
708                   action="callback", callback=opt_implicit_deps,
709                   help="Ignore changes in implicit dependencies.")
710
711     op.add_option('--interact', '--interactive',
712                   dest='interactive', default=False,
713                   action="store_true",
714                   help="Run in interactive mode.")
715
716     op.add_option('-j', '--jobs',
717                   nargs=1, type="int",
718                   dest="num_jobs", default=1,
719                   action="store",
720                   help="Allow N jobs at once.",
721                   metavar="N")
722
723     op.add_option('-k', '--keep-going',
724                   dest='keep_going', default=False,
725                   action="store_true",
726                   help="Keep going when a target can't be made.")
727
728     op.add_option('--max-drift',
729                   nargs=1, type="int",
730                   dest='max_drift', default=SCons.Node.FS.default_max_drift,
731                   action="store",
732                   help="Set maximum system clock drift to N seconds.",
733                   metavar="N")
734
735     op.add_option('--md5-chunksize',
736                   nargs=1, type="int",
737                   dest='md5_chunksize', default=SCons.Node.FS.File.md5_chunksize,
738                   action="store",
739                   help="Set chunk-size for MD5 signature computation to N kilobytes.",
740                   metavar="N")
741
742     op.add_option('-n', '--no-exec', '--just-print', '--dry-run', '--recon',
743                   dest='no_exec', default=False,
744                   action="store_true",
745                   help="Don't build; just print commands.")
746
747     op.add_option('--no-site-dir',
748                   dest='no_site_dir', default=False,
749                   action="store_true",
750                   help="Don't search or use the usual site_scons dir.")
751
752     op.add_option('--profile',
753                   nargs=1,
754                   dest="profile_file", default=None,
755                   action="store",
756                   help="Profile SCons and put results in FILE.",
757                   metavar="FILE")
758
759     op.add_option('-q', '--question',
760                   dest="question", default=False,
761                   action="store_true",
762                   help="Don't build; exit status says if up to date.")
763
764     op.add_option('-Q',
765                   dest='no_progress', default=False,
766                   action="store_true",
767                   help="Suppress \"Reading/Building\" progress messages.")
768
769     op.add_option('--random',
770                   dest="random", default=False,
771                   action="store_true",
772                   help="Build dependencies in random order.")
773
774     op.add_option('-s', '--silent', '--quiet',
775                   dest="silent", default=False,
776                   action="store_true",
777                   help="Don't print commands.")
778
779     op.add_option('--site-dir',
780                   nargs=1,
781                   dest='site_dir', default=None,
782                   action="store",
783                   help="Use DIR instead of the usual site_scons dir.",
784                   metavar="DIR")
785
786     op.add_option('--stack-size',
787                   nargs=1, type="int",
788                   dest='stack_size',
789                   action="store",
790                   help="Set the stack size of the threads used to run jobs to N kilobytes.",
791                   metavar="N")
792
793     op.add_option('--taskmastertrace',
794                   nargs=1,
795                   dest="taskmastertrace_file", default=None,
796                   action="store",
797                   help="Trace Node evaluation to FILE.",
798                   metavar="FILE")
799
800     tree_options = ["all", "derived", "prune", "status"]
801
802     def opt_tree(option, opt, value, parser, tree_options=tree_options):
803         import Main
804         tp = Main.TreePrinter()
805         for o in value.split(','):
806             if o == 'all':
807                 tp.derived = False
808             elif o == 'derived':
809                 tp.derived = True
810             elif o == 'prune':
811                 tp.prune = True
812             elif o == 'status':
813                 tp.status = True
814             else:
815                 raise OptionValueError("Warning:  %s is not a valid --tree option" % o)
816         parser.values.tree_printers.append(tp)
817
818     opt_tree_help = "Print a dependency tree in various formats: %s." \
819                     % ", ".join(tree_options)
820
821     op.add_option('--tree',
822                   nargs=1, type="string",
823                   dest="tree_printers", default=[],
824                   action="callback", callback=opt_tree,
825                   help=opt_tree_help,
826                   metavar="OPTIONS")
827
828     op.add_option('-u', '--up', '--search-up',
829                   dest="climb_up", default=0,
830                   action="store_const", const=1,
831                   help="Search up directory tree for SConstruct,       "
832                        "build targets at or below current directory.")
833
834     op.add_option('-U',
835                   dest="climb_up", default=0,
836                   action="store_const", const=3,
837                   help="Search up directory tree for SConstruct,       "
838                        "build Default() targets from local SConscript.")
839
840     def opt_version(option, opt, value, parser):
841         sys.stdout.write(parser.version + '\n')
842         sys.exit(0)
843     op.add_option("-v", "--version",
844                   action="callback", callback=opt_version,
845                   help="Print the SCons version number and exit.")
846
847     def opt_warn(option, opt, value, parser, tree_options=tree_options):
848         if SCons.Util.is_String(value):
849             value = value.split(',')
850         parser.values.warn.extend(value)
851
852     op.add_option('--warn', '--warning',
853                   nargs=1, type="string",
854                   dest="warn", default=[],
855                   action="callback", callback=opt_warn,
856                   help="Enable or disable warnings.",
857                   metavar="WARNING-SPEC")
858
859     op.add_option('-Y', '--repository', '--srcdir',
860                   nargs=1,
861                   dest="repository", default=[],
862                   action="append",
863                   help="Search REPOSITORY for source and target files.")
864
865     # Options from Make and Cons classic that we do not yet support,
866     # but which we may support someday and whose (potential) meanings
867     # we don't want to change.  These all get a "the -X option is not
868     # yet implemented" message and don't show up in the help output.
869
870     def opt_not_yet(option, opt, value, parser):
871         msg = "Warning:  the %s option is not yet implemented\n" % opt
872         sys.stderr.write(msg)
873
874     op.add_option('-l', '--load-average', '--max-load',
875                   nargs=1, type="int",
876                   dest="load_average", default=0,
877                   action="callback", callback=opt_not_yet,
878                   # action="store",
879                   # help="Don't start multiple jobs unless load is below "
880                   #      "LOAD-AVERAGE."
881                   help=SUPPRESS_HELP)
882     op.add_option('--list-actions',
883                   dest="list_actions",
884                   action="callback", callback=opt_not_yet,
885                   # help="Don't build; list files and build actions."
886                   help=SUPPRESS_HELP)
887     op.add_option('--list-derived',
888                   dest="list_derived",
889                   action="callback", callback=opt_not_yet,
890                   # help="Don't build; list files that would be built."
891                   help=SUPPRESS_HELP)
892     op.add_option('--list-where',
893                   dest="list_where",
894                   action="callback", callback=opt_not_yet,
895                   # help="Don't build; list files and where defined."
896                   help=SUPPRESS_HELP)
897     op.add_option('-o', '--old-file', '--assume-old',
898                   nargs=1, type="string",
899                   dest="old_file", default=[],
900                   action="callback", callback=opt_not_yet,
901                   # action="append",
902                   # help = "Consider FILE to be old; don't rebuild it."
903                   help=SUPPRESS_HELP)
904     op.add_option('--override',
905                   nargs=1, type="string",
906                   action="callback", callback=opt_not_yet,
907                   dest="override",
908                   # help="Override variables as specified in FILE."
909                   help=SUPPRESS_HELP)
910     op.add_option('-p',
911                   action="callback", callback=opt_not_yet,
912                   dest="p",
913                   # help="Print internal environments/objects."
914                   help=SUPPRESS_HELP)
915     op.add_option('-r', '-R', '--no-builtin-rules', '--no-builtin-variables',
916                   action="callback", callback=opt_not_yet,
917                   dest="no_builtin_rules",
918                   # help="Clear default environments and variables."
919                   help=SUPPRESS_HELP)
920     op.add_option('--write-filenames',
921                   nargs=1, type="string",
922                   dest="write_filenames",
923                   action="callback", callback=opt_not_yet,
924                   # help="Write all filenames examined into FILE."
925                   help=SUPPRESS_HELP)
926     op.add_option('-W', '--new-file', '--assume-new', '--what-if',
927                   nargs=1, type="string",
928                   dest="new_file",
929                   action="callback", callback=opt_not_yet,
930                   # help="Consider FILE to be changed."
931                   help=SUPPRESS_HELP)
932     op.add_option('--warn-undefined-variables',
933                   dest="warn_undefined_variables",
934                   action="callback", callback=opt_not_yet,
935                   # help="Warn when an undefined variable is referenced."
936                   help=SUPPRESS_HELP)
937
938     return op
939
940 # Local Variables:
941 # tab-width:4
942 # indent-tabs-mode:nil
943 # End:
944 # vim: set expandtab tabstop=4 shiftwidth=4: