diff options
author | Jussi Pakkanen <jpakkane@gmail.com> | 2020-12-14 15:00:04 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-12-14 15:00:04 +0000 |
commit | 9f1ba4025260e620c8e49c3825ddfd51c1c4c4b6 (patch) | |
tree | 3f430800cb71748cf98af3dc2f4bbdcc95631b23 /mesonbuild/backend | |
parent | bab108742244bdfde769ea34424a0c0722a460c2 (diff) | |
parent | f22d54690ba81da5ba5db7a981e3cfdb04de6ef2 (diff) | |
download | meson-9f1ba4025260e620c8e49c3825ddfd51c1c4c4b6.zip meson-9f1ba4025260e620c8e49c3825ddfd51c1c4c4b6.tar.gz meson-9f1ba4025260e620c8e49c3825ddfd51c1c4c4b6.tar.bz2 |
Merge pull request #8013 from mesonbuild/cppmodules
C++ module support
Diffstat (limited to 'mesonbuild/backend')
-rw-r--r-- | mesonbuild/backend/ninjabackend.py | 122 |
1 files changed, 113 insertions, 9 deletions
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 0fe8a2f..71deab0 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -112,7 +112,7 @@ rsp_threshold = get_rsp_threshold() # variables (or variables we use them in) is interpreted directly by ninja # (e.g. the value of the depfile variable is a pathname that ninja will read # from, etc.), so it must not be shell quoted. -raw_names = {'DEPFILE_UNQUOTED', 'DESC', 'pool', 'description', 'targetdep'} +raw_names = {'DEPFILE_UNQUOTED', 'DESC', 'pool', 'description', 'targetdep', 'dyndep'} NINJA_QUOTE_BUILD_PAT = re.compile(r"[$ :\n]") NINJA_QUOTE_VAR_PAT = re.compile(r"[$ \n]") @@ -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: str, source2object: T.Dict[str, str]): + self.private_dir = private_dir + self.source2object = source2object + @unique class Quoting(Enum): both = 0 @@ -683,10 +688,17 @@ int dummy; return False def generate_target(self, target): + try: + if isinstance(target, build.BuildTarget): + 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): self.generate_run_target(target) + compiled_sources = [] + source2object = {} name = target.get_id() if name in self.processed_targets: return @@ -784,10 +796,12 @@ int dummy; # because we need `header_deps` to be fully generated in the above loop. for src in generated_source_files: if self.environment.is_llvm_ir(src): - o = self.generate_llvm_ir_compile(target, src) + o, s = self.generate_llvm_ir_compile(target, src) else: - o = self.generate_single_compile(target, src, True, + o, s = self.generate_single_compile(target, src, True, order_deps=header_deps) + compiled_sources.append(s) + source2object[s] = o obj_list.append(o) use_pch = self.environment.coredata.base_options.get('b_pch', False) @@ -822,32 +836,84 @@ int dummy; # Passing 'vala' here signifies that we want the compile # arguments to be specialized for C code generated by # valac. For instance, no warnings should be emitted. - obj_list.append(self.generate_single_compile(target, src, 'vala', [], header_deps)) + o, s = self.generate_single_compile(target, src, 'vala', [], header_deps) + obj_list.append(o) # Generate compile targets for all the pre-existing sources for this target for src in target_sources.values(): if not self.environment.is_header(src): if self.environment.is_llvm_ir(src): - obj_list.append(self.generate_llvm_ir_compile(target, src)) + o, s = self.generate_llvm_ir_compile(target, src) + obj_list.append(o) elif is_unity and self.get_target_source_can_unity(target, src): abs_src = os.path.join(self.environment.get_build_dir(), src.rel_to_builddir(self.build_to_src)) unity_src.append(abs_src) else: - obj_list.append(self.generate_single_compile(target, src, False, [], header_deps)) + o, s = self.generate_single_compile(target, src, False, [], header_deps) + obj_list.append(o) + compiled_sources.append(s) + source2object[s] = o + obj_list += self.flatten_object_list(target) if is_unity: for src in self.generate_unity_files(target, unity_src): - obj_list.append(self.generate_single_compile(target, src, True, unity_deps + header_deps)) + o, s = self.generate_single_compile(target, src, True, unity_deps + header_deps) + obj_list.append(o) + compiled_sources.append(s) + 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) else: final_obj_list = obj_list elem = self.generate_link(target, outname, final_obj_list, linker, pch_objects, stdlib_args=stdlib_args) + self.generate_dependency_scan_target(target, compiled_sources, source2object) self.generate_shlib_aliases(target, self.get_target_dir(target)) self.add_build(elem) + def should_scan_target(self, target): + if 'cpp' not in target.compilers: + return False + # Currently only the preview version of Visual Studio is supported. + cpp = target.compilers['cpp'] + if cpp.get_id() != 'msvc': + return False + if not mesonlib.current_vs_supports_modules(): + return False + if mesonlib.version_compare(cpp.version, '<19.28.28617'): + return False + if mesonlib.version_compare(self.ninja_version, '<1.10.0'): + return False + return True + + def generate_dependency_scan_target(self, target, compiled_sources, source2object): + if not self.should_scan_target(target): + return + depscan_file = self.get_dep_scan_file_for(target) + 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' + scan_sources = self.select_sources_to_scan(compiled_sources) + elem = NinjaBuildElement(self.all_outputs, depscan_file, rule_name, scan_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 select_sources_to_scan(self, compiled_sources): + # 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. + selected_sources = [] + for source in compiled_sources: + ext = os.path.splitext(source)[1][1:] + if ext in compilers.lang_suffixes['cpp']: + selected_sources.append(source) + return selected_sources + def process_target_dependencies(self, target): for t in target.get_dependencies(): if t.get_id() not in self.processed_targets: @@ -1074,6 +1140,8 @@ int dummy; self.rules = [] self.ruledict = {} + self.add_rule_comment(NinjaComment('Rules for module scanning.')) + self.generate_scanner_rules() self.add_rule_comment(NinjaComment('Rules for compiling.')) self.generate_compile_rules() self.add_rule_comment(NinjaComment('Rules for linking.')) @@ -1107,6 +1175,8 @@ int dummy; self.build_elements.append(comment) def add_rule(self, rule): + if rule.name in self.ruledict: + raise MesonException('Tried to add rule {} twice.'.format(rule.name)) self.rules.append(rule) self.ruledict[rule.name] = rule @@ -1957,6 +2027,26 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485''')) self.add_rule(NinjaRule(rule, command, [], description, deps=deps, depfile=depfile)) + + def generate_scanner_rules(self): + scanner_languages = {'cpp'} # Fixme, add Fortran. + for for_machine in MachineChoice: + clist = self.environment.coredata.compilers[for_machine] + for langname, compiler in clist.items(): + if langname not in scanner_languages: + continue + rulename = '{}scan'.format(langname) + if rulename in self.ruledict: + # Scanning command is the same for native and cross compilation. + continue + command = self.environment.get_build_command() + \ + ['--internal', 'depscan'] + args = ['$picklefile', '$out', '$in'] + description = 'Module scanner for {}.'.format(langname) + rule = NinjaRule(rulename, command, args, description) + self.add_rule(rule) + + def generate_compile_rules(self): for for_machine in MachineChoice: clist = self.environment.coredata.compilers[for_machine] @@ -2217,7 +2307,7 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485''')) element = NinjaBuildElement(self.all_outputs, rel_obj, compiler_name, rel_src) element.add_item('ARGS', commands) self.add_build(element) - return rel_obj + return (rel_obj, rel_src) def get_source_dir_include_args(self, target, compiler): curdir = target.get_subdir() @@ -2439,8 +2529,22 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485''')) element.add_orderdep(i) element.add_item('DEPFILE', dep_file) element.add_item('ARGS', commands) + + self.add_dependency_scanner_entries_to_element(target, compiler, element) self.add_build(element) - return rel_obj + 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 not self.should_scan_target(target): + return + dep_scan_file = self.get_dep_scan_file_for(target) + element.add_item('dyndep', dep_scan_file) + element.add_orderdep(dep_scan_file) + + def get_dep_scan_file_for(self, target): + return os.path.join(self.get_target_private_dir(target), 'depscan.dd') def add_header_deps(self, target, ninja_element, header_deps): for d in header_deps: |