--- a/src/engine/SCons/Platform/__init__.py +++ b/src/engine/SCons/Platform/__init__.py @@ -47,14 +47,15 @@ __revision__ = "__FILE__ __REVISION__ __ import SCons.compat -import imp import os +import importlib import sys import tempfile import SCons.Errors import SCons.Subst import SCons.Tool +import SCons.Util def platform_default(): @@ -100,13 +101,7 @@ def platform_module(name = platform_defa eval(full_name) else: try: - file, path, desc = imp.find_module(name, - sys.modules['SCons.Platform'].__path__) - try: - mod = imp.load_module(full_name, file, path, desc) - finally: - if file: - file.close() + mod = importlib.import_module(full_name) except ImportError: try: import zipimport @@ -114,7 +109,7 @@ def platform_module(name = platform_defa mod = importer.load_module(full_name) except ImportError: raise SCons.Errors.UserError("No platform named '%s'" % name) - setattr(SCons.Platform, name, mod) + setattr(SCons.Platform, name, mod) return sys.modules[full_name] def DefaultToolList(platform, env): --- a/src/engine/SCons/Script/Main.py +++ b/src/engine/SCons/Script/Main.py @@ -48,6 +48,7 @@ import sys import time import traceback import sysconfig +import platform import SCons.CacheDir import SCons.Debug @@ -622,7 +623,7 @@ def _SConstruct_exists(dirname='', repos current directory. """ if not filelist: - filelist = ['SConstruct', 'Sconstruct', 'sconstruct'] + filelist = ['SConstruct', 'Sconstruct', 'sconstruct', 'SConstruct.py', 'Sconstruct.py', 'sconstruct.py'] for file in filelist: sfile = os.path.join(dirname, file) if os.path.isfile(sfile): @@ -711,54 +712,89 @@ def _load_site_scons_dir(topdir, site_di sys.path = [os.path.abspath(site_dir)] + sys.path site_init_file = os.path.join(site_dir, site_init_filename) site_tools_dir = os.path.join(site_dir, site_tools_dirname) - if os.path.exists(site_init_file): - import imp, re - try: + + if SCons.Util.PY2 or SCons.Util.PY34: + if os.path.exists(site_init_file): + import imp, re try: - fp, pathname, description = imp.find_module(site_init_modname, - [site_dir]) - # Load the file into SCons.Script namespace. This is - # opaque and clever; m is the module object for the - # SCons.Script module, and the exec ... in call executes a - # file (or string containing code) in the context of the - # module's dictionary, so anything that code defines ends - # up adding to that module. This is really short, but all - # the error checking makes it longer. + try: + fp, pathname, description = imp.find_module(site_init_modname, + [site_dir]) + # Load the file into SCons.Script namespace. This is + # opaque and clever; m is the module object for the + # SCons.Script module, and the exec ... in call executes a + # file (or string containing code) in the context of the + # module's dictionary, so anything that code defines ends + # up adding to that module. This is really short, but all + # the error checking makes it longer. + try: + m = sys.modules['SCons.Script'] + except Exception as e: + fmt = 'cannot import site_init.py: missing SCons.Script module {}' + raise SCons.Errors.InternalError(fmt.format(repr(e))) + try: + sfx = description[0] + modname = os.path.basename(pathname)[:-len(sfx)] + site_m = {"__file__": pathname, "__name__": modname, "__doc__": None} + re_special = re.compile("__[^_]+__") + for k in list(m.__dict__.keys()): + if not re_special.match(k): + site_m[k] = m.__dict__[k] + + # This is the magic. + exec(compile(fp.read(), fp.name, 'exec'), site_m) + except KeyboardInterrupt: + raise + except Exception as e: + fmt = '*** Error loading site_init file %s:\n' + sys.stderr.write(fmt % repr(site_init_file)) + raise + else: + for k in site_m: + if not re_special.match(k): + m.__dict__[k] = site_m[k] + except KeyboardInterrupt: + raise + except ImportError as e: + fmt = '*** cannot import site init file %s:\n' + sys.stderr.write(fmt % repr(site_init_file)) + raise + finally: + if fp: + fp.close() + else: # Python >= 3.5 + if os.path.exists(site_init_file): + import importlib.util, importlib.machinery + try: + loader_details = ( + importlib.machinery.ExtensionFileLoader, + importlib.machinery.EXTENSION_SUFFIXES + ) + finder = importlib.machinery.FileFinder(site_dir, + loader_details) + spec = finder.find_spec(site_init_modname) try: m = sys.modules['SCons.Script'] except Exception as e: fmt = 'cannot import site_init.py: missing SCons.Script module %s' raise SCons.Errors.InternalError(fmt % repr(e)) try: - sfx = description[0] - modname = os.path.basename(pathname)[:-len(sfx)] - site_m = {"__file__": pathname, "__name__": modname, "__doc__": None} - re_special = re.compile("__[^_]+__") - for k in list(m.__dict__.keys()): - if not re_special.match(k): - site_m[k] = m.__dict__[k] - + mod = importlib.util.module_from_spec(spec) # This is the magic. - exec(compile(fp.read(), fp.name, 'exec'), site_m) + exec(mod.__loader__.get_code(mod.__name__), m.__dict__) except KeyboardInterrupt: raise except Exception as e: - fmt = '*** Error loading site_init file %s:\n' - sys.stderr.write(fmt % repr(site_init_file)) + fmt = '*** Error loading site_init file {}:\n' + sys.stderr.write(fmt.format(repr(site_init_file))) raise - else: - for k in site_m: - if not re_special.match(k): - m.__dict__[k] = site_m[k] except KeyboardInterrupt: raise except ImportError as e: fmt = '*** cannot import site init file %s:\n' sys.stderr.write(fmt % repr(site_init_file)) raise - finally: - if fp: - fp.close() + if os.path.exists(site_tools_dir): # prepend to DefaultToolpath SCons.Tool.DefaultToolpath.insert(0, os.path.abspath(site_tools_dir)) @@ -1253,11 +1289,11 @@ def _build_targets(fs, options, targets, BuildTask.options = options + is_pypy = platform.python_implementation() == 'PyPy' # As of 3.7, python removed support for threadless platforms. # See https://www.python.org/dev/peps/pep-0011/ - is_37_or_later = sys.version_info.major > 3 or \ - sys.version_info.major == 3 and sys.version_info.minor >= 7 - python_has_threads = sysconfig.get_config_var('WITH_THREAD') or is_37_or_later + is_37_or_later = sys.version_info >= (3, 7) + python_has_threads = sysconfig.get_config_var('WITH_THREAD') or is_pypy or is_37_or_later # to check if python configured with threads. global num_jobs num_jobs = options.num_jobs --- a/src/engine/SCons/Tool/__init__.py +++ b/src/engine/SCons/Tool/__init__.py @@ -54,6 +54,7 @@ import SCons.Scanner.D import SCons.Scanner.LaTeX import SCons.Scanner.Prog import SCons.Scanner.SWIG +import SCons.Util import collections DefaultToolpath=[] @@ -136,7 +137,7 @@ class Tool(object): sys.path = self.toolpath + sys.path # sys.stderr.write("Tool:%s\nPATH:%s\n"%(self.name,sys.path)) - if sys.version_info[0] < 3 or (sys.version_info[0] == 3 and sys.version_info[1] in (0,1,2,3,4)): + if SCons.Util.PY2 or SCons.Util.PY34: # Py 2 code try: try: @@ -238,7 +239,7 @@ class Tool(object): setattr(SCons.Tool, self.name, module) found_module = module - + if found_module is not None: sys.path = oldpythonpath return found_module --- a/src/engine/SCons/Tool/packaging/__init__.py +++ b/src/engine/SCons/Tool/packaging/__init__.py @@ -27,16 +27,20 @@ SCons Packaging Tool. __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" +import SCons.Defaults import SCons.Environment from SCons.Variables import * from SCons.Errors import * -from SCons.Util import is_List, make_path_relative +from SCons.Util import is_List, is_String, make_path_relative, PY2, PY34 from SCons.Warnings import warn, Warning -import os, imp -import SCons.Defaults +import os -__all__ = [ 'src_targz', 'src_tarbz2', 'src_zip', 'tarbz2', 'targz', 'zip', 'rpm', 'msi', 'ipk' ] +__all__ = [ + 'src_targz', 'src_tarbz2', 'src_xz', 'src_zip', + 'targz', 'tarbz2', 'xz', 'zip', + 'rpm', 'msi', 'ipk', +] # # Utility and Builder function @@ -63,7 +67,7 @@ def Tag(env, target, source, *more_tags, for x in more_tags: kw_tags[x] = '' - if not SCons.Util.is_List(target): + if not is_List(target): target=[target] else: # hmm, sometimes the target list, is a list of a list @@ -102,7 +106,7 @@ def Package(env, target=None, source=Non from SCons.Script import GetOption kw['PACKAGETYPE'] = GetOption('package_type') - if kw['PACKAGETYPE'] == None: + if kw['PACKAGETYPE'] is None: if 'Tar' in env['BUILDERS']: kw['PACKAGETYPE']='targz' elif 'Zip' in env['BUILDERS']: @@ -117,8 +121,17 @@ def Package(env, target=None, source=Non # load the needed packagers. def load_packager(type): try: - file,path,desc=imp.find_module(type, __path__) - return imp.load_module(type, file, path, desc) + if PY2: + import imp + file,path,desc=imp.find_module(type, __path__) + return imp.load_module(type, file, path, desc) + else: + import importlib + savepath = sys.path + sys.path = __path__ + mod = importlib.import_module(type) + sys.path = savepath + return mod except ImportError as e: raise EnvironmentError("packager %s not available: %s"%(type,str(e))) @@ -163,15 +176,22 @@ def Package(env, target=None, source=Non # this exception means that a needed argument for the packager is # missing. As our packagers get their "tags" as named function # arguments we need to find out which one is missing. - from inspect import getargspec - args,varargs,varkw,defaults=getargspec(packager.package) - if defaults!=None: - args=args[:-len(defaults)] # throw away arguments with default values + #TODO: getargspec deprecated in Py3. cleanup when Py2.7 dropped. + try: + from inspect import getfullargspec + argspec = getfullargspec(packager.package) + except ImportError: + from inspect import getargspec + argspec = getargspec(packager.package) + args = argspec.args + if argspec.defaults: + # throw away arguments with default values + args = args[:-len(argspec.defaults)] args.remove('env') args.remove('target') args.remove('source') # now remove any args for which we have a value in kw. - args=[x for x in args if x not in kw] + args = [x for x in args if x not in kw] if len(args)==0: raise # must be a different error, so re-raise @@ -251,12 +271,12 @@ def putintopackageroot(target, source, e All attributes of the source file will be copied to the new file. """ # make sure the packageroot is a Dir object. - if SCons.Util.is_String(pkgroot): pkgroot=env.Dir(pkgroot) - if not SCons.Util.is_List(source): source=[source] + if is_String(pkgroot): pkgroot=env.Dir(pkgroot) + if not is_List(source): source=[source] new_source = [] for file in source: - if SCons.Util.is_String(file): file = env.File(file) + if is_String(file): file = env.File(file) if file.is_under(pkgroot): new_source.append(file) @@ -283,10 +303,9 @@ def stripinstallbuilder(target, source, It also warns about files which have no install builder attached. """ def has_no_install_location(file): - return not (file.has_builder() and\ - hasattr(file.builder, 'name') and\ - (file.builder.name=="InstallBuilder" or\ - file.builder.name=="InstallAsBuilder")) + return not (file.has_builder() and hasattr(file.builder, 'name') + and file.builder.name in ["InstallBuilder", "InstallAsBuilder"]) + if len([src for src in source if has_no_install_location(src)]): warn(Warning, "there are files to package which have no\ --- a/src/engine/SCons/Util.py +++ b/src/engine/SCons/Util.py @@ -35,6 +35,8 @@ import codecs import pprint PY3 = sys.version_info[0] == 3 +PY2 = sys.version_info[0] == 2 +PY34 = sys.version_info[0] == 3 and sys.version_info[1] <= 4 try: from collections import UserDict, UserList, UserString --- a/src/engine/SCons/compat/__init__.py +++ b/src/engine/SCons/compat/__init__.py @@ -61,7 +61,7 @@ __revision__ = "__FILE__ __REVISION__ __ import os import sys -import imp # Use the "imp" module to protect imports from fixers. +import importlib # Use the "importlib" module to protect imports from fixers. PYPY = hasattr(sys, 'pypy_translation_info') @@ -71,8 +71,8 @@ def import_as(module, name): Imports the specified module (from our local directory) as the specified name, returning the loaded module object. """ - dir = os.path.split(__file__)[0] - return imp.load_module(name, *imp.find_module(module, [dir])) + sys.modules[name] = importlib.import_module(module) + return sys.modules[name] def rename_module(new, old): @@ -81,7 +81,7 @@ def rename_module(new, old): Used for purely cosmetic name changes in Python 3.x. """ try: - sys.modules[new] = imp.load_module(old, *imp.find_module(old)) + sys.modules[new] = importlib.import_module(old) return True except ImportError: return False