Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions RELEASE.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
73 changes: 67 additions & 6 deletions SCons/Script/SConsOptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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.

Expand Down
3 changes: 2 additions & 1 deletion test/AddOption/basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
DefaultEnvironment(tools=[])
env = Environment(tools=[])
AddOption(
'--force',
'-F', '--force',
action="store_true",
help='force installation (overwrite any existing files)',
)
Expand Down Expand Up @@ -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...
Expand Down
8 changes: 4 additions & 4 deletions test/SCONSFLAGS.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down