diff options
-rw-r--r-- | mesonbuild/backend/ninjabackend.py | 26 | ||||
-rw-r--r-- | mesonbuild/scripts/depscan.py | 87 |
2 files changed, 108 insertions, 5 deletions
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index dffff56..ea04079 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -134,6 +134,11 @@ Please report this error with a test case to the Meson bug tracker.'''.format(te raise MesonException(errmsg) return quote_re.sub(r'$\g<0>', text) +class TargetDependencyScannerInfo: + def __init__(self, private_dir, source2object): + self.private_dir = private_dir + self.source2object = source2object + @unique class Quoting(Enum): both = 0 @@ -683,6 +688,10 @@ int dummy; return False def generate_target(self, target): + try: + os.makedirs(self.get_target_private_dir_abs(target)) + except FileExistsError: + pass if isinstance(target, build.CustomTarget): self.generate_custom_target(target) if isinstance(target, build.RunTarget): @@ -843,7 +852,7 @@ int dummy; o, s = self.generate_single_compile(target, src, False, [], header_deps) obj_list.append(o) compiled_sources.append(s) - source2object[src] = o + source2object[s] = o obj_list += self.flatten_object_list(target) if is_unity: @@ -851,7 +860,7 @@ int dummy; o, s = self.generate_single_compile(target, src, True, unity_deps + header_deps) obj_list.append(o) compiled_sources.append(s) - source2object[src] = o + source2object[s] = o linker, stdlib_args = self.determine_linker_and_stdlib_args(target) if isinstance(target, build.StaticLibrary) and target.prelink: final_obj_list = self.generate_prelink(target, obj_list) @@ -866,10 +875,15 @@ int dummy; if 'cpp' not in target.compilers: return depscan_file = self.get_dep_scan_file_for(target) - pickle_file = os.path.join(self.get_target_private_dir(target), target.name + '.dat').replace('\\', '/') + pickle_base = target.name + '.dat' + 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 = 'cppscan' elem = NinjaBuildElement(self.all_outputs, depscan_file, rule_name, compiled_sources) elem.add_item('picklefile', pickle_file) + scaninfo = TargetDependencyScannerInfo(self.get_target_private_dir(target), source2object) + with open(pickle_abs, 'wb') as p: + pickle.dump(scaninfo, p) self.add_build(elem) def process_target_dependencies(self, target): @@ -1998,7 +2012,7 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485''')) # Scanning command is the same for native and cross compilation. continue command = cmd = self.environment.get_build_command() + \ - ['--internal', 'scan'] + ['--internal', 'depscan'] args = ['$picklefile', '$out', '$in'] description = 'Module scanner for {}.'.format(langname) rule = NinjaRule(rulename, command, args, description) @@ -2490,7 +2504,9 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485''')) self.add_dependency_scanner_entries_to_element(target, compiler, element) self.add_build(element) - return (rel_obj, rel_src) + assert(isinstance(rel_obj, str)) + assert(isinstance(rel_src, str)) + return (rel_obj, rel_src.replace('\\', '/')) def add_dependency_scanner_entries_to_element(self, target, compiler, element): if compiler.get_language() != 'cpp': diff --git a/mesonbuild/scripts/depscan.py b/mesonbuild/scripts/depscan.py new file mode 100644 index 0000000..161faf8 --- /dev/null +++ b/mesonbuild/scripts/depscan.py @@ -0,0 +1,87 @@ +# 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 + +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, outfile, sources): + with open(pickle_file, 'rb') as pf: + self.target_data = pickle.load(pf) + self.outfile = outfile + self.sources = sources + self.provided_by = {} + self.exports = {} + self.needs = {} + self.sources_with_exports = [] + + def scan_file(self, fname): + 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): + return self.target_data.source2object[src] + + def ifcname_for(self, src): + return '{}.ifc'.format(self.exports[src]) + + def scan(self): + 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() |