|
|
|
@ -15,13 +15,12 @@ |
|
|
|
|
# This class contains the basic functionality needed to run any interpreter |
|
|
|
|
# or an interpreter-based tool. |
|
|
|
|
|
|
|
|
|
from .common import CMakeException, CMakeTarget, TargetOptions |
|
|
|
|
from .client import CMakeClient, RequestCMakeInputs, RequestConfigure, RequestCompute, RequestCodeModel |
|
|
|
|
from .common import CMakeException, CMakeTarget, TargetOptions, CMakeConfiguration |
|
|
|
|
from .client import CMakeClient, RequestCMakeInputs, RequestConfigure, RequestCompute, RequestCodeModel, ReplyCMakeInputs, ReplyCodeModel |
|
|
|
|
from .fileapi import CMakeFileAPI |
|
|
|
|
from .executor import CMakeExecutor |
|
|
|
|
from .traceparser import CMakeTraceParser, CMakeGeneratorTarget |
|
|
|
|
from .. import mlog, mesonlib |
|
|
|
|
from ..environment import Environment |
|
|
|
|
from ..mesonlib import MachineChoice, OrderedSet, version_compare |
|
|
|
|
from ..mesondata import mesondata |
|
|
|
|
from ..compilers.compilers import lang_suffixes, header_suffixes, obj_suffixes, lib_suffixes, is_header |
|
|
|
@ -51,6 +50,11 @@ from ..mparser import ( |
|
|
|
|
if T.TYPE_CHECKING: |
|
|
|
|
from ..build import Build |
|
|
|
|
from ..backend.backends import Backend |
|
|
|
|
from ..environment import Environment |
|
|
|
|
|
|
|
|
|
TYPE_mixed = T.Union[str, int, bool, BaseNode] |
|
|
|
|
TYPE_mixed_list = T.Union[TYPE_mixed, T.Sequence[TYPE_mixed]] |
|
|
|
|
TYPE_mixed_kwargs = T.Dict[str, TYPE_mixed_list] |
|
|
|
|
|
|
|
|
|
# Disable all warnings automaticall enabled with --trace and friends |
|
|
|
|
# See https://cmake.org/cmake/help/latest/variable/CMAKE_POLICY_WARNING_CMPNNNN.html |
|
|
|
@ -138,7 +142,7 @@ class OutputTargetMap: |
|
|
|
|
rm_so_version = re.compile(r'(\.[0-9]+)+$') |
|
|
|
|
|
|
|
|
|
def __init__(self, build_dir: str): |
|
|
|
|
self.tgt_map = {} |
|
|
|
|
self.tgt_map = {} # type: T.Dict[str, T.Union['ConverterTarget', 'ConverterCustomTarget']] |
|
|
|
|
self.build_dir = build_dir |
|
|
|
|
|
|
|
|
|
def add(self, tgt: T.Union['ConverterTarget', 'ConverterCustomTarget']) -> None: |
|
|
|
@ -185,8 +189,10 @@ class OutputTargetMap: |
|
|
|
|
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) -> T.Optional[T.Union['ConverterTarget', 'ConverterCustomTarget']]: |
|
|
|
|
return self._return_first_valid_key([self._rel_generated_file_key(name), self._base_generated_file_key(name)]) |
|
|
|
|
def generated(self, name: str) -> T.Optional['ConverterCustomTarget']: |
|
|
|
|
res = self._return_first_valid_key([self._rel_generated_file_key(name), self._base_generated_file_key(name)]) |
|
|
|
|
assert res is None or isinstance(res, ConverterCustomTarget) |
|
|
|
|
return res |
|
|
|
|
|
|
|
|
|
# Utility functions to generate local keys |
|
|
|
|
def _rel_path(self, fname: str) -> T.Optional[str]: |
|
|
|
@ -213,42 +219,44 @@ class OutputTargetMap: |
|
|
|
|
return '__art_{}__'.format(os.path.basename(fname)) |
|
|
|
|
|
|
|
|
|
class ConverterTarget: |
|
|
|
|
def __init__(self, target: CMakeTarget, env: Environment): |
|
|
|
|
self.env = env |
|
|
|
|
self.artifacts = target.artifacts |
|
|
|
|
self.src_dir = target.src_dir |
|
|
|
|
self.build_dir = target.build_dir |
|
|
|
|
self.name = target.name |
|
|
|
|
self.cmake_name = target.name |
|
|
|
|
self.full_name = target.full_name |
|
|
|
|
self.type = target.type |
|
|
|
|
self.install = target.install |
|
|
|
|
self.install_dir = '' |
|
|
|
|
def __init__(self, target: CMakeTarget, env: 'Environment') -> None: |
|
|
|
|
self.env = env |
|
|
|
|
self.artifacts = target.artifacts |
|
|
|
|
self.src_dir = target.src_dir |
|
|
|
|
self.build_dir = target.build_dir |
|
|
|
|
self.name = target.name |
|
|
|
|
self.cmake_name = target.name |
|
|
|
|
self.full_name = target.full_name |
|
|
|
|
self.type = target.type |
|
|
|
|
self.install = target.install |
|
|
|
|
self.install_dir = '' |
|
|
|
|
self.link_libraries = target.link_libraries |
|
|
|
|
self.link_flags = target.link_flags + target.link_lang_flags |
|
|
|
|
self.depends_raw = [] |
|
|
|
|
self.depends = [] |
|
|
|
|
self.link_flags = target.link_flags + target.link_lang_flags |
|
|
|
|
self.depends_raw = [] # type: T.List[str] |
|
|
|
|
self.depends = [] # type: T.List[T.Union[ConverterTarget, ConverterCustomTarget]] |
|
|
|
|
|
|
|
|
|
if target.install_paths: |
|
|
|
|
self.install_dir = target.install_paths[0] |
|
|
|
|
|
|
|
|
|
self.languages = [] |
|
|
|
|
self.sources = [] |
|
|
|
|
self.generated = [] |
|
|
|
|
self.includes = [] |
|
|
|
|
self.sys_includes = [] |
|
|
|
|
self.link_with = [] |
|
|
|
|
self.object_libs = [] |
|
|
|
|
self.compile_opts = {} |
|
|
|
|
self.public_compile_opts = [] |
|
|
|
|
self.pie = False |
|
|
|
|
self.languages = [] # type: T.List[str] |
|
|
|
|
self.sources = [] # type: T.List[str] |
|
|
|
|
self.generated = [] # type: T.List[str] |
|
|
|
|
self.generated_ctgt = [] # type: T.List[CustomTargetReference] |
|
|
|
|
self.includes = [] # type: T.List[str] |
|
|
|
|
self.sys_includes = [] # type: T.List[str] |
|
|
|
|
self.link_with = [] # type: T.List[T.Union[ConverterTarget, ConverterCustomTarget]] |
|
|
|
|
self.object_libs = [] # type: T.List[ConverterTarget] |
|
|
|
|
self.compile_opts = {} # type: T.Dict[str, T.List[str]] |
|
|
|
|
self.public_compile_opts = [] # type: T.List[str] |
|
|
|
|
self.pie = False |
|
|
|
|
|
|
|
|
|
# Project default override options (c_std, cpp_std, etc.) |
|
|
|
|
self.override_options = [] |
|
|
|
|
self.override_options = [] # type: T.List[str] |
|
|
|
|
|
|
|
|
|
# Convert the target name to a valid meson target name |
|
|
|
|
self.name = _sanitize_cmake_name(self.name) |
|
|
|
|
|
|
|
|
|
self.generated_raw = [] # type: T.List[str] |
|
|
|
|
for i in target.files: |
|
|
|
|
# Determine the meson language |
|
|
|
|
lang_cmake_to_meson = {val.lower(): key for key, val in language_map.items()} |
|
|
|
@ -264,12 +272,12 @@ class ConverterTarget: |
|
|
|
|
self.compile_opts[lang] += [x for x in args if x not in self.compile_opts[lang]] |
|
|
|
|
|
|
|
|
|
# Handle include directories |
|
|
|
|
self.includes += [x['path'] for x in i.includes if x not in self.includes and not x['isSystem']] |
|
|
|
|
self.sys_includes += [x['path'] for x in i.includes if x not in self.sys_includes and x['isSystem']] |
|
|
|
|
self.includes += [x.path for x in i.includes if x.path not in self.includes and not x.isSystem] |
|
|
|
|
self.sys_includes += [x.path for x in i.includes if x.path not in self.sys_includes and x.isSystem] |
|
|
|
|
|
|
|
|
|
# Add sources to the right array |
|
|
|
|
if i.is_generated: |
|
|
|
|
self.generated += i.sources |
|
|
|
|
self.generated_raw += i.sources |
|
|
|
|
else: |
|
|
|
|
self.sources += i.sources |
|
|
|
|
|
|
|
|
@ -306,7 +314,7 @@ class ConverterTarget: |
|
|
|
|
# Sometimes projects pass generated source files as compiler |
|
|
|
|
# flags. Add these as generated sources to ensure that the |
|
|
|
|
# corresponding custom target is run.2 |
|
|
|
|
self.generated += [j] |
|
|
|
|
self.generated_raw += [j] |
|
|
|
|
temp += [j] |
|
|
|
|
elif j in blacklist_compiler_flags: |
|
|
|
|
pass |
|
|
|
@ -339,7 +347,7 @@ class ConverterTarget: |
|
|
|
|
cfg = '' |
|
|
|
|
otherDeps = [] |
|
|
|
|
libraries = [] |
|
|
|
|
mlog.debug(tgt) |
|
|
|
|
mlog.debug(str(tgt)) |
|
|
|
|
|
|
|
|
|
if 'INTERFACE_INCLUDE_DIRECTORIES' in tgt.properties: |
|
|
|
|
self.includes += [x for x in tgt.properties['INTERFACE_INCLUDE_DIRECTORIES'] if x] |
|
|
|
@ -424,8 +432,8 @@ class ConverterTarget: |
|
|
|
|
for i in self.languages: |
|
|
|
|
supported += list(lang_suffixes[i]) |
|
|
|
|
supported = ['.{}'.format(x) for x in supported] |
|
|
|
|
self.sources = [x for x in self.sources if any([x.endswith(y) for y in supported])] |
|
|
|
|
self.generated = [x for x in self.generated if any([x.endswith(y) for y in supported])] |
|
|
|
|
self.sources = [x for x in self.sources if any([x.endswith(y) for y in supported])] |
|
|
|
|
self.generated_raw = [x for x in self.generated_raw if any([x.endswith(y) for y in supported])] |
|
|
|
|
|
|
|
|
|
# Make paths relative |
|
|
|
|
def rel_path(x: str, is_header: bool, is_generated: bool) -> T.Optional[str]: |
|
|
|
@ -434,7 +442,7 @@ class ConverterTarget: |
|
|
|
|
x = os.path.normpath(x) |
|
|
|
|
if not os.path.exists(x) and not any([x.endswith(y) for y in obj_suffixes]) and not is_generated: |
|
|
|
|
if ( |
|
|
|
|
any([os.path.commonpath([x, os.path.normpath(os.path.join(root_src_dir, y))]) == x for y in self.generated]) |
|
|
|
|
any([os.path.commonpath([x, os.path.normpath(os.path.join(root_src_dir, y))]) == x for y in self.generated_raw]) |
|
|
|
|
and os.path.commonpath([x, self.env.get_build_dir()]) == self.env.get_build_dir() |
|
|
|
|
): |
|
|
|
|
os.makedirs(x) |
|
|
|
@ -465,29 +473,27 @@ class ConverterTarget: |
|
|
|
|
return os.path.relpath(x, root_src_dir) |
|
|
|
|
return x |
|
|
|
|
|
|
|
|
|
def custom_target(x: str): |
|
|
|
|
ctgt = output_target_map.generated(x) |
|
|
|
|
if ctgt: |
|
|
|
|
assert(isinstance(ctgt, ConverterCustomTarget)) |
|
|
|
|
ref = ctgt.get_ref(x) |
|
|
|
|
assert(isinstance(ref, CustomTargetReference) and ref.valid()) |
|
|
|
|
return ref |
|
|
|
|
return x |
|
|
|
|
|
|
|
|
|
build_dir_rel = os.path.relpath(self.build_dir, os.path.join(self.env.get_build_dir(), subdir)) |
|
|
|
|
self.generated = [rel_path(x, False, True) for x in self.generated] |
|
|
|
|
self.generated_raw = [rel_path(x, False, True) for x in self.generated_raw] |
|
|
|
|
self.includes = list(OrderedSet([rel_path(x, True, False) for x in OrderedSet(self.includes)] + [build_dir_rel])) |
|
|
|
|
self.sys_includes = list(OrderedSet([rel_path(x, True, False) for x in OrderedSet(self.sys_includes)])) |
|
|
|
|
self.sources = [rel_path(x, False, False) for x in self.sources] |
|
|
|
|
|
|
|
|
|
# Resolve custom targets |
|
|
|
|
self.generated = [custom_target(x) for x in self.generated] |
|
|
|
|
for gen_file in self.generated_raw: |
|
|
|
|
ctgt = output_target_map.generated(gen_file) |
|
|
|
|
if ctgt: |
|
|
|
|
assert isinstance(ctgt, ConverterCustomTarget) |
|
|
|
|
ref = ctgt.get_ref(gen_file) |
|
|
|
|
assert isinstance(ref, CustomTargetReference) and ref.valid() |
|
|
|
|
self.generated_ctgt += [ref] |
|
|
|
|
elif gen_file is not None: |
|
|
|
|
self.generated += [gen_file] |
|
|
|
|
|
|
|
|
|
# Remove delete entries |
|
|
|
|
self.includes = [x for x in self.includes if x is not None] |
|
|
|
|
self.includes = [x for x in self.includes if x is not None] |
|
|
|
|
self.sys_includes = [x for x in self.sys_includes if x is not None] |
|
|
|
|
self.sources = [x for x in self.sources if x is not None] |
|
|
|
|
self.generated = [x for x in self.generated if x is not None] |
|
|
|
|
self.sources = [x for x in self.sources if x is not None] |
|
|
|
|
|
|
|
|
|
# Make sure '.' is always in the include directories |
|
|
|
|
if '.' not in self.includes: |
|
|
|
@ -511,20 +517,20 @@ class ConverterTarget: |
|
|
|
|
|
|
|
|
|
# Handle explicit CMake add_dependency() calls |
|
|
|
|
for i in self.depends_raw: |
|
|
|
|
tgt = output_target_map.target(i) |
|
|
|
|
if tgt: |
|
|
|
|
self.depends.append(tgt) |
|
|
|
|
dep_tgt = output_target_map.target(i) |
|
|
|
|
if dep_tgt: |
|
|
|
|
self.depends.append(dep_tgt) |
|
|
|
|
|
|
|
|
|
def process_object_libs(self, obj_target_list: T.List['ConverterTarget'], linker_workaround: bool): |
|
|
|
|
def process_object_libs(self, obj_target_list: T.List['ConverterTarget'], linker_workaround: bool) -> None: |
|
|
|
|
# Try to detect the object library(s) from the generated input sources |
|
|
|
|
temp = [x for x in self.generated if isinstance(x, str)] |
|
|
|
|
temp = [x for x in self.generated] |
|
|
|
|
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 = [x for x in i.sources + i.generated if isinstance(x, str)] |
|
|
|
|
source_files = [x for x in i.sources + i.generated] |
|
|
|
|
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 |
|
|
|
@ -546,15 +552,17 @@ class ConverterTarget: |
|
|
|
|
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])] |
|
|
|
|
self.generated = [x for x in self.generated if 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(OrderedSet(self.sources)) |
|
|
|
|
self.generated = list(OrderedSet(self.generated)) |
|
|
|
|
self.includes = list(OrderedSet(self.includes)) |
|
|
|
|
self.includes += tgt.includes |
|
|
|
|
self.sources += tgt.sources |
|
|
|
|
self.generated += tgt.generated |
|
|
|
|
self.generated_ctgt += tgt.generated_ctgt |
|
|
|
|
self.includes = list(OrderedSet(self.includes)) |
|
|
|
|
self.sources = list(OrderedSet(self.sources)) |
|
|
|
|
self.generated = list(OrderedSet(self.generated)) |
|
|
|
|
self.generated_ctgt = list(OrderedSet(self.generated_ctgt)) |
|
|
|
|
|
|
|
|
|
# Inherit compiler arguments since they may be required for building |
|
|
|
|
for lang, opts in tgt.compile_opts.items(): |
|
|
|
@ -574,9 +582,16 @@ class ConverterTarget: |
|
|
|
|
lang_opts = self.env.coredata.compiler_options.build.get(lang, None) |
|
|
|
|
if not lang_opts or 'std' not in lang_opts: |
|
|
|
|
return [] |
|
|
|
|
return lang_opts['std'].choices |
|
|
|
|
res = lang_opts['std'].choices |
|
|
|
|
|
|
|
|
|
# TODO: Get rid of this once we have propper typing for options |
|
|
|
|
assert isinstance(res, list) |
|
|
|
|
for i in res: |
|
|
|
|
assert isinstance(i, str) |
|
|
|
|
|
|
|
|
|
def process_inter_target_dependencies(self): |
|
|
|
|
return res |
|
|
|
|
|
|
|
|
|
def process_inter_target_dependencies(self) -> None: |
|
|
|
|
# Move the dependencies from all transfer_dependencies_from to the target |
|
|
|
|
to_process = list(self.depends) |
|
|
|
|
processed = [] |
|
|
|
@ -589,7 +604,7 @@ class ConverterTarget: |
|
|
|
|
new_deps += [i] |
|
|
|
|
self.depends = list(OrderedSet(new_deps)) |
|
|
|
|
|
|
|
|
|
def cleanup_dependencies(self): |
|
|
|
|
def cleanup_dependencies(self) -> None: |
|
|
|
|
# Clear the dependencies from targets that where moved from |
|
|
|
|
if self.meson_func() in transfer_dependencies_from: |
|
|
|
|
self.depends = [] |
|
|
|
@ -613,6 +628,7 @@ class ConverterTarget: |
|
|
|
|
mlog.log(' -- sys_includes: ', mlog.bold(str(self.sys_includes))) |
|
|
|
|
mlog.log(' -- sources: ', mlog.bold(str(self.sources))) |
|
|
|
|
mlog.log(' -- generated: ', mlog.bold(str(self.generated))) |
|
|
|
|
mlog.log(' -- generated_ctgt: ', mlog.bold(str(self.generated_ctgt))) |
|
|
|
|
mlog.log(' -- pie: ', mlog.bold('true' if self.pie else 'false')) |
|
|
|
|
mlog.log(' -- override_opts: ', mlog.bold(str(self.override_options))) |
|
|
|
|
mlog.log(' -- depends: ', mlog.bold(str(self.depends))) |
|
|
|
@ -621,7 +637,7 @@ class ConverterTarget: |
|
|
|
|
mlog.log(' -', key, '=', mlog.bold(str(val))) |
|
|
|
|
|
|
|
|
|
class CustomTargetReference: |
|
|
|
|
def __init__(self, ctgt: 'ConverterCustomTarget', index: int): |
|
|
|
|
def __init__(self, ctgt: 'ConverterCustomTarget', index: int) -> None: |
|
|
|
|
self.ctgt = ctgt # type: ConverterCustomTarget |
|
|
|
|
self.index = index # type: int |
|
|
|
|
|
|
|
|
@ -641,24 +657,25 @@ class ConverterCustomTarget: |
|
|
|
|
tgt_counter = 0 # type: int |
|
|
|
|
out_counter = 0 # type: int |
|
|
|
|
|
|
|
|
|
def __init__(self, target: CMakeGeneratorTarget): |
|
|
|
|
assert(target.current_bin_dir is not None) |
|
|
|
|
assert(target.current_src_dir is not None) |
|
|
|
|
def __init__(self, target: CMakeGeneratorTarget) -> None: |
|
|
|
|
assert target.current_bin_dir is not None |
|
|
|
|
assert target.current_src_dir is not None |
|
|
|
|
self.name = target.name |
|
|
|
|
if not self.name: |
|
|
|
|
self.name = 'custom_tgt_{}'.format(ConverterCustomTarget.tgt_counter) |
|
|
|
|
ConverterCustomTarget.tgt_counter += 1 |
|
|
|
|
self.cmake_name = str(self.name) |
|
|
|
|
self.cmake_name = str(self.name) |
|
|
|
|
self.original_outputs = list(target.outputs) |
|
|
|
|
self.outputs = [os.path.basename(x) for x in self.original_outputs] |
|
|
|
|
self.conflict_map = {} |
|
|
|
|
self.command = target.command |
|
|
|
|
self.working_dir = target.working_dir |
|
|
|
|
self.depends_raw = target.depends |
|
|
|
|
self.inputs = [] |
|
|
|
|
self.depends = [] |
|
|
|
|
self.current_bin_dir = Path(target.current_bin_dir) |
|
|
|
|
self.current_src_dir = Path(target.current_src_dir) |
|
|
|
|
self.outputs = [os.path.basename(x) for x in self.original_outputs] |
|
|
|
|
self.conflict_map = {} # type: T.Dict[str, str] |
|
|
|
|
self.command = [] # type: T.List[T.List[T.Union[str, ConverterTarget]]] |
|
|
|
|
self.working_dir = target.working_dir |
|
|
|
|
self.depends_raw = target.depends |
|
|
|
|
self.inputs = [] # type: T.List[T.Union[str, CustomTargetReference]] |
|
|
|
|
self.depends = [] # type: T.List[T.Union[ConverterTarget, ConverterCustomTarget]] |
|
|
|
|
self.current_bin_dir = Path(target.current_bin_dir) |
|
|
|
|
self.current_src_dir = Path(target.current_src_dir) |
|
|
|
|
self._raw_target = target |
|
|
|
|
|
|
|
|
|
# Convert the target name to a valid meson target name |
|
|
|
|
self.name = _sanitize_cmake_name(self.name) |
|
|
|
@ -700,12 +717,12 @@ class ConverterCustomTarget: |
|
|
|
|
self.outputs = temp_outputs |
|
|
|
|
|
|
|
|
|
# Check if the command is a build target |
|
|
|
|
commands = [] |
|
|
|
|
for i in self.command: |
|
|
|
|
assert(isinstance(i, list)) |
|
|
|
|
cmd = [] |
|
|
|
|
commands = [] # type: T.List[T.List[T.Union[str, ConverterTarget]]] |
|
|
|
|
for curr_cmd in self._raw_target.command: |
|
|
|
|
assert(isinstance(curr_cmd, list)) |
|
|
|
|
cmd = [] # type: T.List[T.Union[str, ConverterTarget]] |
|
|
|
|
|
|
|
|
|
for j in i: |
|
|
|
|
for j in curr_cmd: |
|
|
|
|
if not j: |
|
|
|
|
continue |
|
|
|
|
target = output_target_map.executable(j) |
|
|
|
@ -759,9 +776,11 @@ class ConverterCustomTarget: |
|
|
|
|
elif tgt: |
|
|
|
|
self.depends += [tgt] |
|
|
|
|
elif gen: |
|
|
|
|
self.inputs += [gen.get_ref(i)] |
|
|
|
|
ctgt_ref = gen.get_ref(i) |
|
|
|
|
assert ctgt_ref is not None |
|
|
|
|
self.inputs += [ctgt_ref] |
|
|
|
|
|
|
|
|
|
def process_inter_target_dependencies(self): |
|
|
|
|
def process_inter_target_dependencies(self) -> None: |
|
|
|
|
# Move the dependencies from all transfer_dependencies_from to the target |
|
|
|
|
to_process = list(self.depends) |
|
|
|
|
processed = [] |
|
|
|
@ -799,7 +818,7 @@ class CMakeAPI(Enum): |
|
|
|
|
FILE = 2 |
|
|
|
|
|
|
|
|
|
class CMakeInterpreter: |
|
|
|
|
def __init__(self, build: 'Build', subdir: str, src_dir: str, install_prefix: str, env: Environment, backend: 'Backend'): |
|
|
|
|
def __init__(self, build: 'Build', subdir: str, src_dir: str, install_prefix: str, env: 'Environment', backend: 'Backend'): |
|
|
|
|
assert(hasattr(backend, 'name')) |
|
|
|
|
self.build = build |
|
|
|
|
self.subdir = subdir |
|
|
|
@ -815,21 +834,21 @@ class CMakeInterpreter: |
|
|
|
|
self.fileapi = CMakeFileAPI(self.build_dir) |
|
|
|
|
|
|
|
|
|
# Raw CMake results |
|
|
|
|
self.bs_files = [] |
|
|
|
|
self.codemodel_configs = None |
|
|
|
|
self.raw_trace = None |
|
|
|
|
self.bs_files = [] # type: T.List[str] |
|
|
|
|
self.codemodel_configs = None # type: T.Optional[T.List[CMakeConfiguration]] |
|
|
|
|
self.raw_trace = None # type: T.Optional[str] |
|
|
|
|
|
|
|
|
|
# Analysed data |
|
|
|
|
self.project_name = '' |
|
|
|
|
self.languages = [] |
|
|
|
|
self.targets = [] |
|
|
|
|
self.custom_targets = [] # type: T.List[ConverterCustomTarget] |
|
|
|
|
self.trace = CMakeTraceParser('', '') # Will be replaced in analyse |
|
|
|
|
self.project_name = '' |
|
|
|
|
self.languages = [] # type: T.List[str] |
|
|
|
|
self.targets = [] # type: T.List[ConverterTarget] |
|
|
|
|
self.custom_targets = [] # type: T.List[ConverterCustomTarget] |
|
|
|
|
self.trace = CMakeTraceParser('', '') # Will be replaced in analyse |
|
|
|
|
self.output_target_map = OutputTargetMap(self.build_dir) |
|
|
|
|
|
|
|
|
|
# Generated meson data |
|
|
|
|
self.generated_targets = {} |
|
|
|
|
self.internal_name_map = {} |
|
|
|
|
self.generated_targets = {} # type: T.Dict[str, T.Dict[str, T.Optional[str]]] |
|
|
|
|
self.internal_name_map = {} # type: T.Dict[str, str] |
|
|
|
|
|
|
|
|
|
def configure(self, extra_cmake_options: T.List[str]) -> None: |
|
|
|
|
for_machine = MachineChoice.HOST # TODO make parameter |
|
|
|
@ -933,9 +952,11 @@ class CMakeInterpreter: |
|
|
|
|
|
|
|
|
|
# Get CMake build system files |
|
|
|
|
bs_reply = self.client.query_checked(RequestCMakeInputs(), 'Querying build system files') |
|
|
|
|
assert isinstance(bs_reply, ReplyCMakeInputs) |
|
|
|
|
|
|
|
|
|
# Now get the CMake code model |
|
|
|
|
cm_reply = self.client.query_checked(RequestCodeModel(), 'Querying the CMake code model') |
|
|
|
|
assert isinstance(cm_reply, ReplyCodeModel) |
|
|
|
|
|
|
|
|
|
src_dir = bs_reply.src_dir |
|
|
|
|
self.bs_files = [x.file for x in bs_reply.build_files if not x.is_cmake and not x.is_temp] |
|
|
|
@ -958,63 +979,64 @@ class CMakeInterpreter: |
|
|
|
|
|
|
|
|
|
# Find all targets |
|
|
|
|
added_target_names = [] # type: T.List[str] |
|
|
|
|
for i in self.codemodel_configs: |
|
|
|
|
for j in i.projects: |
|
|
|
|
for i_0 in self.codemodel_configs: |
|
|
|
|
for j_0 in i_0.projects: |
|
|
|
|
if not self.project_name: |
|
|
|
|
self.project_name = j.name |
|
|
|
|
for k in j.targets: |
|
|
|
|
self.project_name = j_0.name |
|
|
|
|
for k_0 in j_0.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)] |
|
|
|
|
if k_0.type not in skip_targets and k_0.name not in added_target_names: |
|
|
|
|
added_target_names += [k_0.name] |
|
|
|
|
self.targets += [ConverterTarget(k_0, self.env)] |
|
|
|
|
|
|
|
|
|
# Add interface targets from trace, if not already present. |
|
|
|
|
# This step is required because interface targets were removed from |
|
|
|
|
# the CMake file API output. |
|
|
|
|
api_target_name_list = [x.name for x in self.targets] |
|
|
|
|
for i in self.trace.targets.values(): |
|
|
|
|
if i.type != 'INTERFACE' or i.name in api_target_name_list or i.imported: |
|
|
|
|
for i_1 in self.trace.targets.values(): |
|
|
|
|
if i_1.type != 'INTERFACE' or i_1.name in api_target_name_list or i_1.imported: |
|
|
|
|
continue |
|
|
|
|
dummy = CMakeTarget({ |
|
|
|
|
'name': i.name, |
|
|
|
|
'name': i_1.name, |
|
|
|
|
'type': 'INTERFACE_LIBRARY', |
|
|
|
|
'sourceDirectory': self.src_dir, |
|
|
|
|
'buildDirectory': self.build_dir, |
|
|
|
|
}) |
|
|
|
|
self.targets += [ConverterTarget(dummy, self.env)] |
|
|
|
|
|
|
|
|
|
for i in self.trace.custom_targets: |
|
|
|
|
self.custom_targets += [ConverterCustomTarget(i)] |
|
|
|
|
for i_2 in self.trace.custom_targets: |
|
|
|
|
self.custom_targets += [ConverterCustomTarget(i_2)] |
|
|
|
|
|
|
|
|
|
# generate the output_target_map |
|
|
|
|
for i in [*self.targets, *self.custom_targets]: |
|
|
|
|
self.output_target_map.add(i) |
|
|
|
|
for i_3 in [*self.targets, *self.custom_targets]: |
|
|
|
|
assert isinstance(i_3, (ConverterTarget, ConverterCustomTarget)) |
|
|
|
|
self.output_target_map.add(i_3) |
|
|
|
|
|
|
|
|
|
# First pass: Basic target cleanup |
|
|
|
|
object_libs = [] |
|
|
|
|
custom_target_outputs = [] # type: T.List[str] |
|
|
|
|
for i in self.custom_targets: |
|
|
|
|
i.postprocess(self.output_target_map, self.src_dir, self.subdir, custom_target_outputs, self.trace) |
|
|
|
|
for i in self.targets: |
|
|
|
|
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] |
|
|
|
|
for ctgt in self.custom_targets: |
|
|
|
|
ctgt.postprocess(self.output_target_map, self.src_dir, self.subdir, custom_target_outputs, self.trace) |
|
|
|
|
for tgt in self.targets: |
|
|
|
|
tgt.postprocess(self.output_target_map, self.src_dir, self.subdir, self.install_prefix, self.trace) |
|
|
|
|
if tgt.type == 'OBJECT_LIBRARY': |
|
|
|
|
object_libs += [tgt] |
|
|
|
|
self.languages += [x for x in tgt.languages if x not in self.languages] |
|
|
|
|
|
|
|
|
|
# Second pass: Detect object library dependencies |
|
|
|
|
for i in self.targets: |
|
|
|
|
i.process_object_libs(object_libs, self._object_lib_workaround()) |
|
|
|
|
for tgt in self.targets: |
|
|
|
|
tgt.process_object_libs(object_libs, self._object_lib_workaround()) |
|
|
|
|
|
|
|
|
|
# Third pass: Reassign dependencies to avoid some loops |
|
|
|
|
for i in self.targets: |
|
|
|
|
i.process_inter_target_dependencies() |
|
|
|
|
for i in self.custom_targets: |
|
|
|
|
i.process_inter_target_dependencies() |
|
|
|
|
for tgt in self.targets: |
|
|
|
|
tgt.process_inter_target_dependencies() |
|
|
|
|
for ctgt in self.custom_targets: |
|
|
|
|
ctgt.process_inter_target_dependencies() |
|
|
|
|
|
|
|
|
|
# Fourth pass: Remove rassigned dependencies |
|
|
|
|
for i in self.targets: |
|
|
|
|
i.cleanup_dependencies() |
|
|
|
|
for tgt in self.targets: |
|
|
|
|
tgt.cleanup_dependencies() |
|
|
|
|
|
|
|
|
|
mlog.log('CMake project', mlog.bold(self.project_name), 'has', mlog.bold(str(len(self.targets) + len(self.custom_targets))), 'build targets.') |
|
|
|
|
|
|
|
|
@ -1022,7 +1044,7 @@ class CMakeInterpreter: |
|
|
|
|
if not self.project_name: |
|
|
|
|
raise CMakeException('CMakeInterpreter was not analysed') |
|
|
|
|
|
|
|
|
|
def token(tid: str = 'string', val='') -> Token: |
|
|
|
|
def token(tid: str = 'string', val: TYPE_mixed = '') -> Token: |
|
|
|
|
return Token(tid, self.subdir, 0, 0, 0, None, val) |
|
|
|
|
|
|
|
|
|
def string(value: str) -> StringNode: |
|
|
|
@ -1034,7 +1056,7 @@ class CMakeInterpreter: |
|
|
|
|
def number(value: int) -> NumberNode: |
|
|
|
|
return NumberNode(token(val=value)) |
|
|
|
|
|
|
|
|
|
def nodeify(value): |
|
|
|
|
def nodeify(value: TYPE_mixed_list) -> BaseNode: |
|
|
|
|
if isinstance(value, str): |
|
|
|
|
return string(value) |
|
|
|
|
elif isinstance(value, bool): |
|
|
|
@ -1043,34 +1065,38 @@ class CMakeInterpreter: |
|
|
|
|
return number(value) |
|
|
|
|
elif isinstance(value, list): |
|
|
|
|
return array(value) |
|
|
|
|
return value |
|
|
|
|
elif isinstance(value, BaseNode): |
|
|
|
|
return value |
|
|
|
|
raise RuntimeError('invalid type of value: {} ({})'.format(type(value).__name__, str(value))) |
|
|
|
|
|
|
|
|
|
def indexed(node: BaseNode, index: int) -> IndexNode: |
|
|
|
|
return IndexNode(node, nodeify(index)) |
|
|
|
|
|
|
|
|
|
def array(elements) -> ArrayNode: |
|
|
|
|
def array(elements: TYPE_mixed_list) -> ArrayNode: |
|
|
|
|
args = ArgumentNode(token()) |
|
|
|
|
if not isinstance(elements, list): |
|
|
|
|
elements = [args] |
|
|
|
|
args.arguments += [nodeify(x) for x in elements if x is not None] |
|
|
|
|
return ArrayNode(args, 0, 0, 0, 0) |
|
|
|
|
|
|
|
|
|
def function(name: str, args=None, kwargs=None) -> FunctionNode: |
|
|
|
|
def function(name: str, args: T.Optional[TYPE_mixed_list] = None, kwargs: T.Optional[TYPE_mixed_kwargs] = None) -> FunctionNode: |
|
|
|
|
args = [] if args is None else args |
|
|
|
|
kwargs = {} if kwargs is None else kwargs |
|
|
|
|
args_n = ArgumentNode(token()) |
|
|
|
|
if not isinstance(args, list): |
|
|
|
|
assert isinstance(args, (str, int, bool, BaseNode)) |
|
|
|
|
args = [args] |
|
|
|
|
args_n.arguments = [nodeify(x) for x in args if x is not None] |
|
|
|
|
args_n.kwargs = {id_node(k): nodeify(v) for k, v in kwargs.items() if v is not None} |
|
|
|
|
func_n = FunctionNode(self.subdir, 0, 0, 0, 0, name, args_n) |
|
|
|
|
return func_n |
|
|
|
|
|
|
|
|
|
def method(obj: BaseNode, name: str, args=None, kwargs=None) -> MethodNode: |
|
|
|
|
def method(obj: BaseNode, name: str, args: T.Optional[TYPE_mixed_list] = None, kwargs: T.Optional[TYPE_mixed_kwargs] = None) -> MethodNode: |
|
|
|
|
args = [] if args is None else args |
|
|
|
|
kwargs = {} if kwargs is None else kwargs |
|
|
|
|
args_n = ArgumentNode(token()) |
|
|
|
|
if not isinstance(args, list): |
|
|
|
|
assert isinstance(args, (str, int, bool, BaseNode)) |
|
|
|
|
args = [args] |
|
|
|
|
args_n.arguments = [nodeify(x) for x in args if x is not None] |
|
|
|
|
args_n.kwargs = {id_node(k): nodeify(v) for k, v in kwargs.items() if v is not None} |
|
|
|
@ -1086,9 +1112,9 @@ class CMakeInterpreter: |
|
|
|
|
# Add the run script for custom commands |
|
|
|
|
|
|
|
|
|
# Add the targets |
|
|
|
|
processing = [] |
|
|
|
|
processed = {} |
|
|
|
|
name_map = {} |
|
|
|
|
processing = [] # type: T.List[str] |
|
|
|
|
processed = {} # type: T.Dict[str, T.Dict[str, T.Optional[str]]] |
|
|
|
|
name_map = {} # type: T.Dict[str, str] |
|
|
|
|
|
|
|
|
|
def extract_tgt(tgt: T.Union[ConverterTarget, ConverterCustomTarget, CustomTargetReference]) -> IdNode: |
|
|
|
|
tgt_name = None |
|
|
|
@ -1105,24 +1131,24 @@ class CMakeInterpreter: |
|
|
|
|
raise CMakeException('Cycle in CMake inputs/dependencies detected') |
|
|
|
|
processing.append(tgt.name) |
|
|
|
|
|
|
|
|
|
def resolve_ctgt_ref(ref: CustomTargetReference) -> BaseNode: |
|
|
|
|
def resolve_ctgt_ref(ref: CustomTargetReference) -> T.Union[IdNode, IndexNode]: |
|
|
|
|
tgt_var = extract_tgt(ref) |
|
|
|
|
if len(ref.ctgt.outputs) == 1: |
|
|
|
|
return tgt_var |
|
|
|
|
else: |
|
|
|
|
return indexed(tgt_var, ref.index) |
|
|
|
|
|
|
|
|
|
def process_target(tgt: ConverterTarget): |
|
|
|
|
def process_target(tgt: ConverterTarget) -> None: |
|
|
|
|
detect_cycle(tgt) |
|
|
|
|
|
|
|
|
|
# First handle inter target dependencies |
|
|
|
|
link_with = [] |
|
|
|
|
objec_libs = [] # type: T.List[IdNode] |
|
|
|
|
sources = [] |
|
|
|
|
generated = [] |
|
|
|
|
generated_filenames = [] |
|
|
|
|
custom_targets = [] |
|
|
|
|
dependencies = [] |
|
|
|
|
link_with = [] # type: T.List[IdNode] |
|
|
|
|
objec_libs = [] # type: T.List[IdNode] |
|
|
|
|
sources = [] # type: T.List[str] |
|
|
|
|
generated = [] # type: T.List[T.Union[IdNode, IndexNode]] |
|
|
|
|
generated_filenames = [] # type: T.List[str] |
|
|
|
|
custom_targets = [] # type: T.List[ConverterCustomTarget] |
|
|
|
|
dependencies = [] # type: T.List[IdNode] |
|
|
|
|
for i in tgt.link_with: |
|
|
|
|
assert(isinstance(i, ConverterTarget)) |
|
|
|
|
if i.name not in processed: |
|
|
|
@ -1141,16 +1167,17 @@ class CMakeInterpreter: |
|
|
|
|
dependencies += [extract_tgt(i)] |
|
|
|
|
|
|
|
|
|
# Generate the source list and handle generated sources |
|
|
|
|
for i in tgt.sources + tgt.generated: |
|
|
|
|
if isinstance(i, CustomTargetReference): |
|
|
|
|
if i.ctgt.name not in processed: |
|
|
|
|
process_custom_target(i.ctgt) |
|
|
|
|
generated += [resolve_ctgt_ref(i)] |
|
|
|
|
generated_filenames += [i.filename()] |
|
|
|
|
if i.ctgt not in custom_targets: |
|
|
|
|
custom_targets += [i.ctgt] |
|
|
|
|
else: |
|
|
|
|
sources += [i] |
|
|
|
|
sources += tgt.sources |
|
|
|
|
sources += tgt.generated |
|
|
|
|
|
|
|
|
|
for ctgt_ref in tgt.generated_ctgt: |
|
|
|
|
ctgt = ctgt_ref.ctgt |
|
|
|
|
if ctgt.name not in processed: |
|
|
|
|
process_custom_target(ctgt) |
|
|
|
|
generated += [resolve_ctgt_ref(ctgt_ref)] |
|
|
|
|
generated_filenames += [ctgt_ref.filename()] |
|
|
|
|
if ctgt not in custom_targets: |
|
|
|
|
custom_targets += [ctgt] |
|
|
|
|
|
|
|
|
|
# Add all header files from all used custom targets. This |
|
|
|
|
# ensures that all custom targets are built before any |
|
|
|
@ -1158,12 +1185,12 @@ class CMakeInterpreter: |
|
|
|
|
# header files are present. This step is necessary because |
|
|
|
|
# CMake always ensures that a custom target is executed |
|
|
|
|
# before another target if at least one output is used. |
|
|
|
|
for i in custom_targets: |
|
|
|
|
for j in i.outputs: |
|
|
|
|
for ctgt in custom_targets: |
|
|
|
|
for j in ctgt.outputs: |
|
|
|
|
if not is_header(j) or j in generated_filenames: |
|
|
|
|
continue |
|
|
|
|
|
|
|
|
|
generated += [resolve_ctgt_ref(i.get_ref(j))] |
|
|
|
|
generated += [resolve_ctgt_ref(ctgt.get_ref(j))] |
|
|
|
|
generated_filenames += [j] |
|
|
|
|
|
|
|
|
|
# Determine the meson function to use for the build target |
|
|
|
@ -1190,7 +1217,7 @@ class CMakeInterpreter: |
|
|
|
|
'install': install_tgt, |
|
|
|
|
'override_options': options.get_override_options(tgt.cmake_name, tgt.override_options), |
|
|
|
|
'objects': [method(x, 'extract_all_objects') for x in objec_libs], |
|
|
|
|
} |
|
|
|
|
} # type: TYPE_mixed_kwargs |
|
|
|
|
|
|
|
|
|
# Only set if installed and only override if it is set |
|
|
|
|
if install_tgt and tgt.install_dir: |
|
|
|
@ -1212,7 +1239,7 @@ class CMakeInterpreter: |
|
|
|
|
'link_with': id_node(tgt_var), |
|
|
|
|
'compile_args': tgt.public_compile_opts, |
|
|
|
|
'include_directories': id_node(inc_var), |
|
|
|
|
} |
|
|
|
|
} # type: TYPE_mixed_kwargs |
|
|
|
|
|
|
|
|
|
if dependencies: |
|
|
|
|
generated += dependencies |
|
|
|
@ -1230,7 +1257,7 @@ class CMakeInterpreter: |
|
|
|
|
tgt_var = None |
|
|
|
|
else: |
|
|
|
|
src_node = assign(src_var, function('files', sources)) |
|
|
|
|
tgt_node = assign(tgt_var, function(tgt_func, [tgt_var, [id_node(src_var)] + generated], tgt_kwargs)) |
|
|
|
|
tgt_node = assign(tgt_var, function(tgt_func, [tgt_var, id_node(src_var), *generated], tgt_kwargs)) |
|
|
|
|
node_list += [src_node, tgt_node] |
|
|
|
|
if tgt_func in ['static_library', 'shared_library']: |
|
|
|
|
dep_node = assign(dep_var, function('declare_dependency', kwargs=dep_kwargs)) |
|
|
|
@ -1290,19 +1317,19 @@ class CMakeInterpreter: |
|
|
|
|
'output': tgt.outputs, |
|
|
|
|
'command': command, |
|
|
|
|
'depends': [resolve_source(x) for x in tgt.depends], |
|
|
|
|
} |
|
|
|
|
} # type: TYPE_mixed_kwargs |
|
|
|
|
|
|
|
|
|
root_cb.lines += [assign(tgt_var, function('custom_target', [tgt.name], tgt_kwargs))] |
|
|
|
|
processed[tgt.name] = {'inc': None, 'src': None, 'dep': None, 'tgt': tgt_var, 'func': 'custom_target'} |
|
|
|
|
name_map[tgt.cmake_name] = tgt.name |
|
|
|
|
|
|
|
|
|
# Now generate the target function calls |
|
|
|
|
for i in self.custom_targets: |
|
|
|
|
if i.name not in processed: |
|
|
|
|
process_custom_target(i) |
|
|
|
|
for i in self.targets: |
|
|
|
|
if i.name not in processed: |
|
|
|
|
process_target(i) |
|
|
|
|
for ctgt in self.custom_targets: |
|
|
|
|
if ctgt.name not in processed: |
|
|
|
|
process_custom_target(ctgt) |
|
|
|
|
for tgt in self.targets: |
|
|
|
|
if tgt.name not in processed: |
|
|
|
|
process_target(tgt) |
|
|
|
|
|
|
|
|
|
self.generated_targets = processed |
|
|
|
|
self.internal_name_map = name_map |
|
|
|
|