typing: fully annotate cmake.interpreter

pull/7794/head
Daniel Mensinger 4 years ago
parent c2d55afcb5
commit 2f3ef6f1af
No known key found for this signature in database
GPG Key ID: 54DD94C131E277D4
  1. 369
      mesonbuild/cmake/interpreter.py
  2. 1
      run_mypy.py

@ -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

@ -9,6 +9,7 @@ import typing as T
modules = [
# fully typed submodules
'mesonbuild/ast',
'mesonbuild/cmake',
'mesonbuild/compilers/mixins',
'mesonbuild/scripts',
'mesonbuild/wrap',

Loading…
Cancel
Save