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
pull/13021/head
Dylan Baker 1 year ago
parent fae1363bd3
commit 433117fc5a
  1. 21
      mesonbuild/backend/ninjabackend.py
  2. 32
      mesonbuild/scripts/depscan.py

@ -144,12 +144,13 @@ class TargetDependencyScannerInfo:
:param private_dir: The private scratch directory for the target. :param private_dir: The private scratch directory for the target.
:param source2object: A mapping of source file names to the objects that :param source2object: A mapping of source file names to the objects that
will be created from them. 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 private_dir: str
source2object: T.Dict[str, str] source2object: T.Dict[str, str]
sources: T.List[str] sources: T.List[T.Tuple[str, Literal['cpp', 'fortran']]]
@unique @unique
@ -1098,7 +1099,7 @@ class NinjaBackend(backends.Backend):
pickle_file = os.path.join(self.get_target_private_dir(target), pickle_base).replace('\\', '/') 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('\\', '/') pickle_abs = os.path.join(self.get_target_private_dir_abs(target), pickle_base).replace('\\', '/')
rule_name = 'depscan' rule_name = 'depscan'
scan_sources = self.select_sources_to_scan(compiled_sources) scan_sources = list(self.select_sources_to_scan(compiled_sources))
scaninfo = TargetDependencyScannerInfo( scaninfo = TargetDependencyScannerInfo(
self.get_target_private_dir(target), source2object, scan_sources) self.get_target_private_dir(target), source2object, scan_sources)
@ -1113,19 +1114,17 @@ class NinjaBackend(backends.Backend):
elem.orderdeps.update(object_deps) elem.orderdeps.update(object_deps)
self.add_build(elem) 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 # in practice pick up C++ and Fortran files. If some other language
# requires scanning (possibly Java to deal with inner class files) # requires scanning (possibly Java to deal with inner class files)
# then add them here. # then add them here.
all_suffixes = set(compilers.lang_suffixes['cpp']) | set(compilers.lang_suffixes['fortran'])
selected_sources = []
for source in compiled_sources: for source in compiled_sources:
ext = os.path.splitext(source)[1][1:] ext = os.path.splitext(source)[1][1:]
if ext != 'C': if ext.lower() in compilers.lang_suffixes['cpp'] or ext == 'C':
ext = ext.lower() yield source, 'cpp'
if ext in all_suffixes: elif ext.lower() in compilers.lang_suffixes['fortran']:
selected_sources.append(source) yield source, 'fortran'
return selected_sources
def process_target_dependencies(self, target): def process_target_dependencies(self, target):
for t in target.get_dependencies(): for t in target.get_dependencies():

@ -9,13 +9,12 @@ import os
import pathlib import pathlib
import pickle import pickle
import re import re
import sys
import typing as T import typing as T
from ..backend.ninjabackend import ninja_quote from ..backend.ninjabackend import ninja_quote
from ..compilers.compilers import lang_suffixes
if T.TYPE_CHECKING: if T.TYPE_CHECKING:
from typing_extensions import Literal
from ..backend.ninjabackend import TargetDependencyScannerInfo from ..backend.ninjabackend import TargetDependencyScannerInfo
CPP_IMPORT_RE = re.compile(r'\w*import ([a-zA-Z0-9]+);') 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.needs: collections.defaultdict[str, T.List[str]] = collections.defaultdict(list)
self.sources_with_exports: T.List[str] = [] self.sources_with_exports: T.List[str] = []
def scan_file(self, fname: str) -> None: def scan_file(self, fname: str, lang: Literal['cpp', 'fortran']) -> None:
suffix = os.path.splitext(fname)[1][1:] if lang == 'fortran':
if suffix != 'C':
suffix = suffix.lower()
if suffix in lang_suffixes['fortran']:
self.scan_fortran_file(fname) self.scan_fortran_file(fname)
elif suffix in lang_suffixes['cpp']:
self.scan_cpp_file(fname)
else: else:
sys.exit(f'Can not scan files with suffix .{suffix}.') self.scan_cpp_file(fname)
def scan_fortran_file(self, fname: str) -> None: def scan_fortran_file(self, fname: str) -> None:
fpath = pathlib.Path(fname) fpath = pathlib.Path(fname)
@ -118,9 +112,8 @@ class DependencyScanner:
assert isinstance(objname, str) assert isinstance(objname, str)
return objname return objname
def module_name_for(self, src: str) -> str: def module_name_for(self, src: str, lang: Literal['cpp', 'fortran']) -> str:
suffix = os.path.splitext(src)[1][1:].lower() if lang == 'fortran':
if suffix in lang_suffixes['fortran']:
exported = self.exports[src] exported = self.exports[src]
# Module foo:bar goes to a file name foo@bar.smod # Module foo:bar goes to a file name foo@bar.smod
# Module Foo goes to a file name foo.mod # Module Foo goes to a file name foo.mod
@ -130,23 +123,20 @@ class DependencyScanner:
else: else:
extension = 'mod' extension = 'mod'
return os.path.join(self.target_data.private_dir, f'{namebase}.{extension}') return os.path.join(self.target_data.private_dir, f'{namebase}.{extension}')
elif suffix in lang_suffixes['cpp']:
return '{}.ifc'.format(self.exports[src]) return '{}.ifc'.format(self.exports[src])
else:
raise RuntimeError('Unreachable code.')
def scan(self) -> int: def scan(self) -> int:
for s in self.sources: for s, lang in self.sources:
self.scan_file(s) self.scan_file(s, lang)
with open(self.outfile, 'w', encoding='utf-8') as ofile: with open(self.outfile, 'w', encoding='utf-8') as ofile:
ofile.write('ninja_dyndep_version = 1\n') ofile.write('ninja_dyndep_version = 1\n')
for src in self.sources: for src, lang in self.sources:
objfilename = self.objname_for(src) objfilename = self.objname_for(src)
mods_and_submods_needed = [] mods_and_submods_needed = []
module_files_generated = [] module_files_generated = []
module_files_needed = [] module_files_needed = []
if src in self.sources_with_exports: 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: if src in self.needs:
for modname in self.needs[src]: for modname in self.needs[src]:
if modname not in self.provided_by: if modname not in self.provided_by:
@ -159,7 +149,7 @@ class DependencyScanner:
for modname in mods_and_submods_needed: for modname in mods_and_submods_needed:
provider_src = self.provided_by[modname] 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 # Prune self-dependencies
if provider_src != src: if provider_src != src:
module_files_needed.append(provider_modfile) module_files_needed.append(provider_modfile)

Loading…
Cancel
Save