diff options
author | Daniel Mensinger <daniel@mensinger-ka.de> | 2019-11-28 19:54:08 +0100 |
---|---|---|
committer | Jussi Pakkanen <jpakkane@gmail.com> | 2019-11-30 22:00:18 +0200 |
commit | 36749a1625534386c1adefcd8ced5b45144501d1 (patch) | |
tree | 89e12b74995d6a2491752ae6763f798a487adccf /mesonbuild | |
parent | eed05c9045ae2f5a326bfd418006fa7fd76b7748 (diff) | |
download | meson-36749a1625534386c1adefcd8ced5b45144501d1.zip meson-36749a1625534386c1adefcd8ced5b45144501d1.tar.gz meson-36749a1625534386c1adefcd8ced5b45144501d1.tar.bz2 |
cmake: Make output_target_map more robust (fixes #6208)
This PR refactors the old output_target_map, which was a
raw dict, into it's own class. This makes the access to
the map more uniform and robust (at the cost of more lines
of code).
Additionally relative paths to the build directory are
now also tracked for outputs. This is neccessary to
corretcly distingluish files with the same name, that are
in different directories.
Diffstat (limited to 'mesonbuild')
-rwxr-xr-x | mesonbuild/cmake/data/run_ctgt.py | 1 | ||||
-rw-r--r-- | mesonbuild/cmake/interpreter.py | 158 |
2 files changed, 95 insertions, 64 deletions
diff --git a/mesonbuild/cmake/data/run_ctgt.py b/mesonbuild/cmake/data/run_ctgt.py index 954f8ce..1897a1b 100755 --- a/mesonbuild/cmake/data/run_ctgt.py +++ b/mesonbuild/cmake/data/run_ctgt.py @@ -41,6 +41,7 @@ for i in commands: continue try: + os.makedirs(args.directory, exist_ok=True) subprocess.run(i, cwd=args.directory, check=True) except subprocess.CalledProcessError: exit(1) diff --git a/mesonbuild/cmake/interpreter.py b/mesonbuild/cmake/interpreter.py index 58794ea..cb0416d 100644 --- a/mesonbuild/cmake/interpreter.py +++ b/mesonbuild/cmake/interpreter.py @@ -128,16 +128,78 @@ generated_target_name_prefix = 'cm_' transfer_dependencies_from = ['header_only'] -# Utility functions to generate local keys -def _target_key(tgt_name: str) -> str: - return '__tgt_{}__'.format(tgt_name) +class OutputTargetMap: + rm_so_version = re.compile(r'(\.[0-9]+)+$') + + def __init__(self, build_dir: str): + self.tgt_map = {} + self.build_dir = build_dir + + def add(self, tgt: Union['ConverterTarget', 'ConverterCustomTarget']) -> None: + def assign_keys(keys: List[str]) -> None: + for i in [x for x in keys if x]: + self.tgt_map[i] = tgt + keys = [self._target_key(tgt.cmake_name)] + if isinstance(tgt, ConverterTarget): + keys += [tgt.full_name] + keys += [self._rel_artifact_key(x) for x in tgt.artifacts] + keys += [self._base_artifact_key(x) for x in tgt.artifacts] + if isinstance(tgt, ConverterCustomTarget): + keys += [self._rel_generated_file_key(x) for x in tgt.original_outputs] + keys += [self._base_generated_file_key(x) for x in tgt.original_outputs] + assign_keys(keys) + + def _return_first_valid_key(self, keys: List[str]) -> Optional[Union['ConverterTarget', 'ConverterCustomTarget']]: + for i in keys: + if i and i in self.tgt_map: + return self.tgt_map[i] + return None + + def target(self, name: str) -> Optional[Union['ConverterTarget', 'ConverterCustomTarget']]: + return self._return_first_valid_key([self._target_key(name)]) + + def artifact(self, name: str) -> Optional[Union['ConverterTarget', 'ConverterCustomTarget']]: + keys = [] + candidates = [name, OutputTargetMap.rm_so_version.sub('', name)] + for i in lib_suffixes: + if not name.endswith('.' + i): + continue + new_name = name[:-len(i) - 1] + new_name = OutputTargetMap.rm_so_version.sub('', new_name) + candidates += ['{}.{}'.format(new_name, i)] + for i in candidates: + keys += [self._rel_artifact_key(i), os.path.basename(i), self._base_artifact_key(i)] + return self._return_first_valid_key(keys) + + def generated(self, name: str) -> Optional[Union['ConverterTarget', 'ConverterCustomTarget']]: + return self._return_first_valid_key([self._rel_generated_file_key(name), self._base_generated_file_key(name)]) + + # Utility functions to generate local keys + def _rel_path(self, fname: str) -> Optional[str]: + fname = os.path.normpath(os.path.join(self.build_dir, fname)) + if os.path.commonpath([self.build_dir, fname]) != self.build_dir: + return None + return os.path.relpath(fname, self.build_dir) + + def _target_key(self, tgt_name: str) -> str: + return '__tgt_{}__'.format(tgt_name) + + def _rel_generated_file_key(self, fname: str) -> Optional[str]: + path = self._rel_path(fname) + return '__relgen_{}__'.format(path) if path else None + + def _base_generated_file_key(self, fname: str) -> str: + return '__gen_{}__'.format(os.path.basename(fname)) + + def _rel_artifact_key(self, fname: str) -> Optional[str]: + path = self._rel_path(fname) + return '__relart_{}__'.format(path) if path else None -def _generated_file_key(fname: str) -> str: - return '__gen_{}__'.format(os.path.basename(fname)) + def _base_artifact_key(self, fname: str) -> str: + return '__art_{}__'.format(os.path.basename(fname)) class ConverterTarget: lang_cmake_to_meson = {val.lower(): key for key, val in language_map.items()} - rm_so_version = re.compile(r'(\.[0-9]+)+$') def __init__(self, target: CMakeTarget, env: Environment): self.env = env @@ -204,7 +266,7 @@ class ConverterTarget: std_regex = re.compile(r'([-]{1,2}std=|/std:v?|[-]{1,2}std:)(.*)') - def postprocess(self, output_target_map: dict, root_src_dir: str, subdir: str, install_prefix: str, trace: CMakeTraceParser) -> None: + def postprocess(self, output_target_map: OutputTargetMap, root_src_dir: str, subdir: str, install_prefix: str, trace: CMakeTraceParser) -> None: # Detect setting the C and C++ standard for i in ['c', 'cpp']: if i not in self.compile_opts: @@ -242,29 +304,13 @@ class ConverterTarget: elif self.type.upper() not in ['EXECUTABLE', 'OBJECT_LIBRARY']: mlog.warning('CMake: Target', mlog.bold(self.cmake_name), 'not found in CMake trace. This can lead to build errors') - # Fix link libraries - def try_resolve_link_with(path: str) -> Optional[str]: - basename = os.path.basename(path) - candidates = [basename, ConverterTarget.rm_so_version.sub('', basename)] - for i in lib_suffixes: - if not basename.endswith('.' + i): - continue - new_basename = basename[:-len(i) - 1] - new_basename = ConverterTarget.rm_so_version.sub('', new_basename) - new_basename = '{}.{}'.format(new_basename, i) - candidates += [new_basename] - for i in candidates: - if i in output_target_map: - return output_target_map[i] - return None - temp = [] for i in self.link_libraries: # Let meson handle this arcane magic if ',-rpath,' in i: continue if not os.path.isabs(i): - link_with = try_resolve_link_with(i) + link_with = output_target_map.artifact(i) if link_with: self.link_with += [link_with] continue @@ -297,9 +343,8 @@ class ConverterTarget: return x def custom_target(x: str): - key = _generated_file_key(x) - if key in output_target_map: - ctgt = output_target_map[key] + ctgt = output_target_map.generated(x) + if ctgt: assert(isinstance(ctgt, ConverterCustomTarget)) ref = ctgt.get_ref(x) assert(isinstance(ref, CustomTargetReference) and ref.valid()) @@ -343,7 +388,7 @@ class ConverterTarget: # Handle explicit CMake add_dependency() calls for i in self.depends_raw: - tgt = output_target_map.get(_target_key(i)) + tgt = output_target_map.target(i) if tgt: self.depends.append(tgt) @@ -451,7 +496,7 @@ class ConverterCustomTarget: def __repr__(self) -> str: return '<{}: {} {}>'.format(self.__class__.__name__, self.name, self.outputs) - def postprocess(self, output_target_map: dict, root_src_dir: str, subdir: str, build_dir: str, all_outputs: List[str]) -> None: + def postprocess(self, output_target_map: OutputTargetMap, root_src_dir: str, subdir: str, build_dir: str, all_outputs: List[str]) -> None: # Default the working directory to the CMake build dir. This # is not 100% correct, since it should be the value of # ${CMAKE_CURRENT_BINARY_DIR} when add_custom_command is @@ -498,11 +543,8 @@ class ConverterCustomTarget: for j in i: if not j: continue - target_key = _target_key(j) - if target_key in output_target_map: - cmd += [output_target_map[target_key]] - else: - cmd += [j] + target = output_target_map.target(j) + cmd += [target] if target else [j] commands += [cmd] self.command = commands @@ -516,15 +558,16 @@ class ConverterCustomTarget: for i in self.depends_raw: if not i: continue - tgt_key = _target_key(i) - gen_key = _generated_file_key(i) - - if os.path.basename(i) in output_target_map: - self.depends += [output_target_map[os.path.basename(i)]] - elif tgt_key in output_target_map: - self.depends += [output_target_map[tgt_key]] - elif gen_key in output_target_map: - self.inputs += [output_target_map[gen_key].get_ref(i)] + art = output_target_map.artifact(i) + tgt = output_target_map.target(i) + gen = output_target_map.generated(i) + + if art: + self.depends += [art] + elif tgt: + self.depends += [tgt] + elif gen: + self.inputs += [gen.get_ref(i)] elif not os.path.isabs(i) and os.path.exists(os.path.join(root_src_dir, i)): self.inputs += [i] elif os.path.isabs(i) and os.path.exists(i) and os.path.commonpath([i, root_src_dir]) == root_src_dir: @@ -544,10 +587,11 @@ class ConverterCustomTarget: self.depends = list(set(new_deps)) def get_ref(self, fname: str) -> Optional[CustomTargetReference]: + fname = os.path.basename(fname) try: if fname in self.conflict_map: fname = self.conflict_map[fname] - idx = self.outputs.index(os.path.basename(fname)) + idx = self.outputs.index(fname) return CustomTargetReference(self, idx) except ValueError: return None @@ -592,6 +636,7 @@ class CMakeInterpreter: self.targets = [] self.custom_targets = [] # type: List[ConverterCustomTarget] self.trace = CMakeTraceParser() + self.output_target_map = OutputTargetMap(self.build_dir) # Generated meson data self.generated_targets = {} @@ -770,29 +815,16 @@ class CMakeInterpreter: self.custom_targets += [ConverterCustomTarget(i)] # generate the output_target_map - output_target_map = {} - output_target_map.update({x.full_name: x for x in self.targets}) - output_target_map.update({_target_key(x.cmake_name): x for x in self.targets}) - for i in self.targets: - for j in i.artifacts: - output_target_map[os.path.basename(j)] = i - for i in self.custom_targets: - output_target_map[_target_key(i.cmake_name)] = i - for j in i.original_outputs: - output_target_map[_generated_file_key(j)] = i - object_libs = [] - - # Sometimes an empty string can be inserted (no full name, etc.) - # Delete the entry in this case - if '' in output_target_map: - del output_target_map[''] + for i in [*self.targets, *self.custom_targets]: + self.output_target_map.add(i) # First pass: Basic target cleanup + object_libs = [] custom_target_outputs = [] # type: List[str] for i in self.custom_targets: - i.postprocess(output_target_map, self.src_dir, self.subdir, self.build_dir, custom_target_outputs) + i.postprocess(self.output_target_map, self.src_dir, self.subdir, self.build_dir, custom_target_outputs) for i in self.targets: - i.postprocess(output_target_map, self.src_dir, self.subdir, self.install_prefix, self.trace) + i.postprocess(self.output_target_map, self.src_dir, self.subdir, self.install_prefix, self.trace) if i.type == 'OBJECT_LIBRARY': object_libs += [i] self.languages += [x for x in i.languages if x not in self.languages] @@ -806,12 +838,10 @@ class CMakeInterpreter: i.process_inter_target_dependencies() for i in self.custom_targets: i.process_inter_target_dependencies() - i.log() # Fourth pass: Remove rassigned dependencies for i in self.targets: i.cleanup_dependencies() - i.log() mlog.log('CMake project', mlog.bold(self.project_name), 'has', mlog.bold(str(len(self.targets) + len(self.custom_targets))), 'build targets.') |