diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 0e6311a41..64ea5a65c 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -36,6 +36,8 @@ from ..mesonlib import get_compiler_for_source, has_path_sep from .backends import CleanTrees from ..build import InvalidArguments +FORTRAN_SUBMOD_PAT = r"\s*submodule\s*\((\w+:?\w+)\)\s*(\w+)\s*$" + if mesonlib.is_windows(): quote_func = lambda s: '"{}"'.format(s) execute_wrapper = 'cmd /c' @@ -1812,6 +1814,9 @@ rule FORTRAN_DEP_HACK%s elem.write(outfile) def scan_fortran_module_outputs(self, target): + """ + Find all module and submodule made available in a Fortran code file. + """ compiler = None for lang, c in self.build.compilers.items(): if lang == 'fortran': @@ -1822,8 +1827,9 @@ rule FORTRAN_DEP_HACK%s 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) + submodre = re.compile(FORTRAN_SUBMOD_PAT, re.IGNORECASE) module_files = {} + submodule_files = {} for s in target.get_sources(): # FIXME, does not work for Fortran sources generated by # custom_target() and generator() as those are run after @@ -1847,19 +1853,22 @@ rule FORTRAN_DEP_HACK%s else: submodmatch = submodre.match(line) if submodmatch is not None: - submodname = submodmatch.group(2).lower() - if submodname in module_files: + # '_' is arbitrarily used to distinguish submod from mod. + parents = submodmatch.group(1).lower().split(':') + submodname = parents[0] + '_' + submodmatch.group(2).lower() + + if submodname in submodule_files: raise InvalidArguments( 'Namespace collision: submodule %s defined in ' 'two files %s and %s.' % - (submodname, module_files[submodname], s)) - module_files[submodname] = s + (submodname, submodule_files[submodname], s)) + submodule_files[submodname] = s - self.fortran_deps[target.get_basename()] = module_files + self.fortran_deps[target.get_basename()] = {**module_files, **submodule_files} def get_fortran_deps(self, compiler: FortranCompiler, src: str, target) -> List[str]: """ - Find all modules and submodules needed by a target + Find all module and submodule needed by a Fortran target """ dirname = Path(self.get_target_private_dir(target)) @@ -2774,7 +2783,7 @@ def _scan_fortran_file_deps(src: str, srcdir: Path, dirname: Path, tdeps, compil 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) + submodre = re.compile(FORTRAN_SUBMOD_PAT, re.IGNORECASE) mod_files = [] src = Path(src) @@ -2822,17 +2831,18 @@ def _scan_fortran_file_deps(src: str, srcdir: Path, dirname: Path, tdeps, compil 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 + + ancestor_child = '_'.join(parents) + if ancestor_child not in tdeps: + raise MesonException("submodule {} relies on ancestor module {} that was not found.".format(submodmatch.group(2).lower(), ancestor_child.split('_')[0])) + submodsrcfile = srcdir / tdeps[ancestor_child].fname + if not submodsrcfile.is_file(): + if submodsrcfile.name != src.name: # generated source file + pass + else: # subproject continue - mod_name = compiler.module_name_to_filename(parent) - mod_files.append(str(dirname / mod_name)) + elif submodsrcfile.samefile(src): # self-reference + continue + mod_name = compiler.module_name_to_filename(ancestor_child) + mod_files.append(str(dirname / mod_name)) return mod_files diff --git a/mesonbuild/compilers/fortran.py b/mesonbuild/compilers/fortran.py index 15234eed9..a16f2b554 100644 --- a/mesonbuild/compilers/fortran.py +++ b/mesonbuild/compilers/fortran.py @@ -180,7 +180,18 @@ class FortranCompiler(Compiler): return parameter_list def module_name_to_filename(self, module_name: str) -> str: - return module_name.lower() + '.mod' + if '_' in module_name: # submodule + s = module_name.lower() + if self.id in ('gcc', 'intel'): + filename = s.replace('_', '@') + '.smod' + elif self.id in ('pgi', 'flang'): + filename = s.replace('_', '-') + '.mod' + else: + filename = s + '.mod' + else: # module + filename = module_name.lower() + '.mod' + + return filename def get_std_shared_lib_link_args(self): return CCompiler.get_std_shared_lib_link_args(self) diff --git a/test cases/fortran/12 submodule/child.f90 b/test cases/fortran/12 submodule/child.f90 index aa5bb5e4a..45b5b41c5 100644 --- a/test cases/fortran/12 submodule/child.f90 +++ b/test cases/fortran/12 submodule/child.f90 @@ -1,4 +1,4 @@ -submodule (mother) daughter +submodule (parent) parent contains @@ -6,5 +6,5 @@ module procedure pi2tau pi2tau = 2*pi end procedure pi2tau -end submodule daughter +end submodule parent diff --git a/test cases/fortran/12 submodule/meson.build b/test cases/fortran/12 submodule/meson.build index cd62a30a4..b3ed9debd 100644 --- a/test cases/fortran/12 submodule/meson.build +++ b/test cases/fortran/12 submodule/meson.build @@ -1,6 +1,6 @@ project('submodule single level', 'fortran') -hier2 = executable('single', 'parent.f90','child.f90') +hier2 = executable('single', 'parent.f90', 'child.f90') test('single-level hierarchy', hier2) hierN = executable('multi', 'a1.f90', 'a2.f90', 'a3.f90') diff --git a/test cases/fortran/12 submodule/parent.f90 b/test cases/fortran/12 submodule/parent.f90 index 05fe431db..9d087d0d1 100644 --- a/test cases/fortran/12 submodule/parent.f90 +++ b/test cases/fortran/12 submodule/parent.f90 @@ -1,4 +1,4 @@ -module mother +module parent real, parameter :: pi = 4.*atan(1.) real :: tau @@ -8,13 +8,10 @@ module elemental real function pi2tau(pi) end function pi2tau end interface -contains - -end module mother +end module parent -program hier1 -use mother +use parent tau = pi2tau(pi)