2222# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
2323# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2424
25- """SCons tool selection.
26-
27- Looks for modules that define a callable object that can modify a
28- construction environment as appropriate for a given tool (or tool chain).
29-
30- Note that because this subsystem just *selects* a callable that can
31- modify a construction environment, it's possible for people to define
32- their own "tool specification" in an arbitrary callable function. No
33- one needs to use or tie in to this subsystem in order to roll their own
34- tool specifications.
25+ """SCons tool subsystem.
26+
27+ Tool specification modules are callable objects that modify construction
28+ environments to dynamically enable specific types of builds. This module
29+ provides the support for handling tool modules:
30+
31+ - a Tool class to locate and load a tool module.
32+ - lists of default tools for supported platform types and a mechanism to
33+ select the available ones for the current platform.
34+ - various rules for name remapping, special-cased toolchains, etc.
35+ - utility functions for creating common Builders (eliminating duplication
36+ when multiple tool modules could potentially create a Builder).
37+ - create Scanners for common types used by the Builder utilities.
38+
39+ This is not set up like a traditional Python package: this file implements
40+ the part that other subsystems can import and call. The remaining files
41+ are either dynamically loaded tool modules that present entry points
42+ (``exists()`` and ``generate()``) called through the Tool instance,
43+ or support files/common logic that can be imported by those tool
44+ modules. Neither are expected to be directly imported by any other SCons
45+ subsystem (test code may reach in and do so).
46+
47+ Tool modules are simply callable objects that modify a construction
48+ environment. You can define custom tool specifications in any callable
49+ without needing to integrate with this subsystem.
3550"""
3651
3752from __future__ import annotations
4055import os
4156import importlib .util
4257
58+ import SCons .Action
4359import SCons .Builder
4460import SCons .Errors
4561import SCons .Node .FS
5066import SCons .Scanner .LaTeX
5167import SCons .Scanner .Prog
5268import SCons .Scanner .SWIG
69+ import SCons .Util
5370from SCons .Tool .linkCommon import LibSymlinksActionFunction , LibSymlinksStrFun
5471
5572DefaultToolpath = []
108125
109126
110127class Tool :
128+ """A class for loading and applying tool modules.
129+
130+ *name* is looked up using standard paths plus any specified *toolpath*.
131+ To avoid duplicate creation of instances, recognize if *name*
132+ is actually an existing instance, if so, just return ourselves
133+ without further setup.
134+
135+ .. versionchanged:: NEXT_RELEASE
136+ Accept an existing instance at creation time and don't duplicate it.
137+ """
138+
139+ def __new__ (cls , name , toolpath = None , ** kwargs ) -> "Tool" :
140+ if isinstance (name , Tool ):
141+ return name
142+ return super ().__new__ (cls )
143+
111144 def __init__ (self , name , toolpath = None , ** kwargs ) -> None :
145+ if isinstance (name , Tool ):
146+ return
112147 if toolpath is None :
113148 toolpath = []
114149
@@ -270,6 +305,23 @@ def __call__(self, env, *args, **kw) -> None:
270305 def __str__ (self ) -> str :
271306 return self .name
272307
308+ def __eq__ (self , other ) -> bool :
309+ """Compare a Tool by name.
310+
311+ A Tool is equal to another Tool with the same name, and to a
312+ string matching its name (so a Tool can be used interchangeably
313+ with its name in membership tests and sets). Comparison against
314+ any other type is deferred (returns ``NotImplemented``).
315+ """
316+ if isinstance (other , Tool ):
317+ return self .name == other .name
318+ if isinstance (other , str ):
319+ return self .name == other
320+ return NotImplemented
321+
322+ def __hash__ (self ) -> int :
323+ return hash (self .name )
324+
273325
274326LibSymlinksAction = SCons .Action .Action (LibSymlinksActionFunction , LibSymlinksStrFun )
275327
@@ -671,18 +723,34 @@ def InstallVersionedLib(self, *args, **kw):
671723
672724def FindTool (tools , env ):
673725 for tool in tools :
726+ if not SCons .Util .is_String (tool ):
727+ # Already a Tool instance
728+ if tool .exists (env ):
729+ return tool
730+ continue
674731 t = Tool (tool )
675732 if t .exists (env ):
676- return tool
733+ return t
677734 return None
678735
679736
680737def FindAllTools (tools , env ):
681738 def ToolExists (tool , env = env ):
682- return Tool (tool ).exists (env )
683-
684- return list (filter (ToolExists , tools ))
739+ if not SCons .Util .is_String (tool ):
740+ return tool .exists (env )
741+ t = Tool (tool )
742+ return t .exists (env )
685743
744+ results = []
745+ for tool in tools :
746+ if not SCons .Util .is_String (tool ):
747+ if tool .exists (env ):
748+ results .append (tool )
749+ continue
750+ t = Tool (tool )
751+ if t .exists (env ):
752+ results .append (t )
753+ return results
686754
687755def tool_list (platform , env ):
688756 other_plat_tools = []
@@ -773,7 +841,7 @@ def tool_list(platform, env):
773841
774842 # XXX this logic about what tool provides what should somehow be
775843 # moved into the tool files themselves.
776- if c_compiler and c_compiler == 'mingw' :
844+ if c_compiler and str ( c_compiler ) == 'mingw' :
777845 # MinGW contains a linker, C compiler, C++ compiler,
778846 # Fortran compiler, archiver and assembler:
779847 cxx_compiler = None
@@ -783,7 +851,7 @@ def tool_list(platform, env):
783851 ar = None
784852 else :
785853 # Don't use g++ if the C compiler has built-in C++ support:
786- if c_compiler in ('msvc' , 'intelc' , 'icc' ):
854+ if str ( c_compiler ) in ('msvc' , 'intelc' , 'icc' ):
787855 cxx_compiler = None
788856 else :
789857 cxx_compiler = FindTool (cxx_compilers , env ) or cxx_compilers [0 ]
0 commit comments