diff options
author | William Deegan <bill@baddogconsulting.com> | 2022-05-15 01:18:59 (GMT) |
---|---|---|
committer | William Deegan <bill@baddogconsulting.com> | 2022-05-15 01:18:59 (GMT) |
commit | 0d958e0c58c60384f448060993fa013cbcbcf0dd (patch) | |
tree | 6e3c1bfd8166daf6210c46af9eb0bc958897fce5 /SCons/Scanner | |
parent | 570a59f9028e6bcf95ceac4dee92c03073191550 (diff) | |
parent | fdae889759be56c6299bcedc577aecf2225f0190 (diff) | |
download | SCons-0d958e0c58c60384f448060993fa013cbcbcf0dd.zip SCons-0d958e0c58c60384f448060993fa013cbcbcf0dd.tar.gz SCons-0d958e0c58c60384f448060993fa013cbcbcf0dd.tar.bz2 |
Merge remote-tracking branch 'upstream/master' into feature/minor_java_improvements
Diffstat (limited to 'SCons/Scanner')
-rw-r--r-- | SCons/Scanner/C.py | 49 | ||||
-rw-r--r-- | SCons/Scanner/CTests.py | 18 | ||||
-rw-r--r-- | SCons/Scanner/D.py | 20 | ||||
-rw-r--r-- | SCons/Scanner/DTests.py | 6 | ||||
-rw-r--r-- | SCons/Scanner/Dir.py | 41 | ||||
-rw-r--r-- | SCons/Scanner/DirTests.py | 39 | ||||
-rw-r--r-- | SCons/Scanner/Fortran.py | 19 | ||||
-rw-r--r-- | SCons/Scanner/IDL.py | 5 | ||||
-rw-r--r-- | SCons/Scanner/LaTeX.py | 46 | ||||
-rw-r--r-- | SCons/Scanner/LaTeXTests.py | 2 | ||||
-rw-r--r-- | SCons/Scanner/Prog.py | 8 | ||||
-rw-r--r-- | SCons/Scanner/Python.py | 15 | ||||
-rw-r--r-- | SCons/Scanner/PythonTests.py | 6 | ||||
-rw-r--r-- | SCons/Scanner/RC.py | 4 | ||||
-rw-r--r-- | SCons/Scanner/RCTests.py | 6 | ||||
-rw-r--r-- | SCons/Scanner/SWIG.py | 6 | ||||
-rw-r--r-- | SCons/Scanner/ScannerTests.py | 178 | ||||
-rw-r--r-- | SCons/Scanner/__init__.py | 245 |
18 files changed, 402 insertions, 311 deletions
diff --git a/SCons/Scanner/C.py b/SCons/Scanner/C.py index 91beb04..a066104 100644 --- a/SCons/Scanner/C.py +++ b/SCons/Scanner/C.py @@ -24,10 +24,10 @@ """Dependency scanner for C/C++ code.""" import SCons.Node.FS -import SCons.Scanner +import SCons.cpp import SCons.Util +from . import ClassicCPP, FindPathDirs -import SCons.cpp class SConsCPPScanner(SCons.cpp.PreProcessor): """SCons-specific subclass of the cpp.py module's processing. @@ -36,19 +36,23 @@ class SConsCPPScanner(SCons.cpp.PreProcessor): by Nodes, not strings; 2) we can keep track of the files that are missing. """ - def __init__(self, *args, **kw): - SCons.cpp.PreProcessor.__init__(self, *args, **kw) + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) self.missing = [] + def initialize_result(self, fname): self.result = SCons.Util.UniqueList([fname]) + def finalize_result(self, fname): return self.result[1:] + def find_include_file(self, t): keyword, quote, fname = t result = SCons.Node.FS.find_file(fname, self.searchpath[quote]) if not result: self.missing.append((fname, self.current_file)) return result + def read_file(self, file): try: with open(str(file.rfile())) as fp: @@ -57,7 +61,8 @@ class SConsCPPScanner(SCons.cpp.PreProcessor): self.missing.append((file, self.current_file)) return '' -def dictify_CPPDEFINES(env): +def dictify_CPPDEFINES(env) -> dict: + """Returns CPPDEFINES converted to a dict.""" cppdefines = env.get('CPPDEFINES', {}) if cppdefines is None: return {} @@ -65,7 +70,11 @@ def dictify_CPPDEFINES(env): result = {} for c in cppdefines: if SCons.Util.is_Sequence(c): - result[c[0]] = c[1] + try: + result[c[0]] = c[1] + except IndexError: + # it could be a one-item sequence + result[c[0]] = None else: result[c] = None return result @@ -81,18 +90,22 @@ class SConsCPPScannerWrapper: to look for #include lines with reasonably real C-preprocessor-like evaluation of #if/#ifdef/#else/#elif lines. """ + def __init__(self, name, variable): self.name = name - self.path = SCons.Scanner.FindPathDirs(variable) - def __call__(self, node, env, path = ()): - cpp = SConsCPPScanner(current = node.get_dir(), - cpppath = path, - dict = dictify_CPPDEFINES(env)) + self.path = FindPathDirs(variable) + + def __call__(self, node, env, path=()): + cpp = SConsCPPScanner( + current=node.get_dir(), cpppath=path, dict=dictify_CPPDEFINES(env) + ) result = cpp(node) for included, includer in cpp.missing: - fmt = "No dependency generated for file: %s (included from: %s) -- file not found" - SCons.Warnings.warn(SCons.Warnings.DependencyWarning, - fmt % (included, includer)) + SCons.Warnings.warn( + SCons.Warnings.DependencyWarning, + "No dependency generated for file: %s (included from: %s) " + "-- file not found" % (included, includer), + ) return result def recurse_nodes(self, nodes): @@ -110,7 +123,7 @@ def CScanner(): # right configurability to let users pick between the scanners. # return SConsCPPScannerWrapper("CScanner", "CPPPATH") - cs = SCons.Scanner.ClassicCPP( + cs = ClassicCPP( "CScanner", "$CPPSUFFIXES", "CPPPATH", @@ -132,8 +145,8 @@ class SConsCPPConditionalScanner(SCons.cpp.PreProcessor): missing. """ - def __init__(self, *args, **kw): - SCons.cpp.PreProcessor.__init__(self, *args, **kw) + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) self.missing = [] self._known_paths = [] @@ -177,7 +190,7 @@ class SConsCPPConditionalScannerWrapper: def __init__(self, name, variable): self.name = name - self.path = SCons.Scanner.FindPathDirs(variable) + self.path = FindPathDirs(variable) def __call__(self, node, env, path=(), depth=-1): cpp = SConsCPPConditionalScanner( diff --git a/SCons/Scanner/CTests.py b/SCons/Scanner/CTests.py index 25c6e99..14e156e 100644 --- a/SCons/Scanner/CTests.py +++ b/SCons/Scanner/CTests.py @@ -180,9 +180,9 @@ test.write("f5b.h", "\n") # define some helpers: class DummyEnvironment(collections.UserDict): - def __init__(self, **kw): - collections.UserDict.__init__(self) - self.data.update(kw) + def __init__(self, **kwargs): + super().__init__() + self.data.update(kwargs) self.fs = SCons.Node.FS.FS(test.workpath('')) def Dictionary(self, *args): @@ -490,6 +490,17 @@ class CConditionalScannerTestCase3(unittest.TestCase): headers = ['d1/f1.h'] deps_match(self, deps, headers) +class dictify_CPPDEFINESTestCase(unittest.TestCase): + def runTest(self): + """Make sure single-item tuples convert correctly. + + This is a regression test: AppendUnique turns sequences into + lists of tuples, and dictify could gack on these. + """ + env = DummyEnvironment(CPPDEFINES=(("VALUED_DEFINE", 1), ("UNVALUED_DEFINE", ))) + d = SCons.Scanner.C.dictify_CPPDEFINES(env) + expect = {'VALUED_DEFINE': 1, 'UNVALUED_DEFINE': None} + assert d == expect def suite(): suite = unittest.TestSuite() @@ -510,6 +521,7 @@ def suite(): suite.addTest(CConditionalScannerTestCase1()) suite.addTest(CConditionalScannerTestCase2()) suite.addTest(CConditionalScannerTestCase3()) + suite.addTest(dictify_CPPDEFINESTestCase()) return suite if __name__ == "__main__": diff --git a/SCons/Scanner/D.py b/SCons/Scanner/D.py index 645934b..c9b1e36 100644 --- a/SCons/Scanner/D.py +++ b/SCons/Scanner/D.py @@ -26,21 +26,21 @@ Coded by Andy Friesen, 17 Nov 2003 """ -import SCons.Scanner +import SCons.Node.FS +from . import Classic def DScanner(): """Return a prototype Scanner instance for scanning D source files""" ds = D() return ds -class D(SCons.Scanner.Classic): - def __init__ (self): - SCons.Scanner.Classic.__init__ ( - self, - name = "DScanner", - suffixes = '$DSUFFIXES', - path_variable = 'DPATH', - regex = r'(?:import\s+)([\w\s=,.]+)(?:\s*:[\s\w,=]+)?(?:;)' +class D(Classic): + def __init__(self): + super().__init__( + name="DScanner", + suffixes='$DSUFFIXES', + path_variable='DPATH', + regex=r'(?:import\s+)([\w\s=,.]+)(?:\s*:[\s\w,=]+)?(?:;)', ) def find_include(self, include, source_dir, path): @@ -49,7 +49,7 @@ class D(SCons.Scanner.Classic): i = SCons.Node.FS.find_file(inc + '.d', (source_dir,) + path) if i is None: - i = SCons.Node.FS.find_file (inc + '.di', (source_dir,) + path) + i = SCons.Node.FS.find_file(inc + '.di', (source_dir,) + path) return i, include def find_include_names(self, node): diff --git a/SCons/Scanner/DTests.py b/SCons/Scanner/DTests.py index 7b764aa..25ccca3 100644 --- a/SCons/Scanner/DTests.py +++ b/SCons/Scanner/DTests.py @@ -33,9 +33,9 @@ test = TestCmd.TestCmd(workdir = '') class DummyEnvironment(collections.UserDict): - def __init__(self, **kw): - collections.UserDict.__init__(self) - self.data.update(kw) + def __init__(self, **kwargs): + super().__init__() + self.data.update(kwargs) self.fs = SCons.Node.FS.FS(test.workpath('')) def Dictionary(self, *args): diff --git a/SCons/Scanner/Dir.py b/SCons/Scanner/Dir.py index 617bf2b..239b1ec 100644 --- a/SCons/Scanner/Dir.py +++ b/SCons/Scanner/Dir.py @@ -22,25 +22,25 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. import SCons.Node.FS -import SCons.Scanner +from . import ScannerBase def only_dirs(nodes): is_Dir = lambda n: isinstance(n.disambiguate(), SCons.Node.FS.Dir) return [node for node in nodes if is_Dir(node)] -def DirScanner(**kw): +def DirScanner(**kwargs): """Return a prototype Scanner instance for scanning directories for on-disk files""" - kw['node_factory'] = SCons.Node.FS.Entry - kw['recursive'] = only_dirs - return SCons.Scanner.Base(scan_on_disk, "DirScanner", **kw) + kwargs['node_factory'] = SCons.Node.FS.Entry + kwargs['recursive'] = only_dirs + return ScannerBase(scan_on_disk, "DirScanner", **kwargs) -def DirEntryScanner(**kw): +def DirEntryScanner(**kwargs): """Return a prototype Scanner instance for "scanning" directory Nodes for their in-memory entries""" - kw['node_factory'] = SCons.Node.FS.Entry - kw['recursive'] = None - return SCons.Scanner.Base(scan_in_memory, "DirEntryScanner", **kw) + kwargs['node_factory'] = SCons.Node.FS.Entry + kwargs['recursive'] = None + return ScannerBase(scan_in_memory, "DirEntryScanner", **kwargs) skip_entry = {} @@ -59,6 +59,29 @@ skip_entry_list = [ '.sconsign.bak', # Used by some dbm emulations using Berkeley DB. '.sconsign.db', + # new filenames since multiple hash formats allowed: + '.sconsign_md5.dblite', + '.sconsign_sha1.dblite', + '.sconsign_sha256.dblite', + # and all the duplicate files for each sub-sconsfile type + '.sconsign_md5', + '.sconsign_md5.dir', + '.sconsign_md5.pag', + '.sconsign_md5.dat', + '.sconsign_md5.bak', + '.sconsign_md5.db', + '.sconsign_sha1', + '.sconsign_sha1.dir', + '.sconsign_sha1.pag', + '.sconsign_sha1.dat', + '.sconsign_sha1.bak', + '.sconsign_sha1.db', + '.sconsign_sha256', + '.sconsign_sha256.dir', + '.sconsign_sha256.pag', + '.sconsign_sha256.dat', + '.sconsign_sha256.bak', + '.sconsign_sha256.db', ] for skip in skip_entry_list: diff --git a/SCons/Scanner/DirTests.py b/SCons/Scanner/DirTests.py index 2e73fe2..1c46c6c 100644 --- a/SCons/Scanner/DirTests.py +++ b/SCons/Scanner/DirTests.py @@ -28,6 +28,7 @@ import TestCmd import SCons.Node.FS import SCons.Scanner.Dir +from SCons.SConsign import current_sconsign_filename #class DummyNode: # def __init__(self, name, fs): @@ -57,23 +58,25 @@ class DirScannerTestBase(unittest.TestCase): self.test.subdir('dir', ['dir', 'sub']) + sconsign = current_sconsign_filename() + self.test.write(['dir', 'f1'], "dir/f1\n") self.test.write(['dir', 'f2'], "dir/f2\n") - self.test.write(['dir', '.sconsign'], "dir/.sconsign\n") - self.test.write(['dir', '.sconsign.bak'], "dir/.sconsign.bak\n") - self.test.write(['dir', '.sconsign.dat'], "dir/.sconsign.dat\n") - self.test.write(['dir', '.sconsign.db'], "dir/.sconsign.db\n") - self.test.write(['dir', '.sconsign.dblite'], "dir/.sconsign.dblite\n") - self.test.write(['dir', '.sconsign.dir'], "dir/.sconsign.dir\n") - self.test.write(['dir', '.sconsign.pag'], "dir/.sconsign.pag\n") + self.test.write(['dir', '{}'.format(sconsign)], "dir/{}\n".format(sconsign)) + self.test.write(['dir', '{}.bak'.format(sconsign)], "dir/{}.bak\n".format(sconsign)) + self.test.write(['dir', '{}.dat'.format(sconsign)], "dir/{}.dat\n".format(sconsign)) + self.test.write(['dir', '{}.db'.format(sconsign)], "dir/{}.db\n".format(sconsign)) + self.test.write(['dir', '{}.dblite'.format(sconsign)], "dir/{}.dblite\n".format(sconsign)) + self.test.write(['dir', '{}.dir'.format(sconsign)], "dir/{}.dir\n".format(sconsign)) + self.test.write(['dir', '{}.pag'.format(sconsign)], "dir/{}.pag\n".format(sconsign)) self.test.write(['dir', 'sub', 'f3'], "dir/sub/f3\n") self.test.write(['dir', 'sub', 'f4'], "dir/sub/f4\n") - self.test.write(['dir', 'sub', '.sconsign'], "dir/.sconsign\n") - self.test.write(['dir', 'sub', '.sconsign.bak'], "dir/.sconsign.bak\n") - self.test.write(['dir', 'sub', '.sconsign.dat'], "dir/.sconsign.dat\n") - self.test.write(['dir', 'sub', '.sconsign.dblite'], "dir/.sconsign.dblite\n") - self.test.write(['dir', 'sub', '.sconsign.dir'], "dir/.sconsign.dir\n") - self.test.write(['dir', 'sub', '.sconsign.pag'], "dir/.sconsign.pag\n") + self.test.write(['dir', 'sub', '{}'.format(sconsign)], "dir/{}\n".format(sconsign)) + self.test.write(['dir', 'sub', '{}.bak'.format(sconsign)], "dir/{}.bak\n".format(sconsign)) + self.test.write(['dir', 'sub', '{}.dat'.format(sconsign)], "dir/{}.dat\n".format(sconsign)) + self.test.write(['dir', 'sub', '{}.dblite'.format(sconsign)], "dir/{}.dblite\n".format(sconsign)) + self.test.write(['dir', 'sub', '{}.dir'.format(sconsign)], "dir/{}.dir\n".format(sconsign)) + self.test.write(['dir', 'sub', '{}.pag'.format(sconsign)], "dir/{}.pag\n".format(sconsign)) class DirScannerTestCase(DirScannerTestBase): def runTest(self): @@ -88,7 +91,7 @@ class DirScannerTestCase(DirScannerTestBase): ] deps = s(env.Dir('dir'), env, ()) sss = list(map(str, deps)) - assert sss == expect, sss + assert sss == expect, "Found {}, expected {}".format(sss, expect) expect = [ os.path.join('dir', 'sub', 'f3'), @@ -96,7 +99,7 @@ class DirScannerTestCase(DirScannerTestBase): ] deps = s(env.Dir('dir/sub'), env, ()) sss = list(map(str, deps)) - assert sss == expect, sss + assert sss == expect, "Found {}, expected {}".format(sss, expect) class DirEntryScannerTestCase(DirScannerTestBase): def runTest(self): @@ -106,16 +109,16 @@ class DirEntryScannerTestCase(DirScannerTestBase): deps = s(env.Dir('dir'), env, ()) sss = list(map(str, deps)) - assert sss == [], sss + assert sss == [], "Found {}, expected {}".format(sss, []) deps = s(env.Dir('dir/sub'), env, ()) sss = list(map(str, deps)) - assert sss == [], sss + assert sss == [], "Found {}, expected {}".format(sss, []) # Make sure we don't blow up if handed a non-Dir node. deps = s(env.File('dir/f1'), env, ()) sss = list(map(str, deps)) - assert sss == [], sss + assert sss == [], "Found {}, expected {}".format(sss, []) if __name__ == "__main__": unittest.main() diff --git a/SCons/Scanner/Fortran.py b/SCons/Scanner/Fortran.py index 18e5082..9cf5b22 100644 --- a/SCons/Scanner/Fortran.py +++ b/SCons/Scanner/Fortran.py @@ -27,11 +27,11 @@ import re import SCons.Node import SCons.Node.FS -import SCons.Scanner import SCons.Util import SCons.Warnings +from . import Classic, Current, FindPathDirs -class F90Scanner(SCons.Scanner.Classic): +class F90Scanner(Classic): """ A Classic Scanner subclass for Fortran source files which takes into account both USE and INCLUDE statements. This scanner will @@ -48,7 +48,7 @@ class F90Scanner(SCons.Scanner.Classic): """ def __init__(self, name, suffixes, path_variable, - use_regex, incl_regex, def_regex, *args, **kw): + use_regex, incl_regex, def_regex, *args, **kwargs): self.cre_use = re.compile(use_regex, re.M) self.cre_incl = re.compile(incl_regex, re.M) @@ -62,13 +62,14 @@ class F90Scanner(SCons.Scanner.Classic): return self.scan(node, env, path) - kw['function'] = _scan - kw['path_function'] = SCons.Scanner.FindPathDirs(path_variable) - kw['recursive'] = 1 - kw['skeys'] = suffixes - kw['name'] = name + kwargs['function'] = _scan + kwargs['path_function'] = FindPathDirs(path_variable) + kwargs['recursive'] = 1 + kwargs['skeys'] = suffixes + kwargs['name'] = name - SCons.Scanner.Current.__init__(self, *args, **kw) + # bypasses the parent Classic initializer + Current.__init__(self, *args, **kwargs) def scan(self, node, env, path=()): diff --git a/SCons/Scanner/IDL.py b/SCons/Scanner/IDL.py index 418608f..419ebf6 100644 --- a/SCons/Scanner/IDL.py +++ b/SCons/Scanner/IDL.py @@ -23,12 +23,11 @@ """Dependency scanner for IDL (Interface Definition Language) files.""" -import SCons.Node.FS -import SCons.Scanner +from . import ClassicCPP def IDLScan(): """Return a prototype Scanner instance for scanning IDL source files""" - cs = SCons.Scanner.ClassicCPP( + cs = ClassicCPP( "IDLScan", "$IDLSUFFIXES", "CPPPATH", diff --git a/SCons/Scanner/LaTeX.py b/SCons/Scanner/LaTeX.py index 73f0035..700b7cb 100644 --- a/SCons/Scanner/LaTeX.py +++ b/SCons/Scanner/LaTeX.py @@ -26,8 +26,10 @@ import os.path import re -import SCons.Scanner +import SCons.Node.FS import SCons.Util +import SCons.Warnings +from . import ScannerBase, FindPathDirs # list of graphics file extensions for TeX and LaTeX TexGraphics = ['.eps', '.ps'] @@ -78,6 +80,7 @@ class FindENVPathDirs: """ def __init__(self, variable): self.variable = variable + def __call__(self, env, dir=None, target=None, source=None, argument=None): import SCons.PathList try: @@ -116,7 +119,7 @@ def PDFLaTeXScanner(): return ds -class LaTeX(SCons.Scanner.Base): +class LaTeX(ScannerBase): """Class for scanning LaTeX files for included files. Unlike most scanners, which use regular expressions that just @@ -172,7 +175,7 @@ class LaTeX(SCons.Scanner.Base): 'includefrom', 'subincludefrom', 'inputfrom', 'subinputfrom'] - def __init__(self, name, suffixes, graphics_extensions, *args, **kw): + def __init__(self, name, suffixes, graphics_extensions, *args, **kwargs): regex = r''' \\( include @@ -219,8 +222,7 @@ class LaTeX(SCons.Scanner.Base): def __init__(self, dictionary): self.dictionary = {} for k,n in dictionary.items(): - self.dictionary[k] = ( SCons.Scanner.FindPathDirs(n), - FindENVPathDirs(n) ) + self.dictionary[k] = (FindPathDirs(n), FindENVPathDirs(n)) def __call__(self, env, dir=None, target=None, source=None, argument=None): @@ -234,25 +236,28 @@ class LaTeX(SCons.Scanner.Base): return tuple(di.items()) class LaTeXScanCheck: - """Skip all but LaTeX source files, i.e., do not scan *.eps, - *.pdf, *.jpg, etc. + """Skip all but LaTeX source files. + + Do not scan *.eps, *.pdf, *.jpg, etc. """ + def __init__(self, suffixes): self.suffixes = suffixes + def __call__(self, node, env): current = not node.has_builder() or node.is_up_to_date() scannable = node.get_suffix() in env.subst_list(self.suffixes)[0] # Returning false means that the file is not scanned. return scannable and current - kw['function'] = _scan - kw['path_function'] = FindMultiPathDirs(LaTeX.keyword_paths) - kw['recursive'] = 0 - kw['skeys'] = suffixes - kw['scan_check'] = LaTeXScanCheck(suffixes) - kw['name'] = name + kwargs['function'] = _scan + kwargs['path_function'] = FindMultiPathDirs(LaTeX.keyword_paths) + kwargs['recursive'] = 0 + kwargs['skeys'] = suffixes + kwargs['scan_check'] = LaTeXScanCheck(suffixes) + kwargs['name'] = name - SCons.Scanner.Base.__init__(self, *args, **kw) + super().__init__(*args, **kwargs) def _latex_names(self, include_type, filename): if include_type == 'input': @@ -393,10 +398,10 @@ class LaTeX(SCons.Scanner.Base): inc_type, inc_subdir, inc_filename = include try: - if seen[inc_filename] == 1: + if seen[inc_filename]: continue except KeyError: - seen[inc_filename] = 1 + seen[inc_filename] = True # # Handle multiple filenames in include[1] @@ -406,13 +411,16 @@ class LaTeX(SCons.Scanner.Base): # Do not bother with 'usepackage' warnings, as they most # likely refer to system-level files if inc_type != 'usepackage': - SCons.Warnings.warn(SCons.Warnings.DependencyWarning, - "No dependency generated for file: %s (included from: %s) -- file not found" % (i, node)) + SCons.Warnings.warn( + SCons.Warnings.DependencyWarning, + "No dependency generated for file: %s " + "(included from: %s) -- file not found" % (i, node), + ) else: sortkey = self.sort_key(n) nodes.append((sortkey, n)) # recurse down - queue.extend( self.scan(n, inc_subdir) ) + queue.extend(self.scan(n, inc_subdir)) return [pair[1] for pair in sorted(nodes)] diff --git a/SCons/Scanner/LaTeXTests.py b/SCons/Scanner/LaTeXTests.py index 818cbe4..252d8d4 100644 --- a/SCons/Scanner/LaTeXTests.py +++ b/SCons/Scanner/LaTeXTests.py @@ -82,7 +82,7 @@ test.write('incNO.tex', "\n") # copied from CTest.py class DummyEnvironment(collections.UserDict): def __init__(self, **kw): - collections.UserDict.__init__(self) + super().__init__() self.data.update(kw) self.fs = SCons.Node.FS.FS(test.workpath('')) diff --git a/SCons/Scanner/Prog.py b/SCons/Scanner/Prog.py index 41be1a4..888e121 100644 --- a/SCons/Scanner/Prog.py +++ b/SCons/Scanner/Prog.py @@ -25,17 +25,17 @@ import SCons.Node import SCons.Node.FS -import SCons.Scanner import SCons.Util +from . import ScannerBase, FindPathDirs # global, set by --debug=findlibs print_find_libs = None -def ProgramScanner(**kw): +def ProgramScanner(**kwargs): """Return a prototype Scanner instance for scanning executable files for static-lib dependencies""" - kw['path_function'] = SCons.Scanner.FindPathDirs('LIBPATH') - ps = SCons.Scanner.Base(scan, "ProgramScanner", **kw) + kwargs['path_function'] = FindPathDirs('LIBPATH') + ps = ScannerBase(scan, "ProgramScanner", **kwargs) return ps def _subst_libs(env, libs): diff --git a/SCons/Scanner/Python.py b/SCons/Scanner/Python.py index 86aa001..e20d782 100644 --- a/SCons/Scanner/Python.py +++ b/SCons/Scanner/Python.py @@ -35,7 +35,10 @@ those directories in PYTHONPATH. import itertools import os import re -import SCons.Scanner + +import SCons.Node.FS +import SCons.Util +from . import ScannerBase # Capture python "from a import b" and "import a" statements. from_cre = re.compile(r'^\s*from\s+([^\s]+)\s+import\s+(.*)', re.M) @@ -198,9 +201,13 @@ def scan(node, env, path=()): PythonSuffixes = ['.py'] -PythonScanner = SCons.Scanner.Base(scan, name='PythonScanner', - skeys=PythonSuffixes, - path_function=path_function, recursive=1) +PythonScanner = ScannerBase( + scan, + name='PythonScanner', + skeys=PythonSuffixes, + path_function=path_function, + recursive=True, +) # Local Variables: # tab-width:4 diff --git a/SCons/Scanner/PythonTests.py b/SCons/Scanner/PythonTests.py index 84acd0c..1a4f5b4 100644 --- a/SCons/Scanner/PythonTests.py +++ b/SCons/Scanner/PythonTests.py @@ -66,9 +66,9 @@ def deps_match(self, deps, headers): # Copied from LaTeXTests.py. class DummyEnvironment(collections.UserDict): - def __init__(self, **kw): - collections.UserDict.__init__(self) - self.data.update(kw) + def __init__(self, **kwargs): + super().__init__() + self.data.update(kwargs) self.fs = SCons.Node.FS.FS(test.workpath('')) self['ENV'] = {} diff --git a/SCons/Scanner/RC.py b/SCons/Scanner/RC.py index 12e431d..23afe9d 100644 --- a/SCons/Scanner/RC.py +++ b/SCons/Scanner/RC.py @@ -25,7 +25,7 @@ import SCons.Node.FS -import SCons.Scanner +from . import ClassicCPP def no_tlb(nodes): @@ -44,7 +44,7 @@ def RCScan(): r'\s*.*?)' r'\s*(<|"| )([^>"\s]+)(?:[>"\s])*$' ) - resScanner = SCons.Scanner.ClassicCPP( + resScanner = ClassicCPP( "ResourceScanner", "$RCSUFFIXES", "CPPPATH", res_re, recursive=no_tlb ) diff --git a/SCons/Scanner/RCTests.py b/SCons/Scanner/RCTests.py index 8835e5f..3655269 100644 --- a/SCons/Scanner/RCTests.py +++ b/SCons/Scanner/RCTests.py @@ -71,9 +71,9 @@ for h in headers: # define some helpers: class DummyEnvironment(collections.UserDict): - def __init__(self,**kw): - collections.UserDict.__init__(self) - self.data.update(kw) + def __init__(self, **kwargs): + super().__init__() + self.data.update(kwargs) self.fs = SCons.Node.FS.FS(test.workpath('')) def Dictionary(self, *args): diff --git a/SCons/Scanner/SWIG.py b/SCons/Scanner/SWIG.py index 5a60c74..70a174c 100644 --- a/SCons/Scanner/SWIG.py +++ b/SCons/Scanner/SWIG.py @@ -23,13 +23,13 @@ """Dependency scanner for SWIG code.""" -import SCons.Scanner +from . import ClassicCPP -SWIGSuffixes = [ '.i' ] +SWIGSuffixes = ['.i'] def SWIGScanner(): expr = r'^[ \t]*%[ \t]*(?:include|import|extern)[ \t]*(<|"?)([^>\s"]+)(?:>|"?)' - scanner = SCons.Scanner.ClassicCPP("SWIGScanner", ".i", "SWIGPATH", expr) + scanner = ClassicCPP("SWIGScanner", ".i", "SWIGPATH", expr) return scanner # Local Variables: diff --git a/SCons/Scanner/ScannerTests.py b/SCons/Scanner/ScannerTests.py index 815dabd..68332a0 100644 --- a/SCons/Scanner/ScannerTests.py +++ b/SCons/Scanner/ScannerTests.py @@ -28,15 +28,16 @@ import TestUnit import SCons.compat import SCons.Scanner +from SCons.Scanner import ScannerBase, Selector, Classic, ClassicCPP, Current, FindPathDirs class DummyFS: def File(self, name): return DummyNode(name) class DummyEnvironment(collections.UserDict): - def __init__(self, dict=None, **kw): - collections.UserDict.__init__(self, dict) - self.data.update(kw) + def __init__(self, mapping=None, **kwargs): + super().__init__(mapping) + self.data.update(kwargs) self.fs = DummyFS() def subst(self, strSubst, target=None, source=None, conv=None): if strSubst[0] == '$': @@ -57,12 +58,25 @@ class DummyNode: def __init__(self, name, search_result=()): self.name = name self.search_result = tuple(search_result) + def rexists(self): - return 1 + return True + def __str__(self): return self.name + def Rfindalldirs(self, pathlist): return self.search_result + pathlist + def __repr__(self): + return self.name + def __eq__(self, other): + return self.name == other.name + + def __repr__(self): + return self.name + + def __eq__(self, other): + return self.name == other.name class FindPathDirsTestCase(unittest.TestCase): def test_FindPathDirs(self): @@ -73,7 +87,7 @@ class FindPathDirsTestCase(unittest.TestCase): env.fs._cwd = DummyNode('cwd') dir = DummyNode('dir', ['xxx']) - fpd = SCons.Scanner.FindPathDirs('LIBPATH') + fpd = FindPathDirs('LIBPATH') result = fpd(env) assert str(result) == "('foo',)", result result = fpd(env, dir) @@ -85,25 +99,25 @@ class ScannerTestCase(unittest.TestCase): """Test creation of Scanner objects""" def func(self): pass - s = SCons.Scanner.Base(func) - assert isinstance(s, SCons.Scanner.Base), s - s = SCons.Scanner.Base({}) - assert isinstance(s, SCons.Scanner.Base), s + s = ScannerBase(func) + assert isinstance(s, ScannerBase), s + s = ScannerBase({}) + assert isinstance(s, ScannerBase), s - s = SCons.Scanner.Base(func, name='fooscan') + s = ScannerBase(func, name='fooscan') assert str(s) == 'fooscan', str(s) - s = SCons.Scanner.Base({}, name='barscan') + s = ScannerBase({}, name='barscan') assert str(s) == 'barscan', str(s) - s = SCons.Scanner.Base(func, name='fooscan', argument=9) + s = ScannerBase(func, name='fooscan', argument=9) assert str(s) == 'fooscan', str(s) assert s.argument == 9, s.argument - s = SCons.Scanner.Base({}, name='fooscan', argument=888) + s = ScannerBase({}, name='fooscan', argument=888) assert str(s) == 'fooscan', str(s) assert s.argument == 888, s.argument -class BaseTestCase(unittest.TestCase): +class ScannerBaseTestCase(unittest.TestCase): class skey_node: def __init__(self, key): @@ -141,7 +155,7 @@ class BaseTestCase(unittest.TestCase): self.assertFalse(hasattr(self, "arg"), "an argument was given when it shouldn't have been") def test___call__dict(self): - """Test calling Scanner.Base objects with a dictionary""" + """Test calling ScannerBase objects with a dictionary""" called = [] def s1func(node, env, path, called=called): called.append('s1func') @@ -151,9 +165,9 @@ class BaseTestCase(unittest.TestCase): called.append('s2func') called.append(node) return [] - s1 = SCons.Scanner.Base(s1func) - s2 = SCons.Scanner.Base(s2func) - selector = SCons.Scanner.Base({'.x' : s1, '.y' : s2}) + s1 = ScannerBase(s1func) + s2 = ScannerBase(s2func) + selector = ScannerBase({'.x' : s1, '.y' : s2}) nx = self.skey_node('.x') env = DummyEnvironment() selector(nx, env, []) @@ -164,7 +178,7 @@ class BaseTestCase(unittest.TestCase): assert called == ['s2func', ny], called def test_path(self): - """Test the Scanner.Base path() method""" + """Test the ScannerBase path() method""" def pf(env, cwd, target, source, argument=None): return "pf: %s %s %s %s %s" % \ (env.VARIABLE, cwd, target[0], source[0], argument) @@ -174,17 +188,17 @@ class BaseTestCase(unittest.TestCase): target = DummyNode('target') source = DummyNode('source') - s = SCons.Scanner.Base(self.func, path_function=pf) + s = ScannerBase(self.func, path_function=pf) p = s.path(env, 'here', [target], [source]) assert p == "pf: v1 here target source None", p - s = SCons.Scanner.Base(self.func, path_function=pf, argument="xyz") + s = ScannerBase(self.func, path_function=pf, argument="xyz") p = s.path(env, 'here', [target], [source]) assert p == "pf: v1 here target source xyz", p def test_positional(self): - """Test the Scanner.Base class using positional arguments""" - s = SCons.Scanner.Base(self.func, "Pos") + """Test the ScannerBase class using positional arguments""" + s = ScannerBase(self.func, "Pos") env = DummyEnvironment() env.VARIABLE = "var1" self.test(s, env, DummyNode('f1.cpp'), ['f1.h', 'f1.hpp']) @@ -194,8 +208,8 @@ class BaseTestCase(unittest.TestCase): self.test(s, env, DummyNode('i1.cpp'), ['i1.h', 'i1.hpp']) def test_keywords(self): - """Test the Scanner.Base class using keyword arguments""" - s = SCons.Scanner.Base(function = self.func, name = "Key") + """Test the ScannerBase class using keyword arguments""" + s = ScannerBase(function = self.func, name = "Key") env = DummyEnvironment() env.VARIABLE = "var2" self.test(s, env, DummyNode('f2.cpp'), ['f2.h', 'f2.hpp']) @@ -206,9 +220,9 @@ class BaseTestCase(unittest.TestCase): self.test(s, env, DummyNode('i2.cpp'), ['i2.h', 'i2.hpp']) def test_pos_opt(self): - """Test the Scanner.Base class using both position and optional arguments""" + """Test the ScannerBase class using both position and optional arguments""" arg = "this is the argument" - s = SCons.Scanner.Base(self.func, "PosArg", arg) + s = ScannerBase(self.func, "PosArg", arg) env = DummyEnvironment() env.VARIABLE = "var3" self.test(s, env, DummyNode('f3.cpp'), ['f3.h', 'f3.hpp'], arg) @@ -218,10 +232,9 @@ class BaseTestCase(unittest.TestCase): self.test(s, env, DummyNode('i3.cpp'), ['i3.h', 'i3.hpp'], arg) def test_key_opt(self): - """Test the Scanner.Base class using both keyword and optional arguments""" + """Test the ScannerBase class using both keyword and optional arguments""" arg = "this is another argument" - s = SCons.Scanner.Base(function = self.func, name = "KeyArg", - argument = arg) + s = ScannerBase(function = self.func, name = "KeyArg", argument = arg) env = DummyEnvironment() env.VARIABLE = "var4" self.test(s, env, DummyNode('f4.cpp'), ['f4.h', 'f4.hpp'], arg) @@ -231,29 +244,29 @@ class BaseTestCase(unittest.TestCase): self.test(s, env, DummyNode('i4.cpp'), ['i4.h', 'i4.hpp'], arg) def test___cmp__(self): - """Test the Scanner.Base class __cmp__() method""" - s = SCons.Scanner.Base(self.func, "Cmp") + """Test the ScannerBase class __cmp__() method""" + s = ScannerBase(self.func, "Cmp") assert s is not None def test_hash(self): - """Test the Scanner.Base class __hash__() method""" - s = SCons.Scanner.Base(self.func, "Hash") - dict = {} - dict[s] = 777 + """Test the ScannerBase class __hash__() method""" + s = ScannerBase(self.func, "Hash") + mapping = {} + mapping[s] = 777 i = hash(id(s)) - h = hash(list(dict.keys())[0]) + h = hash(list(mapping)[0]) self.assertTrue(h == i, "hash Scanner base class expected %s, got %s" % (i, h)) def test_scan_check(self): - """Test the Scanner.Base class scan_check() method""" + """Test the ScannerBase class scan_check() method""" def my_scan(filename, env, target, *args): return [] def check(node, env, s=self): s.checked[str(node)] = 1 return 1 env = DummyEnvironment() - s = SCons.Scanner.Base(my_scan, "Check", scan_check = check) + s = ScannerBase(my_scan, "Check", scan_check = check) self.checked = {} path = s.path(env) scanned = s(DummyNode('x'), env, path) @@ -261,56 +274,49 @@ class BaseTestCase(unittest.TestCase): "did not call check function") def test_recursive(self): - """Test the Scanner.Base class recursive flag""" + """Test the ScannerBase class recursive flag""" nodes = [1, 2, 3, 4] - s = SCons.Scanner.Base(function = self.func) + s = ScannerBase(function = self.func) n = s.recurse_nodes(nodes) - self.assertTrue(n == [], - "default behavior returned nodes: %s" % n) + self.assertTrue(n == [], "default behavior returned nodes: %s" % n) - s = SCons.Scanner.Base(function = self.func, recursive = None) + s = ScannerBase(function = self.func, recursive = None) n = s.recurse_nodes(nodes) - self.assertTrue(n == [], - "recursive = None returned nodes: %s" % n) + self.assertTrue(n == [], "recursive = None returned nodes: %s" % n) - s = SCons.Scanner.Base(function = self.func, recursive = 1) + s = ScannerBase(function = self.func, recursive = 1) n = s.recurse_nodes(nodes) - self.assertTrue(n == n, - "recursive = 1 didn't return all nodes: %s" % n) + self.assertTrue(n == n, "recursive = 1 didn't return all nodes: %s" % n) def odd_only(nodes): return [n for n in nodes if n % 2] - s = SCons.Scanner.Base(function = self.func, recursive = odd_only) + s = ScannerBase(function = self.func, recursive = odd_only) n = s.recurse_nodes(nodes) - self.assertTrue(n == [1, 3], - "recursive = 1 didn't return all nodes: %s" % n) + self.assertTrue(n == [1, 3], "recursive = 1 didn't return all nodes: %s" % n) def test_get_skeys(self): - """Test the Scanner.Base get_skeys() method""" - s = SCons.Scanner.Base(function = self.func) + """Test the ScannerBase get_skeys() method""" + s = ScannerBase(function = self.func) sk = s.get_skeys() - self.assertTrue(sk == [], - "did not initialize to expected []") + self.assertTrue(sk == [], "did not initialize to expected []") - s = SCons.Scanner.Base(function = self.func, skeys = ['.1', '.2']) + s = ScannerBase(function = self.func, skeys = ['.1', '.2']) sk = s.get_skeys() - self.assertTrue(sk == ['.1', '.2'], - "sk was %s, not ['.1', '.2']") + self.assertTrue(sk == ['.1', '.2'], "sk was %s, not ['.1', '.2']") - s = SCons.Scanner.Base(function = self.func, skeys = '$LIST') + s = ScannerBase(function = self.func, skeys = '$LIST') env = DummyEnvironment(LIST = ['.3', '.4']) sk = s.get_skeys(env) - self.assertTrue(sk == ['.3', '.4'], - "sk was %s, not ['.3', '.4']") + self.assertTrue(sk == ['.3', '.4'], "sk was %s, not ['.3', '.4']") def test_select(self): - """Test the Scanner.Base select() method""" - scanner = SCons.Scanner.Base(function = self.func) + """Test the ScannerBase select() method""" + scanner = ScannerBase(function = self.func) s = scanner.select('.x') assert s is scanner, s - selector = SCons.Scanner.Base({'.x' : 1, '.y' : 2}) + selector = ScannerBase({'.x' : 1, '.y' : 2}) s = selector.select(self.skey_node('.x')) assert s == 1, s s = selector.select(self.skey_node('.y')) @@ -319,8 +325,8 @@ class BaseTestCase(unittest.TestCase): assert s is None, s def test_add_scanner(self): - """Test the Scanner.Base add_scanner() method""" - selector = SCons.Scanner.Base({'.x' : 1, '.y' : 2}) + """Test the ScannerBase add_scanner() method""" + selector = ScannerBase({'.x' : 1, '.y' : 2}) s = selector.select(self.skey_node('.z')) assert s is None, s selector.add_scanner('.z', 3) @@ -328,11 +334,11 @@ class BaseTestCase(unittest.TestCase): assert s == 3, s def test___str__(self): - """Test the Scanner.Base __str__() method""" - scanner = SCons.Scanner.Base(function = self.func) + """Test the ScannerBase __str__() method""" + scanner = ScannerBase(function = self.func) s = str(scanner) assert s == 'NONE', s - scanner = SCons.Scanner.Base(function = self.func, name = 'xyzzy') + scanner = ScannerBase(function = self.func, name = 'xyzzy') s = str(scanner) assert s == 'xyzzy', s @@ -347,9 +353,9 @@ class SelectorTestCase(unittest.TestCase): def test___init__(self): """Test creation of Scanner.Selector object""" - s = SCons.Scanner.Selector({}) - assert isinstance(s, SCons.Scanner.Selector), s - assert s.dict == {}, s.dict + s = Selector({}) + assert isinstance(s, Selector), s + assert s.mapping == {}, s.mapping def test___call__(self): """Test calling Scanner.Selector objects""" @@ -362,9 +368,9 @@ class SelectorTestCase(unittest.TestCase): called.append('s2func') called.append(node) return [] - s1 = SCons.Scanner.Base(s1func) - s2 = SCons.Scanner.Base(s2func) - selector = SCons.Scanner.Selector({'.x' : s1, '.y' : s2}) + s1 = ScannerBase(s1func) + s2 = ScannerBase(s2func) + selector = Selector({'.x' : s1, '.y' : s2}) nx = self.skey_node('.x') env = DummyEnvironment() selector(nx, env, []) @@ -376,7 +382,7 @@ class SelectorTestCase(unittest.TestCase): def test_select(self): """Test the Scanner.Selector select() method""" - selector = SCons.Scanner.Selector({'.x' : 1, '.y' : 2}) + selector = Selector({'.x' : 1, '.y' : 2}) s = selector.select(self.skey_node('.x')) assert s == 1, s s = selector.select(self.skey_node('.y')) @@ -386,7 +392,7 @@ class SelectorTestCase(unittest.TestCase): def test_add_scanner(self): """Test the Scanner.Selector add_scanner() method""" - selector = SCons.Scanner.Selector({'.x' : 1, '.y' : 2}) + selector = Selector({'.x' : 1, '.y' : 2}) s = selector.select(self.skey_node('.z')) assert s is None, s selector.add_scanner('.z', 3) @@ -425,7 +431,7 @@ class CurrentTestCase(unittest.TestCase): node.func_called = 1 return [] env = DummyEnvironment() - s = SCons.Scanner.Current(func) + s = Current(func) path = s.path(env) hnb = HasNoBuilder() s(hnb, env, path) @@ -458,7 +464,7 @@ class ClassicTestCase(unittest.TestCase): def test_find_include(self): """Test the Scanner.Classic find_include() method""" env = DummyEnvironment() - s = SCons.Scanner.Classic("t", ['.suf'], 'MYPATH', r'^my_inc (\S+)') + s = Classic("t", ['.suf'], 'MYPATH', r'^my_inc (\S+)') def _find_file(filename, paths): return paths[0]+'/'+filename @@ -476,7 +482,7 @@ class ClassicTestCase(unittest.TestCase): def test_name(self): """Test setting the Scanner.Classic name""" - s = SCons.Scanner.Classic("my_name", ['.s'], 'MYPATH', r'^my_inc (\S+)') + s = Classic("my_name", ['.s'], 'MYPATH', r'^my_inc (\S+)') assert s.name == "my_name", s.name def test_scan(self): @@ -497,7 +503,7 @@ class ClassicTestCase(unittest.TestCase): def get_dir(self): return self._dir - class MyScanner(SCons.Scanner.Classic): + class MyScanner(Classic): def find_include(self, include, source_dir, path): return include, include @@ -561,7 +567,7 @@ class ClassicTestCase(unittest.TestCase): nodes = [1, 2, 3, 4] - s = SCons.Scanner.Classic("Test", [], None, "", function=self.func, recursive=1) + s = Classic("Test", [], None, "", function=self.func, recursive=1) n = s.recurse_nodes(nodes) self.assertTrue(n == n, "recursive = 1 didn't return all nodes: %s" % n) @@ -569,7 +575,7 @@ class ClassicTestCase(unittest.TestCase): def odd_only(nodes): return [n for n in nodes if n % 2] - s = SCons.Scanner.Classic("Test", [], None, "", function=self.func, recursive=odd_only) + s = Classic("Test", [], None, "", function=self.func, recursive=odd_only) n = s.recurse_nodes(nodes) self.assertTrue(n == [1, 3], "recursive = 1 didn't return all nodes: %s" % n) @@ -579,7 +585,7 @@ class ClassicCPPTestCase(unittest.TestCase): def test_find_include(self): """Test the Scanner.ClassicCPP find_include() method""" env = DummyEnvironment() - s = SCons.Scanner.ClassicCPP("Test", [], None, "") + s = ClassicCPP("Test", [], None, "") def _find_file(filename, paths): return paths[0]+'/'+filename @@ -608,7 +614,7 @@ def suite(): tclasses = [ FindPathDirsTestCase, ScannerTestCase, - BaseTestCase, + ScannerBaseTestCase, SelectorTestCase, CurrentTestCase, ClassicTestCase, diff --git a/SCons/Scanner/__init__.py b/SCons/Scanner/__init__.py index d98f651..fe44ee0 100644 --- a/SCons/Scanner/__init__.py +++ b/SCons/Scanner/__init__.py @@ -26,6 +26,7 @@ import re import SCons.Node.FS +import SCons.PathList import SCons.Util @@ -36,22 +37,22 @@ class _Null: # used as an actual argument value. _null = _Null -def Scanner(function, *args, **kw): +def Scanner(function, *args, **kwargs): """Factory function to create a Scanner Object. Creates the appropriate Scanner based on the type of "function". TODO: Deprecate this some day. We've moved the functionality - inside the Base class and really don't need this factory function + inside the ScannerBase class and really don't need this factory function any more. It was, however, used by some of our Tool modules, so the call probably ended up in various people's custom modules patterned on SCons code. """ if SCons.Util.is_Dict(function): - return Selector(function, *args, **kw) - else: - return Base(function, *args, **kw) + return Selector(function, *args, **kwargs) + + return ScannerBase(function, *args, **kwargs) class FindPathDirs: @@ -60,8 +61,8 @@ class FindPathDirs: """ def __init__(self, variable): self.variable = variable + def __call__(self, env, dir=None, target=None, source=None, argument=None): - import SCons.PathList try: path = env[self.variable] except KeyError: @@ -72,12 +73,79 @@ class FindPathDirs: return tuple(dir.Rfindalldirs(path)) - -class Base: +class ScannerBase: """Base class for dependency scanners. - This implements straightforward, single-pass scanning of a single file. + Implements straightforward, single-pass scanning of a single file. + + A Scanner is usually set up with a scanner function (and optionally + a path function), but can also be a kind of dispatcher which + passes control to other Scanners. + + A scanner function takes three arguments: a Node to scan for + dependecies, the construction environment to use, and an optional + tuple of paths (as generated by the optional path function). + It must return a list containing the Nodes for all the direct + dependencies of the file. + + The optional path function is called to return paths that can be + searched for implicit dependency files. It takes five arguments: + a construction environment, a Node for the directory containing + the SConscript file that defined the primary target, a list of + target nodes, a list of source nodes, and the optional argument + for this instance. + + Examples:: + + s = Scanner(my_scanner_function) + s = Scanner(function=my_scanner_function) + s = Scanner(function=my_scanner_function, argument='foo') + + Args: + function: either a scanner function taking two or three arguments + and returning a list of File Nodes; or a mapping of keys to + other Scanner objects. + + name: an optional name for identifying this scanner object + (defaults to "NONE"). + + argument: an optional argument that will be passed to both + *function* and *path_function*. + + skeys: an optional list argument that can be used + to determine if this scanner can be used for a given Node. + In the case of File nodes, for example, the *skeys* + would be file suffixes. + + path_function: an optional function which returns a tuple + of the directories that can be searched for implicit + dependency files. May also return a callable which + is called with no args and returns the tuple (supporting + Bindable class). + + node_class: optional class of Nodes which this scan will return. + If not specified, defaults to :class:`SCons.Node.FS.Base`. + If *node_class* is ``None``, then this scanner will not enforce + any Node conversion and will return the raw results from *function*. + + node_factory: optional factory function to be called to + translate the raw results returned by *function* + into the expected *node_class* objects. + + scan_check: optional function to be called to first check whether + this node really needs to be scanned. + + recursive: optional specifier of whether this scanner should be + invoked recursively on all of the implicit dependencies it returns + (for example `#include` lines in C source files, which may refer + to header files which should themselves be scanned). + May be a callable, which will be called to filter + the list of nodes found to select a subset for recursive + scanning (the canonical example being only recursively + scanning subdirectories within a directory). The default + is to not do recursive scanning. """ + def __init__( self, function, @@ -92,66 +160,7 @@ class Base: scan_check=None, recursive=None, ): - """Construct a new scanner object given a scanner function. - - The scanner function's first argument will be a Node that should - be scanned for dependencies, the second argument will be an - Environment object, the third argument will be the tuple of paths - returned by the path_function, and the fourth argument will be - the value passed into 'argument', and the returned list should - contain the Nodes for all the direct dependencies of the file. - - Examples: - - s = Scanner(my_scanner_function) - s = Scanner(function = my_scanner_function) - s = Scanner(function = my_scanner_function, argument = 'foo') - - Args: - function: a scanner function taking two or three arguments - and returning a list of strings. - - name: a name for identifying this scanner object. - - argument: an optional argument that, if specified, will be - passed to both the scanner function and the path_function. - - skeys: an optional list argument that can be used - to determine which scanner should be used for a given - Node. In the case of File nodes, for example, the 'skeys' - would be file suffixes. - - path_function: a function that takes four or five arguments - (a construction environment, Node for the directory - containing the SConscript file that defined the primary - target, list of target nodes, list of source nodes, and - optional argument for this instance) and returns a tuple - of the directories that can be searched for implicit - dependency files. May also return a callable() which - is called with no args and returns the tuple (supporting - Bindable class). - - node_class: the class of Nodes which this scan will return. - If node_class is None, then this scanner will not enforce - any Node conversion and will return the raw results from - the underlying scanner function. - - node_factory: the factory function to be called to - translate the raw results returned by the scanner function - into the expected node_class objects. - - scan_check: a function to be called to first check whether - this node really needs to be scanned. - - recursive: specifies that this scanner should be invoked - recursively on all of the implicit dependencies it returns - (the canonical example being #include lines in C source - files). May be a callable, which will be called to filter - the list of nodes found to select a subset for recursive - scanning (the canonical example being only recursively - scanning subdirectories within a directory). - - """ + """Construct a new scanner object given a scanner function.""" # Note: this class could easily work with scanner functions that take # something other than a filename as an argument (e.g. a database # node) and a dependencies list that aren't file names. All that @@ -182,17 +191,19 @@ class Base: def path(self, env, dir=None, target=None, source=None): if not self.path_function: return () + if self.argument is not _null: return self.path_function(env, dir, target, source, self.argument) - else: - return self.path_function(env, dir, target, source) - def __call__(self, node, env, path=()): + return self.path_function(env, dir, target, source) + + def __call__(self, node, env, path=()) -> list: """Scans a single object. Args: node: the node that will be passed to the scanner function env: the environment that will be passed to the scanner function. + path: tuple of paths from the `path_function` Returns: A list of direct dependency nodes for the specified node. @@ -201,7 +212,8 @@ class Base: if self.scan_check and not self.scan_check(node, env): return [] - self = self.select(node) + # here we may morph into a different Scanner instance: + self = self.select(node) # pylint: disable=self-cls-assignment if self.argument is not _null: node_list = self.function(node, env, path, self.argument) @@ -211,13 +223,10 @@ class Base: kw = {} if hasattr(node, 'dir'): kw['directory'] = node.dir - node_factory = env.get_factory(self.node_factory) - nodes = [] - for l in node_list: - if self.node_class and not isinstance(l, self.node_class): - l = node_factory(l, **kw) - nodes.append(l) - return nodes + conv = env.get_factory(self.node_factory) + cls = self.node_class + nl = [conv(n, **kw) if cls and not isinstance(n, cls) else n for n in node_list] + return nl def __eq__(self, other): try: @@ -251,10 +260,12 @@ class Base: else: return self - def _recurse_all_nodes(self, nodes): + @staticmethod + def _recurse_all_nodes(nodes): return nodes - def _recurse_no_nodes(self, nodes): + @staticmethod + def _recurse_no_nodes(nodes): return [] # recurse_nodes = _recurse_no_nodes @@ -264,49 +275,55 @@ class Base: self.add_skey(skey) -class Selector(Base): +# keep the old name for a while in case external users are using. +# there are no more internal uses of this class by the name "Base" +Base = ScannerBase + + +class Selector(ScannerBase): """ A class for selecting a more specific scanner based on the - scanner_key() (suffix) for a specific Node. + :func:`scanner_key` (suffix) for a specific Node. TODO: This functionality has been moved into the inner workings of - the Base class, and this class will be deprecated at some point. + the ScannerBase class, and this class will be deprecated at some point. (It was never exposed directly as part of the public interface, - although it is used by the Scanner() factory function that was + although it is used by the :func:`Scanner` factory function that was used by various Tool modules and therefore was likely a template for custom modules that may be out there.) """ - def __init__(self, dict, *args, **kw): - Base.__init__(self, None, *args, **kw) - self.dict = dict - self.skeys = list(dict.keys()) + def __init__(self, mapping, *args, **kwargs): + super().__init__(None, *args, **kwargs) + self.mapping = mapping + self.skeys = list(mapping.keys()) def __call__(self, node, env, path=()): return self.select(node)(node, env, path) def select(self, node): try: - return self.dict[node.scanner_key()] + return self.mapping[node.scanner_key()] except KeyError: return None def add_scanner(self, skey, scanner): - self.dict[skey] = scanner + self.mapping[skey] = scanner self.add_skey(skey) -class Current(Base): +class Current(ScannerBase): """ A class for scanning files that are source files (have no builder) or are derived files and are current (which implies that they exist, either locally or in a repository). """ - def __init__(self, *args, **kw): + def __init__(self, *args, **kwargs): def current_check(node, env): return not node.has_builder() or node.is_up_to_date() - kw['scan_check'] = current_check - Base.__init__(self, *args, **kw) + + kwargs['scan_check'] = current_check + super().__init__(*args, **kwargs) class Classic(Current): """ @@ -315,13 +332,12 @@ class Classic(Current): regular expressions to find the includes. Note that in order for this to work "out of the box" (without - overriding the find_include() and sort_key() methods), the regular - expression passed to the constructor must return the name of the - include file in group 0. + overriding the :meth:`find_include` and :meth:`sort_key1` methods), + the regular expression passed to the constructor must return the + name of the include file in group 0. """ - def __init__(self, name, suffixes, path_variable, regex, *args, **kw): - + def __init__(self, name, suffixes, path_variable, regex, *args, **kwargs): self.cre = re.compile(regex, re.M) def _scan(node, _, path=(), self=self): @@ -330,31 +346,32 @@ class Classic(Current): return [] return self.scan(node, path) - kw['function'] = _scan - kw['path_function'] = FindPathDirs(path_variable) + kwargs['function'] = _scan + kwargs['path_function'] = FindPathDirs(path_variable) # Allow recursive to propagate if child class specifies. # In this case resource scanner needs to specify a filter on which files # get recursively processed. Previously was hardcoded to 1 instead of # defaulted to 1. - kw['recursive'] = kw.get('recursive', 1) - kw['skeys'] = suffixes - kw['name'] = name + kwargs['recursive'] = kwargs.get('recursive', True) + kwargs['skeys'] = suffixes + kwargs['name'] = name - Current.__init__(self, *args, **kw) + super().__init__(*args, **kwargs) - def find_include(self, include, source_dir, path): + @staticmethod + def find_include(include, source_dir, path): n = SCons.Node.FS.find_file(include, (source_dir,) + tuple(path)) return n, include - def sort_key(self, include): + @staticmethod + def sort_key(include): return SCons.Node.FS._my_normcase(include) def find_include_names(self, node): return self.cre.findall(node.get_text_contents()) def scan(self, node, path=()): - # cache the includes list in node so we only scan it once: if node.includes is not None: includes = node.includes @@ -378,8 +395,11 @@ class Classic(Current): n, i = self.find_include(include, source_dir, path) if n is None: - SCons.Warnings.warn(SCons.Warnings.DependencyWarning, - "No dependency generated for file: %s (included from: %s) -- file not found" % (i, node)) + SCons.Warnings.warn( + SCons.Warnings.DependencyWarning, + "No dependency generated for file: %s " + "(included from: %s) -- file not found" % (i, node), + ) else: nodes.append((self.sort_key(include), n)) @@ -403,7 +423,6 @@ class ClassicCPP(Classic): paths = tuple(path) + (source_dir,) n = SCons.Node.FS.find_file(include[1], paths) - i = SCons.Util.silent_intern(include[1]) return n, i |