diff options
Diffstat (limited to 'mesonbuild/scripts')
-rw-r--r-- | mesonbuild/scripts/depscan.py | 91 |
1 files changed, 91 insertions, 0 deletions
diff --git a/mesonbuild/scripts/depscan.py b/mesonbuild/scripts/depscan.py new file mode 100644 index 0000000..6eebbfe --- /dev/null +++ b/mesonbuild/scripts/depscan.py @@ -0,0 +1,91 @@ +# Copyright 2020 The Meson development team + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import pathlib +import pickle +import re +import typing as T + +from ..backend.ninjabackend import TargetDependencyScannerInfo + +import_re = re.compile('\w*import ([a-zA-Z0-9]+);') +export_re = re.compile('\w*export module ([a-zA-Z0-9]+);') + +class DependencyScanner: + def __init__(self, pickle_file: str, outfile: str, sources: T.List[str]): + with open(pickle_file, 'rb') as pf: + self.target_data = pickle.load(pf) # type: TargetDependencyScannerInfo + self.outfile = outfile + self.sources = sources + self.provided_by = {} # type: T.Dict[str, str] + self.exports = {} # type: T.Dict[str, str] + self.needs = {} # type: T.Dict[str, T.List[str]] + self.sources_with_exports = [] # type: T.List[str] + + def scan_file(self, fname: str) -> None: + for line in pathlib.Path(fname).read_text().split('\n'): + import_match = import_re.match(line) + export_match = export_re.match(line) + if import_match: + needed = import_match.group(1) + if fname in self.needs: + self.needs[fname].append(needed) + else: + self.needs[fname] = [needed] + if export_match: + exported_module = export_match.group(1) + if exported_module in self.provided_by: + raise RuntimeError('Multiple files provide module {}.'.format(exported_module)) + self.sources_with_exports.append(fname) + self.provided_by[exported_module] = fname + self.exports[fname] = exported_module + + def objname_for(self, src: str) -> str: + objname = self.target_data.source2object[src] + assert(isinstance(objname, str)) + return objname + + def ifcname_for(self, src: str) -> str: + return '{}.ifc'.format(self.exports[src]) + + def scan(self) -> int: + for s in self.sources: + self.scan_file(s) + with open(self.outfile, 'w') as ofile: + ofile.write('ninja_dyndep_version = 1\n') + for src in self.sources: + objfilename = self.objname_for(src) + if src in self.sources_with_exports: + ifc_entry = '| ' + self.ifcname_for(src) + else: + ifc_entry = '' + if src in self.needs: + # FIXME, handle all sources, not just the first one + modname = self.needs[src][0] + provider_src = self.provided_by[modname] + provider_ifc = self.ifcname_for(provider_src) + mod_dep = '| ' + provider_ifc + else: + mod_dep = '' + ofile.write('build {} {}: dyndep {}\n'.format(objfilename, + ifc_entry, + mod_dep)) + return 0 + +def run(args: T.List[str]) -> int: + pickle_file = args[0] + outfile = args[1] + sources = args[2:] + scanner = DependencyScanner(pickle_file, outfile, sources) + return scanner.scan() |