Add Fortran support.
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Thu, 28 Mar 2002 06:20:14 +0000 (06:20 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Thu, 28 Mar 2002 06:20:14 +0000 (06:20 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@311 fdb21ef1-2011-0410-befe-b5e4ea1792b1

doc/man/scons.1
src/CHANGES.txt
src/engine/SCons/Defaults.py
src/engine/SCons/Scanner/C.py
test/F77.py [new file with mode: 0644]
test/F77FLAGS.py [new file with mode: 0644]

index 51d7c91fbe6d42846ba534cb38119c931ae564b0..527b45d4174720d985ca230e198374722556b823 100644 (file)
@@ -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.
index 5986cf0e15b435c891989bf4eb0b200aeb7954d6..b3dc356234cef77251dca1b719857592cfed0f30 100644 (file)
@@ -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.
index e0763b216447829dc08de46a5a999f3499ad6733..d14bbc745efa17060b7ebae11ddcbde4f8648894 100644 (file)
@@ -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',
index 8d4497c70a5cfd7e0be3307fa52045466d775566..0a2654eecc327fb871e472279ae9fa4805444c20 100644 (file)
@@ -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 (file)
index 0000000..40a749e
--- /dev/null
@@ -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 (file)
index 0000000..b3e7b31
--- /dev/null
@@ -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()