From 39696fb565547639be518a9f92525904118b55c1 Mon Sep 17 00:00:00 2001 From: Peter Diener Date: Mon, 4 Mar 2019 09:52:44 -0600 Subject: Fix issue #3135. This fixes issue #3135 regarding the issue with using type bound procedures in Fortran submodules. The fix consists of changing the regex used in the scanner and the emitter to ignore lines starting with: module subroutine and module function as these are used to define type bound procedures instead of modules named 'subroutine' or 'function'. The regex is case insensitive. --- src/engine/SCons/Scanner/Fortran.py | 29 +++++++++++++++++------------ src/engine/SCons/Tool/FortranCommon.py | 3 ++- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/engine/SCons/Scanner/Fortran.py b/src/engine/SCons/Scanner/Fortran.py index 6065bbd..171a2e8 100644 --- a/src/engine/SCons/Scanner/Fortran.py +++ b/src/engine/SCons/Scanner/Fortran.py @@ -285,21 +285,26 @@ def FortranScan(path_variable="FORTRANPATH"): # but *not* the following: # # MODULE PROCEDURE procedure_name +# MODULE SUBROUTINE subroutine_name +# MODULE FUNCTION function_name # # Here is a breakdown of the regex: # -# (?i) : regex is case insensitive -# ^\s* : any amount of white space -# MODULE : match the string MODULE, case insensitive -# \s+ : match one or more white space characters -# (?!PROCEDURE) : but *don't* match if the next word matches -# PROCEDURE (negative lookahead assertion), -# case insensitive -# (\w+) : match one or more alphanumeric characters -# that make up the defined module name and -# save it in a group - - def_regex = """(?i)^\s*MODULE\s+(?!PROCEDURE)(\w+)""" +# (?i) : regex is case insensitive +# ^\s* : any amount of white space +# MODULE : match the string MODULE, case +# insensitive +# \s+ : match one or more white space +# characters +# (?!PROCEDURE|SUBROUTINE|FUNCTION) : but *don't* match if the next word +# matches PROCEDURE, SUBROUTINE or +# FUNCTION (negative lookahead +# assertion), case insensitive +# (\w+) : match one or more alphanumeric +# characters that make up the defined +# module name and save it in a group + + def_regex = """(?i)^\s*MODULE\s+(?!PROCEDURE|SUBROUTINE|FUNCTION)(\w+)""" scanner = F90Scanner("FortranScan", "$FORTRANSUFFIXES", diff --git a/src/engine/SCons/Tool/FortranCommon.py b/src/engine/SCons/Tool/FortranCommon.py index ec409c0..b16dee5 100644 --- a/src/engine/SCons/Tool/FortranCommon.py +++ b/src/engine/SCons/Tool/FortranCommon.py @@ -64,7 +64,8 @@ def _fortranEmitter(target, source, env): if not node.exists() and not node.is_derived(): print("Could not locate " + str(node.name)) return ([], []) - mod_regex = """(?i)^\s*MODULE\s+(?!PROCEDURE)(\w+)""" + # This has to match the def_regex in the Fortran scanner + mod_regex = """(?i)^\s*MODULE\s+(?!PROCEDURE|SUBROUTINE|FUNCTION)(\w+)""" cre = re.compile(mod_regex,re.M) # Retrieve all USE'd module names modules = cre.findall(node.get_text_contents()) -- cgit v0.12 From 762190cd9b7f9fd2123513fd0c36408878c2c436 Mon Sep 17 00:00:00 2001 From: Peter Diener Date: Wed, 6 Mar 2019 10:27:14 -0600 Subject: Use raw strings in regexes. --- src/engine/SCons/Scanner/Fortran.py | 6 +++--- src/engine/SCons/Tool/FortranCommon.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/engine/SCons/Scanner/Fortran.py b/src/engine/SCons/Scanner/Fortran.py index 171a2e8..a932f21 100644 --- a/src/engine/SCons/Scanner/Fortran.py +++ b/src/engine/SCons/Scanner/Fortran.py @@ -187,7 +187,7 @@ def FortranScan(path_variable="FORTRANPATH"): # (\w+) : match the module name that is being USE'd # # - use_regex = "(?i)(?:^|;)\s*USE(?:\s+|(?:(?:\s*,\s*(?:NON_)?INTRINSIC)?\s*::))\s*(\w+)" + use_regex = r"(?i)(?:^|;)\s*USE(?:\s+|(?:(?:\s*,\s*(?:NON_)?INTRINSIC)?\s*::))\s*(\w+)" # The INCLUDE statement regex matches the following: @@ -275,7 +275,7 @@ def FortranScan(path_variable="FORTRANPATH"): # set of semicolon-separated INCLUDE statements # (as allowed by the F2003 standard) - include_regex = """(?i)(?:^|['">]\s*;)\s*INCLUDE\s+(?:\w+_)?[<"'](.+?)(?=["'>])""" + include_regex = r"""(?i)(?:^|['">]\s*;)\s*INCLUDE\s+(?:\w+_)?[<"'](.+?)(?=["'>])""" # The MODULE statement regex finds module definitions by matching # the following: @@ -304,7 +304,7 @@ def FortranScan(path_variable="FORTRANPATH"): # characters that make up the defined # module name and save it in a group - def_regex = """(?i)^\s*MODULE\s+(?!PROCEDURE|SUBROUTINE|FUNCTION)(\w+)""" + def_regex = r"""(?i)^\s*MODULE\s+(?!PROCEDURE|SUBROUTINE|FUNCTION)(\w+)""" scanner = F90Scanner("FortranScan", "$FORTRANSUFFIXES", diff --git a/src/engine/SCons/Tool/FortranCommon.py b/src/engine/SCons/Tool/FortranCommon.py index b16dee5..a38c6e7 100644 --- a/src/engine/SCons/Tool/FortranCommon.py +++ b/src/engine/SCons/Tool/FortranCommon.py @@ -65,7 +65,7 @@ def _fortranEmitter(target, source, env): print("Could not locate " + str(node.name)) return ([], []) # This has to match the def_regex in the Fortran scanner - mod_regex = """(?i)^\s*MODULE\s+(?!PROCEDURE|SUBROUTINE|FUNCTION)(\w+)""" + mod_regex = r"""(?i)^\s*MODULE\s+(?!PROCEDURE|SUBROUTINE|FUNCTION)(\w+)""" cre = re.compile(mod_regex,re.M) # Retrieve all USE'd module names modules = cre.findall(node.get_text_contents()) -- cgit v0.12 From 20e0363c1bc82b27217e6a953441626d9c37f53f Mon Sep 17 00:00:00 2001 From: William Deegan Date: Thu, 25 Apr 2019 09:37:19 -0400 Subject: PEP8 plus switch to unittest.main() when running directly --- src/engine/SCons/Scanner/FortranTests.py | 140 +++++++++++++++---------------- 1 file changed, 69 insertions(+), 71 deletions(-) diff --git a/src/engine/SCons/Scanner/FortranTests.py b/src/engine/SCons/Scanner/FortranTests.py index 5a09e3b..2e10473 100644 --- a/src/engine/SCons/Scanner/FortranTests.py +++ b/src/engine/SCons/Scanner/FortranTests.py @@ -25,7 +25,6 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import os import os.path -import sys import unittest import SCons.Scanner.Fortran @@ -33,17 +32,16 @@ import SCons.Node.FS import SCons.Warnings import TestCmd -import TestUnit original = os.getcwd() -test = TestCmd.TestCmd(workdir = '') +test = TestCmd.TestCmd(workdir='') os.chdir(test.workpath('')) # create some source files and headers: -test.write('fff1.f',""" +test.write('fff1.f', """ PROGRAM FOO INCLUDE 'f1.f' include 'f2.f' @@ -51,7 +49,7 @@ test.write('fff1.f',""" END """) -test.write('fff2.f',""" +test.write('fff2.f', """ PROGRAM FOO INCLUDE 'f2.f' include 'd1/f2.f' @@ -60,30 +58,28 @@ test.write('fff2.f',""" END """) -test.write('fff3.f',""" +test.write('fff3.f', """ PROGRAM FOO INCLUDE 'f3.f' ; INCLUDE\t'd1/f3.f' STOP END """) - # for Emacs -> " test.subdir('d1', ['d1', 'd2']) -headers = ['fi.f', 'never.f', - 'd1/f1.f', 'd1/f2.f', 'd1/f3.f', 'd1/fi.f', - 'd1/d2/f1.f', 'd1/d2/f2.f', 'd1/d2/f3.f', - 'd1/d2/f4.f', 'd1/d2/fi.f'] +test_headers = ['fi.f', 'never.f', + 'd1/f1.f', 'd1/f2.f', 'd1/f3.f', 'd1/fi.f', + 'd1/d2/f1.f', 'd1/d2/f2.f', 'd1/d2/f3.f', + 'd1/d2/f4.f', 'd1/d2/fi.f'] -for h in headers: +for h in test_headers: test.write(h, "\n") - test.subdir('include', 'subdir', ['subdir', 'include']) -test.write('fff4.f',""" +test.write('fff4.f', """ PROGRAM FOO INCLUDE 'f4.f' STOP @@ -93,7 +89,7 @@ test.write('fff4.f',""" test.write('include/f4.f', "\n") test.write('subdir/include/f4.f', "\n") -test.write('fff5.f',""" +test.write('fff5.f', """ PROGRAM FOO INCLUDE 'f5.f' INCLUDE 'not_there.f' @@ -104,7 +100,7 @@ test.write('fff5.f',""" test.write('f5.f', "\n") test.subdir('repository', ['repository', 'include'], - [ 'repository', 'src' ]) + ['repository', 'src']) test.subdir('work', ['work', 'src']) test.write(['repository', 'include', 'iii.f'], "\n") @@ -117,26 +113,25 @@ test.write(['work', 'src', 'fff.f'], """ END """) -test.write([ 'work', 'src', 'aaa.f'], """ +test.write(['work', 'src', 'aaa.f'], """ PROGRAM FOO INCLUDE 'bbb.f' STOP END """) -test.write([ 'work', 'src', 'bbb.f'], "\n") +test.write(['work', 'src', 'bbb.f'], "\n") -test.write([ 'repository', 'src', 'ccc.f'], """ +test.write(['repository', 'src', 'ccc.f'], """ PROGRAM FOO INCLUDE 'ddd.f' STOP END """) -test.write([ 'repository', 'src', 'ddd.f'], "\n") +test.write(['repository', 'src', 'ddd.f'], "\n") - -test.write('fff90a.f90',""" +test.write('fff90a.f90', """ PROGRAM FOO ! Test comments - these includes should NOT be picked up @@ -194,18 +189,19 @@ USE mod25 ! Test USE statement at the beginning of line END """) -modules = ['mod01.mod', 'mod02.mod', 'mod03.mod', 'mod04.mod', 'mod05.mod', - 'mod06.mod', 'mod07.mod', 'mod08.mod', 'mod09.mod', 'mod10.mod', - 'mod11.mod', 'mod12.mod', 'mod13.mod', 'mod14.mod', 'mod15.mod', - 'mod16.mod', 'mod17.mod', 'mod18.mod', 'mod19.mod', 'mod20.mod', - 'mod21.mod', 'mod22.mod', 'mod23.mod', 'mod24.mod', 'mod25.mod'] +test_modules = ['mod01.mod', 'mod02.mod', 'mod03.mod', 'mod04.mod', 'mod05.mod', + 'mod06.mod', 'mod07.mod', 'mod08.mod', 'mod09.mod', 'mod10.mod', + 'mod11.mod', 'mod12.mod', 'mod13.mod', 'mod14.mod', 'mod15.mod', + 'mod16.mod', 'mod17.mod', 'mod18.mod', 'mod19.mod', 'mod20.mod', + 'mod21.mod', 'mod22.mod', 'mod23.mod', 'mod24.mod', 'mod25.mod'] -for m in modules: +for m in test_modules: test.write(m, "\n") test.subdir('modules') test.write(['modules', 'use.mod'], "\n") + # define some helpers: class DummyEnvironment(object): @@ -215,7 +211,7 @@ class DummyEnvironment(object): def Dictionary(self, *args): if not args: - return { 'FORTRANPATH': self.path, 'FORTRANMODSUFFIX' : ".mod" } + return {'FORTRANPATH': self.path, 'FORTRANMODSUFFIX': ".mod"} elif len(args) == 1 and args[0] == 'FORTRANPATH': return self.path else: @@ -224,13 +220,13 @@ class DummyEnvironment(object): def has_key(self, key): return key in self.Dictionary() - def __getitem__(self,key): + def __getitem__(self, key): return self.Dictionary()[key] - def __setitem__(self,key,value): + def __setitem__(self, key, value): self.Dictionary()[key] = value - def __delitem__(self,key): + def __delitem__(self, key): del self.Dictionary()[key] def subst(self, arg, target=None, source=None, conv=None): @@ -255,11 +251,13 @@ class DummyEnvironment(object): def File(self, filename): return self.fs.File(filename) + def deps_match(self, deps, headers): scanned = list(map(os.path.normpath, list(map(str, deps)))) expect = list(map(os.path.normpath, headers)) self.assertTrue(scanned == expect, "expect %s != scanned %s" % (expect, scanned)) + # define some tests: class FortranScannerTestCase1(unittest.TestCase): @@ -275,6 +273,7 @@ class FortranScannerTestCase1(unittest.TestCase): test.unlink('f1.f') test.unlink('f2.f') + class FortranScannerTestCase2(unittest.TestCase): def runTest(self): test.write('f1.f', "\n") @@ -288,6 +287,7 @@ class FortranScannerTestCase2(unittest.TestCase): test.unlink('f1.f') test.unlink('f2.f') + class FortranScannerTestCase3(unittest.TestCase): def runTest(self): env = DummyEnvironment([test.workpath("d1")]) @@ -297,6 +297,7 @@ class FortranScannerTestCase3(unittest.TestCase): headers = ['d1/f1.f', 'd1/f2.f'] deps_match(self, deps, headers) + class FortranScannerTestCase4(unittest.TestCase): def runTest(self): test.write(['d1', 'f2.f'], " INCLUDE 'fi.f'\n") @@ -308,6 +309,7 @@ class FortranScannerTestCase4(unittest.TestCase): deps_match(self, deps, headers) test.write(['d1', 'f2.f'], "\n") + class FortranScannerTestCase5(unittest.TestCase): def runTest(self): env = DummyEnvironment([test.workpath("d1")]) @@ -317,6 +319,7 @@ class FortranScannerTestCase5(unittest.TestCase): headers = ['d1/f2.f', 'd1/d2/f2.f', 'd1/f2.f'] deps_match(self, deps, headers) + class FortranScannerTestCase6(unittest.TestCase): def runTest(self): test.write('f2.f', "\n") @@ -324,19 +327,21 @@ class FortranScannerTestCase6(unittest.TestCase): s = SCons.Scanner.Fortran.FortranScan() path = s.path(env) deps = s(env.File('fff2.f'), env, path) - headers = ['d1/f2.f', 'd1/d2/f2.f', 'f2.f'] + headers = ['d1/f2.f', 'd1/d2/f2.f', 'f2.f'] deps_match(self, deps, headers) test.unlink('f2.f') + class FortranScannerTestCase7(unittest.TestCase): def runTest(self): env = DummyEnvironment([test.workpath("d1/d2"), test.workpath("d1")]) s = SCons.Scanner.Fortran.FortranScan() path = s.path(env) deps = s(env.File('fff2.f'), env, path) - headers = ['d1/f2.f', 'd1/d2/f2.f', 'd1/d2/f2.f'] + headers = ['d1/f2.f', 'd1/d2/f2.f', 'd1/d2/f2.f'] deps_match(self, deps, headers) + class FortranScannerTestCase8(unittest.TestCase): def runTest(self): test.write('f2.f', "\n") @@ -344,10 +349,11 @@ class FortranScannerTestCase8(unittest.TestCase): s = SCons.Scanner.Fortran.FortranScan() path = s.path(env) deps = s(env.File('fff2.f'), env, path) - headers = ['d1/f2.f', 'd1/d2/f2.f', 'f2.f'] + headers = ['d1/f2.f', 'd1/d2/f2.f', 'f2.f'] deps_match(self, deps, headers) test.unlink('f2.f') + class FortranScannerTestCase9(unittest.TestCase): def runTest(self): test.write('f3.f', "\n") @@ -356,9 +362,11 @@ class FortranScannerTestCase9(unittest.TestCase): path = s.path(env) n = env.File('fff3.f') - def my_rexists(s): - s.Tag('rexists_called', 1) - return SCons.Node._rexists_map[s.GetTag('old_rexists')](s) + + def my_rexists(s1): + s1.Tag('rexists_called', 1) + return SCons.Node._rexists_map[s.GetTag('old_rexists')](s1) + n.Tag('old_rexists', n._func_rexists) SCons.Node._rexists_map[3] = my_rexists n._func_rexists = 3 @@ -369,10 +377,11 @@ class FortranScannerTestCase9(unittest.TestCase): # scanned, essential for cooperation with VariantDir functionality. assert n.GetTag('rexists_called') - headers = ['d1/f3.f', 'f3.f'] + headers = ['d1/f3.f', 'f3.f'] deps_match(self, deps, headers) test.unlink('f3.f') + class FortranScannerTestCase10(unittest.TestCase): def runTest(self): env = DummyEnvironment(["include"]) @@ -380,18 +389,20 @@ class FortranScannerTestCase10(unittest.TestCase): path = s.path(env) deps1 = s(env.File('fff4.f'), env, path) env.fs.chdir(env.Dir('subdir')) - dir = env.fs.getcwd() + test_dir = env.fs.getcwd() env.fs.chdir(env.Dir('')) - path = s.path(env, dir) + path = s.path(env, test_dir) deps2 = s(env.File('#fff4.f'), env, path) - headers1 = list(map(test.workpath, ['include/f4.f'])) - headers2 = ['include/f4.f'] + headers1 = [test.workpath(f) for f in ['include/f4.f']] + headers2 = ['include/f4.f'] deps_match(self, deps1, headers1) deps_match(self, deps2, headers2) + class FortranScannerTestCase11(unittest.TestCase): def runTest(self): SCons.Warnings.enableWarningClass(SCons.Warnings.DependencyWarning) + class TestOut(object): def __call__(self, x): self.out = x @@ -407,7 +418,8 @@ class FortranScannerTestCase11(unittest.TestCase): # Did we catch the warning from not finding not_there.f? assert to.out - deps_match(self, deps, [ 'f5.f' ]) + deps_match(self, deps, ['f5.f']) + class FortranScannerTestCase12(unittest.TestCase): def runTest(self): @@ -421,6 +433,7 @@ class FortranScannerTestCase12(unittest.TestCase): deps_match(self, deps, ['f4.f']) test.unlink('include/fff4.f') + class FortranScannerTestCase13(unittest.TestCase): def runTest(self): os.chdir(test.workpath('work')) @@ -429,9 +442,9 @@ class FortranScannerTestCase13(unittest.TestCase): # Create a derived file in a directory that does not exist yet. # This was a bug at one time. - f1=fs.File('include2/jjj.f') - f1.builder=1 - env = DummyEnvironment(['include','include2']) + f1 = fs.File('include2/jjj.f') + f1.builder = 1 + env = DummyEnvironment(['include', 'include2']) env.fs = fs s = SCons.Scanner.Fortran.FortranScan() path = s.path(env) @@ -439,6 +452,7 @@ class FortranScannerTestCase13(unittest.TestCase): deps_match(self, deps, [test.workpath('repository/include/iii.f'), 'include2/jjj.f']) os.chdir(test.workpath('')) + class FortranScannerTestCase14(unittest.TestCase): def runTest(self): os.chdir(test.workpath('work')) @@ -451,15 +465,16 @@ class FortranScannerTestCase14(unittest.TestCase): s = SCons.Scanner.Fortran.FortranScan() path = s.path(env) deps1 = s(fs.File('build1/aaa.f'), env, path) - deps_match(self, deps1, [ 'build1/bbb.f' ]) + deps_match(self, deps1, ['build1/bbb.f']) deps2 = s(fs.File('build2/aaa.f'), env, path) - deps_match(self, deps2, [ 'src/bbb.f' ]) + deps_match(self, deps2, ['src/bbb.f']) deps3 = s(fs.File('build1/ccc.f'), env, path) - deps_match(self, deps3, [ 'build1/ddd.f' ]) + deps_match(self, deps3, ['build1/ddd.f']) deps4 = s(fs.File('build2/ccc.f'), env, path) - deps_match(self, deps4, [ test.workpath('repository/src/ddd.f') ]) + deps_match(self, deps4, [test.workpath('repository/src/ddd.f')]) os.chdir(test.workpath('')) + class FortranScannerTestCase15(unittest.TestCase): def runTest(self): class SubstEnvironment(DummyEnvironment): @@ -468,6 +483,7 @@ class FortranScannerTestCase15(unittest.TestCase): return test.workpath("d1") else: return arg + test.write(['d1', 'f2.f'], " INCLUDE 'fi.f'\n") env = SubstEnvironment(["$junk"]) s = SCons.Scanner.Fortran.FortranScan() @@ -477,6 +493,7 @@ class FortranScannerTestCase15(unittest.TestCase): deps_match(self, deps, headers) test.write(['d1', 'f2.f'], "\n") + class FortranScannerTestCase16(unittest.TestCase): def runTest(self): test.write('f1.f', "\n") @@ -512,28 +529,9 @@ class FortranScannerTestCase16(unittest.TestCase): test.unlink('f9.f') test.unlink('f10.f') -def suite(): - suite = unittest.TestSuite() - suite.addTest(FortranScannerTestCase1()) - suite.addTest(FortranScannerTestCase2()) - suite.addTest(FortranScannerTestCase3()) - suite.addTest(FortranScannerTestCase4()) - suite.addTest(FortranScannerTestCase5()) - suite.addTest(FortranScannerTestCase6()) - suite.addTest(FortranScannerTestCase7()) - suite.addTest(FortranScannerTestCase8()) - suite.addTest(FortranScannerTestCase9()) - suite.addTest(FortranScannerTestCase10()) - suite.addTest(FortranScannerTestCase11()) - suite.addTest(FortranScannerTestCase12()) - suite.addTest(FortranScannerTestCase13()) - suite.addTest(FortranScannerTestCase14()) - suite.addTest(FortranScannerTestCase15()) - suite.addTest(FortranScannerTestCase16()) - return suite if __name__ == "__main__": - TestUnit.run(suite()) + unittest.main() # Local Variables: # tab-width:4 -- cgit v0.12 From 9ddd2ab49faddd45ae5d1d0253d5f5135f348099 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Thu, 25 Apr 2019 12:22:53 -0400 Subject: Add initializing fixture_dirs from shell variable FIXTURE_DIRS --- testing/framework/TestCmd.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/testing/framework/TestCmd.py b/testing/framework/TestCmd.py index a6a8045..bf010e1 100644 --- a/testing/framework/TestCmd.py +++ b/testing/framework/TestCmd.py @@ -974,6 +974,12 @@ class TestCmd(object): self.subdir(subdir) self.fixture_dirs = [] + try: + self.fixture_dirs = (os.environ['FIXTURE_DIRS']).split(os.pathsep) + except KeyError: + pass + + def __del__(self): self.cleanup() @@ -1630,6 +1636,7 @@ class TestCmd(object): os.mkdir(new) except OSError as e: print("Got error :%s"%e) + import pdb; pdb.set_trace() pass else: count = count + 1 -- cgit v0.12 From e820fbfd1c89ef3ef5a0e59fcdbc055b9e80e687 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Thu, 25 Apr 2019 12:25:57 -0400 Subject: Fix Issue #3135 - Also add tests to check that emitter is properly processing interface module declarations --- src/CHANGES.txt | 3 + src/engine/SCons/Scanner/FortranTests.py | 7 +- src/engine/SCons/Tool/FortranCommonTests.py | 125 +++++++++++++++++++++ test/fixture/fortran_unittests/test_1.f90 | 46 ++++++++ test/fixture/fortran_unittests/test_2.f90 | 46 ++++++++ test/fixture/fortran_unittests/test_submodules.f90 | 15 +++ 6 files changed, 239 insertions(+), 3 deletions(-) create mode 100644 src/engine/SCons/Tool/FortranCommonTests.py create mode 100644 test/fixture/fortran_unittests/test_1.f90 create mode 100644 test/fixture/fortran_unittests/test_2.f90 create mode 100644 test/fixture/fortran_unittests/test_submodules.f90 diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 4d24611..98a414e 100755 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -18,6 +18,9 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER Decider and not clearing it when the configure context is completed. - Add default paths for yacc tool on windows to include cygwin, mingw, and chocolatey + From Github User pdiener: + - Fix issue #3135 - Handle Fortran submodules and type bound procedures + From Daniel Moody: - Change the default for AppendENVPath to delete_existing=0, so path order will not be changed, unless explicitly set (Issue #3276) diff --git a/src/engine/SCons/Scanner/FortranTests.py b/src/engine/SCons/Scanner/FortranTests.py index 2e10473..c958474 100644 --- a/src/engine/SCons/Scanner/FortranTests.py +++ b/src/engine/SCons/Scanner/FortranTests.py @@ -363,9 +363,9 @@ class FortranScannerTestCase9(unittest.TestCase): n = env.File('fff3.f') - def my_rexists(s1): - s1.Tag('rexists_called', 1) - return SCons.Node._rexists_map[s.GetTag('old_rexists')](s1) + def my_rexists(s): + s.Tag('rexists_called', 1) + return SCons.Node._rexists_map[s.GetTag('old_rexists')](s) n.Tag('old_rexists', n._func_rexists) SCons.Node._rexists_map[3] = my_rexists @@ -530,6 +530,7 @@ class FortranScannerTestCase16(unittest.TestCase): test.unlink('f10.f') + if __name__ == "__main__": unittest.main() diff --git a/src/engine/SCons/Tool/FortranCommonTests.py b/src/engine/SCons/Tool/FortranCommonTests.py new file mode 100644 index 0000000..6ccd206 --- /dev/null +++ b/src/engine/SCons/Tool/FortranCommonTests.py @@ -0,0 +1,125 @@ +# +# __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. +# +# from typing import Dict, Any + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +import os +import os.path +import unittest + +import SCons.Node.FS +import SCons.Warnings +import SCons.Tool.FortranCommon + +import TestCmd + +original = os.getcwd() + +test = TestCmd.TestCmd(workdir='') + +os.chdir(test.workpath('')) + + +class DummyEnvironment(object): + dictionary = None # type: Dict[Any, Any] + + def __init__(self, list_cpp_path): + self.path = list_cpp_path + self.fs = SCons.Node.FS.FS(test.workpath('')) + self.dictionary = {} + + def has_key(self, key): + return key in self.dictionary + + def __getitem__(self, key): + return self.dictionary[key] + + def __setitem__(self, key, value): + self.dictionary[key] = value + + def __delitem__(self, key): + del self.dictionary[key] + + def subst(self, arg, target=None, source=None, conv=None): + if arg[0] == '$': + return self[arg[1:]] + return arg + + 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) + + +class FortranScannerSubmodulesTestCase(unittest.TestCase): + def runTest(self): + """ + Check that test_1.f90 and test_2.f90 which have interface specifications + Don't generate targets for those modules listed in the interface section + """ + + test.dir_fixture('fortran_unittests') + env = DummyEnvironment([test.workpath('modules')]) + env['FORTRANMODDIR'] = 'modules' + env['FORTRANMODSUFFIX'] = '.mod' + emitter = SCons.Tool.FortranCommon._fortranEmitter + # path = s.path(env) + + for fort in ['test_1.f90', 'test_2.f90']: + file_base, _ = os.path.splitext(fort) + file_mod = '%s.mod' % file_base + f = env.File(fort) + (target, source) = emitter([], [f, ], env) + + # print("Targets:%s\nSources:%s"%([str(a) for a in target], [str(a) for a in source])) + + # should only be 1 target and 1 source + self.assertEqual(len(target), 1, + msg="More than 1 target: %d [%s]" % (len(target), [str(t) for t in target])) + self.assertEqual(len(source), 1, + msg="More than 1 source: %d [%s]" % (len(source), [str(t) for t in source])) + + # target should be file_base.mod + self.assertEqual(str(target[0]).endswith(file_mod), True, + msg="Target[0]=%s doesn't end with '%s'" % (str(target[0]), file_mod)) + + # source should be file_base .f90 + self.assertEqual(str(source[0]).endswith(fort), True, + msg="Source[0]=%s doesn't end with '%s'" % (str(source[0]), fort)) + + +if __name__ == "__main__": + unittest.main() diff --git a/test/fixture/fortran_unittests/test_1.f90 b/test/fixture/fortran_unittests/test_1.f90 new file mode 100644 index 0000000..25ba9d6 --- /dev/null +++ b/test/fixture/fortran_unittests/test_1.f90 @@ -0,0 +1,46 @@ +module test_1 + + type test_type_1 + integer :: n + contains + procedure :: set_n + procedure :: get_n + end type test_type_1 + + +interface + + module subroutine set_n ( this, n ) + class(test_type_1), intent(inout) :: this + integer, intent(in) :: n + end subroutine + + module function get_n ( this ) + class(test_type_1), intent(in) :: this + integer :: get_n + end function get_n + +end interface + +end module test_1 + + +submodule(test_1) test_1_impl + +contains + + module procedure set_n + + implicit none + + this%n = n + end procedure set_n + + module procedure get_n + + implicit none + + get_n = this%n + end procedure get_n + +end submodule test_1_impl diff --git a/test/fixture/fortran_unittests/test_2.f90 b/test/fixture/fortran_unittests/test_2.f90 new file mode 100644 index 0000000..7a39b0d --- /dev/null +++ b/test/fixture/fortran_unittests/test_2.f90 @@ -0,0 +1,46 @@ +module test_2 + + type test_type_2 + integer :: m + contains + procedure :: set_m + procedure :: get_m + end type test_type_2 + + +interface + + module subroutine set_m ( this, m ) + class(test_type_2), intent(inout) :: this + integer, intent(in) :: m + end subroutine + + module function get_m ( this ) + class(test_type_2), intent(in) :: this + integer :: get_m + end function get_m + +end interface + +end module test_2 + + +submodule(test_2) test_2_impl + +contains + + module procedure set_m + + implicit none + + this%m = m + end procedure set_m + + module procedure get_m + + implicit none + + get_m = this%m + end procedure get_m + +end submodule test_2_impl diff --git a/test/fixture/fortran_unittests/test_submodules.f90 b/test/fixture/fortran_unittests/test_submodules.f90 new file mode 100644 index 0000000..5deec37 --- /dev/null +++ b/test/fixture/fortran_unittests/test_submodules.f90 @@ -0,0 +1,15 @@ +program test_submodules + + use test_1 + use test_2 + + type(test_type_1) :: var1 + type(test_type_2) :: var2 + + call var1%set_n(42) + call var2%set_m(21) + + print*,'var1%n = ', var1%get_n() + print*,'var2%m = ', var2%get_m() + +end program test_submodules -- cgit v0.12 From 4007416e4125db5d3598c3113bb1a3052c4460a7 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Thu, 25 Apr 2019 16:41:34 -0400 Subject: Remove debug logic --- testing/framework/TestCmd.py | 1 - 1 file changed, 1 deletion(-) diff --git a/testing/framework/TestCmd.py b/testing/framework/TestCmd.py index bf010e1..d4557e7 100644 --- a/testing/framework/TestCmd.py +++ b/testing/framework/TestCmd.py @@ -1636,7 +1636,6 @@ class TestCmd(object): os.mkdir(new) except OSError as e: print("Got error :%s"%e) - import pdb; pdb.set_trace() pass else: count = count + 1 -- cgit v0.12 From fbe96c0207942918a0d5f34addaac57d3b9b528c Mon Sep 17 00:00:00 2001 From: Peter Diener Date: Fri, 26 Apr 2019 15:55:03 -0500 Subject: Also ignore PURE and ELEMENTAL after MODULE in Scanner and Emitter. Add subroutines that are declared pure and elemental to the test of the Emitter. Note that in test_1.f90, the interface is repeated in the submodule, whereas in test_2.f90 the interface is taken from the module. Also note that the regex does not check whether "module pure" or "module elemental" is actually followed by "subroutine" or "function". This would be a syntax error and should trigger a compile time error. --- src/engine/SCons/Scanner/Fortran.py | 13 +++++++----- src/engine/SCons/Tool/FortranCommon.py | 2 +- test/fixture/fortran_unittests/test_1.f90 | 22 ++++++++++++++++++++ test/fixture/fortran_unittests/test_2.f90 | 24 ++++++++++++++++++++++ test/fixture/fortran_unittests/test_submodules.f90 | 12 +++++++++++ 5 files changed, 67 insertions(+), 6 deletions(-) diff --git a/src/engine/SCons/Scanner/Fortran.py b/src/engine/SCons/Scanner/Fortran.py index a932f21..67e6180 100644 --- a/src/engine/SCons/Scanner/Fortran.py +++ b/src/engine/SCons/Scanner/Fortran.py @@ -287,6 +287,8 @@ def FortranScan(path_variable="FORTRANPATH"): # MODULE PROCEDURE procedure_name # MODULE SUBROUTINE subroutine_name # MODULE FUNCTION function_name +# MODULE PURE SUBROUTINE|FUNCTION subroutine_name|function_name +# MODULE ELEMENTAL SUBROUTINE|FUNCTION subroutine_name|function_name # # Here is a breakdown of the regex: # @@ -296,15 +298,16 @@ def FortranScan(path_variable="FORTRANPATH"): # insensitive # \s+ : match one or more white space # characters -# (?!PROCEDURE|SUBROUTINE|FUNCTION) : but *don't* match if the next word -# matches PROCEDURE, SUBROUTINE or -# FUNCTION (negative lookahead -# assertion), case insensitive +# (?!PROCEDURE|SUBROUTINE|FUNCTION|PURE|ELEMENTAL) +# : but *don't* match if the next word +# matches PROCEDURE, SUBROUTINE, +# FUNCTION, PURE or ELEMENTAL (negative +# lookahead assertion), case insensitive # (\w+) : match one or more alphanumeric # characters that make up the defined # module name and save it in a group - def_regex = r"""(?i)^\s*MODULE\s+(?!PROCEDURE|SUBROUTINE|FUNCTION)(\w+)""" + def_regex = r"""(?i)^\s*MODULE\s+(?!PROCEDURE|SUBROUTINE|FUNCTION|PURE|ELEMENTAL)(\w+)""" scanner = F90Scanner("FortranScan", "$FORTRANSUFFIXES", diff --git a/src/engine/SCons/Tool/FortranCommon.py b/src/engine/SCons/Tool/FortranCommon.py index a38c6e7..cbb9a03 100644 --- a/src/engine/SCons/Tool/FortranCommon.py +++ b/src/engine/SCons/Tool/FortranCommon.py @@ -65,7 +65,7 @@ def _fortranEmitter(target, source, env): print("Could not locate " + str(node.name)) return ([], []) # This has to match the def_regex in the Fortran scanner - mod_regex = r"""(?i)^\s*MODULE\s+(?!PROCEDURE|SUBROUTINE|FUNCTION)(\w+)""" + mod_regex = r"""(?i)^\s*MODULE\s+(?!PROCEDURE|SUBROUTINE|FUNCTION|PURE|ELEMENTAL)(\w+)""" cre = re.compile(mod_regex,re.M) # Retrieve all USE'd module names modules = cre.findall(node.get_text_contents()) diff --git a/test/fixture/fortran_unittests/test_1.f90 b/test/fixture/fortran_unittests/test_1.f90 index 25ba9d6..50ab99e 100644 --- a/test/fixture/fortran_unittests/test_1.f90 +++ b/test/fixture/fortran_unittests/test_1.f90 @@ -5,6 +5,8 @@ module test_1 contains procedure :: set_n procedure :: get_n + procedure :: increment_n + procedure :: decrement_n end type test_type_1 @@ -20,6 +22,14 @@ interface integer :: get_n end function get_n + module pure subroutine increment_n ( this ) + class(test_type_1), intent(inout) :: this + end subroutine increment_n + + module elemental subroutine decrement_n ( this ) + class(test_type_1), intent(inout) :: this + end subroutine decrement_n + end interface end module test_1 @@ -43,4 +53,16 @@ contains get_n = this%n end procedure get_n + module pure subroutine increment_n ( this ) + class(test_type_1), intent(inout) :: this + + this%n = this%n+1 + end subroutine increment_n + + module elemental subroutine decrement_n ( this ) + class(test_type_1), intent(inout) :: this + + this%n = this%n-1 + end subroutine decrement_n + end submodule test_1_impl diff --git a/test/fixture/fortran_unittests/test_2.f90 b/test/fixture/fortran_unittests/test_2.f90 index 7a39b0d..e271953 100644 --- a/test/fixture/fortran_unittests/test_2.f90 +++ b/test/fixture/fortran_unittests/test_2.f90 @@ -5,6 +5,8 @@ module test_2 contains procedure :: set_m procedure :: get_m + procedure :: increment_m + procedure :: decrement_m end type test_type_2 @@ -20,6 +22,14 @@ interface integer :: get_m end function get_m + module pure subroutine increment_m ( this ) + class(test_type_2), intent(inout) :: this + end subroutine increment_m + + module elemental subroutine decrement_m ( this ) + class(test_type_2), intent(inout) :: this + end subroutine decrement_m + end interface end module test_2 @@ -43,4 +53,18 @@ contains get_m = this%m end procedure get_m + module procedure increment_m + + implicit none + + this%m = this%m+1 + end procedure increment_m + + module procedure decrement_m + + implicit none + + this%m = this%m-1 + end procedure decrement_m + end submodule test_2_impl diff --git a/test/fixture/fortran_unittests/test_submodules.f90 b/test/fixture/fortran_unittests/test_submodules.f90 index 5deec37..08e472c 100644 --- a/test/fixture/fortran_unittests/test_submodules.f90 +++ b/test/fixture/fortran_unittests/test_submodules.f90 @@ -12,4 +12,16 @@ program test_submodules print*,'var1%n = ', var1%get_n() print*,'var2%m = ', var2%get_m() + call var1%increment_n() + call var2%increment_m() + + print*,'var1%n = ', var1%get_n() + print*,'var2%m = ', var2%get_m() + + call var1%decrement_n() + call var2%decrement_m() + + print*,'var1%n = ', var1%get_n() + print*,'var2%m = ', var2%get_m() + end program test_submodules -- cgit v0.12 From 706f641d9d0c1ebf4197d65bc3bf8d049e65d1a9 Mon Sep 17 00:00:00 2001 From: Peter Diener Date: Fri, 26 Apr 2019 18:13:43 -0500 Subject: Update with fixes to handling of submodules. --- src/CHANGES.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 98a414e..5c02f97 100755 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -7,6 +7,9 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER + From Peter Diener: + - Additional fix to issue #3135 - Also handle 'pure' and 'elemental' type bound procedures + From William Deegan: - Fix Issue #3283 - Handle using --config=force in combination with Decider('MD5-timestamp'). @@ -18,7 +21,7 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER Decider and not clearing it when the configure context is completed. - Add default paths for yacc tool on windows to include cygwin, mingw, and chocolatey - From Github User pdiener: + From Peter Diener: - Fix issue #3135 - Handle Fortran submodules and type bound procedures From Daniel Moody: -- cgit v0.12 From b6bc88eaeb6e254525399a1143e7e536b2dbe4ef Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sat, 27 Apr 2019 15:21:10 -0400 Subject: Update CHANGES.txt fixed CHANGES.txt to have only one section for Peter Dienier --- src/CHANGES.txt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 5c02f97..024e697 100755 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -9,6 +9,7 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER From Peter Diener: - Additional fix to issue #3135 - Also handle 'pure' and 'elemental' type bound procedures + - Fix issue #3135 - Handle Fortran submodules and type bound procedures From William Deegan: @@ -21,10 +22,7 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER Decider and not clearing it when the configure context is completed. - Add default paths for yacc tool on windows to include cygwin, mingw, and chocolatey - From Peter Diener: - - Fix issue #3135 - Handle Fortran submodules and type bound procedures - - From Daniel Moody: +From Daniel Moody: - Change the default for AppendENVPath to delete_existing=0, so path order will not be changed, unless explicitly set (Issue #3276) - Fixed bug which threw error when running SCons on windows system with no MSVC installed. -- cgit v0.12