diff options
Diffstat (limited to 'mesonbuild/cmake/interpreter.py')
-rw-r--r-- | mesonbuild/cmake/interpreter.py | 64 |
1 files changed, 56 insertions, 8 deletions
diff --git a/mesonbuild/cmake/interpreter.py b/mesonbuild/cmake/interpreter.py index d9f1e18..66d0994 100644 --- a/mesonbuild/cmake/interpreter.py +++ b/mesonbuild/cmake/interpreter.py @@ -25,9 +25,10 @@ from ..environment import Environment from ..mesonlib import MachineChoice, version_compare from ..compilers.compilers import lang_suffixes, header_suffixes, obj_suffixes, lib_suffixes, is_header from subprocess import Popen, PIPE -from typing import Any, List, Dict, Optional, Union, TYPE_CHECKING +from typing import Any, List, Dict, Optional, Set, Union, TYPE_CHECKING from threading import Thread from enum import Enum +from functools import lru_cache import os, re from ..mparser import ( @@ -391,23 +392,60 @@ class ConverterTarget: if tgt: self.depends.append(tgt) - def process_object_libs(self, obj_target_list: List['ConverterTarget']): + def process_object_libs(self, obj_target_list: List['ConverterTarget'], linker_workaround: bool): # Try to detect the object library(s) from the generated input sources temp = [x for x in self.generated if isinstance(x, str)] temp = [os.path.basename(x) for x in temp] temp = [x for x in temp if any([x.endswith('.' + y) for y in obj_suffixes])] temp = [os.path.splitext(x)[0] for x in temp] + exts = self._all_source_suffixes() # Temp now stores the source filenames of the object files for i in obj_target_list: - source_files = [os.path.basename(x) for x in i.sources + i.generated] - for j in source_files: - if j in temp: - self.object_libs += [i] + source_files = [x for x in i.sources + i.generated if isinstance(x, str)] + source_files = [os.path.basename(x) for x in source_files] + for j in temp: + # On some platforms (specifically looking at you Windows with vs20xy backend) CMake does + # not produce object files with the format `foo.cpp.obj`, instead it skipps the language + # suffix and just produces object files like `foo.obj`. Thus we have to do our best to + # undo this step and guess the correct language suffix of the object file. This is done + # by trying all language suffixes meson knows and checking if one of them fits. + candidates = [j] # type: List[str] + if not any([j.endswith('.' + x) for x in exts]): + mlog.warning('Object files do not contain source file extensions, thus falling back to guessing them.', once=True) + candidates += ['{}.{}'.format(j, x) for x in exts] + if any([x in source_files for x in candidates]): + if linker_workaround: + self._append_objlib_sources(i) + else: + self.includes += i.includes + self.includes = list(set(self.includes)) + self.object_libs += [i] break # Filter out object files from the sources self.generated = [x for x in self.generated if not isinstance(x, str) or not any([x.endswith('.' + y) for y in obj_suffixes])] + def _append_objlib_sources(self, tgt: 'ConverterTarget') -> None: + self.includes += tgt.includes + self.sources += tgt.sources + self.generated += tgt.generated + self.sources = list(set(self.sources)) + self.generated = list(set(self.generated)) + self.includes = list(set(self.includes)) + + # Inherit compiler arguments since they may be required for building + for lang, opts in tgt.compile_opts.items(): + if lang not in self.compile_opts: + self.compile_opts[lang] = [] + self.compile_opts[lang] += [x for x in opts if x not in self.compile_opts[lang]] + + @lru_cache(maxsize=None) + def _all_source_suffixes(self) -> List[str]: + suffixes = [] # type: List[str] + for exts in lang_suffixes.values(): + suffixes += [x for x in exts] + return suffixes + def process_inter_target_dependencies(self): # Move the dependencies from all transfer_dependencies_from to the target to_process = list(self.depends) @@ -620,6 +658,7 @@ class CMakeInterpreter: self.install_prefix = install_prefix self.env = env self.backend_name = backend.name + self.linkers = set() # type: Set[str] self.cmake_api = CMakeAPI.SERVER self.client = CMakeClient(self.env) self.fileapi = CMakeFileAPI(self.build_dir) @@ -661,6 +700,7 @@ class CMakeInterpreter: for lang, comp in self.env.coredata.compilers[for_machine].items(): if lang not in language_map: continue + self.linkers.add(comp.get_linker_id()) cmake_lang = language_map[lang] exelist = comp.get_exelist() if len(exelist) == 1: @@ -787,12 +827,16 @@ class CMakeInterpreter: self.trace.parse(self.raw_trace) # Find all targets + added_target_names = [] # type: List[str] for i in self.codemodel_configs: for j in i.projects: if not self.project_name: self.project_name = j.name for k in j.targets: - if k.type not in skip_targets: + # Avoid duplicate targets from different configurations and known + # dummy CMake internal target types + if k.type not in skip_targets and k.name not in added_target_names: + added_target_names += [k.name] self.targets += [ConverterTarget(k, self.env)] # Add interface targets from trace, if not already present. @@ -830,7 +874,7 @@ class CMakeInterpreter: # Second pass: Detect object library dependencies for i in self.targets: - i.process_object_libs(object_libs) + i.process_object_libs(object_libs, self._object_lib_workaround()) # Third pass: Reassign dependencies to avoid some loops for i in self.targets: @@ -1010,6 +1054,7 @@ class CMakeInterpreter: # Generate target kwargs tgt_kwargs = { + 'build_by_default': False, 'link_args': tgt.link_flags + tgt.link_libraries, 'link_with': link_with, 'include_directories': id_node(inc_var), @@ -1144,3 +1189,6 @@ class CMakeInterpreter: res = [x for x in self.generated_targets.keys()] res = [x[prx_len:] if x.startswith(prx_str) else x for x in res] return res + + def _object_lib_workaround(self) -> bool: + return 'link' in self.linkers and self.backend_name.startswith('vs') |