diff --git a/CHANGES.txt b/CHANGES.txt index e9f445d93..cebc9728f 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -12,6 +12,9 @@ NOTE: Python 3.6 support is deprecated and will be dropped in a future release. RELEASE VERSION/DATE TO BE FILLED IN LATER + From Dillan Mills: + - Fix support for short options (`-x`). + From Mats Wichmann: - PackageVariable now does what the documentation always said it does if the variable is used on the command line with one of the enabling diff --git a/RELEASE.txt b/RELEASE.txt index 1ccc056fb..d27c18ad6 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -43,6 +43,11 @@ FIXES it always produced True in this case). - Temporary files created by TempFileMunge() are now cleaned up on scons exit, instead of at the time they're used. Fixes #4595. +- AddOption now correctly adds short (single-character) options. + Previously an added short option would always report as unknown, + while long option names for the same option worked. Short options + that take a value require the user to specify the value immediately + following the option, with no spaces (e.g. -j5 and not -j 5). - Fix a problem with compilation_db component initialization - the entries for assembler files were not being set up correctly. diff --git a/SCons/Script/SConsOptions.py b/SCons/Script/SConsOptions.py index c9c7aa054..08531814f 100644 --- a/SCons/Script/SConsOptions.py +++ b/SCons/Script/SConsOptions.py @@ -342,12 +342,13 @@ def error(self, msg: str) -> None: def _process_long_opt(self, rargs, values) -> None: """SCons-specific processing of long options. - This is copied directly from the normal - ``optparse._process_long_opt()`` method, except that, if configured - to do so, we catch the exception thrown when an unknown option - is encountered and just stick it back on the "leftover" arguments - for later (re-)processing. This is because we may see the option - definition later, while processing SConscript files. + This is copied directly from the normal Optparse + :meth:`~optparse.OptionParser._process_long_opt` method, except + that, if configured to do so, we catch the exception thrown + when an unknown option is encountered and just stick it back + on the "leftover" arguments for later (re-)processing. This is + because we may see the option definition later, while processing + SConscript files. """ arg = rargs.pop(0) @@ -414,6 +415,66 @@ def _process_long_opt(self, rargs, values) -> None: option.process(opt, value, values, self) + + def _process_short_opts(self, rargs, values) -> None: + """SCons-specific processing of short options. + + This is copied directly from the normal Optparse + :meth:`~optparse.OptionParser._process_short_opts` method, except + that, if configured to do so, we catch the exception thrown + when an unknown option is encountered and just stick it back + on the "leftover" arguments for later (re-)processing. This is + because we may see the option definition later, while processing + SConscript files. + """ + arg = rargs.pop(0) + stop = False + i = 1 + for ch in arg[1:]: + opt = "-" + ch + option = self._short_opt.get(opt) + i += 1 # we have consumed a character + + try: + if not option: + raise optparse.BadOptionError(opt) + except optparse.BadOptionError: + # SCons addition: if requested, add unknown options to + # the "leftover arguments" list for later processing. + if self.preserve_unknown_options: + self.largs.append(arg) + return + raise + + if option.takes_value(): + # Any characters left in arg? Pretend they're the + # next arg, and stop consuming characters of arg. + if i < len(arg): + rargs.insert(0, arg[i:]) + stop = True + + nargs = option.nargs + if len(rargs) < nargs: + if nargs == 1: + self.error(_("%s option requires an argument") % opt) + else: + self.error(_("%s option requires %d arguments") + % (opt, nargs)) + elif nargs == 1: + value = rargs.pop(0) + else: + value = tuple(rargs[0:nargs]) + del rargs[0:nargs] + + else: # option doesn't take a value + value = None + + option.process(opt, value, values, self) + + if stop: + break + + def reparse_local_options(self) -> None: """Re-parse the leftover command-line options. diff --git a/test/AddOption/basic.py b/test/AddOption/basic.py index 83297a827..f488c704c 100644 --- a/test/AddOption/basic.py +++ b/test/AddOption/basic.py @@ -38,7 +38,7 @@ DefaultEnvironment(tools=[]) env = Environment(tools=[]) AddOption( - '--force', + '-F', '--force', action="store_true", help='force installation (overwrite any existing files)', ) @@ -74,6 +74,7 @@ test.run('-Q -q .', stdout="None\nNone\n") test.run('-Q -q . --force', stdout="True\nNone\n") +test.run('-Q -q . -F', stdout="True\nNone\n") test.run('-Q -q . --prefix=/home/foo', stdout="None\n/home/foo\n") test.run('-Q -q . -- --prefix=/home/foo --force', status=1, stdout="None\nNone\n") # check that SetOption works on prefix... diff --git a/test/SCONSFLAGS.py b/test/SCONSFLAGS.py index 4a44f30b0..ffccf1f2f 100644 --- a/test/SCONSFLAGS.py +++ b/test/SCONSFLAGS.py @@ -62,15 +62,15 @@ test.must_not_contain_any_line(test.stdout(), ['Help text.']) test.must_contain_all_lines(test.stdout(), ['-H, --help-options']) -os.environ['SCONSFLAGS'] = '-Z' - expect = r"""usage: scons [OPTIONS] [VARIABLES] [TARGETS] SCons Error: no such option: -Z """ -test.run(arguments = "-H", status = 2, - stderr = expect, match=TestSCons.match_exact) +test.run(arguments="-Z", status=2, stderr=expect, match=TestSCons.match_exact) + +os.environ['SCONSFLAGS'] = '-Z' +test.run(status=2, stderr=expect, match=TestSCons.match_exact) test.pass_test()