Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
2 changes: 2 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,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.
- Don't duplicate the cration of Tool objects if "default" is part of
the tool list.


RELEASE 4.10.1 - Sun, 16 Nov 2025 10:51:57 -0700
Expand Down
3 changes: 3 additions & 0 deletions RELEASE.txt
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ long function signature lines, and some linting-insipired cleanups.

- Test suite: end to end tests don't use assert in result checks

- Don't duplicate the cration of Tool objects if "default" is part of
the tool list.


PACKAGING
---------
Expand Down
91 changes: 74 additions & 17 deletions SCons/Tool/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,31 @@
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

"""SCons tool selection.

Looks for modules that define a callable object that can modify a
construction environment as appropriate for a given tool (or tool chain).

Note that because this subsystem just *selects* a callable that can
modify a construction environment, it's possible for people to define
their own "tool specification" in an arbitrary callable function. No
one needs to use or tie in to this subsystem in order to roll their own
tool specifications.
"""SCons tool subsystem.

Tool specification modules are callable objects that modify construction
environments to dynamically enable specific types of builds. This module
provides the support for handling tool modules:

- a Tool class to locate and load a tool module.
- lists of default tools for supported platform types and a mechanism to
select the available ones for the current platform.
- various rules for name remapping, special-cased toolchains, etc.
- utility functions for creating common Builders (eliminating duplication
when multiple tool modules could potentially create a Builder).
- create Scanners for common types used by the Builder utilities.

This is not set up like a traditional Python package: this file implements
the part that other subsystems can import and call. The remaining files
are either dynamically loaded tool modules that present entry points
(``exists()`` and ``generate()``) called through the Tool instance,
or support files/common logic that can be imported by those tool
modules. Neither are expected to be directly imported by any other SCons
subsystem (test code may reach in and do so).

Tool modules are simply callable objects that modify a construction
environment. You can define custom tool specifications in any callable
without needing to integrate with this subsystem.
"""

from __future__ import annotations
Expand All @@ -40,6 +55,7 @@
import os
import importlib.util

import SCons.Action
import SCons.Builder
import SCons.Errors
import SCons.Node.FS
Expand All @@ -50,6 +66,7 @@
import SCons.Scanner.LaTeX
import SCons.Scanner.Prog
import SCons.Scanner.SWIG
import SCons.Util
from SCons.Tool.linkCommon import LibSymlinksActionFunction, LibSymlinksStrFun

DefaultToolpath = []
Expand Down Expand Up @@ -108,7 +125,25 @@


class Tool:
"""A class for loading and applying tool modules.

*name* is looked up using standard paths plus any specified *toolpath*.
To avoid duplicate creation of instances, recognize if *name*
is actually an existing instance, if so, just return ourselves
without further setup.

.. versionchanged:: NEXT_RELEASE
Accept an exsiting instance at creation time and don't duplicate it.
"""

def __new__(cls, name, toolpath=None, **kwargs) -> None:
if isinstance(name, Tool):
return name
return super().__new__(cls)

def __init__(self, name, toolpath=None, **kwargs) -> None:
if isinstance(name, Tool):
return
if toolpath is None:
toolpath = []

Expand Down Expand Up @@ -254,7 +289,7 @@ def __call__(self, env, *args, **kw) -> None:
kw.update(call_kw)
else:
kw = self.init_kw
env.AppendUnique(TOOLS=[self.name])
env.AppendUnique(TOOLS=[str(self.name)])
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this needed? isn't .name already a string?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, probably you're right. I'll check eventually.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reverted.

if hasattr(self, 'options'):
import SCons.Variables
if 'options' not in env:
Expand All @@ -270,6 +305,12 @@ def __call__(self, env, *args, **kw) -> None:
def __str__(self) -> str:
return self.name

def __eq__(self, other) -> bool:
return str(other) == self.name

def __hash__(self) -> int:
return hash(self.name)


LibSymlinksAction = SCons.Action.Action(LibSymlinksActionFunction, LibSymlinksStrFun)

Expand Down Expand Up @@ -671,18 +712,34 @@ def InstallVersionedLib(self, *args, **kw):

def FindTool(tools, env):
for tool in tools:
if not SCons.Util.is_String(tool):
# Already a Tool instance
if tool.exists(env):
return tool
continue
t = Tool(tool)
if t.exists(env):
return tool
return t
return None


def FindAllTools(tools, env):
def ToolExists(tool, env=env):
return Tool(tool).exists(env)

return list(filter(ToolExists, tools))
if not SCons.Util.is_String(tool):
return tool.exists(env)
t = Tool(tool)
return t.exists(env)

results = []
for tool in tools:
if not SCons.Util.is_String(tool):
Comment thread
bdbaddog marked this conversation as resolved.
if tool.exists(env):
results.append(tool)
continue
t = Tool(tool)
if t.exists(env):
results.append(t)
return results

def tool_list(platform, env):
other_plat_tools = []
Expand Down Expand Up @@ -773,7 +830,7 @@ def tool_list(platform, env):

# XXX this logic about what tool provides what should somehow be
# moved into the tool files themselves.
if c_compiler and c_compiler == 'mingw':
if c_compiler and str(c_compiler) == 'mingw':
# MinGW contains a linker, C compiler, C++ compiler,
# Fortran compiler, archiver and assembler:
cxx_compiler = None
Expand All @@ -783,7 +840,7 @@ def tool_list(platform, env):
ar = None
else:
# Don't use g++ if the C compiler has built-in C++ support:
if c_compiler in ('msvc', 'intelc', 'icc'):
if str(c_compiler) in ('msvc', 'intelc', 'icc'):
cxx_compiler = None
else:
cxx_compiler = FindTool(cxx_compilers, env) or cxx_compilers[0]
Expand Down
3 changes: 2 additions & 1 deletion SCons/Tool/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,12 @@
selection method.
"""

import SCons.Platform
import SCons.Tool

def generate(env) -> None:
"""Add default tools."""
for t in SCons.Tool.tool_list(env['PLATFORM'], env):
for t in SCons.Platform.DefaultToolList(env['PLATFORM'], env):
SCons.Tool.Tool(t)(env)

def exists(env) -> bool:
Expand Down
Loading