Fortran.py
上传用户:market2
上传日期:2018-11-18
资源大小:18786k
文件大小:14k
源码类别:

外挂编程

开发平台:

Windows_Unix

  1. """SCons.Scanner.Fortran
  2. This module implements the dependency scanner for Fortran code.
  3. """
  4. #
  5. # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
  6. #
  7. # Permission is hereby granted, free of charge, to any person obtaining
  8. # a copy of this software and associated documentation files (the
  9. # "Software"), to deal in the Software without restriction, including
  10. # without limitation the rights to use, copy, modify, merge, publish,
  11. # distribute, sublicense, and/or sell copies of the Software, and to
  12. # permit persons to whom the Software is furnished to do so, subject to
  13. # the following conditions:
  14. #
  15. # The above copyright notice and this permission notice shall be included
  16. # in all copies or substantial portions of the Software.
  17. #
  18. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
  19. # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  20. # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  21. # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  22. # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  23. # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  24. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  25. #
  26. __revision__ = "src/engine/SCons/Scanner/Fortran.py 3057 2008/06/09 22:21:00 knight"
  27. import re
  28. import string
  29. import SCons.Node
  30. import SCons.Node.FS
  31. import SCons.Scanner
  32. import SCons.Util
  33. import SCons.Warnings
  34. class F90Scanner(SCons.Scanner.Classic):
  35.     """
  36.     A Classic Scanner subclass for Fortran source files which takes
  37.     into account both USE and INCLUDE statements.  This scanner will
  38.     work for both F77 and F90 (and beyond) compilers.
  39.     Currently, this scanner assumes that the include files do not contain
  40.     USE statements.  To enable the ability to deal with USE statements
  41.     in include files, add logic right after the module names are found
  42.     to loop over each include file, search for and locate each USE
  43.     statement, and append each module name to the list of dependencies.
  44.     Caching the search results in a common dictionary somewhere so that
  45.     the same include file is not searched multiple times would be a
  46.     smart thing to do.
  47.     """
  48.     def __init__(self, name, suffixes, path_variable,
  49.                  use_regex, incl_regex, def_regex, *args, **kw):
  50.         self.cre_use = re.compile(use_regex, re.M)
  51.         self.cre_incl = re.compile(incl_regex, re.M)
  52.         self.cre_def = re.compile(def_regex, re.M)
  53.         def _scan(node, env, path, self=self):
  54.             node = node.rfile()
  55.             if not node.exists():
  56.                 return []
  57.             return self.scan(node, env, path)
  58.         kw['function'] = _scan
  59.         kw['path_function'] = SCons.Scanner.FindPathDirs(path_variable)
  60.         kw['recursive'] = 1
  61.         kw['skeys'] = suffixes
  62.         kw['name'] = name
  63.         apply(SCons.Scanner.Current.__init__, (self,) + args, kw)
  64.     def scan(self, node, env, path=()):
  65.         # cache the includes list in node so we only scan it once:
  66.         if node.includes != None:
  67.             mods_and_includes = node.includes
  68.         else:
  69.             # retrieve all included filenames
  70.             includes = self.cre_incl.findall(node.get_contents())
  71.             # retrieve all USE'd module names
  72.             modules = self.cre_use.findall(node.get_contents())
  73.             # retrieve all defined module names
  74.             defmodules = self.cre_def.findall(node.get_contents())
  75.             # Remove all USE'd module names that are defined in the same file
  76.             d = {}
  77.             for m in defmodules:
  78.                 d[m] = 1
  79.             modules = filter(lambda m, d=d: not d.has_key(m), modules)
  80.             #modules = self.undefinedModules(modules, defmodules)
  81.             # Convert module name to a .mod filename
  82.             suffix = env.subst('$FORTRANMODSUFFIX')
  83.             modules = map(lambda x, s=suffix: string.lower(x) + s, modules)
  84.             # Remove unique items from the list
  85.             mods_and_includes = SCons.Util.unique(includes+modules)
  86.             node.includes = mods_and_includes
  87.         # This is a hand-coded DSU (decorate-sort-undecorate, or
  88.         # Schwartzian transform) pattern.  The sort key is the raw name
  89.         # of the file as specifed on the USE or INCLUDE line, which lets
  90.         # us keep the sort order constant regardless of whether the file
  91.         # is actually found in a Repository or locally.
  92.         nodes = []
  93.         source_dir = node.get_dir()
  94.         if callable(path):
  95.             path = path()
  96.         for dep in mods_and_includes:
  97.             n, i = self.find_include(dep, source_dir, path)
  98.             if n is None:
  99.                 SCons.Warnings.warn(SCons.Warnings.DependencyWarning,
  100.                                     "No dependency generated for file: %s (referenced by: %s) -- file not found" % (i, node))
  101.             else:
  102.                 sortkey = self.sort_key(dep)
  103.                 nodes.append((sortkey, n))
  104.         nodes.sort()
  105.         nodes = map(lambda pair: pair[1], nodes)
  106.         return nodes
  107. def FortranScan(path_variable="FORTRANPATH"):
  108.     """Return a prototype Scanner instance for scanning source files
  109.     for Fortran USE & INCLUDE statements"""
  110. #   The USE statement regex matches the following:
  111. #
  112. #   USE module_name
  113. #   USE :: module_name
  114. #   USE, INTRINSIC :: module_name
  115. #   USE, NON_INTRINSIC :: module_name
  116. #
  117. #   Limitations
  118. #
  119. #   --  While the regex can handle multiple USE statements on one line,
  120. #       it cannot properly handle them if they are commented out.
  121. #       In either of the following cases:
  122. #
  123. #            !  USE mod_a ; USE mod_b         [entire line is commented out]
  124. #               USE mod_a ! ; USE mod_b       [in-line comment of second USE statement]
  125. #
  126. #       the second module name (mod_b) will be picked up as a dependency
  127. #       even though it should be ignored.  The only way I can see
  128. #       to rectify this would be to modify the scanner to eliminate
  129. #       the call to re.findall, read in the contents of the file,
  130. #       treating the comment character as an end-of-line character
  131. #       in addition to the normal linefeed, loop over each line,
  132. #       weeding out the comments, and looking for the USE statements.
  133. #       One advantage to this is that the regex passed to the scanner
  134. #       would no longer need to match a semicolon.
  135. #
  136. #   --  I question whether or not we need to detect dependencies to
  137. #       INTRINSIC modules because these are built-in to the compiler.
  138. #       If we consider them a dependency, will SCons look for them, not
  139. #       find them, and kill the build?  Or will we there be standard
  140. #       compiler-specific directories we will need to point to so the
  141. #       compiler and SCons can locate the proper object and mod files?
  142. #   Here is a breakdown of the regex:
  143. #
  144. #   (?i)               : regex is case insensitive
  145. #   ^                  : start of line
  146. #   (?:                : group a collection of regex symbols without saving the match as a "group"
  147. #      ^|;             : matches either the start of the line or a semicolon - semicolon
  148. #   )                  : end the unsaved grouping
  149. #   s*                : any amount of white space
  150. #   USE                : match the string USE, case insensitive
  151. #   (?:                : group a collection of regex symbols without saving the match as a "group"
  152. #      s+|            : match one or more whitespace OR ....  (the next entire grouped set of regex symbols)
  153. #      (?:             : group a collection of regex symbols without saving the match as a "group"
  154. #         (?:          : establish another unsaved grouping of regex symbols
  155. #            s*          : any amount of white space
  156. #            ,         : match a comma
  157. #            s*       : any amount of white space
  158. #            (?:NON_)? : optionally match the prefix NON_, case insensitive
  159. #            INTRINSIC : match the string INTRINSIC, case insensitive
  160. #         )?           : optionally match the ", INTRINSIC/NON_INTRINSIC" grouped expression
  161. #         s*          : any amount of white space
  162. #         ::           : match a double colon that must appear after the INTRINSIC/NON_INTRINSIC attribute
  163. #      )               : end the unsaved grouping
  164. #   )                  : end the unsaved grouping
  165. #   s*                : match any amount of white space
  166. #   (w+)              : match the module name that is being USE'd
  167. #
  168. #
  169.     use_regex = "(?i)(?:^|;)s*USE(?:s+|(?:(?:s*,s*(?:NON_)?INTRINSIC)?s*::))s*(w+)"
  170. #   The INCLUDE statement regex matches the following:
  171. #
  172. #   INCLUDE 'some_Text'
  173. #   INCLUDE "some_Text"
  174. #   INCLUDE "some_Text" ; INCLUDE "some_Text"
  175. #   INCLUDE kind_"some_Text"
  176. #   INCLUDE kind_'some_Text"
  177. #
  178. #   where some_Text can include any alphanumeric and/or special character
  179. #   as defined by the Fortran 2003 standard.
  180. #
  181. #   Limitations:
  182. #
  183. #   --  The Fortran standard dictates that a " or ' in the INCLUDE'd
  184. #       string must be represented as a "" or '', if the quotes that wrap
  185. #       the entire string are either a ' or ", respectively.   While the
  186. #       regular expression below can detect the ' or " characters just fine,
  187. #       the scanning logic, presently is unable to detect them and reduce
  188. #       them to a single instance.  This probably isn't an issue since,
  189. #       in practice, ' or " are not generally used in filenames.
  190. #
  191. #   --  This regex will not properly deal with multiple INCLUDE statements
  192. #       when the entire line has been commented out, ala
  193. #
  194. #           ! INCLUDE 'some_file' ; INCLUDE 'some_file'
  195. #
  196. #       In such cases, it will properly ignore the first INCLUDE file,
  197. #       but will actually still pick up the second.  Interestingly enough,
  198. #       the regex will properly deal with these cases:
  199. #
  200. #             INCLUDE 'some_file'
  201. #             INCLUDE 'some_file' !; INCLUDE 'some_file'
  202. #
  203. #       To get around the above limitation, the FORTRAN programmer could
  204. #       simply comment each INCLUDE statement separately, like this
  205. #
  206. #           ! INCLUDE 'some_file' !; INCLUDE 'some_file'
  207. #
  208. #       The way I see it, the only way to get around this limitation would
  209. #       be to modify the scanning logic to replace the calls to re.findall
  210. #       with a custom loop that processes each line separately, throwing
  211. #       away fully commented out lines before attempting to match against
  212. #       the INCLUDE syntax.
  213. #
  214. #   Here is a breakdown of the regex:
  215. #
  216. #   (?i)               : regex is case insensitive
  217. #   (?:                : begin a non-saving group that matches the following:
  218. #      ^               :    either the start of the line
  219. #      |               :                or
  220. #      ['">]s*;       :    a semicolon that follows a single quote,
  221. #                           double quote or greater than symbol (with any
  222. #                           amount of whitespace in between).  This will
  223. #                           allow the regex to match multiple INCLUDE
  224. #                           statements per line (although it also requires
  225. #                           the positive lookahead assertion that is
  226. #                           used below).  It will even properly deal with
  227. #                           (i.e. ignore) cases in which the additional
  228. #                           INCLUDES are part of an in-line comment, ala
  229. #                                           "  INCLUDE 'someFile' ! ; INCLUDE 'someFile2' "
  230. #   )                  : end of non-saving group
  231. #   s*                : any amount of white space
  232. #   INCLUDE            : match the string INCLUDE, case insensitive
  233. #   s+                : match one or more white space characters
  234. #   (?w+_)?           : match the optional "kind-param _" prefix allowed by the standard
  235. #   [<"']              : match the include delimiter - an apostrophe, double quote, or less than symbol
  236. #   (.+?)              : match one or more characters that make up
  237. #                        the included path and file name and save it
  238. #                        in a group.  The Fortran standard allows for
  239. #                        any non-control character to be used.  The dot
  240. #                        operator will pick up any character, including
  241. #                        control codes, but I can't conceive of anyone
  242. #                        putting control codes in their file names.
  243. #                        The question mark indicates it is non-greedy so
  244. #                        that regex will match only up to the next quote,
  245. #                        double quote, or greater than symbol
  246. #   (?=["'>])          : positive lookahead assertion to match the include
  247. #                        delimiter - an apostrophe, double quote, or
  248. #                        greater than symbol.  This level of complexity
  249. #                        is required so that the include delimiter is
  250. #                        not consumed by the match, thus allowing the
  251. #                        sub-regex discussed above to uniquely match a
  252. #                        set of semicolon-separated INCLUDE statements
  253. #                        (as allowed by the F2003 standard)
  254.     include_regex = """(?i)(?:^|['">]s*;)s*INCLUDEs+(?:w+_)?[<"'](.+?)(?=["'>])"""
  255. #   The MODULE statement regex finds module definitions by matching
  256. #   the following:
  257. #
  258. #   MODULE module_name
  259. #
  260. #   but *not* the following:
  261. #
  262. #   MODULE PROCEDURE procedure_name
  263. #
  264. #   Here is a breakdown of the regex:
  265. #
  266. #   (?i)               : regex is case insensitive
  267. #   ^s*               : any amount of white space
  268. #   MODULE             : match the string MODULE, case insensitive
  269. #   s+                : match one or more white space characters
  270. #   (?!PROCEDURE)      : but *don't* match if the next word matches
  271. #                        PROCEDURE (negative lookahead assertion),
  272. #                        case insensitive
  273. #   (w+)              : match one or more alphanumeric characters
  274. #                        that make up the defined module name and
  275. #                        save it in a group
  276.     def_regex = """(?i)^s*MODULEs+(?!PROCEDURE)(w+)"""
  277.     scanner = F90Scanner("FortranScan",
  278.                          "$FORTRANSUFFIXES",
  279.                          path_variable,
  280.                          use_regex,
  281.                          include_regex,
  282.                          def_regex)
  283.     return scanner