__init__.py (SCons-4.3.0) | : | __init__.py (SCons-4.4.0) | ||
---|---|---|---|---|
skipping to change at line 24 | skipping to change at line 24 | |||
# in all copies or substantial portions of the Software. | # in all copies or substantial portions of the Software. | |||
# | # | |||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||
"""SCons.Tool | """SCons tool selection. | |||
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). | ||||
This 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 | Note that because this subsystem just *selects* a callable that can | |||
modify a construction environment, it's possible for people to define | modify a construction environment, it's possible for people to define | |||
their own "tool specification" in an arbitrary callable function. No | 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 | one needs to use or tie in to this subsystem in order to roll their own | |||
tool specifications. | tool specifications. | |||
""" | """ | |||
import sys | import sys | |||
import os | import os | |||
import importlib.util | import importlib.util | |||
from typing import Optional | ||||
import SCons.Builder | import SCons.Builder | |||
import SCons.Errors | import SCons.Errors | |||
import SCons.Node.FS | import SCons.Node.FS | |||
import SCons.Scanner | import SCons.Scanner | |||
import SCons.Scanner.C | import SCons.Scanner.C | |||
import SCons.Scanner.D | import SCons.Scanner.D | |||
import SCons.Scanner.Java | ||||
import SCons.Scanner.LaTeX | import SCons.Scanner.LaTeX | |||
import SCons.Scanner.Prog | import SCons.Scanner.Prog | |||
import SCons.Scanner.SWIG | import SCons.Scanner.SWIG | |||
from SCons.Tool.linkCommon import LibSymlinksActionFunction, LibSymlinksStrFun | from SCons.Tool.linkCommon import LibSymlinksActionFunction, LibSymlinksStrFun | |||
DefaultToolpath = [] | DefaultToolpath = [] | |||
CScanner = SCons.Scanner.C.CScanner() | CScanner = SCons.Scanner.C.CScanner() | |||
DScanner = SCons.Scanner.D.DScanner() | DScanner = SCons.Scanner.D.DScanner() | |||
JavaScanner = SCons.Scanner.Java.JavaScanner() | ||||
LaTeXScanner = SCons.Scanner.LaTeX.LaTeXScanner() | LaTeXScanner = SCons.Scanner.LaTeX.LaTeXScanner() | |||
PDFLaTeXScanner = SCons.Scanner.LaTeX.PDFLaTeXScanner() | PDFLaTeXScanner = SCons.Scanner.LaTeX.PDFLaTeXScanner() | |||
ProgramScanner = SCons.Scanner.Prog.ProgramScanner() | ProgramScanner = SCons.Scanner.Prog.ProgramScanner() | |||
SourceFileScanner = SCons.Scanner.ScannerBase({}, name='SourceFileScanner') | SourceFileScanner = SCons.Scanner.ScannerBase({}, name='SourceFileScanner') | |||
SWIGScanner = SCons.Scanner.SWIG.SWIGScanner() | SWIGScanner = SCons.Scanner.SWIG.SWIGScanner() | |||
CSuffixes = [".c", ".C", ".cxx", ".cpp", ".c++", ".cc", | CSuffixes = [".c", ".C", ".cxx", ".cpp", ".c++", ".cc", | |||
".h", ".H", ".hxx", ".hpp", ".hh", | ".h", ".H", ".hxx", ".hpp", ".hh", | |||
".F", ".fpp", ".FPP", | ".F", ".fpp", ".FPP", | |||
".m", ".mm", | ".m", ".mm", | |||
skipping to change at line 123 | skipping to change at line 123 | |||
self.toolpath = toolpath + DefaultToolpath | self.toolpath = toolpath + DefaultToolpath | |||
# remember these so we can merge them into the call | # remember these so we can merge them into the call | |||
self.init_kw = kwargs | self.init_kw = kwargs | |||
module = self._tool_module() | module = self._tool_module() | |||
self.generate = module.generate | self.generate = module.generate | |||
self.exists = module.exists | self.exists = module.exists | |||
if hasattr(module, 'options'): | if hasattr(module, 'options'): | |||
self.options = module.options | self.options = module.options | |||
def _load_dotted_module_py2(self, short_name, full_name, searchpaths=None): | ||||
import imp | ||||
splitname = short_name.split('.') | ||||
index = 0 | ||||
srchpths = searchpaths | ||||
for item in splitname: | ||||
file, path, desc = imp.find_module(item, srchpths) | ||||
mod = imp.load_module(full_name, file, path, desc) | ||||
srchpths = [path] | ||||
return mod, file | ||||
def _tool_module(self): | def _tool_module(self): | |||
"""Try to load a tool module. | ||||
This will hunt in the toolpath for both a Python file (toolname.py) | ||||
and a Python module (toolname directory), then try the regular | ||||
import machinery, then fallback to try a zipfile. | ||||
""" | ||||
oldpythonpath = sys.path | oldpythonpath = sys.path | |||
sys.path = self.toolpath + sys.path | sys.path = self.toolpath + sys.path | |||
# sys.stderr.write("Tool:%s\nPATH:%s\n"%(self.name,sys.path)) | # These could be enabled under "if debug:" | |||
# sys.stderr.write(f"Tool: {self.name}\n") | ||||
# From: http://stackoverflow.com/questions/67631/how-to-import-a-module- | # sys.stderr.write(f"PATH: {sys.path}\n") | |||
given-the-full-path/67692#67692 | # sys.stderr.write(f"toolpath: {self.toolpath}\n") | |||
# import importlib.util | # sys.stderr.write(f"SCONS.TOOL path: {sys.modules['SCons.Tool'].__path_ | |||
# spec = importlib.util.spec_from_file_location("module.name", "/path/to | _}\n") | |||
/file.py") | ||||
# foo = importlib.util.module_from_spec(spec) | ||||
# spec.loader.exec_module(foo) | ||||
# foo.MyClass() | ||||
# Py 3 code | ||||
# sys.stderr.write("toolpath:%s\n" % self.toolpath) | ||||
# sys.stderr.write("SCONS.TOOL path:%s\n" % sys.modules['SCons.Tool'].__ | ||||
path__) | ||||
debug = False | debug = False | |||
spec = None | spec = None | |||
found_name = self.name | found_name = self.name | |||
add_to_scons_tools_namespace = False | add_to_scons_tools_namespace = False | |||
# Search for the tool module, but don't import it, yet. | ||||
# | ||||
# First look in the toolpath: these take priority. | ||||
# TODO: any reason to not just use find_spec here? | ||||
for path in self.toolpath: | for path in self.toolpath: | |||
sepname = self.name.replace('.', os.path.sep) | sepname = self.name.replace('.', os.path.sep) | |||
file_path = os.path.join(path, "%s.py" % sepname) | file_path = os.path.join(path, sepname + ".py") | |||
file_package = os.path.join(path, sepname) | file_package = os.path.join(path, sepname) | |||
if debug: sys.stderr.write("Trying:%s %s\n" % (file_path, file_packa ge)) | if debug: sys.stderr.write(f"Trying: {file_path} {file_package}\n") | |||
if os.path.isfile(file_path): | if os.path.isfile(file_path): | |||
spec = importlib.util.spec_from_file_location(self.name, file_pa th) | spec = importlib.util.spec_from_file_location(self.name, file_pa th) | |||
if debug: print("file_Path:%s FOUND" % file_path) | if debug: sys.stderr.write(f"file_Path: {file_path} FOUND\n") | |||
break | break | |||
elif os.path.isdir(file_package): | elif os.path.isdir(file_package): | |||
file_package = os.path.join(file_package, '__init__.py') | file_package = os.path.join(file_package, '__init__.py') | |||
spec = importlib.util.spec_from_file_location(self.name, file_pa ckage) | spec = importlib.util.spec_from_file_location(self.name, file_pa ckage) | |||
if debug: print("PACKAGE:%s Found" % file_package) | if debug: sys.stderr.write(f"PACKAGE: {file_package} Found\n") | |||
break | break | |||
else: | else: | |||
continue | continue | |||
# Now look in the builtin tools (SCons.Tool package) | ||||
if spec is None: | if spec is None: | |||
if debug: sys.stderr.write("NO SPEC :%s\n" % self.name) | if debug: sys.stderr.write(f"NO SPEC: {self.name}\n") | |||
spec = importlib.util.find_spec("." + self.name, package='SCons.Tool ') | spec = importlib.util.find_spec("." + self.name, package='SCons.Tool ') | |||
if spec: | if spec: | |||
found_name = 'SCons.Tool.' + self.name | found_name = 'SCons.Tool.' + self.name | |||
add_to_scons_tools_namespace = True | add_to_scons_tools_namespace = True | |||
if debug: sys.stderr.write("Spec Found? .%s :%s\n" % (self.name, spe c)) | if debug: sys.stderr.write(f"Spec Found? .{self.name}: {spec}\n") | |||
if spec is None: | if spec is None: | |||
# we are going to bail out here, format up stuff for the msg | ||||
sconstools = os.path.normpath(sys.modules['SCons.Tool'].__path__[0]) | sconstools = os.path.normpath(sys.modules['SCons.Tool'].__path__[0]) | |||
if self.toolpath: | if self.toolpath: | |||
sconstools = ", ".join(self.toolpath) + ", " + sconstools | sconstools = ", ".join(self.toolpath) + ", " + sconstools | |||
error_string = "No tool module '%s' found in %s" % (self.name, scons | msg = f"No tool module '{self.name}' found in {sconstools}" | |||
tools) | raise SCons.Errors.UserError(msg) | |||
raise SCons.Errors.UserError(error_string) | ||||
# We have a module spec, so we're good to go. | ||||
module = importlib.util.module_from_spec(spec) | module = importlib.util.module_from_spec(spec) | |||
if module is None: | if module is None: | |||
if debug: print("MODULE IS NONE:%s" % self.name) | if debug: sys.stderr.write(f"MODULE IS NONE: {self.name}\n") | |||
error_string = "Tool module '%s' failed import" % self.name | msg = f"Tool module '{self.name}' failed import" | |||
raise SCons.Errors.SConsEnvironmentError(error_string) | raise SCons.Errors.SConsEnvironmentError(msg) | |||
# Don't reload a tool we already loaded. | # Don't reload a tool we already loaded. | |||
sys_modules_value = sys.modules.get(found_name, False) | sys_modules_value = sys.modules.get(found_name, False) | |||
found_module = None | found_module = None | |||
if sys_modules_value and sys_modules_value.__file__ == spec.origin: | if sys_modules_value and sys_modules_value.__file__ == spec.origin: | |||
found_module = sys.modules[found_name] | found_module = sys.modules[found_name] | |||
else: | else: | |||
# Not sure what to do in the case that there already | # Not sure what to do in the case that there already | |||
# exists sys.modules[self.name] but the source file is | # exists sys.modules[self.name] but the source file is | |||
# different.. ? | # different.. ? | |||
module = spec.loader.load_module(spec.name) | ||||
sys.modules[found_name] = module | sys.modules[found_name] = module | |||
spec.loader.exec_module(module) | ||||
if add_to_scons_tools_namespace: | if add_to_scons_tools_namespace: | |||
# If we found it in SCons.Tool, then add it to the module | # If we found it in SCons.Tool, add it to the module | |||
setattr(SCons.Tool, self.name, module) | setattr(SCons.Tool, self.name, module) | |||
found_module = module | found_module = module | |||
if found_module is not None: | if found_module is not None: | |||
sys.path = oldpythonpath | sys.path = oldpythonpath | |||
return found_module | return found_module | |||
sys.path = oldpythonpath | sys.path = oldpythonpath | |||
# We try some other things here, but this is essentially dead code, | ||||
# because we bailed out above if we didn't find a module spec. | ||||
full_name = 'SCons.Tool.' + self.name | full_name = 'SCons.Tool.' + self.name | |||
try: | try: | |||
return sys.modules[full_name] | return sys.modules[full_name] | |||
except KeyError: | except KeyError: | |||
try: | try: | |||
smpath = sys.modules['SCons.Tool'].__path__ | # This support was added to enable running inside | |||
try: | # a py2exe bundle a long time ago - unclear if it's | |||
module, file = self._load_dotted_module_py2(self.name, full_ | # still needed. It is *not* intended to load individual | |||
name, smpath) | # tool modules stored in a zipfile. | |||
setattr(SCons.Tool, self.name, module) | import zipimport | |||
if file: | ||||
file.close() | tooldir = sys.modules['SCons.Tool'].__path__[0] | |||
return module | importer = zipimport.zipimporter(tooldir) | |||
except ImportError as e: | if not hasattr(importer, 'find_spec'): | |||
if str(e) != "No module named %s" % self.name: | # zipimport only added find_spec, exec_module in 3.10, | |||
raise SCons.Errors.SConsEnvironmentError(e) | # unlike importlib, where they've been around since 3.4. | |||
try: | # If we don't have 'em, use the old way. | |||
import zipimport | module = importer.load_module(full_name) | |||
importer = zipimport.zipimporter(sys.modules['SCons.Tool | else: | |||
'].__path__[0]) | spec = importer.find_spec(full_name) | |||
module = importer.load_module(full_name) | module = importlib.util.module_from_spec(spec) | |||
setattr(SCons.Tool, self.name, module) | importer.exec_module(module) | |||
return module | sys.modules[full_name] = module | |||
except ImportError as e: | setattr(SCons.Tool, self.name, module) | |||
m = "No tool named '%s': %s" % (self.name, e) | return module | |||
raise SCons.Errors.SConsEnvironmentError(m) | except zipimport.ZipImportError as e: | |||
except ImportError as e: | msg = "No tool named '{self.name}': {e}" | |||
m = "No tool named '%s': %s" % (self.name, e) | raise SCons.Errors.SConsEnvironmentError(msg) | |||
raise SCons.Errors.SConsEnvironmentError(m) | ||||
def __call__(self, env, *args, **kw): | def __call__(self, env, *args, **kw): | |||
if self.init_kw is not None: | if self.init_kw is not None: | |||
# Merge call kws into init kws; | # Merge call kws into init kws; | |||
# but don't bash self.init_kw. | # but don't bash self.init_kw. | |||
if kw is not None: | if kw is not None: | |||
call_kw = kw | call_kw = kw | |||
kw = self.init_kw.copy() | kw = self.init_kw.copy() | |||
kw.update(call_kw) | kw.update(call_kw) | |||
else: | else: | |||
skipping to change at line 510 | skipping to change at line 505 | |||
java_class_file = env['BUILDERS']['JavaClassFile'] | java_class_file = env['BUILDERS']['JavaClassFile'] | |||
except KeyError: | except KeyError: | |||
fs = SCons.Node.FS.get_default_fs() | fs = SCons.Node.FS.get_default_fs() | |||
javac_com = SCons.Action.Action('$JAVACCOM', '$JAVACCOMSTR') | javac_com = SCons.Action.Action('$JAVACCOM', '$JAVACCOMSTR') | |||
java_class_file = SCons.Builder.Builder(action=javac_com, | java_class_file = SCons.Builder.Builder(action=javac_com, | |||
emitter={}, | emitter={}, | |||
# suffix = '$JAVACLASSSUFFIX', | # suffix = '$JAVACLASSSUFFIX', | |||
src_suffix='$JAVASUFFIX', | src_suffix='$JAVASUFFIX', | |||
src_builder=['JavaFile'], | src_builder=['JavaFile'], | |||
target_factory=fs.Entry, | target_factory=fs.Entry, | |||
source_factory=fs.File) | source_factory=fs.File, | |||
target_scanner=JavaScanner) | ||||
env['BUILDERS']['JavaClassFile'] = java_class_file | env['BUILDERS']['JavaClassFile'] = java_class_file | |||
return java_class_file | return java_class_file | |||
def CreateJavaClassDirBuilder(env): | def CreateJavaClassDirBuilder(env): | |||
try: | try: | |||
java_class_dir = env['BUILDERS']['JavaClassDir'] | java_class_dir = env['BUILDERS']['JavaClassDir'] | |||
except KeyError: | except KeyError: | |||
fs = SCons.Node.FS.get_default_fs() | fs = SCons.Node.FS.get_default_fs() | |||
javac_com = SCons.Action.Action('$JAVACCOM', '$JAVACCOMSTR') | javac_com = SCons.Action.Action('$JAVACCOM', '$JAVACCOMSTR') | |||
java_class_dir = SCons.Builder.Builder(action=javac_com, | java_class_dir = SCons.Builder.Builder(action=javac_com, | |||
emitter={}, | emitter={}, | |||
target_factory=fs.Dir, | target_factory=fs.Dir, | |||
source_factory=fs.Dir) | source_factory=fs.Dir, | |||
target_scanner=JavaScanner) | ||||
env['BUILDERS']['JavaClassDir'] = java_class_dir | env['BUILDERS']['JavaClassDir'] = java_class_dir | |||
return java_class_dir | return java_class_dir | |||
def CreateJavaFileBuilder(env): | def CreateJavaFileBuilder(env): | |||
try: | try: | |||
java_file = env['BUILDERS']['JavaFile'] | java_file = env['BUILDERS']['JavaFile'] | |||
except KeyError: | except KeyError: | |||
java_file = SCons.Builder.Builder(action={}, | java_file = SCons.Builder.Builder(action={}, | |||
emitter={}, | emitter={}, | |||
suffix={None: '$JAVASUFFIX'}) | suffix={None: '$JAVASUFFIX'}) | |||
skipping to change at line 810 | skipping to change at line 807 | |||
c_compiler, | c_compiler, | |||
cxx_compiler, | cxx_compiler, | |||
fortran_compiler, | fortran_compiler, | |||
assembler, | assembler, | |||
ar, | ar, | |||
d_compiler, | d_compiler, | |||
] + other_tools | ] + other_tools | |||
return [x for x in tools if x] | return [x for x in tools if x] | |||
def find_program_path(env, key_program, default_paths=None): | def find_program_path(env, key_program, default_paths=None, add_path=False) -> O ptional[str]: | |||
""" | """ | |||
Find the location of a tool using various means. | Find the location of a tool using various means. | |||
Mainly for windows where tools aren't all installed in /usr/bin, etc. | Mainly for windows where tools aren't all installed in /usr/bin, etc. | |||
:param env: Current Construction Environment. | Args: | |||
:param key_program: Tool to locate. | env: Current Construction Environment. | |||
:param default_paths: List of additional paths this tool might be found in. | key_program: Tool to locate. | |||
default_paths: List of additional paths this tool might be found in. | ||||
add_path: If true, add path found if it was from *default_paths*. | ||||
""" | """ | |||
# First search in the SCons path | # First search in the SCons path | |||
path = env.WhereIs(key_program) | path = env.WhereIs(key_program) | |||
if path: | if path: | |||
return path | return path | |||
# Then in the OS path | # Then in the OS path | |||
path = SCons.Util.WhereIs(key_program) | path = SCons.Util.WhereIs(key_program) | |||
if path: | if path: | |||
if add_path: | ||||
env.AppendENVPath('PATH', os.path.dirname(path)) | ||||
return path | return path | |||
# Finally, add the defaults and check again. Do not change | # Finally, add the defaults and check again. | |||
# ['ENV']['PATH'] permananetly, the caller can do that if needed. | ||||
if default_paths is None: | if default_paths is None: | |||
return path | return path | |||
save_path = env['ENV']['PATH'] | save_path = env['ENV']['PATH'] | |||
for p in default_paths: | for p in default_paths: | |||
env.AppendENVPath('PATH', p) | env.AppendENVPath('PATH', p) | |||
path = env.WhereIs(key_program) | path = env.WhereIs(key_program) | |||
# By default, do not change ['ENV']['PATH'] permananetly | ||||
# leave that to the caller, unless add_path is true. | ||||
env['ENV']['PATH'] = save_path | env['ENV']['PATH'] = save_path | |||
if path and add_path: | ||||
env.AppendENVPath('PATH', os.path.dirname(path)) | ||||
return path | return path | |||
# Local Variables: | # Local Variables: | |||
# tab-width:4 | # tab-width:4 | |||
# indent-tabs-mode:nil | # indent-tabs-mode:nil | |||
# End: | # End: | |||
# vim: set expandtab tabstop=4 shiftwidth=4: | # vim: set expandtab tabstop=4 shiftwidth=4: | |||
End of changes. 36 change blocks. | ||||
82 lines changed or deleted | 84 lines changed or added |