Skip to content
Open
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
10 changes: 10 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
- Fix Appveyor scripting to install unavailable python versions when needed and use them
for testing.

From Dillan Mills:
- Fix handling of AddOption for option-arguments with spaces
(when omitting the = sign or when an option takes multiple
option-arguments). Arguments unknown at the time the first pass
splits the command line into arguments and targets would put such
arguments into targets, and they remained there even after the
AddOption calls were seen. Closes #2748, #2805, #2977.

From Mats Wichmann:
- Introduce some unit tests for the file locking utility routines
- More clarifications in manpage Builder Methods section.
Expand Down Expand Up @@ -81,6 +89,8 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
on a one-time uuid to make a path to the file.
- Clarify VariantDir behavior when switching to not duplicate sources
and tweak wording a bit.
- Complete the work from PR #3799 on AddOption handling (original work
credited to Dillan Mills)


RELEASE 4.10.1 - Sun, 16 Nov 2025 10:51:57 -0700
Expand Down
9 changes: 8 additions & 1 deletion RELEASE.txt
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,13 @@ FIXES
directory for each writer before doing the move, instead of depending
on a one-time uuid to make a path to the file.

- Fix handling of AddOption for option-arguments with spaces (when omitting
the = sign or when an option takes multiple option-arguments). Arguments
unknown at the time the first pass splits the command line into arguments
and targets would put such arguments into targets, and they remained
there even after the AddOption calls were seen.


IMPROVEMENTS
------------

Expand All @@ -71,7 +78,7 @@ IMPROVEMENTS
- Simplified and sped up compilation database generation. No longer requires
each entry to have a dedicated node that's always built; instead, the database
*itself* is set to always build.

- Switch remaining "original style" docstring parameter listings to Google style.

- Additional small tweaks to Environment.py type hints, fold some overly
Expand Down
27 changes: 11 additions & 16 deletions SCons/Script/Main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -172,22 +172,17 @@ see the &f-Help; documentation for details.

<note>
<para>
As an artifact of the internal implementation,
the behavior of options added by &AddOption;
which take option arguments is undefined
<emphasis>if</emphasis> whitespace
(rather than an <literal>=</literal> sign) is used as
the separator on the command line.
Users should avoid such usage; it is recommended
to add a note to this effect to project documentation
if the situation is likely to arise.
In addition, if the <parameter>nargs</parameter>
keyword is used to specify more than one following
option argument (that is, with a value of <constant>2</constant>
or greater), such arguments would necessarily
be whitespace separated, triggering the issue.
Developers should not use &AddOption; this way.
Future versions of &SCons; will likely forbid such usage.
Prior to version NEXT_RELEASE,
the behavior when options added by &AddOption;
are specified with whitespace was undefined and discouraged.
Such usage covers both the use of a space as the separator
(<literal>--opt arg</literal> vs
<literal>--opt=arg</literal>),
and where the number of option arguments
(<parameter>nargs</parameter>) is specified as greater than one.
While this issue has been corrected,
developers should be aware of it in case the
project is built with an older &SCons; version.
</para>
</note>

Expand Down
84 changes: 22 additions & 62 deletions SCons/Script/SConsOptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -405,9 +405,18 @@ def _process_long_opt(self, rargs, values) -> None:
% (opt, nargs))
elif nargs == 1:
value = rargs.pop(0)
if not had_explicit_value:
SCons.Script._Remove_Target(value)
if '=' in value:
SCons.Script._Remove_Argument(value)
else:
value = tuple(rargs[0:nargs])
del rargs[0:nargs]
for i in range(len(value)):
if not had_explicit_value or i > 0:
SCons.Script._Remove_Target(value[i])
if '=' in value[i]:
SCons.Script._Remove_Argument(value[i])

elif had_explicit_value:
self.error(_("%s option does not take a value") % opt)
Expand Down Expand Up @@ -447,11 +456,13 @@ def _process_short_opts(self, rargs, values) -> None:
raise

if option.takes_value():
had_explicit_value = False
# 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
had_explicit_value = True

nargs = option.nargs
if len(rargs) < nargs:
Expand All @@ -462,9 +473,19 @@ def _process_short_opts(self, rargs, values) -> None:
% (opt, nargs))
elif nargs == 1:
value = rargs.pop(0)
if not had_explicit_value:
SCons.Script._Remove_Target(value)
if '=' in value:
SCons.Script._Remove_Argument(value)
else:
value = tuple(rargs[0:nargs])
del rargs[0:nargs]
for i in range(len(value)):
if not had_explicit_value or i > 0:
SCons.Script._Remove_Target(value[i])
if '=' in value[i]:
SCons.Script._Remove_Argument(value[i])


else: # option doesn't take a value
value = None
Expand All @@ -475,67 +496,6 @@ def _process_short_opts(self, rargs, values) -> None:
break


def reparse_local_options(self) -> None:
"""Re-parse the leftover command-line options.

Leftover options are stored in ``self.largs``, so that any value
overridden on the command line is immediately available
if the user turns around and does a :func:`~SCons.Script.Main.GetOption`
right away.

We mimic the processing of the single args
in the original OptionParser :func:`_process_args`, but here we
allow exact matches for long-opts only (no partial argument names!).
Otherwise there could be problems in :meth:`add_local_option`
below. When called from there, we try to reparse the
command-line arguments that haven't been processed so far
(``self.largs``), but are possibly not added to the options list yet.

So, when we only have a value for ``--myargument`` so far,
a command-line argument of ``--myarg=test`` would set it,
per the behaviour of :func:`_match_long_opt`,
which allows for partial matches of the option name,
as long as the common prefix appears to be unique.
This would lead to further confusion, because we might want
to add another option ``--myarg`` later on (see issue #2929).
"""
rargs = []
largs_restore = []
# Loop over all remaining arguments
skip = False
for larg in self.largs:
if skip:
# Accept all remaining arguments as they are
largs_restore.append(larg)
else:
if len(larg) > 2 and larg[0:2] == "--":
# Check long option
lopt = [larg]
if "=" in larg:
# Split into option and value
lopt = larg.split("=", 1)

if lopt[0] in self._long_opt:
# Argument is already known
rargs.append('='.join(lopt))
else:
# Not known yet, so reject for now
largs_restore.append('='.join(lopt))
else:
if larg in("--", "-"):
# Stop normal processing and don't
# process the rest of the command-line opts
largs_restore.append(larg)
skip = True
else:
rargs.append(larg)

# Parse the filtered list
self.parse_args(rargs, self.values)
# Restore the list of leftover arguments for the
# next call of AddOption/add_local_option...
self.largs = self.largs + largs_restore

def add_local_option(self, *args, **kw) -> SConsOption:
"""Add a local option to the parser.

Expand Down Expand Up @@ -574,7 +534,7 @@ def add_local_option(self, *args, **kw) -> SConsOption:
# right away.
# TODO: what if dest is None?
setattr(self.values.__defaults__, result.dest, result.default)
self.reparse_local_options()
self.parse_args(self.largs, self.values)
if result.settable:
SConsValues.settable.append(result.dest)

Expand Down
10 changes: 5 additions & 5 deletions SCons/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
__version__="4.10.2"
__copyright__="Copyright (c) 2001 - 2026 The SCons Foundation"
__developer__="bdbaddog"
__date__="Sat, 03 Jan 2026 14:09:24 -0700"
__buildsys__="M1Dog2021"
__revision__="9573362eaf4dadc6368ad27a75bc2790a3e5e813"
__build__="9573362eaf4dadc6368ad27a75bc2790a3e5e813"
__developer__="mats"
__date__="Wed, 15 Apr 2026 13:01:10 -0600"
__buildsys__="boulder"
__revision__="359b2fbd043519b0609acdc2deffc609d31095b3"
__build__="359b2fbd043519b0609acdc2deffc609d31095b3"
# make sure compatibility is always in place
import SCons.compat # noqa
69 changes: 26 additions & 43 deletions doc/user/command-line.xml
Original file line number Diff line number Diff line change
Expand Up @@ -741,51 +741,34 @@ foo.in
</scons_output>

<note>

<para>
The <systemitem>optparse</systemitem> parser which &SCons; uses
allows option-arguments to follow their options after either
an <literal>=</literal> or space separator,
however the latter form does not work well in &SCons; for
added options and should be avoided.
&SCons; does not place an ordering constraint on the
types of command-line arguments,
so while <option>--input=ARG</option> is unambiguous,
for <option>--input ARG</option>
it is not possible to tell without instructions whether
<parameter>ARG</parameter> is an argument belonging to the
<parameter>input</parameter> option or a standalone word.
&SCons; considers words on the command line which do not
begin with hyphen as either command-line build variables
or command-line targets,
both of which are made available for use in an &SConscript;
(see the immediately following sections for details).
Thus, they must be collected before &SConscript; processing
takes place. &AddOption; calls do provide the
necessary instructions to resolve the ambiguity,
but as they appear in &SConscript; files,
&SCons; does not have the information early enough,
and unexpected things may happen,
such as option-arguments appearing in the list of targets,
and processing exceptions due to missing option-arguments.
</para>
<para>
As a result,
this usage style should be avoided when invoking &scons;.
For single-argument options,
tell your users to use the <option>--input=ARG</option>
form on the command line.
For multiple-argument options
(<parameter>nargs</parameter> value greater than one),
set <parameter>nargs</parameter> to one in the
&AddOption; call and either: combine the option-arguments into one word
with a separator, and parse the result in your own code
(see the built-in <option>--debug</option> option, which
allows specifying multiple arguments as a single comma-separated
word, for an example of such usage); or allow the option to
be specified multiple times by setting
<literal>action='append'</literal>. Both methods can be
supported at the same time.
Prior to version NEXT_RELEASE,
the behavior when options added by &AddOption;
are specified on the command line with spaces was undefined,
and such usage was discouraged.
This applies to both using a space as the separator
(<literal>--opt arg</literal> vs
<literal>--opt=arg</literal>),
and when more than one option arguments is required.
In case your project may need to be built with an older
&SCons; version, you can take steps to avoid problems.
You cannot programmatically avoid the former case,
since it is allowed by the underlying &Python; module
that performs the command-line parsing,
but you can tell your users not to call that way.
For the latter case,
you can set the <parameter>nargs</parameter> to <literal>1</literal>,
tell users to supply the arguments in comma-separated form
(<literal>--opt=arg1,arg2</literal> instead of
<literal>--opt arg1 arg2</literal>),
and validate/parse that in the build script,
or alternatively,
allow the option to be specified multiple times by setting
<literal>action='append'</literal>.
Both methods can be supported at the same time.
</para>

</note>

</section>
Expand Down
4 changes: 0 additions & 4 deletions test/AddOption/.exclude_tests

This file was deleted.

Loading
Loading