Skip to content

Commit

Permalink
Restore caching of scanned Fortran modules
Browse files Browse the repository at this point in the history
When splitting the old mods_and_includes, which was used to cache
both discovered types for any future scan of the same base file,
we lost the ability to cache modules.  Now caching those in
node.attributes (we can't add a node.modules without changing the
slots for class Node, which is another option).

Note that at the moment this may not always have the expected results:
in the case of variant directories, if there are multiple references
to the same base Fortran source file in the build, its node.attributes
end up being replaced behind the scenes. There's a lengthy description
of the reason for this in SCons.Node.Node.rdirs().

Signed-off-by: Mats Wichmann <mats@linux.com>
  • Loading branch information
mwichmann committed Dec 31, 2023
1 parent 037ce3c commit f36ff25
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 33 deletions.
4 changes: 3 additions & 1 deletion SCons/Node/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -591,7 +591,9 @@ def __init__(self) -> None:
self.cached = 0 # is this node pulled from cache?
self.always_build = None
self.includes = None
self.attributes = self.Attrs() # Generic place to stick information about the Node.
# Place to store arbitrary (non-generic) data in the node since we
# cannot just add node attributes from outside, due to using slots:
self.attributes = self.Attrs()
self.side_effect = 0 # true iff this node is a side effect
self.side_effects = [] # the side effects of building this target
self.linked = 0 # is this node linked to the variant directory?
Expand Down
56 changes: 24 additions & 32 deletions SCons/Scanner/Fortran.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,39 +72,33 @@ def _scan(node, env, path, self=self):
Current.__init__(self, *args, **kwargs)

def scan(self, node, env, path=()):

# cache the includes list in node so we only scan it once:
if node.includes is not None:
#mods_and_includes = node.includes
# TODO: this doesn't work: if we found cached includes, it's the
# combined list and we can no longer identify which are modules.
# Just use empty list for modules for now.
"""The actual Fortran scanner function."""
if node.includes is not None: # Use cached includes if found
includes = node.includes
modules = [] # maybe: node.attributes.modules ?
try: # also look for cached modules
modules = node.attributes.modules
except AttributeError:
modules = []
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())
# Retrieve all INCLUDE, USE, and MODULE filenames
contents = node.get_text_contents()
# TODO: do we want to strip comments here to avoid regex issues?
includes = unique(self.cre_incl.findall(contents))
modules = self.cre_use.findall(contents)
defmodules = self.cre_def.findall(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]
defset = {m.lower() for m in defmodules}
modules = [m for m in modules if m.lower() not in defset]

# 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
# Not sure why combine these, as they are not the same. But there's
# no modules placeholder in a Node, which is restricted via slots.
mods_and_includes = unique(includes+modules)
node.includes = mods_and_includes
# TODO: maybe cache modules separately in node.attributes
# Remove dup items (case-insensitively)
modules = unique([x.lower() + suffix for x in modules])
# Cache the includes and modules in node so we only scan once
node.includes = includes
node.attributes.modules = modules

# This is a hand-coded DSU (decorate-sort-undecorate, or
# Schwartzian transform) pattern. The sort key is the raw name
Expand All @@ -117,14 +111,12 @@ def scan(self, node, env, path=()):
# need to exist to work, but module files are generated from
# compiling the module source, so if modules are from *this*
# project we need them as deps even if they don't currently exist.
# So now process those separately - leaving this note and
# some other breadcrumbs in case we need to go back.
# So now process those separately - leaving this note just in case.
nodes = []
source_dir = node.get_dir()
if callable(path):
path = path()
# for dep in mods_and_includes: # old way
for dep in unique(includes):
for dep in includes:
n, i = self.find_include(dep, source_dir, path)

if n is None:
Expand All @@ -134,10 +126,10 @@ def scan(self, node, env, path=()):
sortkey = self.sort_key(dep)
nodes.append((sortkey, n))

# Module use should trigger a dep even if mod not found, so don't
# call find_includes. Account for a module directory maybe being used.
# Module use should trigger a dep even if mod not found, so don't call
# find_includes(). Account for a module directory maybe being in use.
moddir = env.subst('$FORTRANMODDIR')
for module in unique(modules):
for module in modules:
sortkey = self.sort_key(module)
nodes.append((sortkey, env.fs.File(module, moddir)))

Expand Down

0 comments on commit f36ff25

Please sign in to comment.