Index: scons-2.0.1/engine/SCons/Scanner/Fortran.py |
=================================================================== |
--- scons-2.0.1/engine/SCons/Scanner/Fortran.py (revision 0) |
+++ scons-2.0.1/engine/SCons/Scanner/Fortran.py (revision 0) |
@@ -0,0 +1,316 @@ |
+"""SCons.Scanner.Fortran |
+ |
+This module implements the dependency scanner for Fortran code. |
+ |
+""" |
+ |
+# |
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 The SCons Foundation |
+# |
+# 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__ = "src/engine/SCons/Scanner/Fortran.py 5134 2010/08/16 23:02:40 bdeegan" |
+ |
+import re |
+ |
+import SCons.Node |
+import SCons.Node.FS |
+import SCons.Scanner |
+import SCons.Util |
+import SCons.Warnings |
+ |
+class F90Scanner(SCons.Scanner.Classic): |
+ """ |
+ A Classic Scanner subclass for Fortran source files which takes |
+ into account both USE and INCLUDE statements. This scanner will |
+ work for both F77 and F90 (and beyond) compilers. |
+ |
+ Currently, this scanner assumes that the include files do not contain |
+ USE statements. To enable the ability to deal with USE statements |
+ in include files, add logic right after the module names are found |
+ to loop over each include file, search for and locate each USE |
+ statement, and append each module name to the list of dependencies. |
+ Caching the search results in a common dictionary somewhere so that |
+ the same include file is not searched multiple times would be a |
+ smart thing to do. |
+ """ |
+ |
+ def __init__(self, name, suffixes, path_variable, |
+ use_regex, incl_regex, def_regex, *args, **kw): |
+ |
+ self.cre_use = re.compile(use_regex, re.M) |
+ self.cre_incl = re.compile(incl_regex, re.M) |
+ self.cre_def = re.compile(def_regex, re.M) |
+ |
+ def _scan(node, env, path, self=self): |
+ node = node.rfile() |
+ |
+ if not node.exists(): |
+ return [] |
+ |
+ return self.scan(node, env, path) |
+ |
+ kw['function'] = _scan |
+ kw['path_function'] = SCons.Scanner.FindPathDirs(path_variable) |
+ kw['recursive'] = 1 |
+ kw['skeys'] = suffixes |
+ kw['name'] = name |
+ |
+ SCons.Scanner.Current.__init__(self, *args, **kw) |
+ |
+ def scan(self, node, env, path=()): |
+ |
+ # cache the includes list in node so we only scan it once: |
+ if node.includes != None: |
+ mods_and_includes = node.includes |
+ else: |
+ # retrieve all included filenames |
+ includes = self.cre_incl.findall(node.get_text_contents()) |
+ # retrieve all USE'd module names |
+ modules = self.cre_use.findall(node.get_text_contents()) |
+ # retrieve all defined module names |
+ defmodules = self.cre_def.findall(node.get_text_contents()) |
+ |
+ # Remove all USE'd module names that are defined in the same file |
+ # (case-insensitively) |
+ d = {} |
+ for m in defmodules: |
+ d[m.lower()] = 1 |
+ modules = [m for m in modules if m.lower() not in d] |
+ |
+ # Convert module name to a .mod filename |
+ suffix = env.subst('$FORTRANMODSUFFIX') |
+ modules = [x.lower() + suffix for x in modules] |
+ # Remove unique items from the list |
+ mods_and_includes = SCons.Util.unique(includes+modules) |
+ node.includes = mods_and_includes |
+ |
+ # This is a hand-coded DSU (decorate-sort-undecorate, or |
+ # Schwartzian transform) pattern. The sort key is the raw name |
+ # of the file as specifed on the USE or INCLUDE line, which lets |
+ # us keep the sort order constant regardless of whether the file |
+ # is actually found in a Repository or locally. |
+ nodes = [] |
+ source_dir = node.get_dir() |
+ if callable(path): |
+ path = path() |
+ for dep in mods_and_includes: |
+ n, i = self.find_include(dep, source_dir, path) |
+ |
+ if n is None: |
+ SCons.Warnings.warn(SCons.Warnings.DependencyWarning, |
+ "No dependency generated for file: %s (referenced by: %s) -- file not found" % (i, node)) |
+ else: |
+ sortkey = self.sort_key(dep) |
+ nodes.append((sortkey, n)) |
+ |
+ return [pair[1] for pair in sorted(nodes)] |
+ |
+def FortranScan(path_variable="FORTRANPATH"): |
+ """Return a prototype Scanner instance for scanning source files |
+ for Fortran USE & INCLUDE statements""" |
+ |
+# The USE statement regex matches the following: |
+# |
+# USE module_name |
+# USE :: module_name |
+# USE, INTRINSIC :: module_name |
+# USE, NON_INTRINSIC :: module_name |
+# |
+# Limitations |
+# |
+# -- While the regex can handle multiple USE statements on one line, |
+# it cannot properly handle them if they are commented out. |
+# In either of the following cases: |
+# |
+# ! USE mod_a ; USE mod_b [entire line is commented out] |
+# USE mod_a ! ; USE mod_b [in-line comment of second USE statement] |
+# |
+# the second module name (mod_b) will be picked up as a dependency |
+# even though it should be ignored. The only way I can see |
+# to rectify this would be to modify the scanner to eliminate |
+# the call to re.findall, read in the contents of the file, |
+# treating the comment character as an end-of-line character |
+# in addition to the normal linefeed, loop over each line, |
+# weeding out the comments, and looking for the USE statements. |
+# One advantage to this is that the regex passed to the scanner |
+# would no longer need to match a semicolon. |
+# |
+# -- I question whether or not we need to detect dependencies to |
+# INTRINSIC modules because these are built-in to the compiler. |
+# If we consider them a dependency, will SCons look for them, not |
+# find them, and kill the build? Or will we there be standard |
+# compiler-specific directories we will need to point to so the |
+# compiler and SCons can locate the proper object and mod files? |
+ |
+# Here is a breakdown of the regex: |
+# |
+# (?i) : regex is case insensitive |
+# ^ : start of line |
+# (?: : group a collection of regex symbols without saving the match as a "group" |
+# ^|; : matches either the start of the line or a semicolon - semicolon |
+# ) : end the unsaved grouping |
+# \s* : any amount of white space |
+# USE : match the string USE, case insensitive |
+# (?: : group a collection of regex symbols without saving the match as a "group" |
+# \s+| : match one or more whitespace OR .... (the next entire grouped set of regex symbols) |
+# (?: : group a collection of regex symbols without saving the match as a "group" |
+# (?: : establish another unsaved grouping of regex symbols |
+# \s* : any amount of white space |
+# , : match a comma |
+# \s* : any amount of white space |
+# (?:NON_)? : optionally match the prefix NON_, case insensitive |
+# INTRINSIC : match the string INTRINSIC, case insensitive |
+# )? : optionally match the ", INTRINSIC/NON_INTRINSIC" grouped expression |
+# \s* : any amount of white space |
+# :: : match a double colon that must appear after the INTRINSIC/NON_INTRINSIC attribute |
+# ) : end the unsaved grouping |
+# ) : end the unsaved grouping |
+# \s* : match any amount of white space |
+# (\w+) : match the module name that is being USE'd |
+# |
+# |
+ use_regex = "(?i)(?:^|;)\s*USE(?:\s+|(?:(?:\s*,\s*(?:NON_)?INTRINSIC)?\s*::))\s*(\w+)" |
+ |
+ |
+# The INCLUDE statement regex matches the following: |
+# |
+# INCLUDE 'some_Text' |
+# INCLUDE "some_Text" |
+# INCLUDE "some_Text" ; INCLUDE "some_Text" |
+# INCLUDE kind_"some_Text" |
+# INCLUDE kind_'some_Text" |
+# |
+# where some_Text can include any alphanumeric and/or special character |
+# as defined by the Fortran 2003 standard. |
+# |
+# Limitations: |
+# |
+# -- The Fortran standard dictates that a " or ' in the INCLUDE'd |
+# string must be represented as a "" or '', if the quotes that wrap |
+# the entire string are either a ' or ", respectively. While the |
+# regular expression below can detect the ' or " characters just fine, |
+# the scanning logic, presently is unable to detect them and reduce |
+# them to a single instance. This probably isn't an issue since, |
+# in practice, ' or " are not generally used in filenames. |
+# |
+# -- This regex will not properly deal with multiple INCLUDE statements |
+# when the entire line has been commented out, ala |
+# |
+# ! INCLUDE 'some_file' ; INCLUDE 'some_file' |
+# |
+# In such cases, it will properly ignore the first INCLUDE file, |
+# but will actually still pick up the second. Interestingly enough, |
+# the regex will properly deal with these cases: |
+# |
+# INCLUDE 'some_file' |
+# INCLUDE 'some_file' !; INCLUDE 'some_file' |
+# |
+# To get around the above limitation, the FORTRAN programmer could |
+# simply comment each INCLUDE statement separately, like this |
+# |
+# ! INCLUDE 'some_file' !; INCLUDE 'some_file' |
+# |
+# The way I see it, the only way to get around this limitation would |
+# be to modify the scanning logic to replace the calls to re.findall |
+# with a custom loop that processes each line separately, throwing |
+# away fully commented out lines before attempting to match against |
+# the INCLUDE syntax. |
+# |
+# Here is a breakdown of the regex: |
+# |
+# (?i) : regex is case insensitive |
+# (?: : begin a non-saving group that matches the following: |
+# ^ : either the start of the line |
+# | : or |
+# ['">]\s*; : a semicolon that follows a single quote, |
+# double quote or greater than symbol (with any |
+# amount of whitespace in between). This will |
+# allow the regex to match multiple INCLUDE |
+# statements per line (although it also requires |
+# the positive lookahead assertion that is |
+# used below). It will even properly deal with |
+# (i.e. ignore) cases in which the additional |
+# INCLUDES are part of an in-line comment, ala |
+# " INCLUDE 'someFile' ! ; INCLUDE 'someFile2' " |
+# ) : end of non-saving group |
+# \s* : any amount of white space |
+# INCLUDE : match the string INCLUDE, case insensitive |
+# \s+ : match one or more white space characters |
+# (?\w+_)? : match the optional "kind-param _" prefix allowed by the standard |
+# [<"'] : match the include delimiter - an apostrophe, double quote, or less than symbol |
+# (.+?) : match one or more characters that make up |
+# the included path and file name and save it |
+# in a group. The Fortran standard allows for |
+# any non-control character to be used. The dot |
+# operator will pick up any character, including |
+# control codes, but I can't conceive of anyone |
+# putting control codes in their file names. |
+# The question mark indicates it is non-greedy so |
+# that regex will match only up to the next quote, |
+# double quote, or greater than symbol |
+# (?=["'>]) : positive lookahead assertion to match the include |
+# delimiter - an apostrophe, double quote, or |
+# greater than symbol. This level of complexity |
+# is required so that the include delimiter is |
+# not consumed by the match, thus allowing the |
+# sub-regex discussed above to uniquely match a |
+# set of semicolon-separated INCLUDE statements |
+# (as allowed by the F2003 standard) |
+ |
+ include_regex = """(?i)(?:^|['">]\s*;)\s*INCLUDE\s+(?:\w+_)?[<"'](.+?)(?=["'>])""" |
+ |
+# The MODULE statement regex finds module definitions by matching |
+# the following: |
+# |
+# MODULE module_name |
+# |
+# but *not* the following: |
+# |
+# MODULE PROCEDURE procedure_name |
+# |
+# Here is a breakdown of the regex: |
+# |
+# (?i) : regex is case insensitive |
+# ^\s* : any amount of white space |
+# MODULE : match the string MODULE, case insensitive |
+# \s+ : match one or more white space characters |
+# (?!PROCEDURE) : but *don't* match if the next word matches |
+# PROCEDURE (negative lookahead assertion), |
+# case insensitive |
+# (\w+) : match one or more alphanumeric characters |
+# that make up the defined module name and |
+# save it in a group |
+ |
+ def_regex = """(?i)^\s*MODULE\s+(?!PROCEDURE)(\w+)""" |
+ |
+ scanner = F90Scanner("FortranScan", |
+ "$FORTRANSUFFIXES", |
+ path_variable, |
+ use_regex, |
+ include_regex, |
+ def_regex) |
+ return scanner |
+ |
+# Local Variables: |
+# tab-width:4 |
+# indent-tabs-mode:nil |
+# End: |
+# vim: set expandtab tabstop=4 shiftwidth=4: |
Property changes on: scons-2.0.1/engine/SCons/Scanner/Fortran.py |
___________________________________________________________________ |
Added: svn:eol-style |
+ LF |