From 433117fc5a604e34a847b3480bbec15e59585b96 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 20 Nov 2023 23:56:06 -0800 Subject: [PATCH] scripts/depscan: pick language once, at configure time We already have to decide whether to scan a file at configure time, so we don't want to have to do it again at compile time, every time the depscan rule is run. We can do this by saving and passing the language to use in the pickle, so depscan doesn't have to re-calculate it. As an added bonus, this removes an import from depscan --- mesonbuild/backend/ninjabackend.py | 21 +++++++++--------- mesonbuild/scripts/depscan.py | 34 +++++++++++------------------- 2 files changed, 22 insertions(+), 33 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 77e1b04db..462ea27fe 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -144,12 +144,13 @@ class TargetDependencyScannerInfo: :param private_dir: The private scratch directory for the target. :param source2object: A mapping of source file names to the objects that will be created from them. - :param sources: A list of all the sources in this target + :param sources: a list of sources mapping them to the language rules to use + to scan them. """ private_dir: str source2object: T.Dict[str, str] - sources: T.List[str] + sources: T.List[T.Tuple[str, Literal['cpp', 'fortran']]] @unique @@ -1098,7 +1099,7 @@ class NinjaBackend(backends.Backend): pickle_file = os.path.join(self.get_target_private_dir(target), pickle_base).replace('\\', '/') pickle_abs = os.path.join(self.get_target_private_dir_abs(target), pickle_base).replace('\\', '/') rule_name = 'depscan' - scan_sources = self.select_sources_to_scan(compiled_sources) + scan_sources = list(self.select_sources_to_scan(compiled_sources)) scaninfo = TargetDependencyScannerInfo( self.get_target_private_dir(target), source2object, scan_sources) @@ -1113,19 +1114,17 @@ class NinjaBackend(backends.Backend): elem.orderdeps.update(object_deps) self.add_build(elem) - def select_sources_to_scan(self, compiled_sources: T.List[str]) -> T.List[str]: + def select_sources_to_scan(self, compiled_sources: T.List[str] + ) -> T.Iterable[T.Tuple[str, Literal['cpp', 'fortran']]]: # in practice pick up C++ and Fortran files. If some other language # requires scanning (possibly Java to deal with inner class files) # then add them here. - all_suffixes = set(compilers.lang_suffixes['cpp']) | set(compilers.lang_suffixes['fortran']) - selected_sources = [] for source in compiled_sources: ext = os.path.splitext(source)[1][1:] - if ext != 'C': - ext = ext.lower() - if ext in all_suffixes: - selected_sources.append(source) - return selected_sources + if ext.lower() in compilers.lang_suffixes['cpp'] or ext == 'C': + yield source, 'cpp' + elif ext.lower() in compilers.lang_suffixes['fortran']: + yield source, 'fortran' def process_target_dependencies(self, target): for t in target.get_dependencies(): diff --git a/mesonbuild/scripts/depscan.py b/mesonbuild/scripts/depscan.py index c0ac09b52..79934fb5f 100644 --- a/mesonbuild/scripts/depscan.py +++ b/mesonbuild/scripts/depscan.py @@ -9,13 +9,12 @@ import os import pathlib import pickle import re -import sys import typing as T from ..backend.ninjabackend import ninja_quote -from ..compilers.compilers import lang_suffixes if T.TYPE_CHECKING: + from typing_extensions import Literal from ..backend.ninjabackend import TargetDependencyScannerInfo CPP_IMPORT_RE = re.compile(r'\w*import ([a-zA-Z0-9]+);') @@ -41,16 +40,11 @@ class DependencyScanner: self.needs: collections.defaultdict[str, T.List[str]] = collections.defaultdict(list) self.sources_with_exports: T.List[str] = [] - def scan_file(self, fname: str) -> None: - suffix = os.path.splitext(fname)[1][1:] - if suffix != 'C': - suffix = suffix.lower() - if suffix in lang_suffixes['fortran']: + def scan_file(self, fname: str, lang: Literal['cpp', 'fortran']) -> None: + if lang == 'fortran': self.scan_fortran_file(fname) - elif suffix in lang_suffixes['cpp']: - self.scan_cpp_file(fname) else: - sys.exit(f'Can not scan files with suffix .{suffix}.') + self.scan_cpp_file(fname) def scan_fortran_file(self, fname: str) -> None: fpath = pathlib.Path(fname) @@ -118,9 +112,8 @@ class DependencyScanner: assert isinstance(objname, str) return objname - def module_name_for(self, src: str) -> str: - suffix = os.path.splitext(src)[1][1:].lower() - if suffix in lang_suffixes['fortran']: + def module_name_for(self, src: str, lang: Literal['cpp', 'fortran']) -> str: + if lang == 'fortran': exported = self.exports[src] # Module foo:bar goes to a file name foo@bar.smod # Module Foo goes to a file name foo.mod @@ -130,23 +123,20 @@ class DependencyScanner: else: extension = 'mod' return os.path.join(self.target_data.private_dir, f'{namebase}.{extension}') - elif suffix in lang_suffixes['cpp']: - return '{}.ifc'.format(self.exports[src]) - else: - raise RuntimeError('Unreachable code.') + return '{}.ifc'.format(self.exports[src]) def scan(self) -> int: - for s in self.sources: - self.scan_file(s) + for s, lang in self.sources: + self.scan_file(s, lang) with open(self.outfile, 'w', encoding='utf-8') as ofile: ofile.write('ninja_dyndep_version = 1\n') - for src in self.sources: + for src, lang in self.sources: objfilename = self.objname_for(src) mods_and_submods_needed = [] module_files_generated = [] module_files_needed = [] if src in self.sources_with_exports: - module_files_generated.append(self.module_name_for(src)) + module_files_generated.append(self.module_name_for(src, lang)) if src in self.needs: for modname in self.needs[src]: if modname not in self.provided_by: @@ -159,7 +149,7 @@ class DependencyScanner: for modname in mods_and_submods_needed: provider_src = self.provided_by[modname] - provider_modfile = self.module_name_for(provider_src) + provider_modfile = self.module_name_for(provider_src, lang) # Prune self-dependencies if provider_src != src: module_files_needed.append(provider_modfile)