Skip to content

Commit

Permalink
cmake: Add TARGET_ generator expression support (fixes mesonbuild#9305)
Browse files Browse the repository at this point in the history
  • Loading branch information
mensinda committed Jan 23, 2022
1 parent 99aae9b commit 42843c4
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 13 deletions.
62 changes: 59 additions & 3 deletions mesonbuild/cmake/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,18 @@
# limitations under the License.

from .. import mesonlib
from .common import cmake_is_debug
import typing as T

if T.TYPE_CHECKING:
from .traceparser import CMakeTraceParser

def parse_generator_expressions(raw: str, trace: 'CMakeTraceParser') -> str:
from .traceparser import CMakeTraceParser, CMakeTarget

def parse_generator_expressions(
raw: str,
trace: 'CMakeTraceParser',
*,
context_tgt: T.Optional['CMakeTarget'] = None,
) -> str:
'''Parse CMake generator expressions
Most generator expressions are simply ignored for
Expand Down Expand Up @@ -47,13 +53,57 @@ def vers_comp(op: str, arg: str) -> str:
else:
return '1' if mesonlib.version_compare(arg[:col_pos], '{}{}'.format(op, arg[col_pos + 1:])) else '0'

def target_property(arg: str) -> str:
# We can't really support this since we don't have any context
if ',' not in arg:
if context_tgt is None:
return ''
return ';'.join(context_tgt.properties.get(arg, []))

args = arg.split(',')
props = trace.targets[args[0]].properties.get(args[1], []) if args[0] in trace.targets else []
return ';'.join(props)

def target_file(arg: str) -> str:
if arg not in trace.targets:
return ''
tgt = trace.targets[arg]

cfgs = []
cfg = ''

if 'IMPORTED_CONFIGURATIONS' in tgt.properties:
cfgs = [x for x in tgt.properties['IMPORTED_CONFIGURATIONS'] if x]
cfg = cfgs[0]

if cmake_is_debug(trace.env):
if 'DEBUG' in cfgs:
cfg = 'DEBUG'
elif 'RELEASE' in cfgs:
cfg = 'RELEASE'
else:
if 'RELEASE' in cfgs:
cfg = 'RELEASE'

if f'IMPORTED_IMPLIB_{cfg}' in tgt.properties:
return ';'.join([x for x in tgt.properties[f'IMPORTED_IMPLIB_{cfg}'] if x])
elif 'IMPORTED_IMPLIB' in tgt.properties:
return ';'.join([x for x in tgt.properties['IMPORTED_IMPLIB'] if x])
elif f'IMPORTED_LOCATION_{cfg}' in tgt.properties:
return ';'.join([x for x in tgt.properties[f'IMPORTED_LOCATION_{cfg}'] if x])
elif 'IMPORTED_LOCATION' in tgt.properties:
return ';'.join([x for x in tgt.properties['IMPORTED_LOCATION'] if x])
return ''

supported = {
# Boolean functions
'BOOL': lambda x: '0' if x.upper() in ['0', 'FALSE', 'OFF', 'N', 'NO', 'IGNORE', 'NOTFOUND'] or x.endswith('-NOTFOUND') else '1',
'AND': lambda x: '1' if all([y == '1' for y in x.split(',')]) else '0',
'OR': lambda x: '1' if any([y == '1' for y in x.split(',')]) else '0',
'NOT': lambda x: '0' if x == '1' else '1',

'IF': lambda x: x.split(',')[1] if x.split(',')[0] == '1' else x.split(',')[2],

'0': lambda x: '',
'1': lambda x: x,

Expand Down Expand Up @@ -81,6 +131,12 @@ def vers_comp(op: str, arg: str) -> str:
'ANGLE-R': lambda x: '>',
'COMMA': lambda x: ',',
'SEMICOLON': lambda x: ';',

# Target related expressions
'TARGET_EXISTS': lambda x: '1' if x in trace.targets else '0',
'TARGET_NAME_IF_EXISTS': lambda x: x if x in trace.targets else '',
'TARGET_PROPERTY': target_property,
'TARGET_FILE': target_file,
} # type: T.Dict[str, T.Callable[[str], str]]

# Recursively evaluate generator expressions
Expand Down
4 changes: 2 additions & 2 deletions mesonbuild/cmake/interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -793,7 +793,7 @@ def __init__(self, build: 'Build', subdir: Path, src_dir: Path, install_prefix:
self.languages = [] # type: T.List[str]
self.targets = [] # type: T.List[ConverterTarget]
self.custom_targets = [] # type: T.List[ConverterCustomTarget]
self.trace = CMakeTraceParser('', Path('.')) # Will be replaced in analyse
self.trace = CMakeTraceParser('', Path('.'), self.env) # Will be replaced in analyse
self.output_target_map = OutputTargetMap(self.build_dir)

# Generated meson data
Expand All @@ -814,7 +814,7 @@ def configure(self, extra_cmake_options: T.List[str]) -> CMakeExecutor:
cmake_exe = CMakeExecutor(self.env, '>=3.7', MachineChoice.BUILD)
if not cmake_exe.found():
raise CMakeException('Unable to find CMake')
self.trace = CMakeTraceParser(cmake_exe.version(), self.build_dir, permissive=True)
self.trace = CMakeTraceParser(cmake_exe.version(), self.build_dir, self.env, permissive=True)

preload_file = DataFile('cmake/data/preload.cmake').write_to_private(self.env)
toolchain = CMakeToolchain(cmake_exe, self.env, self.for_machine, CMakeExecScope.SUBPROJECT, self.build_dir, preload_file)
Expand Down
2 changes: 1 addition & 1 deletion mesonbuild/cmake/toolchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ def update_cmake_compiler_state(self) -> None:
temp_toolchain_file.write_text(CMakeToolchain._print_vars(self.variables), encoding='utf-8')

# Configure
trace = CMakeTraceParser(self.cmakebin.version(), build_dir)
trace = CMakeTraceParser(self.cmakebin.version(), build_dir, self.env)
self.cmakebin.set_exec_mode(print_cmout=False, always_capture_stderr=trace.requires_stderr())
cmake_args = []
cmake_args += trace.trace_args()
Expand Down
15 changes: 10 additions & 5 deletions mesonbuild/cmake/traceparser.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@
import json
import textwrap

if T.TYPE_CHECKING:
from ..environment import Environment

class CMakeTraceLine:
def __init__(self, file_str: str, line: int, func: str, args: T.List[str]) -> None:
self.file = CMakeTraceLine._to_path(file_str)
Expand Down Expand Up @@ -90,7 +93,7 @@ def __init__(self, name: str) -> None:
self.working_dir = None # type: T.Optional[Path]

class CMakeTraceParser:
def __init__(self, cmake_version: str, build_dir: Path, permissive: bool = True) -> None:
def __init__(self, cmake_version: str, build_dir: Path, env: 'Environment', permissive: bool = True) -> None:
self.vars: T.Dict[str, T.List[str]] = {}
self.vars_by_file: T.Dict[Path, T.Dict[str, T.List[str]]] = {}
self.targets: T.Dict[str, CMakeTarget] = {}
Expand All @@ -101,6 +104,7 @@ def __init__(self, cmake_version: str, build_dir: Path, permissive: bool = True)
# T.List of targes that were added with add_custom_command to generate files
self.custom_targets = [] # type: T.List[CMakeGeneratorTarget]

self.env = env
self.permissive = permissive # type: bool
self.cmake_version = cmake_version # type: str
self.trace_file = 'cmake_trace.txt'
Expand Down Expand Up @@ -202,12 +206,13 @@ def parse(self, trace: T.Optional[str] = None) -> None:
}

for tgt in self.targets.values():
tgt.name = parse_generator_expressions(tgt.name, self)
tgt.type = parse_generator_expressions(tgt.type, self)
tgtlist_gen: T.Callable[[T.List[str], CMakeTarget], T.List[str]] = lambda strlist, t: [parse_generator_expressions(x, self, context_tgt=t) for x in strlist]
tgt.name = parse_generator_expressions(tgt.name, self, context_tgt=tgt)
tgt.type = parse_generator_expressions(tgt.type, self, context_tgt=tgt)
tgt.properties = {
k: strlist_gen(v) for k, v in tgt.properties.items()
k: tgtlist_gen(v, tgt) for k, v in tgt.properties.items()
} if tgt.properties is not None else None
tgt.depends = strlist_gen(tgt.depends)
tgt.depends = tgtlist_gen(tgt.depends, tgt)

for ctgt in self.custom_targets:
ctgt.outputs = pathlist_gen(ctgt.outputs)
Expand Down
4 changes: 2 additions & 2 deletions mesonbuild/dependencies/cmake.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ def __init__(self, name: str, environment: 'Environment', kwargs: T.Dict[str, T.
return

# Setup the trace parser
self.traceparser = CMakeTraceParser(self.cmakebin.version(), self._get_build_dir())
self.traceparser = CMakeTraceParser(self.cmakebin.version(), self._get_build_dir(), self.env)

cm_args = stringlistify(extract_as_list(kwargs, 'cmake_args'))
cm_args = check_cmake_args(cm_args)
Expand Down Expand Up @@ -166,7 +166,7 @@ def _get_cmake_info(self, cm_args: T.List[str]) -> T.Optional[CMakeInfo]:
gen_list += [CMakeDependency.class_working_generator]
gen_list += CMakeDependency.class_cmake_generators

temp_parser = CMakeTraceParser(self.cmakebin.version(), self._get_build_dir())
temp_parser = CMakeTraceParser(self.cmakebin.version(), self._get_build_dir(), self.env)
toolchain = CMakeToolchain(self.cmakebin, self.env, self.for_machine, CMakeExecScope.DEPENDENCY, self._get_build_dir())
toolchain.write()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,21 @@ target_compile_options(cmModLib
INTERFACE "-DCMAKE_FLAG_REQUIRED_A"
INTERFACE $<$<AND:1,$<STREQUAL:asd,$<LOWER_CASE:AsD>>,$<NOT:$<EQUAL:4,2>>>:-DCMAKE_FLAG_REQUIRED_B>
INTERFACE $<$<VERSION_LESS:1.2.3,2.1.0>:-DCMAKE_FLAG_REQUIRED_C>
INTERFACE $<IF:$<NOT:$<BOOL:OFF>>,-DCMAKE_TRUE_FLAG,-DCMAKE_FALSE_FLAG>
INTERFACE $<IF:$<TARGET_EXISTS:cmModLib>,-DCMAKE_TGT_EXISTS,-DCMAKE_TGT_NEXISTS>
INTERFACE $<IF:$<TARGET_PROPERTY:IMPORTED_NO_SONAME>,-DCMAKE_PROP1_OK,-DCMAKE_PROP1_ERROR>
INTERFACE $<IF:$<TARGET_PROPERTY:cmModLib,IMPORT_SUFFIX>,-DCMAKE_PROP2_ERROR,-DCMAKE_PROP2_OK>
)

target_include_directories(cmModLib INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)

set_target_properties(cmModLib
PROPERTIES
IMPORTED_NO_SONAME 1
IMPORT_SUFFIX 0
)

target_compile_definitions(cmModLib INTERFACE -DCMAKE_COMPILER_DEFINE_STR="compDef")
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,38 @@
#error "The flag CMAKE_FLAG_ERROR_A was set"
#endif

#ifndef CMAKE_TRUE_FLAG
#error "The flag CMAKE_TRUE_FLAG was not set"
#endif

#ifdef CMAKE_FALSE_FLAG
#error "The flag CMAKE_FALSE_FLAG was set"
#endif

#ifndef CMAKE_TGT_EXISTS
#error "The flag CMAKE_TGT_EXISTS was not set"
#endif

#ifdef CMAKE_TGT_NEXISTS
#error "The flag CMAKE_TGT_NEXISTS was set"
#endif

#ifndef CMAKE_PROP1_OK
#error "The flag CMAKE_PROP1_OK was not set"
#endif

#ifdef CMAKE_PROP1_ERROR
#error "The flag CMAKE_PROP1_ERROR was set"
#endif

#ifndef CMAKE_PROP2_OK
#error "The flag CMAKE_PROP2_OK was not set"
#endif

#ifdef CMAKE_PROP2_ERROR
#error "The flag CMAKE_PROP2_ERROR was set"
#endif

class cmModClass {
private:
std::string str;
Expand Down

0 comments on commit 42843c4

Please sign in to comment.