summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/man/scons.145
-rw-r--r--src/CHANGES.txt3
-rw-r--r--src/engine/SCons/Defaults.py18
-rw-r--r--src/engine/SCons/Scanner/C.py6
-rw-r--r--test/F77.py205
-rw-r--r--test/F77FLAGS.py208
6 files changed, 474 insertions, 11 deletions
diff --git a/doc/man/scons.1 b/doc/man/scons.1
index 51d7c91..527b45d 100644
--- a/doc/man/scons.1
+++ b/doc/man/scons.1
@@ -596,13 +596,16 @@ env.Program('bar', ['bar.c', 'foo.c'])
provides the following builders:
.IP Object
-Builds an object file from one or more C/C++ source files. Source files
-must have one of the following extensions: .c, .C, .cc, .cpp, .cxx, .c++, .C++.
+Builds an object file from one or more C, C++, or Fortran source files.
+Source files must have one of the following extensions:
+.c, .C, .cc, .cpp, .cxx, .c++, .C++, .f, .F, .for, .FOR, .fpp, .FPP.
The target object file prefix and suffix (if any) are automatically
-added. Example:
+added. Examples:
.ES
-env.Object(target = 'bar', source = 'bar.c')
+env.Object(target = 'aaa', source = 'aaa.c')
+env.Object(target = 'bbb.o', source = 'bbb.c++')
+env.Object(target = 'ccc.obj', source = 'ccc.f')
.EE
.IP Program
@@ -612,7 +615,7 @@ compiled to object files. The executable prefix and suffix (if any) are
automatically added to the target. Example:
.ES
-env.Program(target = 'bar', source = 'bar.c foo.o')
+env.Program(target = 'foo', source = 'foo.o bar.c baz.f')
.EE
.IP Library
@@ -688,10 +691,13 @@ env.PostScript(target = 'aaa.ps', source = 'aaa.tex') # builds from aaa.tex
env.PostScript(target = 'bbb', source = 'bbb.dvi') # builds bbb.dvi
.EE
.LP
-C/C++ source files are automatically scanned for dependencies by
-.B scons
-so the dependencies do not need to be provided. In addition, all builder
-targets automatically depend on their sources. An explicit dependency can
+.B scons automatically scans
+C, C++ and Fortran source files with .F, .fpp, or .FOR file extensions
+for C preprocessor dependencies,
+so the dependencies do not need to be specified explicitly.
+In addition, all builder
+targets automatically depend on their sources.
+An explicit dependency can
be specified using the
.B Depends
method of a construction environment (see below).
@@ -862,6 +868,12 @@ when generating C files from Lex (.l) or YACC (.y) input files.
The default suffix, of course, is
.IR .c .
+.IP CPPFLAGS
+C preprocessor options.
+These will be included in the $F77PPCOM
+command line used to compile a Fortran source file to an object file
+after first running the file through the C preprocessor.
+
.IP CPPPATH
The list of directories that the C preprocessor will search for include
directories. The C/C++ implicit dependency scanner will search these
@@ -961,6 +973,21 @@ import os
env = Environment(ENV = {'PATH' : os.environ['PATH']})
.EE
+.IP F77
+The Fortran compiler.
+
+.IP F77FLAGS
+General options that are passed to the Fortran compiler.
+
+.IP F77COM
+The command line used to compile a Fortran source file to an object file.
+
+.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.
+Any options specified in the $CPPFLAGS construction variable
+are included on this command line.
+
.IP INCPREFIX
The prefix used to specify an include directory on the C compiler command
line.
diff --git a/src/CHANGES.txt b/src/CHANGES.txt
index 5986cf0..b3dc356 100644
--- a/src/CHANGES.txt
+++ b/src/CHANGES.txt
@@ -61,6 +61,9 @@ RELEASE 0.06 -
- Add PDF and PostScript document builders.
+ - Add support for compiling Fortran programs from a variety of
+ suffixes (a la GNU Make): .f, .F, .for, .FOR, .fpp and .FPP
+
From Steve Leblanc:
- Add support for the -U option.
diff --git a/src/engine/SCons/Defaults.py b/src/engine/SCons/Defaults.py
index e0763b2..d14bbc7 100644
--- a/src/engine/SCons/Defaults.py
+++ b/src/engine/SCons/Defaults.py
@@ -66,6 +66,10 @@ CXXFile = SCons.Builder.Builder(name = 'CXXFile',
CPlusPlusAction = SCons.Action.Action('$CXXCOM')
+FortranAction = SCons.Action.Action('$F77COM')
+
+FortranPPAction = SCons.Action.Action('$F77PPCOM')
+
Object = SCons.Builder.Builder(name = 'Object',
action = { '.c' : '$CCCOM',
'.C' : CPlusPlusAction,
@@ -74,6 +78,12 @@ Object = SCons.Builder.Builder(name = 'Object',
'.cxx' : CPlusPlusAction,
'.c++' : CPlusPlusAction,
'.C++' : CPlusPlusAction,
+ '.f' : FortranAction,
+ '.for' : FortranAction,
+ '.FOR' : FortranAction,
+ '.F' : FortranPPAction,
+ '.fpp' : FortranPPAction,
+ '.FPP' : FortranPPAction,
},
prefix = '$OBJPREFIX',
suffix = '$OBJSUFFIX',
@@ -239,6 +249,10 @@ def make_win32_env_from_paths(include, lib, path):
'CXXFLAGS' : '$CCFLAGS',
'CXXCOM' : '$CXX $CXXFLAGS $_INCFLAGS /c $SOURCES /Fo$TARGET',
'CXXFILESUFFIX' : '.cc',
+ 'F77' : 'g77',
+ 'F77FLAGS' : '',
+ 'F77COM' : '$F77 $F77FLAGS $_INCFLAGS -c -o $TARGET $SOURCES',
+ 'F77PPCOM' : '$F77 $F77FLAGS $CPPFLAGS $_INCFLAGS -c -o $TARGET $SOURCES',
'LINK' : 'link',
'LINKFLAGS' : '/nologo',
'LINKCOM' : '$LINK $LINKFLAGS /OUT:$TARGET $_LIBDIRFLAGS $_LIBFLAGS $SOURCES',
@@ -317,6 +331,10 @@ if os.name == 'posix':
'CXXFLAGS' : '$CCFLAGS',
'CXXCOM' : '$CXX $CXXFLAGS $_INCFLAGS -c -o $TARGET $SOURCES',
'CXXFILESUFFIX' : '.cc',
+ 'F77' : 'g77',
+ 'F77FLAGS' : '',
+ 'F77COM' : '$F77 $F77FLAGS $_INCFLAGS -c -o $TARGET $SOURCES',
+ 'F77PPCOM' : '$F77 $F77FLAGS $CPPFLAGS $_INCFLAGS -c -o $TARGET $SOURCES',
'LINK' : '$CXX',
'LINKFLAGS' : '',
'LINKCOM' : '$LINK $LINKFLAGS -o $TARGET $SOURCES $_LIBDIRFLAGS $_LIBFLAGS',
diff --git a/src/engine/SCons/Scanner/C.py b/src/engine/SCons/Scanner/C.py
index 8d4497c..0a2654e 100644
--- a/src/engine/SCons/Scanner/C.py
+++ b/src/engine/SCons/Scanner/C.py
@@ -44,10 +44,12 @@ include_re = re.compile('^[ \t]*#[ \t]*include[ \t]+(<|")([\\w./\\\\]+)(>|")', r
include_cache = {}
def CScan(fs = SCons.Node.FS.default_fs):
- "Return a prototype Scanner instance for scanning C/C++ source files"
+ """Return a prototype Scanner instance for scanning source files
+ that use the C pre-processor"""
cs = CScanner(scan, "CScan", [fs, ()],
[".c", ".C", ".cxx", ".cpp", ".c++", ".cc",
- ".h", ".H", ".hxx", ".hpp", ".hh"])
+ ".h", ".H", ".hxx", ".hpp", ".hh",
+ ".F", ".fpp", ".FPP"])
cs.fs = fs
return cs
diff --git a/test/F77.py b/test/F77.py
new file mode 100644
index 0000000..40a749e
--- /dev/null
+++ b/test/F77.py
@@ -0,0 +1,205 @@
+#!/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 string
+import sys
+import TestSCons
+
+python = sys.executable
+
+if sys.platform == 'win32':
+ _exe = '.exe'
+else:
+ _exe = ''
+
+test = TestSCons.TestSCons()
+
+
+
+if sys.platform == 'win32':
+
+ test.write('mylink.py', r"""
+import getopt
+import os
+import sys
+args = sys.argv[1:]
+while args:
+ a = args[0]
+ if a[0] != '/':
+ break
+ args.pop(0)
+ if a[:5] == '/OUT:': out = a[5:]
+infile = open(args[0], 'rb')
+outfile = open(out, 'wb')
+for l in infile.readlines():
+ if l[:5] != '#link':
+ outfile.write(l)
+sys.exit(0)
+""")
+
+else:
+
+ test.write('mylink.py', r"""
+import getopt
+import os
+import sys
+opts, args = getopt.getopt(sys.argv[1:], 'o:')
+for opt, arg in opts:
+ if opt == '-o': out = arg
+infile = open(args[0], 'rb')
+outfile = open(out, 'wb')
+for l in infile.readlines():
+ if l[:5] != '#link':
+ outfile.write(l)
+sys.exit(0)
+""")
+
+test.write('myg77.py', r"""
+import getopt
+import os
+import sys
+opts, args = getopt.getopt(sys.argv[1:], 'co:')
+for opt, arg in opts:
+ if opt == '-o': out = arg
+infile = open(args[0], 'rb')
+outfile = open(out, 'wb')
+for l in infile.readlines():
+ if l[:4] != '#g77':
+ outfile.write(l)
+sys.exit(0)
+""")
+
+test.write('SConstruct', """
+env = Environment(LINK = r'%s mylink.py',
+ F77 = r'%s myg77.py')
+env.Program(target = 'test1', source = 'test1.f')
+env.Program(target = 'test2', source = 'test2.for')
+env.Program(target = 'test3', source = 'test3.FOR')
+env.Program(target = 'test4', source = 'test4.F')
+env.Program(target = 'test5', source = 'test5.fpp')
+env.Program(target = 'test6', source = 'test6.FPP')
+""" % (python, python))
+
+test.write('test1.f', r"""This is a .f file.
+#g77
+#link
+""")
+
+test.write('test2.for', r"""This is a .for file.
+#g77
+#link
+""")
+
+test.write('test3.FOR', r"""This is a .FOR file.
+#g77
+#link
+""")
+
+test.write('test4.F', r"""This is a .F file.
+#g77
+#link
+""")
+
+test.write('test5.fpp', r"""This is a .fpp file.
+#g77
+#link
+""")
+
+test.write('test6.FPP', r"""This is a .FPP file.
+#g77
+#link
+""")
+
+test.run(arguments = '.', stderr = None)
+
+test.fail_test(test.read('test1' + _exe) != "This is a .f file.\n")
+
+test.fail_test(test.read('test2' + _exe) != "This is a .for file.\n")
+
+test.fail_test(test.read('test3' + _exe) != "This is a .FOR file.\n")
+
+test.fail_test(test.read('test4' + _exe) != "This is a .F file.\n")
+
+test.fail_test(test.read('test5' + _exe) != "This is a .fpp file.\n")
+
+test.fail_test(test.read('test6' + _exe) != "This is a .FPP file.\n")
+
+
+
+g77 = None
+for dir in string.split(os.environ['PATH'], os.pathsep):
+ g = os.path.join(dir, 'g77' + _exe)
+ if os.path.exists(g):
+ g77 = g
+ break
+
+if g77:
+
+ test.write("wrapper.py",
+"""import os
+import string
+import sys
+open('%s', 'wb').write("wrapper.py\\n")
+os.system(string.join(sys.argv[1:], " "))
+""" % string.replace(test.workpath('wrapper.out'), '\\', '\\\\'))
+
+ test.write('SConstruct', """
+foo = Environment(LIBS = 'g2c')
+f77 = foo.Dictionary('F77')
+bar = foo.Copy(F77 = r'%s wrapper.py ' + f77)
+foo.Program(target = 'foo', source = 'foo.f')
+bar.Program(target = 'bar', source = 'bar.f')
+""" % python)
+
+ test.write('foo.f', r"""
+ PROGRAM FOO
+ PRINT *,'foo.f'
+ STOP
+ END
+""")
+
+ test.write('bar.f', r"""
+ PROGRAM BAR
+ PRINT *,'bar.f'
+ STOP
+ END
+""")
+
+
+ test.run(arguments = 'foo' + _exe, stderr = None)
+
+ test.run(program = test.workpath('foo'), stdout = " foo.f\n")
+
+ test.fail_test(os.path.exists(test.workpath('wrapper.out')))
+
+ test.run(arguments = 'bar' + _exe)
+
+ test.run(program = test.workpath('bar'), stdout = " bar.f\n")
+
+ test.fail_test(test.read('wrapper.out') != "wrapper.py\n")
+
+test.pass_test()
diff --git a/test/F77FLAGS.py b/test/F77FLAGS.py
new file mode 100644
index 0000000..b3e7b31
--- /dev/null
+++ b/test/F77FLAGS.py
@@ -0,0 +1,208 @@
+#!/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 string
+import sys
+import TestSCons
+
+python = sys.executable
+
+if sys.platform == 'win32':
+ _exe = '.exe'
+else:
+ _exe = ''
+
+test = TestSCons.TestSCons()
+
+
+
+if sys.platform == 'win32':
+
+ test.write('mylink.py', r"""
+import getopt
+import os
+import sys
+args = sys.argv[1:]
+while args:
+ a = args[0]
+ if a[0] != '/':
+ break
+ args.pop(0)
+ if a[:5] == '/OUT:': out = a[5:]
+infile = open(args[0], 'rb')
+outfile = open(out, 'wb')
+for l in infile.readlines():
+ if l[:5] != '#link':
+ outfile.write(l)
+sys.exit(0)
+""")
+
+else:
+
+ test.write('mylink.py', r"""
+import getopt
+import os
+import sys
+opts, args = getopt.getopt(sys.argv[1:], 'o:')
+for opt, arg in opts:
+ if opt == '-o': out = arg
+infile = open(args[0], 'rb')
+outfile = open(out, 'wb')
+for l in infile.readlines():
+ if l[:5] != '#link':
+ outfile.write(l)
+sys.exit(0)
+""")
+
+test.write('myg77.py', r"""
+import getopt
+import os
+import sys
+opts, args = getopt.getopt(sys.argv[1:], 'co:x')
+optstring = ''
+for opt, arg in opts:
+ if opt == '-o': out = arg
+ else: optstring = optstring + ' ' + opt
+infile = open(args[0], 'rb')
+outfile = open(out, 'wb')
+outfile.write(optstring + "\n")
+for l in infile.readlines():
+ if l[:4] != '#g77':
+ outfile.write(l)
+sys.exit(0)
+""")
+
+test.write('SConstruct', """
+env = Environment(LINK = r'%s mylink.py',
+ F77 = r'%s myg77.py', F77FLAGS = '-x')
+env.Program(target = 'test1', source = 'test1.f')
+env.Program(target = 'test2', source = 'test2.for')
+env.Program(target = 'test3', source = 'test3.FOR')
+env.Program(target = 'test4', source = 'test4.F')
+env.Program(target = 'test5', source = 'test5.fpp')
+env.Program(target = 'test6', source = 'test6.FPP')
+""" % (python, python))
+
+test.write('test1.f', r"""This is a .f file.
+#g77
+#link
+""")
+
+test.write('test2.for', r"""This is a .for file.
+#g77
+#link
+""")
+
+test.write('test3.FOR', r"""This is a .FOR file.
+#g77
+#link
+""")
+
+test.write('test4.F', r"""This is a .F file.
+#g77
+#link
+""")
+
+test.write('test5.fpp', r"""This is a .fpp file.
+#g77
+#link
+""")
+
+test.write('test6.FPP', r"""This is a .FPP file.
+#g77
+#link
+""")
+
+test.run(arguments = '.', stderr = None)
+
+test.fail_test(test.read('test1' + _exe) != " -x -c\nThis is a .f file.\n")
+
+test.fail_test(test.read('test2' + _exe) != " -x -c\nThis is a .for file.\n")
+
+test.fail_test(test.read('test3' + _exe) != " -x -c\nThis is a .FOR file.\n")
+
+test.fail_test(test.read('test4' + _exe) != " -x -c\nThis is a .F file.\n")
+
+test.fail_test(test.read('test5' + _exe) != " -x -c\nThis is a .fpp file.\n")
+
+test.fail_test(test.read('test6' + _exe) != " -x -c\nThis is a .FPP file.\n")
+
+
+
+g77 = None
+for dir in string.split(os.environ['PATH'], os.pathsep):
+ g = os.path.join(dir, 'g77' + _exe)
+ if os.path.exists(g):
+ g77 = g
+ break
+
+if g77:
+
+ test.write("wrapper.py",
+"""import os
+import string
+import sys
+open('%s', 'wb').write("wrapper.py\\n")
+os.system(string.join(sys.argv[1:], " "))
+""" % string.replace(test.workpath('wrapper.out'), '\\', '\\\\'))
+
+ test.write('SConstruct', """
+foo = Environment(LIBS = 'g2c')
+f77 = foo.Dictionary('F77')
+bar = foo.Copy(F77 = r'%s wrapper.py ' + f77, F77FLAGS = '-Ix')
+foo.Program(target = 'foo', source = 'foo.f')
+bar.Program(target = 'bar', source = 'bar.f')
+""" % python)
+
+ test.write('foo.f', r"""
+ PROGRAM FOO
+ PRINT *,'foo.f'
+ STOP
+ END
+""")
+
+ test.write('bar.f', r"""
+ PROGRAM BAR
+ PRINT *,'bar.f'
+ STOP
+ END
+""")
+
+
+ test.run(arguments = 'foo' + _exe, stderr = None)
+
+ test.run(program = test.workpath('foo'), stdout = " foo.f\n")
+
+ test.fail_test(os.path.exists(test.workpath('wrapper.out')))
+
+ test.run(arguments = 'bar' + _exe)
+
+ test.run(program = test.workpath('bar'), stdout = " bar.f\n")
+
+ test.fail_test(test.read('wrapper.out') != "wrapper.py\n")
+
+test.pass_test()