functionalize per-file Fortran dep scan

allow recursion for Fortran include

qualify include by file extension

correct suffix
pull/5081/head
Michael Hirsch, Ph.D 6 years ago
parent e7c4601fe3
commit 6d215476c0
No known key found for this signature in database
GPG Key ID: 6D23CDADAB0294F9
  1. 143
      mesonbuild/backend/ninjabackend.py

@ -1811,6 +1811,7 @@ rule FORTRAN_DEP_HACK%s
if compiler is None:
self.fortran_deps[target.get_basename()] = {}
return
modre = re.compile(r"\s*\bmodule\b\s+(\w+)\s*$", re.IGNORECASE)
submodre = re.compile(r"\s*\bsubmodule\b\s+\((\w+:?\w+)\)\s+(\w+)\s*$", re.IGNORECASE)
module_files = {}
@ -1848,64 +1849,15 @@ rule FORTRAN_DEP_HACK%s
self.fortran_deps[target.get_basename()] = module_files
def get_fortran_deps(self, compiler: FortranCompiler, src: str, target) -> List[str]:
mod_files = []
usere = re.compile(r"\s*use,?\s*(?:non_intrinsic)?\s*(?:::)?\s*(\w+)", re.IGNORECASE)
submodre = re.compile(r"\s*\bsubmodule\b\s+\((\w+:?\w+)\)\s+(\w+)\s*$", re.IGNORECASE)
"""
Find all modules and submodules needed by a target
"""
dirname = Path(self.get_target_private_dir(target))
tdeps = self.fortran_deps[target.get_basename()]
src = Path(src)
srcdir = Path(self.source_dir)
with src.open(encoding='ascii', errors='ignore') as f:
for line in f:
usematch = usere.match(line)
if usematch is not None:
usename = usematch.group(1).lower()
if usename == 'intrinsic': # this keeps the regex simpler
continue
if usename not in tdeps:
# The module is not provided by any source file. This
# is due to:
# a) missing file/typo/etc
# b) using a module provided by the compiler, such as
# OpenMP
# There's no easy way to tell which is which (that I
# know of) so just ignore this and go on. Ideally we
# would print a warning message to the user but this is
# a common occurrence, which would lead to lots of
# distracting noise.
continue
srcfile = srcdir / tdeps[usename].fname
if not srcfile.is_file():
if srcfile.name != src.name: # generated source file
pass
else: # subproject
continue
elif srcfile.samefile(src): # self-reference
continue
mod_name = compiler.module_name_to_filename(usename)
mod_files.append(str(dirname / mod_name))
else:
submodmatch = submodre.match(line)
if submodmatch is not None:
parents = submodmatch.group(1).lower().split(':')
assert len(parents) in (1, 2), (
'submodule ancestry must be specified as'
' ancestor:parent but Meson found {}'.parents)
for parent in parents:
if parent not in tdeps:
raise MesonException("submodule {} relies on parent module {} that was not found.".format(submodmatch.group(2).lower(), parent))
submodsrcfile = srcdir / tdeps[parent].fname
if not submodsrcfile.is_file():
if submodsrcfile.name != src.name: # generated source file
pass
else: # subproject
continue
elif submodsrcfile.samefile(src): # self-reference
continue
mod_name = compiler.module_name_to_filename(parent)
mod_files.append(str(dirname / mod_name))
mod_files = _scan_fortran_file_deps(src, srcdir, dirname, tdeps, compiler)
return mod_files
def get_cross_stdlib_args(self, target, compiler):
@ -2797,3 +2749,86 @@ def load(build_dir):
with open(filename, 'rb') as f:
obj = pickle.load(f)
return obj
def _scan_fortran_file_deps(src: str, srcdir: Path, dirname: Path, tdeps, compiler) -> List[str]:
"""
scan a Fortran file for dependencies. Needs to be distinct from target
to allow for recursion induced by `include` statements.er
It makes a number of assumptions, including
* `use`, `module`, `submodule` name is not on a continuation line
Regex
-----
* `incre` works for `#include "foo.f90"` and `include "foo.f90"`
* `usere` works for legacy and Fortran 2003 `use` statements
* `submodre` is for Fortran >= 2008 `submodule`
"""
incre = re.compile(r"#?include\s*['\"](\w+\.\w+)['\"]\s*$", re.IGNORECASE)
usere = re.compile(r"\s*use,?\s*(?:non_intrinsic)?\s*(?:::)?\s*(\w+)", re.IGNORECASE)
submodre = re.compile(r"\s*\bsubmodule\b\s+\((\w+:?\w+)\)\s+(\w+)\s*$", re.IGNORECASE)
mod_files = []
src = Path(src)
with src.open(encoding='ascii', errors='ignore') as f:
for line in f:
# included files
incmatch = incre.match(line)
if incmatch is not None:
incfile = srcdir / incmatch.group(1)
if incfile.suffix.lower()[1:] in compiler.file_suffixes:
mod_files.extend(_scan_fortran_file_deps(incfile, srcdir, dirname, tdeps, compiler))
# modules
usematch = usere.match(line)
if usematch is not None:
usename = usematch.group(1).lower()
if usename == 'intrinsic': # this keeps the regex simpler
continue
if usename not in tdeps:
# The module is not provided by any source file. This
# is due to:
# a) missing file/typo/etc
# b) using a module provided by the compiler, such as
# OpenMP
# There's no easy way to tell which is which (that I
# know of) so just ignore this and go on. Ideally we
# would print a warning message to the user but this is
# a common occurrence, which would lead to lots of
# distracting noise.
continue
srcfile = srcdir / tdeps[usename].fname
if not srcfile.is_file():
if srcfile.name != src.name: # generated source file
pass
else: # subproject
continue
elif srcfile.samefile(src): # self-reference
continue
mod_name = compiler.module_name_to_filename(usename)
mod_files.append(str(dirname / mod_name))
else: # submodules
submodmatch = submodre.match(line)
if submodmatch is not None:
parents = submodmatch.group(1).lower().split(':')
assert len(parents) in (1, 2), (
'submodule ancestry must be specified as'
' ancestor:parent but Meson found {}'.parents)
for parent in parents:
if parent not in tdeps:
raise MesonException("submodule {} relies on parent module {} that was not found.".format(submodmatch.group(2).lower(), parent))
submodsrcfile = srcdir / tdeps[parent].fname
if not submodsrcfile.is_file():
if submodsrcfile.name != src.name: # generated source file
pass
else: # subproject
continue
elif submodsrcfile.samefile(src): # self-reference
continue
mod_name = compiler.module_name_to_filename(parent)
mod_files.append(str(dirname / mod_name))
return mod_files

Loading…
Cancel
Save