Add a native Fortran include scanner.
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Wed, 12 Jun 2002 21:16:35 +0000 (21:16 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Wed, 12 Jun 2002 21:16:35 +0000 (21:16 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@388 fdb21ef1-2011-0410-befe-b5e4ea1792b1

doc/man/scons.1
rpm/scons.spec
src/CHANGES.txt
src/engine/MANIFEST.in
src/engine/SCons/Defaults.py
src/engine/SCons/Environment.py
src/engine/SCons/EnvironmentTests.py
src/engine/SCons/Scanner/Fortran.py [new file with mode: 0644]
src/engine/SCons/Scanner/FortranTests.py [new file with mode: 0644]
test/BuildDir.py
test/F77PATH.py [new file with mode: 0644]

index 5570ec1bf38930e612b4ce6770dcbe03e09ed100..62522673b4296d9ebb537898312e7b6ba30caf25 100644 (file)
@@ -1278,6 +1278,31 @@ The command line used to compile a Fortran source file to an object file.
 .IP F77FLAGS
 General options that are passed to the Fortran compiler.
 
+.IP F77PATH
+The list of directories that the Fortran compiler will search for include
+directories. The Fortran implicit dependency scanner will search these
+directories for include files. Don't explicitly put include directory
+arguments in F77FLAGS because the result will be non-portable
+and the directories will not be searched by the dependency scanner. Note:
+directory names in F77PATH will be looked-up relative to the SConscript
+directory when they are used in a command. To force 
+.B scons
+to look-up a directory relative to the root of the source tree use #:
+
+.ES
+env = Environment(F77PATH='#/include')
+.EE
+
+.IP
+The directory look-up can also be forced using the 
+.BR Dir ()
+function:
+
+.ES
+include = Dir('include')
+env = Environment(F77PATH=include)
+.EE
+
 .IP F77PPCOM 
 The command line used to compile a Fortran source file to an object file
 after first running the file through the C preprocessor.
index 326b1574452f8c11d5062459fa9002f361bf5eae..0bfa0e057d09635bb6b937bd185dde762bb2695d 100644 (file)
@@ -78,6 +78,8 @@ rm -rf $RPM_BUILD_ROOT
 /usr/lib/scons/SCons/Platform/__init__.pyc
 /usr/lib/scons/SCons/Scanner/C.py
 /usr/lib/scons/SCons/Scanner/C.pyc
+/usr/lib/scons/SCons/Scanner/Fortran.py
+/usr/lib/scons/SCons/Scanner/Fortran.pyc
 /usr/lib/scons/SCons/Scanner/Prog.py
 /usr/lib/scons/SCons/Scanner/Prog.pyc
 /usr/lib/scons/SCons/Scanner/__init__.py
index 9d76afe3a41d598649b8d0f517e3fbe8700c610e..82627e3ddf375c0d4c2b5d1d51b4c262be3abd48 100644 (file)
@@ -41,6 +41,9 @@ RELEASE 0.08 -
   - Remove the old feature of automatically splitting strings
     of file names on white space.
 
+  - Add a dependency Scanner for native Fortran "include" statements,
+    using a new "F77PATH" construction variable.
+
   From Jeff Petkau:
 
   - Fix --implicit-cache if the scanner returns an empty list.
index 1c9ac9189ebaa3d663b0fca73ef470247e673572..dce529153fdb4281403ff860b524ab353d169f95 100644 (file)
@@ -15,6 +15,7 @@ SCons/Platform/posix.py
 SCons/Platform/win32.py
 SCons/Scanner/__init__.py
 SCons/Scanner/C.py
+SCons/Scanner/Fortran.py
 SCons/Scanner/Prog.py
 SCons/Script/SConscript.py
 SCons/Script/__init__.py
index e8bf3769eb055b7534394347edfc77b91719b599..aa8a6430ceda30699c66db3c587c042619e64c7d 100644 (file)
@@ -49,6 +49,7 @@ import SCons.Node.Alias
 import SCons.Node.FS
 import SCons.Platform
 import SCons.Scanner.C
+import SCons.Scanner.Fortran
 import SCons.Scanner.Prog
 import SCons.Util
 
@@ -306,6 +307,8 @@ PostScript = SCons.Builder.Builder(action = '$PSCOM',
 
 CScan = SCons.Scanner.C.CScan()
 
+FortranScan = SCons.Scanner.Fortran.FortranScan()
+
 def alias_builder(env, target, source):
     pass
 
@@ -416,26 +419,26 @@ def make_win32_env_from_paths(include, lib, path):
     return {
         'CC'         : 'cl',
         'CCFLAGS'    : '/nologo',
-        'CCCOM'      : '$CC $CCFLAGS $CPPFLAGS $_INCFLAGS /c $SOURCES /Fo$TARGET',
+        'CCCOM'      : '$CC $CCFLAGS $CPPFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET',
         'SHCC'      : '$CC',
         'SHCCFLAGS' : '$CCFLAGS',
-        'SHCCCOM'    : '$SHCC $SHCCFLAGS $CPPFLAGS $_INCFLAGS /c $SOURCES /Fo$TARGET',
+        'SHCCCOM'    : '$SHCC $SHCCFLAGS $CPPFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET',
         'CFILESUFFIX' : '.c',
         'CXX'        : '$CC',
         'CXXFLAGS'   : '$CCFLAGS',
-        'CXXCOM'     : '$CXX $CXXFLAGS $CPPFLAGS $_INCFLAGS /c $SOURCES /Fo$TARGET',
+        'CXXCOM'     : '$CXX $CXXFLAGS $CPPFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET',
         'SHCXX'      : '$CXX',
         'SHCXXFLAGS' : '$CXXFLAGS',
-        'SHCXXCOM'   : '$SHCXX $SHCXXFLAGS $CPPFLAGS $_INCFLAGS /c $SOURCES /Fo$TARGET',
+        'SHCXXCOM'   : '$SHCXX $SHCXXFLAGS $CPPFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET',
         'CXXFILESUFFIX' : '.cc',
         'F77'        : 'g77',
         'F77FLAGS'   : '',
-        'F77COM'     : '$F77 $F77FLAGS $_INCFLAGS /c $SOURCES /Fo$TARGET',
-        'F77PPCOM'   : '$F77 $F77FLAGS $CPPFLAGS $_INCFLAGS /c $SOURCES /Fo$TARGET',
+        'F77COM'     : '$F77 $F77FLAGS $_F77INCFLAGS /c $SOURCES /Fo$TARGET',
+        'F77PPCOM'   : '$F77 $F77FLAGS $CPPFLAGS $_F77INCFLAGS /c $SOURCES /Fo$TARGET',
         'SHF77'      : '$F77',
         'SHF77FLAGS' : '$F77FLAGS',
-        'SHF77COM'   : '$SHF77 $SHF77FLAGS $_INCFLAGS /c $SOURCES /Fo$TARGET',
-        'SHF77PPCOM' : '$SHF77 $SHF77FLAGS $CPPFLAGS $_INCFLAGS /c $SOURCES /Fo$TARGET',
+        'SHF77COM'   : '$SHF77 $SHF77FLAGS $_F77INCFLAGS /c $SOURCES /Fo$TARGET',
+        'SHF77PPCOM' : '$SHF77 $SHF77FLAGS $CPPFLAGS $_F77INCFLAGS /c $SOURCES /Fo$TARGET',
         'LINK'       : 'link',
         'LINKFLAGS'  : '/nologo',
         'LINKCOM'    : SCons.Action.CommandGenerator(win32LinkGenerator),
@@ -482,7 +485,7 @@ def make_win32_env_from_paths(include, lib, path):
                          'PDF'            : PDF,
                          'PostScript'     : PostScript,
                          'Program'        : Program },
-        'SCANNERS'   : [CScan],
+        'SCANNERS'   : [CScan, FortranScan],
         'LIBDIRPREFIX'          : '/LIBPATH:',
         'LIBDIRSUFFIX'          : '',
         'LIBLINKPREFIX'         : '',
@@ -522,29 +525,29 @@ if os.name == 'posix':
     ConstructionEnvironment = {
         'CC'         : 'cc',
         'CCFLAGS'    : '',
-        'CCCOM'      : '$CC $CCFLAGS $CPPFLAGS $_INCFLAGS -c -o $TARGET $SOURCES',
+        'CCCOM'      : '$CC $CCFLAGS $CPPFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES',
         'SHCC'       : '$CC',
         'SHCCFLAGS'  : '$CCFLAGS -fPIC',
-        'SHCCCOM'    : '$SHCC $SHCCFLAGS $_INCFLAGS -c -o $TARGET $SOURCES',
+        'SHCCCOM'    : '$SHCC $SHCCFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES',
         'CFILESUFFIX' : '.c',
         'CXX'        : 'c++',
         'CXXFLAGS'   : '$CCFLAGS',
-        'CXXCOM'     : '$CXX $CXXFLAGS $CPPFLAGS $_INCFLAGS -c -o $TARGET $SOURCES',
+        'CXXCOM'     : '$CXX $CXXFLAGS $CPPFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES',
         'CXXFILESUFFIX' : '.cc',
         'SHCXX'      : '$CXX',
         'SHCXXFLAGS' : '$CXXFLAGS -fPIC',
-        'SHCXXCOM'   : '$SHCXX $SHCXXFLAGS $_INCFLAGS -c -o $TARGET $SOURCES',
+        'SHCXXCOM'   : '$SHCXX $SHCXXFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES',
         'F77'        : 'g77',
         'F77FLAGS'   : '',
-        'F77COM'     : '$F77 $F77FLAGS $_INCFLAGS -c -o $TARGET $SOURCES',
-        'F77PPCOM'   : '$F77 $F77FLAGS $CPPFLAGS $_INCFLAGS -c -o $TARGET $SOURCES',
+        'F77COM'     : '$F77 $F77FLAGS $_F77INCFLAGS -c -o $TARGET $SOURCES',
+        'F77PPCOM'   : '$F77 $F77FLAGS $CPPFLAGS $_F77INCFLAGS -c -o $TARGET $SOURCES',
         'SHF77FLAGS' : '$F77FLAGS -fPIC',
-        'SHF77COM'   : '$F77 $SHF77FLAGS $_INCFLAGS -c -o $TARGET $SOURCES',
-        'SHF77PPCOM' : '$F77 $SHF77FLAGS $CPPFLAGS $_INCFLAGS -c -o $TARGET $SOURCES',
+        'SHF77COM'   : '$F77 $SHF77FLAGS $_F77INCFLAGS -c -o $TARGET $SOURCES',
+        'SHF77PPCOM' : '$F77 $SHF77FLAGS $CPPFLAGS $_F77INCFLAGS -c -o $TARGET $SOURCES',
         'SHF77'      : '$F77',
         'SHF77FLAGS' : '$F77FLAGS -fPIC',
-        'SHF77COM'   : '$SHF77 $SHF77FLAGS $_INCFLAGS -c -o $TARGET $SOURCES',
-        'SHF77PPCOM' : '$SHF77 $SHF77FLAGS $CPPFLAGS $_INCFLAGS -c -o $TARGET $SOURCES',
+        'SHF77COM'   : '$SHF77 $SHF77FLAGS $_F77INCFLAGS -c -o $TARGET $SOURCES',
+        'SHF77PPCOM' : '$SHF77 $SHF77FLAGS $CPPFLAGS $_F77INCFLAGS -c -o $TARGET $SOURCES',
         'LINK'       : '$CXX',
         'LINKFLAGS'  : '',
         'LINKCOM'    : '$LINK $LINKFLAGS -o $TARGET $SOURCES $_LIBDIRFLAGS $_LIBFLAGS',
@@ -591,7 +594,7 @@ if os.name == 'posix':
                          'PDF'            : PDF,
                          'PostScript'     : PostScript,
                          'Program'        : Program },
-        'SCANNERS'   : [CScan],
+        'SCANNERS'   : [CScan, FortranScan],
         'LIBDIRPREFIX'          : '-L',
         'LIBDIRSUFFIX'          : '',
         'LIBLINKPREFIX'         : '-l',
index 787a66dfceae78e869137fa3a47845bacf303064..a95adf99ef3f354c3bc944d621d6d9cec00ade45 100644 (file)
@@ -148,9 +148,13 @@ class Environment:
                                            'LIBPATH',
                                            'LIBDIRPREFIX',
                                            'LIBDIRSUFFIX' ),
-                              DirVarInterp('_INCFLAGS',
+                              DirVarInterp('_CPPINCFLAGS',
                                            'CPPPATH',
                                            'INCPREFIX',
+                                           'INCSUFFIX'),
+                              DirVarInterp('_F77INCFLAGS',
+                                           'F77PATH',
+                                           'INCPREFIX',
                                            'INCSUFFIX') )
 
     def __cmp__(self, other):
@@ -391,16 +395,23 @@ class Environment:
 class VarInterpolator:
     def __init__(self, dest, src, prefix, suffix):
         self.dest = dest
+        if not SCons.Util.is_List(src):
+            src = [ src ]
         self.src = src
         self.prefix = prefix
         self.suffix = suffix
 
     def prepareSrc(self, dict):
-        src = dict[self.src]
-       if SCons.Util.is_String(src):
-           src = string.split(src)
-        elif not SCons.Util.is_List(src):
-            src = [ src ]
+        src = []
+        for s in self.src:
+            if dict.has_key(s):
+                cv = dict[s]
+                if SCons.Util.is_String(cv):
+                    src.extend(string.split(cv))
+                elif SCons.Util.is_List(cv):
+                    src.extend(cv)
+                else:
+                    src.append(cv)
 
         def prepare(x, dict=dict):
             if isinstance(x, SCons.Node.Node):
@@ -411,10 +422,6 @@ class VarInterpolator:
         return map(prepare, src)
 
     def generate(self, ddict, sdict):
-        if not sdict.has_key(self.src):
-            ddict[self.dest] = ''
-            return
-
         src = filter(lambda x: not x is None, self.prepareSrc(sdict))
 
         if not src:
index 25712b2df95b21a7a98fa965950d48a7f9e38a5c..167c662af2bff7af82ddda1238596f65ed99d9ca 100644 (file)
@@ -419,32 +419,57 @@ class EnvironmentTestCase(unittest.TestCase):
                dict['_LIBFLAGS'][2]
 
         blat = SCons.Node.FS.default_fs.File('blat')
-        env = Environment(CPPPATH = [ 'foo', '$FOO/bar', blat],
+
+        env = Environment(CPPPATH = [ 'foo', '$FOO/bar', blat ],
+                          INCPREFIX = 'foo ',
+                          INCSUFFIX = 'bar',
+                          FOO = 'baz')
+        dict = env.autogenerate(dir = SCons.Node.FS.default_fs.Dir('/xx'))
+        assert len(dict['_CPPINCFLAGS']) == 8, dict['_CPPINCFLAGS']
+        assert dict['_CPPINCFLAGS'][0] == '$(', \
+               dict['_CPPINCFLAGS'][0]
+        assert dict['_CPPINCFLAGS'][1] == os.path.normpath('foo'), \
+               dict['_CPPINCFLAGS'][1]
+        assert dict['_CPPINCFLAGS'][2] == os.path.normpath('/xx/foobar'), \
+               dict['_CPPINCFLAGS'][2]
+        assert dict['_CPPINCFLAGS'][3] == os.path.normpath('foo'), \
+               dict['_CPPINCFLAGS'][3]
+        assert dict['_CPPINCFLAGS'][4] == os.path.normpath('/xx/baz/barbar'), \
+               dict['_CPPINCFLAGS'][4]
+        assert dict['_CPPINCFLAGS'][5] == os.path.normpath('foo'), \
+               dict['_CPPINCFLAGS'][5]
+        assert dict['_CPPINCFLAGS'][6] == os.path.normpath('blatbar'), \
+               dict['_CPPINCFLAGS'][6]
+        assert dict['_CPPINCFLAGS'][7] == '$)', \
+               dict['_CPPINCFLAGS'][7]
+
+        env = Environment(F77PATH = [ 'foo', '$FOO/bar', blat ],
                           INCPREFIX = 'foo ',
                           INCSUFFIX = 'bar',
                           FOO = 'baz')
         dict = env.autogenerate(dir = SCons.Node.FS.default_fs.Dir('/xx'))
-        assert len(dict['_INCFLAGS']) == 8, dict['_INCFLAGS']
-        assert dict['_INCFLAGS'][0] == '$(', \
-               dict['_INCFLAGS'][0]
-        assert dict['_INCFLAGS'][1] == os.path.normpath('foo'), \
-               dict['_INCFLAGS'][1]
-        assert dict['_INCFLAGS'][2] == os.path.normpath('/xx/foobar'), \
-               dict['_INCFLAGS'][2]
-        assert dict['_INCFLAGS'][3] == os.path.normpath('foo'), \
-               dict['_INCFLAGS'][3]
-        assert dict['_INCFLAGS'][4] == os.path.normpath('/xx/baz/barbar'), \
-               dict['_INCFLAGS'][4]
-        assert dict['_INCFLAGS'][5] == os.path.normpath('foo'), \
-               dict['_INCFLAGS'][5]
-        assert dict['_INCFLAGS'][6] == os.path.normpath('blatbar'), \
-               dict['_INCFLAGS'][6]
-        assert dict['_INCFLAGS'][7] == '$)', \
-               dict['_INCFLAGS'][7]
-
-        env = Environment(CPPPATH = '', LIBPATH = '')
+        assert len(dict['_F77INCFLAGS']) == 8, dict['_F77INCFLAGS']
+        assert dict['_F77INCFLAGS'][0] == '$(', \
+               dict['_F77INCFLAGS'][0]
+        assert dict['_F77INCFLAGS'][1] == os.path.normpath('foo'), \
+               dict['_F77INCFLAGS'][1]
+        assert dict['_F77INCFLAGS'][2] == os.path.normpath('/xx/foobar'), \
+               dict['_F77INCFLAGS'][2]
+        assert dict['_F77INCFLAGS'][3] == os.path.normpath('foo'), \
+               dict['_F77INCFLAGS'][3]
+        assert dict['_F77INCFLAGS'][4] == os.path.normpath('/xx/baz/barbar'), \
+               dict['_F77INCFLAGS'][4]
+        assert dict['_F77INCFLAGS'][5] == os.path.normpath('foo'), \
+               dict['_F77INCFLAGS'][5]
+        assert dict['_F77INCFLAGS'][6] == os.path.normpath('blatbar'), \
+               dict['_F77INCFLAGS'][6]
+        assert dict['_F77INCFLAGS'][7] == '$)', \
+               dict['_F77INCFLAGS'][7]
+
+        env = Environment(CPPPATH = '', F77PATH = '', LIBPATH = '')
         dict = env.autogenerate(dir = SCons.Node.FS.default_fs.Dir('/yy'))
-        assert len(dict['_INCFLAGS']) == 0, dict['_INCFLAGS']
+        assert len(dict['_CPPINCFLAGS']) == 0, dict['_CPPINCFLAGS']
+        assert len(dict['_F77INCFLAGS']) == 0, dict['_F77INCFLAGS']
         assert len(dict['_LIBDIRFLAGS']) == 0, dict['_LIBDIRFLAGS']
 
     def test_platform(self):
diff --git a/src/engine/SCons/Scanner/Fortran.py b/src/engine/SCons/Scanner/Fortran.py
new file mode 100644 (file)
index 0000000..17c9241
--- /dev/null
@@ -0,0 +1,128 @@
+"""SCons.Scanner.Fortran
+
+This module implements the dependency scanner for Fortran code. 
+
+"""
+
+#
+# Copyright (c) 2001, 2002 Steven Knight
+#
+# 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 copy
+import os.path
+import re
+
+import SCons.Node
+import SCons.Node.FS
+import SCons.Scanner
+import SCons.Util
+import SCons.Warnings
+
+include_re = re.compile("INCLUDE[ \t]+'([\\w./\\\\]+)'", re.M)
+
+def FortranScan(fs = SCons.Node.FS.default_fs):
+    """Return a prototype Scanner instance for scanning source files
+    for Fortran INCLUDE statements"""
+    scanner = SCons.Scanner.Recursive(scan, "FortranScan", fs,
+                                      [".f", ".F", ".for", ".FOR"])
+    return scanner
+
+def scan(node, env, target, fs = SCons.Node.FS.default_fs):
+    """
+    scan(node, Environment) -> [node]
+
+    the Fortran dependency scanner function
+
+    This function is intentionally simple. There are two rules it
+    follows:
+    
+    1) #include <foo.h> - search for foo.h in F77PATH followed by the
+        directory 'filename' is in
+    2) #include \"foo.h\" - search for foo.h in the directory 'filename' is
+       in followed by F77PATH
+
+    These rules approximate the behaviour of most C/C++ compilers.
+
+    This scanner also ignores #ifdef and other preprocessor conditionals, so
+    it may find more depencies than there really are, but it never misses
+    dependencies.
+    """
+
+    # This function caches various information in node and target:
+    # target.f77path - env['F77PATH'] converted to nodes
+    # node.found_includes - include files found by previous call to scan, 
+    #     keyed on f77path
+    # node.includes - the result of include_re.findall()
+
+    if not hasattr(target, 'f77path'):
+        def Dir(x, dir=target.cwd, fs=fs): return fs.Dir(x,dir)
+        try:
+            target.f77path = tuple(SCons.Node.arg2nodes(env['F77PATH'],Dir))
+        except KeyError:
+            target.f77path = ()
+
+    f77path = target.f77path
+
+    nodes = []
+
+    try:
+        nodes = node.found_includes[f77path]
+    except KeyError:
+        if node.exists():
+
+            # cache the includes list in node so we only scan it once:
+            if node.includes != None:
+                includes = node.includes
+            else:
+                includes = include_re.findall(node.get_contents())
+                node.includes = includes
+
+            source_dir = node.get_dir()
+            
+            for include in includes:
+                n = SCons.Node.FS.find_file(include,
+                                            (source_dir,) + f77path,
+                                            fs.File)
+                if not n is None:
+                    nodes.append(n)
+                else:
+                    SCons.Warnings.warn(SCons.Warnings.DependencyWarning,
+                                        "No dependency generated for file: %s (included from: %s) -- file not found" % (include, node))
+        node.found_includes[f77path] = nodes
+
+    # Schwartzian transform from the Python FAQ Wizard
+    def st(List, Metric):
+        def pairing(element, M = Metric):
+            return (M(element), element)
+        def stripit(pair):
+            return pair[1]
+        paired = map(pairing, List)
+        paired.sort()
+        return map(stripit, paired)
+
+    def normalize(node):
+        return str(node)
+
+    return st(nodes, normalize)
diff --git a/src/engine/SCons/Scanner/FortranTests.py b/src/engine/SCons/Scanner/FortranTests.py
new file mode 100644 (file)
index 0000000..2883daf
--- /dev/null
@@ -0,0 +1,301 @@
+#
+# Copyright (c) 2001, 2002 Steven Knight
+#
+# 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 os.path
+import sys
+import unittest
+
+import SCons.Scanner.Fortran
+import SCons.Node.FS
+import SCons.Warnings
+
+import TestCmd
+
+original = os.getcwd()
+
+test = TestCmd.TestCmd(workdir = '')
+
+os.chdir(test.workpath(''))
+
+# create some source files and headers:
+
+test.write('fff1.f',"""
+      PROGRAM FOO
+      INCLUDE 'f1.f'
+      INCLUDE 'f2.f'
+      STOP
+      END
+""")
+
+test.write('fff2.f',"""
+      PROGRAM FOO
+      INCLUDE 'f2.f'
+      INCLUDE 'd1/f2.f'
+      INCLUDE 'd2/f2.f'
+      STOP
+      END
+""")
+
+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']
+
+for h in headers:
+    test.write(h, "\n")
+
+
+test.subdir('include', 'subdir', ['subdir', 'include'])
+
+test.write('fff4.f',"""
+      PROGRAM FOO
+      INCLUDE 'f4.f'
+      STOP
+      END
+""")
+
+test.write('include/f4.f', "\n")
+test.write('subdir/include/f4.f', "\n")
+
+# define some helpers:
+
+class DummyTarget:
+    def __init__(self, cwd=None):
+        self.cwd = cwd
+
+class DummyEnvironment:
+    def __init__(self, listCppPath):
+        self.path = listCppPath
+        
+    def Dictionary(self, *args):
+        if not args:
+            return { 'F77PATH': self.path }
+        elif len(args) == 1 and args[0] == 'F77PATH':
+            return self.path
+        else:
+            raise KeyError, "Dummy environment only has F77PATH attribute."
+
+    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 deps_match(self, deps, headers):
+    scanned = map(os.path.normpath, map(str, deps))
+    expect = map(os.path.normpath, headers)
+    self.failUnless(scanned == expect, "expect %s != scanned %s" % (expect, scanned))
+
+def make_node(filename, fs=SCons.Node.FS.default_fs):
+    return fs.File(test.workpath(filename))
+
+# define some tests:
+
+class FortranScannerTestCase1(unittest.TestCase):
+    def runTest(self):
+        test.write('f1.f', "\n")
+        test.write('f2.f', "      INCLUDE 'fi.f'\n")
+        env = DummyEnvironment([])
+        s = SCons.Scanner.Fortran.FortranScan()
+       fs = SCons.Node.FS.FS(original)
+        deps = s.scan(make_node('fff1.f', fs), env, DummyTarget())
+        headers = ['f1.f', 'f2.f', 'fi.f']
+        deps_match(self, deps, map(test.workpath, headers))
+       test.unlink('f1.f')
+       test.unlink('f2.f')
+
+class FortranScannerTestCase2(unittest.TestCase):
+    def runTest(self):
+        test.write('f1.f', "\n")
+        test.write('f2.f', "      INCLUDE 'fi.f'\n")
+        env = DummyEnvironment([test.workpath("d1")])
+        s = SCons.Scanner.Fortran.FortranScan()
+       fs = SCons.Node.FS.FS(original)
+        deps = s.scan(make_node('fff1.f', fs), env, DummyTarget())
+        headers = ['f1.f', 'f2.f', 'fi.f']
+        deps_match(self, deps, map(test.workpath, headers))
+       test.unlink('f1.f')
+       test.unlink('f2.f')
+
+class FortranScannerTestCase3(unittest.TestCase):
+    def runTest(self):
+        env = DummyEnvironment([test.workpath("d1")])
+        s = SCons.Scanner.Fortran.FortranScan()
+       fs = SCons.Node.FS.FS(original)
+        deps = s.scan(make_node('fff1.f', fs), env, DummyTarget())
+        headers = ['d1/f1.f', 'd1/f2.f']
+        deps_match(self, deps, map(test.workpath, headers))
+
+class FortranScannerTestCase4(unittest.TestCase):
+    def runTest(self):
+        test.write(['d1', 'f2.f'], "      INCLUDE 'fi.f'\n")
+        env = DummyEnvironment([test.workpath("d1")])
+        s = SCons.Scanner.Fortran.FortranScan()
+       fs = SCons.Node.FS.FS(original)
+        deps = s.scan(make_node('fff1.f', fs), env, DummyTarget())
+        headers = ['d1/f1.f', 'd1/f2.f']
+        deps_match(self, deps, map(test.workpath, headers))
+        test.write(['d1', 'f2.f'], "\n")
+
+class FortranScannerTestCase5(unittest.TestCase):
+    def runTest(self):
+        env = DummyEnvironment([test.workpath("d1")])
+        s = SCons.Scanner.Fortran.FortranScan()
+       fs = SCons.Node.FS.FS(original)
+        deps = s.scan(make_node('fff2.f', fs), env, DummyTarget())
+        headers = ['d1/d2/f2.f', 'd1/f2.f', 'd1/f2.f']
+        deps_match(self, deps, map(test.workpath, headers))
+
+class FortranScannerTestCase6(unittest.TestCase):
+    def runTest(self):
+        test.write('f2.f', "\n")
+        env = DummyEnvironment([test.workpath("d1")])
+        s = SCons.Scanner.Fortran.FortranScan()
+       fs = SCons.Node.FS.FS(original)
+        deps = s.scan(make_node('fff2.f', fs), env, DummyTarget())
+        headers =  ['d1/d2/f2.f', 'd1/f2.f', 'f2.f']
+        deps_match(self, deps, map(test.workpath, 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()
+       fs = SCons.Node.FS.FS(original)
+        deps = s.scan(make_node('fff2.f', fs), env, DummyTarget())
+        headers =  ['d1/d2/f2.f', 'd1/d2/f2.f', 'd1/f2.f']
+        deps_match(self, deps, map(test.workpath, headers))
+
+class FortranScannerTestCase8(unittest.TestCase):
+    def runTest(self):
+        test.write('f2.f', "\n")
+        env = DummyEnvironment([test.workpath("d1/d2"), test.workpath("d1")])
+        s = SCons.Scanner.Fortran.FortranScan()
+       fs = SCons.Node.FS.FS(original)
+        deps = s.scan(make_node('fff2.f', fs), env, DummyTarget())
+        headers =  ['d1/d2/f2.f', 'd1/f2.f', 'f2.f']
+        deps_match(self, deps, map(test.workpath, headers))
+        test.unlink('f2.f')
+        
+class FortranScannerTestCase9(unittest.TestCase):
+    def runTest(self):
+        test.write('f3.f', "\n")
+        env = DummyEnvironment([])
+        s = SCons.Scanner.Fortran.FortranScan()
+        deps = s.scan(make_node('fff3.f'), env, DummyTarget())
+        
+        # Make sure exists() gets called on the file node being
+        # scanned, essential for cooperation with BuildDir functionality.
+        assert SCons.Node.FS.default_fs.File(test.workpath('fff3.f')).created
+        
+        headers =  ['d1/f3.f', 'f3.f']
+        deps_match(self, deps, map(test.workpath, headers))
+        test.unlink('f3.f')
+
+class FortranScannerTestCase10(unittest.TestCase):
+    def runTest(self):
+        fs = SCons.Node.FS.FS(test.workpath(''))
+        env = DummyEnvironment(["include"])
+        s = SCons.Scanner.Fortran.FortranScan(fs = fs)
+        deps1 = s.scan(fs.File('fff4.f'), env, DummyTarget())
+        fs.chdir(fs.Dir('subdir'))
+        target = DummyTarget(fs.getcwd())
+        fs.chdir(fs.Dir('..'))
+        deps2 = s.scan(fs.File('#fff4.f'), env, target)
+        headers1 =  ['include/f4.f']
+        headers2 =  ['subdir/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:
+            def __call__(self, x):
+                self.out = x
+
+        to = TestOut()
+        to.out = None
+        SCons.Warnings._warningOut = to
+        test.write('f4.f',"      INCLUDE 'not_there.f'\n")
+        fs = SCons.Node.FS.FS(test.workpath(''))
+        s = SCons.Scanner.Fortran.FortranScan(fs=fs)
+        env = DummyEnvironment([])
+        deps = s.scan(fs.File('fff4.f'), env, DummyTarget())
+
+        # Did we catch the warning from not finding not_there.f?
+        assert to.out
+        
+        deps_match(self, deps, [ 'f4.f' ])
+        test.unlink('f4.f')
+
+class FortranScannerTestCase12(unittest.TestCase):
+    def runTest(self):
+        fs = SCons.Node.FS.FS(test.workpath(''))
+        fs.chdir(fs.Dir('include'))
+        s = SCons.Scanner.Fortran.FortranScan(fs=fs)
+        env = DummyEnvironment([])
+        test.write('include/fff4.f', test.read('fff4.f'))
+        deps = s.scan(fs.File('#include/fff4.f'), env, DummyTarget())
+        deps_match(self, deps, ['include/f4.f'])
+        test.unlink('include/fff4.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())
+    return suite
+
+if __name__ == "__main__":
+    runner = unittest.TextTestRunner()
+    result = runner.run(suite())
+    if not result.wasSuccessful():
+        sys.exit(1)
index c547ef90232b06f94eddc35c2c1144e369c60672..aae3a78f79d091d679207d89bf3a0d6672a09041 100644 (file)
@@ -47,6 +47,17 @@ foo42 = test.workpath('build', 'var4', 'foo2' + _exe)
 foo51 = test.workpath('build', 'var5', 'foo1' + _exe)
 foo52 = test.workpath('build', 'var5', 'foo2' + _exe)
 
+bar11 = test.workpath('build', 'var1', 'bar1' + _exe)
+bar12 = test.workpath('build', 'var1', 'bar2' + _exe)
+bar21 = test.workpath('build', 'var2', 'bar1' + _exe)
+bar22 = test.workpath('build', 'var2', 'bar2' + _exe)
+bar31 = test.workpath('build', 'var3', 'bar1' + _exe)
+bar32 = test.workpath('build', 'var3', 'bar2' + _exe)
+bar41 = test.workpath('build', 'var4', 'bar1' + _exe)
+bar42 = test.workpath('build', 'var4', 'bar2' + _exe)
+bar51 = test.workpath('build', 'var5', 'bar1' + _exe)
+bar52 = test.workpath('build', 'var5', 'bar2' + _exe)
+
 test.write('SConstruct', """
 src = Dir('src')
 var2 = Dir('build/var2')
@@ -61,20 +72,20 @@ BuildDir(var3, src, duplicate=0)
 BuildDir(var4, src, duplicate=0)
 BuildDir(var5, src, duplicate=0)
 
-env = Environment(CPPPATH='#src')
+env = Environment(CPPPATH='#src', F77PATH='#src')
 SConscript('build/var1/SConscript', "env")
 SConscript('build/var2/SConscript', "env")
 
-env = Environment(CPPPATH=src)
+env = Environment(CPPPATH=src, F77PATH=src)
 SConscript('build/var3/SConscript', "env")
 SConscript(File('SConscript', var4), "env")
 
-env = Environment(CPPPATH='.')
+env = Environment(CPPPATH='.', F77PATH='.')
 SConscript('build/var5/SConscript', "env")
 """) 
 
 test.subdir('src')
-test.write('src/SConscript', """
+test.write(['src', 'SConscript'], """
 import os
 import os.path
 
@@ -91,6 +102,10 @@ Import("env")
 env.Command(target='f2.c', source='f2.in', action=buildIt)
 env.Program(target='foo2', source='f2.c')
 env.Program(target='foo1', source='f1.c')
+
+env.Command(target='b2.f', source='b2.in', action=buildIt)
+env.Copy(LIBS = 'g2c').Program(target='bar2', source='b2.f')
+env.Copy(LIBS = 'g2c').Program(target='bar1', source='b1.f')
 """)
 
 test.write('src/f1.c', r"""
@@ -125,6 +140,28 @@ test.write('src/f2.h', r"""
 #define F2_STR "f2.c\n"
 """)
 
+test.write(['src', 'b1.f'], r"""
+      PROGRAM FOO
+      INCLUDE 'b1.for'
+      STOP
+      END
+""")
+
+test.write(['src', 'b2.in'], r"""
+      PROGRAM FOO
+      INCLUDE 'b2.for'
+      STOP
+      END
+""")
+
+test.write(['src', 'b1.for'], r"""
+      PRINT *, 'b1.for'
+""")
+
+test.write(['src', 'b2.for'], r"""
+      PRINT *, 'b2.for'
+""")
+
 test.run(arguments = '.')
 
 test.run(program = foo11, stdout = "f1.c\n")
@@ -138,17 +175,34 @@ test.run(program = foo42, stdout = "f2.c\n")
 test.run(program = foo51, stdout = "f1.c\n")
 test.run(program = foo52, stdout = "f2.c\n")
 
+test.run(program = bar11, stdout = " b1.for\n")
+test.run(program = bar12, stdout = " b2.for\n")
+test.run(program = bar21, stdout = " b1.for\n")
+test.run(program = bar22, stdout = " b2.for\n")
+test.run(program = bar31, stdout = " b1.for\n")
+test.run(program = bar32, stdout = " b2.for\n")
+test.run(program = bar41, stdout = " b1.for\n")
+test.run(program = bar42, stdout = " b2.for\n")
+test.run(program = bar51, stdout = " b1.for\n")
+test.run(program = bar52, stdout = " b2.for\n")
+
 # Make sure we didn't duplicate the source files in build/var3.
 test.fail_test(os.path.exists(test.workpath('build', 'var3', 'f1.c')))
 test.fail_test(os.path.exists(test.workpath('build', 'var3', 'f2.in')))
+test.fail_test(os.path.exists(test.workpath('build', 'var3', 'b1.f')))
+test.fail_test(os.path.exists(test.workpath('build', 'var3', 'b2.in')))
 
 # Make sure we didn't duplicate the source files in build/var4.
 test.fail_test(os.path.exists(test.workpath('build', 'var4', 'f1.c')))
 test.fail_test(os.path.exists(test.workpath('build', 'var4', 'f2.in')))
+test.fail_test(os.path.exists(test.workpath('build', 'var4', 'b1.f')))
+test.fail_test(os.path.exists(test.workpath('build', 'var4', 'b2.in')))
 
 # Make sure we didn't duplicate the source files in build/var5.
 test.fail_test(os.path.exists(test.workpath('build', 'var5', 'f1.c')))
 test.fail_test(os.path.exists(test.workpath('build', 'var5', 'f2.in')))
+test.fail_test(os.path.exists(test.workpath('build', 'var5', 'b1.f')))
+test.fail_test(os.path.exists(test.workpath('build', 'var5', 'b2.in')))
 
 
 test.pass_test()
diff --git a/test/F77PATH.py b/test/F77PATH.py
new file mode 100644 (file)
index 0000000..e8b540f
--- /dev/null
@@ -0,0 +1,200 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001, 2002 Steven Knight
+#
+# 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 sys
+import TestSCons
+
+if sys.platform == 'win32':
+    _exe = '.exe'
+else:
+    _exe = ''
+
+prog = 'prog' + _exe
+subdir_prog = os.path.join('subdir', 'prog' + _exe)
+variant_prog = os.path.join('variant', 'prog' + _exe)
+
+args = prog + ' ' + subdir_prog + ' ' + variant_prog
+
+test = TestSCons.TestSCons()
+
+test.subdir('include', 'subdir', ['subdir', 'include'], 'inc2')
+
+test.write('SConstruct', """
+env = Environment(F77PATH = ['include'], LIBS = 'g2c')
+obj = env.Object(target='foobar/prog', source='subdir/prog.f')
+env.Program(target='prog', source=obj)
+SConscript('subdir/SConscript', "env")
+
+BuildDir('variant', 'subdir', 0)
+include = Dir('include')
+env = Environment(F77PATH=[include], LIBS = 'g2c')
+SConscript('variant/SConscript', "env")
+""")
+
+test.write(['subdir', 'SConscript'],
+"""
+Import("env")
+env.Program(target='prog', source='prog.f')
+""")
+
+test.write(['include', 'foo.f'],
+r"""
+      PRINT *, 'include/foo.f 1'
+      INCLUDE 'bar.f'
+""")
+
+test.write(['include', 'bar.f'],
+r"""
+      PRINT *, 'include/bar.f 1'
+""")
+
+test.write(['subdir', 'prog.f'],
+r"""
+      PROGRAM PROG
+      PRINT *, 'subdir/prog.f'
+      INCLUDE 'foo.f'
+      STOP
+      END
+""")
+
+test.write(['subdir', 'include', 'foo.f'],
+r"""
+      PRINT *, 'subdir/include/foo.f 1'
+      INCLUDE 'bar.f'
+""")
+
+test.write(['subdir', 'include', 'bar.f'],
+r"""
+      PRINT *, 'subdir/include/bar.f 1'
+""")
+
+
+
+test.run(arguments = args)
+
+test.run(program = test.workpath(prog),
+         stdout = " subdir/prog.f\n include/foo.f 1\n include/bar.f 1\n")
+
+test.run(program = test.workpath(subdir_prog),
+         stdout = " subdir/prog.f\n subdir/include/foo.f 1\n subdir/include/bar.f 1\n")
+
+test.run(program = test.workpath(variant_prog),
+         stdout = " subdir/prog.f\n include/foo.f 1\n include/bar.f 1\n")
+
+# Make sure we didn't duplicate the source file in the variant subdirectory.
+test.fail_test(os.path.exists(test.workpath('variant', 'prog.f')))
+
+test.up_to_date(arguments = args)
+
+test.write(['include', 'foo.f'],
+r"""
+      PRINT *, 'include/foo.f 2'
+      INCLUDE 'bar.f'
+""")
+
+test.run(arguments = args)
+
+test.run(program = test.workpath(prog),
+         stdout = " subdir/prog.f\n include/foo.f 2\n include/bar.f 1\n")
+
+test.run(program = test.workpath(subdir_prog),
+         stdout = " subdir/prog.f\n subdir/include/foo.f 1\n subdir/include/bar.f 1\n")
+
+test.run(program = test.workpath(variant_prog),
+         stdout = " subdir/prog.f\n include/foo.f 2\n include/bar.f 1\n")
+
+# Make sure we didn't duplicate the source file in the variant subdirectory.
+test.fail_test(os.path.exists(test.workpath('variant', 'prog.f')))
+
+test.up_to_date(arguments = args)
+
+#
+test.write(['include', 'bar.f'],
+r"""
+      PRINT *, 'include/bar.f 2'
+""")
+
+test.run(arguments = args)
+
+test.run(program = test.workpath(prog),
+         stdout = " subdir/prog.f\n include/foo.f 2\n include/bar.f 2\n")
+
+test.run(program = test.workpath(subdir_prog),
+         stdout = " subdir/prog.f\n subdir/include/foo.f 1\n subdir/include/bar.f 1\n")
+
+test.run(program = test.workpath(variant_prog),
+         stdout = " subdir/prog.f\n include/foo.f 2\n include/bar.f 2\n")
+
+# Make sure we didn't duplicate the source file in the variant subdirectory.
+test.fail_test(os.path.exists(test.workpath('variant', 'prog.f')))
+
+test.up_to_date(arguments = args)
+
+# Change F77PATH and make sure we don't rebuild because of it.
+test.write('SConstruct', """
+env = Environment(F77PATH = Split('inc2 include'), LIBS = 'g2c')
+obj = env.Object(target='foobar/prog', source='subdir/prog.f')
+env.Program(target='prog', source=obj)
+SConscript('subdir/SConscript', "env")
+
+BuildDir('variant', 'subdir', 0)
+include = Dir('include')
+env = Environment(F77PATH=['inc2', include], LIBS = 'g2c')
+SConscript('variant/SConscript', "env")
+""")
+
+test.up_to_date(arguments = args)
+
+#
+test.write(['inc2', 'foo.f'],
+r"""
+      PRINT *, 'inc2/foo.f 1'
+      INCLUDE 'bar.f'
+""")
+
+test.run(arguments = args)
+
+test.run(program = test.workpath(prog),
+         stdout = " subdir/prog.f\n inc2/foo.f 1\n include/bar.f 2\n")
+
+test.run(program = test.workpath(subdir_prog),
+         stdout = " subdir/prog.f\n subdir/include/foo.f 1\n subdir/include/bar.f 1\n")
+
+test.run(program = test.workpath(variant_prog),
+         stdout = " subdir/prog.f\n include/foo.f 2\n include/bar.f 2\n")
+
+test.up_to_date(arguments = args)
+
+# Check that a null-string F77PATH doesn't blow up.
+test.write('SConstruct', """
+env = Environment(F77PATH = '', LIBS = 'g2c')
+env.Library('foo', source = '')
+""")
+
+test.run(arguments = '.')
+
+test.pass_test()