diff options
author | William Deegan <bill@baddogconsulting.com> | 2016-08-13 19:59:35 (GMT) |
---|---|---|
committer | William Deegan <bill@baddogconsulting.com> | 2016-08-13 19:59:35 (GMT) |
commit | 4a4322da66d5a757cf97992ef6925226952ab5e2 (patch) | |
tree | 4849798ba2c5a9c41a583ce37aea860eef25e6f4 /src | |
parent | f6ec68e2eb534bf8fff6f470b7e8facdde045b63 (diff) | |
parent | 52815bb68d058b12288a4f05fc3b7051d520a89c (diff) | |
download | SCons-4a4322da66d5a757cf97992ef6925226952ab5e2.zip SCons-4a4322da66d5a757cf97992ef6925226952ab5e2.tar.gz SCons-4a4322da66d5a757cf97992ef6925226952ab5e2.tar.bz2 |
Merged in williamblevins/scons (pull request #353)
Issue 1924: Updated D Language scanner support.
Diffstat (limited to 'src')
-rw-r--r-- | src/CHANGES.txt | 11 | ||||
-rw-r--r-- | src/engine/SCons/Scanner/D.py | 16 | ||||
-rw-r--r-- | src/engine/SCons/Scanner/DTests.py | 282 |
3 files changed, 301 insertions, 8 deletions
diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 8c185c8..282e80b 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -29,6 +29,17 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - Added LoadableModule to the list of global functions (DefaultEnvironment builders). + From William Blevins: + - Updated D language scanner support to latest: 2.071.1. (PR #1924) + https://dlang.org/spec/module.html accessed 11 August 2016 + - Enhancements: + - Added support for selective imports: "import A : B, C;" -> A + - Added support for renamed imports. "import B = A;" -> A + - Supports valid combinations: "import A, B, CCC = C, DDD = D : EEE = FFF;" -> A, B, C, D + - Notes: + - May find new (previously missed) Dlang dependencies. + - May cause rebuild after upgrade due to dependency changes. + RELEASE 2.5.0 - Mon, 09 Apr 2016 11:27:42 -0700 From Dirk Baechle: diff --git a/src/engine/SCons/Scanner/D.py b/src/engine/SCons/Scanner/D.py index 9402ed1..95496d5 100644 --- a/src/engine/SCons/Scanner/D.py +++ b/src/engine/SCons/Scanner/D.py @@ -32,8 +32,6 @@ Coded by Andy Friesen __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" -import re - import SCons.Scanner def DScanner(): @@ -43,13 +41,13 @@ def DScanner(): class D(SCons.Scanner.Classic): def __init__ (self): - SCons.Scanner.Classic.__init__ (self, + SCons.Scanner.Classic.__init__ ( + self, name = "DScanner", suffixes = '$DSUFFIXES', path_variable = 'DPATH', - regex = 'import\s+(?:[a-zA-Z0-9_.]+)\s*(?:,\s*(?:[a-zA-Z0-9_.]+)\s*)*;') - - self.cre2 = re.compile ('(?:import\s)?\s*([a-zA-Z0-9_.]+)\s*(?:,|;)', re.M) + regex = '(?:import\s+)([\w\s=,.]+)(?:\s*:[\s\w,=]+)?(?:;)' + ) def find_include(self, include, source_dir, path): # translate dots (package separators) to slashes @@ -62,8 +60,10 @@ class D(SCons.Scanner.Classic): def find_include_names(self, node): includes = [] - for i in self.cre.findall(node.get_text_contents()): - includes = includes + self.cre2.findall(i) + for iii in self.cre.findall(node.get_text_contents()): + for jjj in iii.split(','): + kkk = jjj.split('=')[-1] + includes.append(kkk.strip()) return includes # Local Variables: diff --git a/src/engine/SCons/Scanner/DTests.py b/src/engine/SCons/Scanner/DTests.py new file mode 100644 index 0000000..51e527a --- /dev/null +++ b/src/engine/SCons/Scanner/DTests.py @@ -0,0 +1,282 @@ +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +import unittest + +import TestCmd +import TestUnit + +import SCons.Scanner.D + +test = TestCmd.TestCmd(workdir = '') + +import collections +import os + +class DummyEnvironment(collections.UserDict): + def __init__(self, **kw): + collections.UserDict.__init__(self) + self.data.update(kw) + self.fs = SCons.Node.FS.FS(test.workpath('')) + + def Dictionary(self, *args): + return self.data + + def subst(self, strSubst, target=None, source=None, conv=None): + if strSubst[0] == '$': + return self.data[strSubst[1:]] + return strSubst + + def subst_list(self, strSubst, target=None, source=None, conv=None): + if strSubst[0] == '$': + return [self.data[strSubst[1:]]] + return [[strSubst]] + + def subst_path(self, path, target=None, source=None, conv=None): + if not isinstance(path, list): + path = [path] + return list(map(self.subst, path)) + + def get_calculator(self): + return None + + def get_factory(self, factory): + return factory or self.fs.File + + def Dir(self, filename): + return self.fs.Dir(filename) + + def File(self, filename): + return self.fs.File(filename) + +if os.path.normcase('foo') == os.path.normcase('FOO'): + my_normpath = os.path.normcase +else: + my_normpath = os.path.normpath + +def deps_match(self, deps, headers): + global my_normpath + scanned = list(map(my_normpath, list(map(str, deps)))) + expect = list(map(my_normpath, headers)) + self.failUnless(scanned == expect, "expect %s != scanned %s" % (expect, scanned)) + +""" +Examples from https://dlang.org/spec/module.html + +D Language: 2.071.1 +Accessed: 11 August 2016 +""" + +# Regular import +test.write('basic.d',""" +import A; + +void main() {} +""") + +# Static import +test.write('static.d',""" +static import A; + +void main() +{ + std.stdio.writeln("hello!"); // ok, writeln is fully qualified +} +""") + +# Public import +test.write('public.d',""" +public import A; + +void main() {} +""") + +# Renamed import +test.write('rename.d',""" +import B = A; + +void main() +{ + io.writeln("hello!"); // ok, calls std.stdio.writeln +} +""") + +# Selective import +test.write('selective.d',""" +import A : B, C; + +void main() +{ + writeln("hello!"); // ok, writeln bound into current namespace + foo("world"); // ok, calls std.stdio.write() +} +""") + +# Renamed and Selective import +test.write('renameAndSelective.d',""" +import B = A : C = D; + +void main() +{ +} +""") + +# Scoped import +test.write('scoped.d',""" +void main() +{ + import A; +} +""") + +# Combinatorial import +test.write('combinatorial.d',""" +import A, B, CCC = C, DDD = D : EEE = FFF; + +void main() +{ +} +""") + +# Subdirs import +test.write('subdirs.d',""" +import X.Y, X.Z, X.X.X; + +void main() {} +""") + +# Multiple import +test.write('multiple.d',""" +public import B; +static import C; + +import X = X.Y : Q, R, S, T = U; +void main() +{ + import A; +} +""") + +# Multiline import +test.write('multiline.d',""" +import +A; + +void main() {} +""") + +test.write('A.d',""" +module A; +void main() {} +""") + +test.write('B.d',""" +module B; +void main() {} +""") + +test.write('C.d',""" +module C; +void main() {} +""") + +test.write('D.d',""" +module D; +void main() {} +""") + +test.subdir('X', os.path.join('X','X')) + +test.write(os.path.join('X','Y.d'),""" +module Y; +void main() {} +""") + +test.write(os.path.join('X','Z.d'),""" +module Z; +void main() {} +""") + +test.write(os.path.join('X','X','X.d'),""" +module X; +void main() {} +""") + +class DScannerTestCase(unittest.TestCase): + def helper(self, filename, headers): + env = DummyEnvironment() + s = SCons.Scanner.D.DScanner() + path = s.path(env) + deps = s(env.File(filename), env, path) + deps_match(self, deps, headers) + + def test_BasicImport(self): + self.helper('basic.d', ['A.d']) + + def test_StaticImport(self): + self.helper('static.d', ['A.d']) + + def test_publicImport(self): + self.helper('public.d', ['A.d']) + + def test_RenameImport(self): + self.helper('rename.d', ['A.d']) + + def test_SelectiveImport(self): + self.helper('selective.d', ['A.d']) + + def test_RenameAndSelectiveImport(self): + self.helper('renameAndSelective.d', ['A.d']) + + def test_ScopedImport(self): + self.helper('scoped.d', ['A.d']) + + def test_CombinatorialImport(self): + self.helper('combinatorial.d', ['A.d', 'B.d', 'C.d', 'D.d']) + + def test_SubdirsImport(self): + self.helper('subdirs.d', [os.path.join('X','X','X.d'), os.path.join('X','Y.d'), os.path.join('X','Z.d')]) + + def test_MultipleImport(self): + self.helper('multiple.d', ['A.d', 'B.d', 'C.d', os.path.join('X','Y.d')]) + + def test_MultilineImport(self): + self.helper('multiline.d', ['A.d']) + +if __name__ == "__main__": + suite = unittest.TestSuite() + tclasses = [ + DScannerTestCase, + ] + for tclass in tclasses: + names = unittest.getTestCaseNames(tclass, 'test_') + suite.addTests(list(map(tclass, names))) + TestUnit.run(suite) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: |