Skip to content

Commit fef3edf

Browse files
authored
Merge branch 'master' into tool-init
2 parents 842c50b + 4c3b8a7 commit fef3edf

29 files changed

Lines changed: 673 additions & 845 deletions

CHANGES.txt

Lines changed: 61 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,19 +44,61 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
4444
new contributors. (Using Gemini AI)
4545
- Fix Appveyor scripting to install unavailable python versions when needed and use them
4646
for testing.
47+
- Subst: Fixed ListSubber.expanded(), which never detected an already-expanded
48+
string (dead code since its 2019 introduction), so fully-expanded values are
49+
no longer recursively re-processed during scons_subst_list().
50+
- Subst: scons_subst() and scons_subst_list() no longer leak a __builtins__
51+
key into the construction environment's dictionary if an exception is
52+
raised during substitution.
53+
- Subst: the result of the inspect.signature() check for callable
54+
construction variables is now cached per callable, speeding up expansion
55+
of function-valued variables. Callables whose signature cannot be
56+
determined (some C/builtin callables) are now treated as not matching
57+
the (target, source, env, for_signature) convention instead of raising.
58+
- Subst: variable values which are plain strings with no further '$'
59+
expansions are now returned directly, skipping an unneeded dict copy
60+
and recursive substitution pass. Combined, the substitution speedups
61+
measured on a representative command line (this from a micro benchmark
62+
and not on a full build) are:
63+
('$CC $CCFLAGS $CPPDEFINES $GEN -c -o $TARGET $SOURCES'):
64+
old new improvement
65+
scons_subst 20.7 us 12.8 us ~38% faster
66+
scons_subst_list 37.4 us 25.1 us ~33% faster
67+
- Subst: a NameError raised during scons_subst_list() now includes the
68+
name of the unknown variable in the error message.
69+
- Subst: the overrides argument to scons_subst()/scons_subst_list() no
70+
longer mutates a caller-supplied lvars dictionary; also removed mutable
71+
default arguments. Removed Literal.__neq__, a misspelled (and therefore
72+
never-invoked) version of __ne__; Python derives inequality from
73+
Literal.__eq__.
74+
75+
From Prabhu S. Khalsa:
76+
- Fix typo in preface
77+
- Remove word "below" when content is not actually located below
78+
- Fix typos in preface, Chapter 6, Chapter 9 and Chapter 10 of User Guide
79+
- Fix broken links in Chapter 1 of User Guide
80+
4781

4882
From Mats Wichmann:
4983
- Introduce some unit tests for the file locking utility routines
50-
- More clarifications in manpage Builder Methods section.
84+
- Subst: Improved variable substitution by consolidating dictionary
85+
merging operations, reducing unnecessary copy operations when no
86+
TARGET/SOURCE variables or overrides need to be applied. Combined
87+
with other substitution improvements (lru_cache for callable signature
88+
caching, action hashability, for_signature bug fix, f-string
89+
modernization), typical builds see up to 8-12% improvement with larger gains
90+
(up to 20-30%) on builds with many callable construction variables.
5191
- Reduce unneeded computation of overrides. The Mkdir builder used an
5292
unknown argument ('explain') on creation, causing it to be considered
5393
an override. Also, if override dict is empty, don't even call the
5494
Override factory function.
5595
- Handle the default (unset) ProgressObject differently for the sole
5696
purpose of avoiding Sphinx 9.0+ blowing up on it (it's been giving
5797
a warning for years, but now it's a fatal error).
58-
- Improve covarage of API doc build by ignoring any setting of
98+
- Improve coverage of "API docs" build by ignoring any setting of
5999
__all__ in a package and not showing inherited members from optparse.
100+
Add a trick to make sure the build sees PathListCache while
101+
generating docs (in normal operation, it's removed so was not visible).
60102
- Fix --debug=includes for case of multiple source files.
61103
- Unify internal "_null" sentinel usage.
62104
- All functions/classes/non-dunder methods in Environment have docstrings.
@@ -84,15 +126,25 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
84126
- Don't duplicate the creation of Tool objects if "default" is part of
85127
the tool list.
86128
- Test suite: add support for Python 3.15 in the Action unit tests.
87-
- Update documentation of the three file-finding functions in the API
88-
(FindFile, FindInstalledFiles, FindSourceFiles) as well as FindPathDirs.
89-
Also add a test for FindFile to be sure it locates non-existing
90-
derived files as advertised.
129+
- Add a test for FindFile to be sure it locates non-existing derived
130+
files as advertised.
91131
- zip tool now uses zipfile module's "from_file" method to populate
92132
ZipInfo records, rather than doing manually.
93-
- Update documentation for linking-related construction variables.
94-
Add a few more live links.
95-
- Update documentation for CacheDir and related option flags
133+
- Reference manual improvements:
134+
* More clarifications in Builder Methods section.
135+
* Clarify VariantDir behavior when switching from duplicate=True (the
136+
default) to duplicate=False, and tweak wording a bit.
137+
* Update documentation of the three file-finding functions (FindFile,
138+
FindInstalledFiles, FindSourceFiles) as well as FindPathDirs.
139+
* Update linking-related construction variables.
140+
* Add a few more live links.
141+
* Clarify CacheDir and related option flags.
142+
* Drop old example section from manpage (not a visible change: has been
143+
commented out since v4.0, when the content moved to the new Cookbook)
144+
* Reword IMPLICIT_COMMAND_DEPENDENCIES construction variable.
145+
* Add info on the type of an option variable to GetOption and SetOption.
146+
* Tighten up the doc for Variables-Add() and add a note on file contents
147+
not being preserved to Variables-Save()
96148

97149

98150
RELEASE 4.10.1 - Sun, 16 Nov 2025 10:51:57 -0700

RELEASE.txt

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,18 @@ IMPROVEMENTS
6363
an override. Also, if override dict is empty, don't even call the
6464
Override factory function.
6565

66+
- Subst: Multiple substitution optimizations providing up to 8-12% performance
67+
improvement on typical builds, with larger gains (up to 20-30%) on builds with
68+
many callable construction variables:
69+
* Consolidate dictionary merging operations to reduce unnecessary copies
70+
* Use functools.lru_cache for callable signature inspection with bounded
71+
memory (256 entries max, ~1MB)
72+
* Make Action classes (CommandAction, FunctionAction, ListAction) hashable
73+
to enable cache optimization
74+
* Fix for_signature logic bug in ListSubber.expand() for correct signature
75+
generation
76+
* Modernize string formatting to f-strings
77+
6678
- Test runner reworked to use Python logging; the portion of the test suite
6779
which tests the runner was adjusted to match.
6880

@@ -112,8 +124,10 @@ DOCUMENTATION
112124
a warning for years, but now it's a fatal error). Affects only the
113125
API doc build.
114126

115-
- Improve coverage of API doc build by ignoring any setting of
127+
- Improve coverage of "API doc" build by ignoring any setting of
116128
__all__ in a package and not showing inherited members from optparse.
129+
Add a trick to make sure build sees PathListCache while generating
130+
docs (in normal operation, it's removed so was not visible).
117131

118132
- All functions/classes/non-dunder methods in Environment now have docstrings.
119133

@@ -132,6 +146,23 @@ DOCUMENTATION
132146

133147
- Update documentation for CacheDir and related option flags
134148

149+
- Drop old example section from manpage. This has been commented out since
150+
release 4.0, when the content was used to populate the new SCons Cookbook.
151+
152+
- Update documentation for IMPLICIT_COMMAND_DEPENDENCIES construction variable.
153+
154+
- Fix typo in preface
155+
- Remove word "below" when content is not actually located below
156+
157+
- Fix typos in preface, Chapter 6, Chapter 9 and Chapter 10 of User Guide
158+
159+
- Fix broken links in Chapter 1 of User Guide
160+
161+
- Add info on the type of an option variable to GetOption and SetOption.
162+
163+
- Tighten up the doc for Variables-Add() and add a note on file contents
164+
not being preserved to Variables-Save()
165+
135166
DEVELOPMENT
136167
-----------
137168

SCons/Action.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,6 +1000,9 @@ def __str__(self) -> str:
10001000
return ' '.join(map(str, self.cmd_list))
10011001
return str(self.cmd_list)
10021002

1003+
def __hash__(self) -> int:
1004+
""" Added so CommandAction can be used as a key in a dict and/or cached. """
1005+
return id(self)
10031006

10041007
def process(self, target, source, env, executor: Executor | None = None, overrides: dict | None = None) -> tuple[list, bool, bool]:
10051008
if executor:
@@ -1421,6 +1424,10 @@ def __str__(self) -> str:
14211424
return str(self.execfunction)
14221425
return "%s(target, source, env)" % name
14231426

1427+
def __hash__(self) -> int:
1428+
""" Added so FunctionAction can be used as a key in a dict and/or cached. """
1429+
return id(self)
1430+
14241431
def execute(self, target, source, env, executor: Executor | None = None):
14251432
exc_info = (None,None,None)
14261433
try:
@@ -1482,6 +1489,10 @@ def genstring(self, target, source, env, executor: Executor | None = None) -> st
14821489
def __str__(self) -> str:
14831490
return '\n'.join(map(str, self.list))
14841491

1492+
def __hash__(self) -> int:
1493+
""" Added so ListAction can be used as a key in a dict and/or cached. """
1494+
return id(self)
1495+
14851496
def presub_lines(self, env):
14861497
return SCons.Util.flatten_sequence(
14871498
[a.presub_lines(env) for a in self.list])

SCons/Action.xml

Lines changed: 45 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -27,109 +27,66 @@ This file is processed by the bin/SConsDoc.py module.
2727
<cvar name="IMPLICIT_COMMAND_DEPENDENCIES">
2828
<summary>
2929
<para>
30-
Controls whether or not &SCons; will
31-
add implicit dependencies for the commands
32-
executed to build targets.
30+
Controls how &SCons; adds implicit dependencies from action strings
31+
(the commands executed to build targets).
32+
These implicit dependencies allow &SCons; to trigger target rebuilds
33+
when the compilers, tools, or scripts that affect build outcomes change
34+
(for example, when a new version of a compiler is installed).
35+
By default, &SCons; adds an implicit dependency on the executable
36+
represented by the first argument of the command line.
37+
This executable is first checked as a path;
38+
if not found, it is searched for in the execution environment's search path
39+
(<literal>env['ENV']['PATH']</literal>).
3340
</para>
3441

3542
<para>
36-
By default, &SCons; will add to each target
37-
an implicit dependency on the command
38-
represented by the first argument of any
39-
command line it executes (which is typically
40-
the command itself). By setting such
41-
a dependency, &SCons; can determine that
42-
a target should be rebuilt if the command changes,
43-
such as when a compiler is upgraded to a new version.
44-
The specific file for the dependency is
45-
found by searching the
46-
<varname>PATH</varname>
47-
variable in the
48-
<varname>ENV</varname> dictionary
49-
in the &consenv; used to execute the command.
50-
The default is the same as
51-
setting the &consvar;
43+
If &cv-IMPLICIT_COMMAND_DEPENDENCIES; is set to a falsy value
44+
(<literal>"none"</literal>, <literal>"false"</literal>,
45+
<literal>"no"</literal>, <literal>"off"</literal>,
46+
<literal>"0"</literal> or integer <literal>0</literal>),
47+
no implicit dependencies from action strings are added.
48+
If set to an integer value greater than <literal>1</literal>,
49+
or to <literal>"all"</literal>,
50+
additional command-line arguments are checked.
51+
A numeric value <emphasis>N</emphasis>
52+
checks the first <emphasis>N</emphasis> arguments,
53+
while <literal>"all"</literal> checks every argument.
54+
A checked argument beyond the first is added as a dependency
55+
if it is an absolute path or it refers to
56+
an existing file in the filesystem;
57+
unlike the first argument, these additional arguments are not searched
58+
using the execution environment's search path.
59+
All other values of &cv-IMPLICIT_COMMAND_DEPENDENCIES;
60+
are treated the same as the default.
61+
The value of
5262
&cv-IMPLICIT_COMMAND_DEPENDENCIES;
53-
to a True-like value (<quote>true</quote>,
54-
<quote>yes</quote>,
55-
or <quote>1</quote> - but not a number
56-
greater than one, as that has a different meaning).
63+
is subject to substitution before it is used,
64+
so it can contain a &consvar; reference.
5765
</para>
5866

5967
<para>
60-
Action strings can be segmented by the
61-
use of an AND operator, <literal>&amp;&amp;</literal>.
62-
In a segmented string, each segment is a separate
63-
<quote>command line</quote>, these are run
64-
sequentially until one fails, or the entire
65-
sequence has been executed. If an
66-
action string is segmented, then the selected
67-
behavior of &cv-IMPLICIT_COMMAND_DEPENDENCIES;
68-
is applied to each segment.
68+
Action strings may be segmented with a logical AND operator
69+
(<literal>&amp;&amp;</literal>),
70+
indicating each segment is executed sequentially,
71+
but only if the previous segment succeeded.
72+
If &cv-IMPLICIT_COMMAND_DEPENDENCIES; is set to
73+
<literal>"all"</literal> or an integer greater than <literal>1</literal>,
74+
each segment is treated as a separate command line
75+
for computing dependencies;
76+
otherwise, only the first segment is checked.
6977
</para>
70-
71-
<para>
72-
If &cv-IMPLICIT_COMMAND_DEPENDENCIES;
73-
is set to a False-like value
74-
(<quote>none</quote>,
75-
<quote>false</quote>,
76-
<quote>no</quote>,
77-
<quote>0</quote>,
78-
etc.),
79-
then the implicit dependency will
80-
not be added to the targets
81-
built with that &consenv;.
82-
</para>
83-
84-
<para>
85-
If &cv-IMPLICIT_COMMAND_DEPENDENCIES;
86-
is set to <quote>2</quote> or higher,
87-
then that number of arguments in the command line
88-
will be scanned for relative or absolute paths.
89-
If any are present, they will be added as
90-
implicit dependencies to the targets built
91-
with that &consenv;.
92-
The first argument in the command line will be
93-
searched for using the <varname>PATH</varname>
94-
variable in the <varname>ENV</varname> dictionary
95-
in the &consenv; used to execute the command.
96-
The other arguments will only be found if they
97-
are absolute paths or valid paths relative
98-
to the working directory.
99-
</para>
100-
101-
<para>
102-
If &cv-IMPLICIT_COMMAND_DEPENDENCIES;
103-
is set to <quote>all</quote>,
104-
then all arguments in the command line will be
105-
scanned for relative or absolute paths.
106-
If any are present, they will be added as
107-
implicit dependencies to the targets built
108-
with that &consenv;.
109-
The first argument in the command line will be
110-
searched for using the <varname>PATH</varname>
111-
variable in the <varname>ENV</varname> dictionary
112-
in the &consenv; used to execute the command.
113-
The other arguments will only be found if they
114-
are absolute paths or valid paths relative
115-
to the working directory.
116-
</para>
117-
118-
<example_commands>
119-
env = Environment(IMPLICIT_COMMAND_DEPENDENCIES=False)
120-
</example_commands>
12178
</summary>
12279
</cvar>
12380

12481
<cvar name="PRINT_CMD_LINE_FUNC">
12582
<summary>
12683
<para>
127-
A Python function used to print the command lines as they are executed
128-
(assuming command printing is not disabled by the
129-
<option>-q</option>
84+
A &Python; function used to print the command lines as they are executed
85+
(unless command printing is disabled by the
86+
<link linkend="opt-question"><option>-q</option>/<option>--question</option></link>
13087
or
131-
<option>-s</option>
132-
options or their equivalents).
88+
<link linkend="opt-silent"><option>-s</option>/<option>--silent</option></link>
89+
options).
13390
The function must accept four arguments:
13491
<varname>s</varname>,
13592
<varname>target</varname>,

SCons/Defaults.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -266,8 +266,8 @@ directly into &cv-link-CCFLAGS; or &cv-link-CXXFLAGS;
266266
as the result will be non-portable
267267
and the directories will not be searched by the dependency scanner.
268268
&cv-CPPPATH; should be a list of path strings,
269-
or a single string, not a pathname list joined by
270-
Python's <systemitem>os.pathsep</systemitem>.
269+
or a single string, not a pathname list joined by the
270+
&Python; <systemitem>os.pathsep</systemitem> character.
271271
</para>
272272

273273
<para>
@@ -527,7 +527,7 @@ The list of directories that will be searched for libraries
527527
specified by the &cv-link-LIBS; &consvar;.
528528
&cv-LIBPATH; should be a list of path strings,
529529
or a single string, not a pathname list joined by
530-
Python's <systemitem>os.pathsep</systemitem>.
530+
the &Python; <systemitem>os.pathsep</systemitem> character.
531531
<!-- XXX OLD
532532
The implicit dependency scanner will search these
533533
directories for include files. Don't explicitly put include directory

SCons/PathList.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
"""
3131

3232
import os
33+
import sys
3334

3435
import SCons.Memoize
3536
import SCons.Node
@@ -216,6 +217,7 @@ def PathList(self, pathlist, split=True):
216217

217218
PathList = PathListCache().PathList
218219

219-
# TODO: removing the class object here means Sphinx doesn't pick up its
220-
# docstrings: they're fine for reading here, but are not in API Docs.
221-
del PathListCache
220+
# Some trickery here so Sphinx can still see the class when building API docs
221+
_SPHINX_BUILD = os.environ.get("SPHINX_BUILD") == "1"
222+
if not _SPHINX_BUILD:
223+
del PathListCache

0 commit comments

Comments
 (0)