diff options
7 files changed, 160 insertions, 66 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.') diff --git a/test cases/cmake/8 custom command/subprojects/cmMod/CMakeLists.txt b/test cases/cmake/8 custom command/subprojects/cmMod/CMakeLists.txt index 3c3297e..776ce52 100644 --- a/test cases/cmake/8 custom command/subprojects/cmMod/CMakeLists.txt +++ b/test cases/cmake/8 custom command/subprojects/cmMod/CMakeLists.txt @@ -10,6 +10,7 @@ add_definitions("-DDO_NOTHING_JUST_A_FLAG=1") add_executable(gen main.cpp) add_executable(mycpy cp.cpp) +# cpyBase add_custom_command( OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/genTest.cpp" "${CMAKE_CURRENT_BINARY_DIR}/genTest.hpp" COMMAND gen ARGS genTest @@ -42,7 +43,53 @@ add_custom_command( DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/cpyBase.hpp.something" ) -add_library(cmModLib SHARED cmMod.cpp genTest.cpp cpyBase.cpp cpyBase.hpp) +# cpyNext (out of order is on purpose) +# -- first copy round +add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/s1_a_hpp/file.txt" + COMMAND mycpy "${CMAKE_CURRENT_SOURCE_DIR}/cpyNext.hpp.am" file.txt + DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/cpyNext.hpp.am" + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/s1_a_hpp" +) + +add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/s1_b_cpp/file.txt" + COMMAND mycpy "${CMAKE_CURRENT_SOURCE_DIR}/cpyNext.cpp.am" file.txt + DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/cpyNext.cpp.am" + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/s1_b_cpp" +) + +# -- final cpy round +add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/cpyNext.hpp" + COMMAND mycpy "${CMAKE_CURRENT_BINARY_DIR}/s2_b_hpp/file.txt" cpyNext.hpp + DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/s2_b_hpp/file.txt" + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" +) + +add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/cpyNext.cpp" + COMMAND mycpy "${CMAKE_CURRENT_BINARY_DIR}/s2_a_cpp/file.txt" cpyNext.cpp + DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/s2_a_cpp/file.txt" + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" +) + +# -- second copy round +add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/s2_b_hpp/file.txt" + COMMAND mycpy "${CMAKE_CURRENT_BINARY_DIR}/s1_a_hpp/file.txt" file.txt + DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/s1_a_hpp/file.txt" + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/s2_b_hpp" +) + +add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/s2_a_cpp/file.txt" + COMMAND mycpy "${CMAKE_CURRENT_BINARY_DIR}/s1_b_cpp/file.txt" file.txt + DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/s1_b_cpp/file.txt" + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/s2_a_cpp" +) + +add_library(cmModLib SHARED cmMod.cpp genTest.cpp cpyBase.cpp cpyBase.hpp cpyNext.cpp cpyNext.hpp) include(GenerateExportHeader) generate_export_header(cmModLib) diff --git a/test cases/cmake/8 custom command/subprojects/cmMod/cmMod.cpp b/test cases/cmake/8 custom command/subprojects/cmMod/cmMod.cpp index b7e8200..e6236e4 100644 --- a/test cases/cmake/8 custom command/subprojects/cmMod/cmMod.cpp +++ b/test cases/cmake/8 custom command/subprojects/cmMod/cmMod.cpp @@ -1,6 +1,7 @@ #include "cmMod.hpp" #include "genTest.hpp" #include "cpyBase.hpp" +#include "cpyNext.hpp" #include "cmModLib.hpp" #ifndef FOO @@ -18,5 +19,5 @@ string cmModClass::getStr() const { } string cmModClass::getOther() const { - return getStr() + " -- " + getStrCpy(); + return "Srings:\n - " + getStrCpy() + "\n - " + getStrNext(); } diff --git a/test cases/cmake/8 custom command/subprojects/cmMod/cp.cpp b/test cases/cmake/8 custom command/subprojects/cmMod/cp.cpp index 2744da8..09433f2 100644 --- a/test cases/cmake/8 custom command/subprojects/cmMod/cp.cpp +++ b/test cases/cmake/8 custom command/subprojects/cmMod/cp.cpp @@ -12,6 +12,11 @@ int main(int argc, char *argv[]) { ifstream src(argv[1]); ofstream dst(argv[2]); + if(!src.is_open()) { + cerr << "Failed to open " << argv[1] << endl; + return 2; + } + dst << src.rdbuf(); return 0; } diff --git a/test cases/cmake/8 custom command/subprojects/cmMod/cpyNext.cpp.am b/test cases/cmake/8 custom command/subprojects/cmMod/cpyNext.cpp.am new file mode 100644 index 0000000..20a8815 --- /dev/null +++ b/test cases/cmake/8 custom command/subprojects/cmMod/cpyNext.cpp.am @@ -0,0 +1,5 @@ +#include "cpyNext.hpp" + +std::string getStrNext() { + return "Hello Copied File -- now even more convoluted!"; +} diff --git a/test cases/cmake/8 custom command/subprojects/cmMod/cpyNext.hpp.am b/test cases/cmake/8 custom command/subprojects/cmMod/cpyNext.hpp.am new file mode 100644 index 0000000..41919d8 --- /dev/null +++ b/test cases/cmake/8 custom command/subprojects/cmMod/cpyNext.hpp.am @@ -0,0 +1,5 @@ +#pragma once + +#include <string> + +std::string getStrNext(); |