Handle Fortran MODULE definitions in the same file. (Craig Scott)
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Sun, 21 Aug 2005 01:06:51 +0000 (01:06 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Sun, 21 Aug 2005 01:06:51 +0000 (01:06 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@1340 fdb21ef1-2011-0410-befe-b5e4ea1792b1

src/CHANGES.txt
src/engine/SCons/Scanner/Fortran.py
test/Fortran/USE-MODULE.py [new file with mode: 0644]

index b62fbef11ae40e2d3dde11a5a4d6281ab1f18f1f..93846b57c6bb76b496b96cd4e08ae9a5af45d36a 100644 (file)
@@ -592,6 +592,9 @@ RELEASE 0.97 - XXX
     file when they're one of the many synonyms for a default True or
     False value.
 
+  - Fix the Fortran Scanner's ability to handle a module being used
+    in the same file in which it is defined.
+
   From Jeff Squyres:
 
   - Documentation changes:  Use $CPPDEFINES instead of $CCFLAGS in man
index 66bec0c835a03c9ce413371234277539afbd3737..8f7a6ce14712bc61161b487627719283faa12edc 100644 (file)
@@ -54,11 +54,12 @@ class F90Scanner(SCons.Scanner.Classic):
     smart thing to do.
     """
 
-    def __init__(self, name, suffixes, path_variable, use_regex,
-                 incl_regex, *args, **kw):
+    def __init__(self, name, suffixes, path_variable,
+                 use_regex, incl_regex, def_regex, *args, **kw):
 
         self.cre_use = re.compile(use_regex, re.M)
         self.cre_incl = re.compile(incl_regex, re.M)
+        self.cre_def = re.compile(def_regex, re.M)
 
         def _scan(node, env, path, self=self):
             node = node.rfile()
@@ -78,7 +79,7 @@ class F90Scanner(SCons.Scanner.Classic):
 
     def scan(self, node, env, path=()):
         "__cacheable__"
-        
+
         # cache the includes list in node so we only scan it once:
         if node.includes != None:
             mods_and_includes = node.includes
@@ -87,6 +88,15 @@ class F90Scanner(SCons.Scanner.Classic):
             includes = self.cre_incl.findall(node.get_contents())
             # retrieve all USE'd module names
             modules = self.cre_use.findall(node.get_contents())
+            # retrieve all defined module names
+            defmodules = self.cre_def.findall(node.get_contents())
+
+            # Remove all USE'd module names that are defined in the same file
+            d = {}
+            for m in defmodules:
+                d[m] = 1
+            modules = filter(lambda m, d=d: not d.has_key(m), modules)
+            #modules = self.undefinedModules(modules, defmodules)
 
             # Convert module name to a .mod filename
             suffix = env.subst('$FORTRANMODSUFFIX')
@@ -270,9 +280,34 @@ def FortranScan(path_variable="FORTRANPATH"):
 
     include_regex = """(?i)(?:^|['">]\s*;)\s*INCLUDE\s+(?:\w+_)?[<"'](.+?)(?=["'>])"""
 
+#   The MODULE statement regex finds module definitions by matching
+#   the following:
+#
+#   MODULE module_name
+#
+#   but *not* the following:
+#
+#   MODULE PROCEDURE procedure_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+)"""
+
     scanner = F90Scanner("FortranScan",
                          "$FORTRANSUFFIXES",
                          path_variable,
                          use_regex,
-                         include_regex)
+                         include_regex,
+                         def_regex)
     return scanner
diff --git a/test/Fortran/USE-MODULE.py b/test/Fortran/USE-MODULE.py
new file mode 100644 (file)
index 0000000..541c457
--- /dev/null
@@ -0,0 +1,92 @@
+#!/usr/bin/env python
+#
+# __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 os
+import string
+import sys
+import TestSCons
+
+python = TestSCons.python
+_exe   = TestSCons._exe
+
+test = TestSCons.TestSCons()
+
+
+
+test.write('myfortran.py', r"""
+import os.path
+import re
+import string
+import sys
+mod_regex = "(?im)^\\s*MODULE\\s+(?!PROCEDURE)(\\w+)"
+contents = open(sys.argv[1]).read()
+modules = re.findall(mod_regex, contents)
+modules = map(lambda m: string.lower(m)+'.mod', modules)
+for t in sys.argv[2:] + modules:
+    open(t, 'wb').write('myfortran.py wrote %s\n' % os.path.split(t)[1])
+sys.exit(0)
+""")
+
+test.write('SConstruct', """
+env = Environment(FORTRANCOM = r'%(python)s myfortran.py $SOURCE $TARGET')
+env.Object(target = 'test1.obj', source = 'test1.f')
+""" % locals())
+
+test.write('test1.f', """\
+      PROGRAM TEST
+      USE MOD_FOO
+      USE MOD_BAR
+      PRINT *,'TEST.f'
+      CALL P
+      STOP
+      END
+      MODULE MOD_FOO
+         IMPLICIT NONE
+         CONTAINS
+         SUBROUTINE P
+            PRINT *,'mod_foo'
+         END SUBROUTINE P
+      END MODULE MOD_FOO
+      MODULE PROCEDURE MOD_BAR
+         IMPLICIT NONE
+         CONTAINS
+         SUBROUTINE P
+            PRINT *,'mod_bar'
+         END SUBROUTINE P
+      END MODULE MOD_BAR
+""")
+
+test.run(arguments = '.', stderr = None)
+
+test.must_match('test1.obj', "myfortran.py wrote test1.obj\n")
+test.must_match('mod_foo.mod', "myfortran.py wrote mod_foo.mod\n")
+test.must_not_exist('mod_bar.mod')
+
+test.up_to_date(arguments = '.')
+
+
+
+test.pass_test()