summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteven Knight <knight@baldmt.com>2002-06-12 21:16:35 (GMT)
committerSteven Knight <knight@baldmt.com>2002-06-12 21:16:35 (GMT)
commit8facc7d3e757ab88aacd8d430fd7319d13d8a558 (patch)
tree9036a7ce043bb3d4508ab9dab00f214c0c6376e6
parentd1b81f58263a824ca39ecc179f61ad541c5a0f7a (diff)
downloadSCons-8facc7d3e757ab88aacd8d430fd7319d13d8a558.zip
SCons-8facc7d3e757ab88aacd8d430fd7319d13d8a558.tar.gz
SCons-8facc7d3e757ab88aacd8d430fd7319d13d8a558.tar.bz2
Add a native Fortran include scanner.
-rw-r--r--doc/man/scons.125
-rw-r--r--rpm/scons.spec2
-rw-r--r--src/CHANGES.txt3
-rw-r--r--src/engine/MANIFEST.in1
-rw-r--r--src/engine/SCons/Defaults.py43
-rw-r--r--src/engine/SCons/Environment.py27
-rw-r--r--src/engine/SCons/EnvironmentTests.py67
-rw-r--r--src/engine/SCons/Scanner/Fortran.py128
-rw-r--r--src/engine/SCons/Scanner/FortranTests.py301
-rw-r--r--test/BuildDir.py62
-rw-r--r--test/F77PATH.py200
11 files changed, 804 insertions, 55 deletions
diff --git a/doc/man/scons.1 b/doc/man/scons.1
index 5570ec1..6252267 100644
--- a/doc/man/scons.1
+++ b/doc/man/scons.1
@@ -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.
diff --git a/rpm/scons.spec b/rpm/scons.spec
index 326b157..0bfa0e0 100644
--- a/rpm/scons.spec
+++ b/rpm/scons.spec
@@ -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
diff --git a/src/CHANGES.txt b/src/CHANGES.txt
index 9d76afe..82627e3 100644
--- a/src/CHANGES.txt
+++ b/src/CHANGES.txt
@@ -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.
diff --git a/src/engine/MANIFEST.in b/src/engine/MANIFEST.in
index 1c9ac91..dce5291 100644
--- a/src/engine/MANIFEST.in
+++ b/src/engine/MANIFEST.in
@@ -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
diff --git a/src/engine/SCons/Defaults.py b/src/engine/SCons/Defaults.py
index e8bf376..aa8a643 100644
--- a/src/engine/SCons/Defaults.py
+++ b/src/engine/SCons/Defaults.py
@@ -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',
diff --git a/src/engine/SCons/Environment.py b/src/engine/SCons/Environment.py
index 787a66d..a95adf9 100644
--- a/src/engine/SCons/Environment.py
+++ b/src/engine/SCons/Environment.py
@@ -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:
diff --git a/src/engine/SCons/EnvironmentTests.py b/src/engine/SCons/EnvironmentTests.py
index 25712b2..167c662 100644
--- a/src/engine/SCons/EnvironmentTests.py
+++ b/src/engine/SCons/EnvironmentTests.py
@@ -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
index 0000000..17c9241
--- /dev/null
+++ b/src/engine/SCons/Scanner/Fortran.py
@@ -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
index 0000000..2883daf
--- /dev/null
+++ b/src/engine/SCons/Scanner/FortranTests.py
@@ -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)
diff --git a/test/BuildDir.py b/test/BuildDir.py
index c547ef9..aae3a78 100644
--- a/test/BuildDir.py
+++ b/test/BuildDir.py
@@ -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
index 0000000..e8b540f
--- /dev/null
+++ b/test/F77PATH.py
@@ -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()