cmake: Fix old style dependency lookup with imported targets

This also includes some refactoring, since the alternaticve would
have been to duplicate the huge traceparser target code block again.

fixes #9581
This commit is contained in:
Daniel Mensinger 2021-11-27 19:58:04 +01:00 committed by Jussi Pakkanen
parent 4f882ff8ec
commit 45c5300496
7 changed files with 171 additions and 169 deletions

View File

@ -34,9 +34,12 @@ __all__ = [
'cmake_get_generator_args',
'cmake_defines_to_args',
'check_cmake_args',
'cmake_is_debug',
'resolve_cmake_trace_targets',
'ResolvedTarget',
]
from .common import CMakeException, SingleTargetOptions, TargetOptions, cmake_defines_to_args, language_map, backend_generator_map, cmake_get_generator_args, check_cmake_args
from .common import CMakeException, SingleTargetOptions, TargetOptions, cmake_defines_to_args, language_map, backend_generator_map, cmake_get_generator_args, check_cmake_args, cmake_is_debug
from .client import CMakeClient
from .executor import CMakeExecutor
from .fileapi import CMakeFileAPI
@ -44,3 +47,4 @@ from .generator import parse_generator_expressions
from .interpreter import CMakeInterpreter
from .toolchain import CMakeToolchain, CMakeExecScope
from .traceparser import CMakeTarget, CMakeTraceLine, CMakeTraceParser
from .tracetargets import resolve_cmake_trace_targets, ResolvedTarget

View File

@ -61,6 +61,18 @@ blacklist_cmake_defs = [
'MESON_CMAKE_ROOT',
]
def cmake_is_debug(env: 'Environment') -> bool:
if OptionKey('b_vscrt') in env.coredata.options:
is_debug = env.coredata.get_option(OptionKey('buildtype')) == 'debug'
if env.coredata.options[OptionKey('b_vscrt')].value in {'mdd', 'mtd'}:
is_debug = True
return is_debug
else:
# Don't directly assign to is_debug to make mypy happy
debug_opt = env.coredata.get_option(OptionKey('debug'))
assert isinstance(debug_opt, bool)
return debug_opt
class CMakeException(MesonException):
pass

View File

@ -21,6 +21,7 @@ from .fileapi import CMakeFileAPI
from .executor import CMakeExecutor
from .toolchain import CMakeToolchain, CMakeExecScope
from .traceparser import CMakeTraceParser, CMakeGeneratorTarget
from .tracetargets import resolve_cmake_trace_targets
from .. import mlog, mesonlib
from ..mesonlib import MachineChoice, OrderedSet, version_compare, path_is_in_root, relative_to_if_possible, OptionKey
from ..mesondata import mesondata
@ -342,84 +343,12 @@ class ConverterTarget:
if tgt:
self.depends_raw = trace.targets[self.cmake_name].depends
# TODO refactor this copy paste from CMakeDependency for future releases
reg_is_lib = re.compile(r'^(-l[a-zA-Z0-9_]+|-l?pthread)$')
to_process = [self.cmake_name]
processed = []
while len(to_process) > 0:
curr = to_process.pop(0)
rtgt = resolve_cmake_trace_targets(self.cmake_name, trace, self.env)
self.includes += [Path(x) for x in rtgt.include_directories]
self.link_flags += rtgt.link_flags
self.public_compile_opts += rtgt.public_compile_opts
self.link_libraries += rtgt.libraries
if curr in processed or curr not in trace.targets:
continue
tgt = trace.targets[curr]
cfgs = []
cfg = ''
otherDeps = []
libraries = []
mlog.debug(str(tgt))
if 'INTERFACE_INCLUDE_DIRECTORIES' in tgt.properties:
self.includes += [Path(x) for x in tgt.properties['INTERFACE_INCLUDE_DIRECTORIES'] if x]
if 'INTERFACE_LINK_OPTIONS' in tgt.properties:
self.link_flags += [x for x in tgt.properties['INTERFACE_LINK_OPTIONS'] if x]
if 'INTERFACE_COMPILE_DEFINITIONS' in tgt.properties:
self.public_compile_opts += ['-D' + re.sub('^-D', '', x) for x in tgt.properties['INTERFACE_COMPILE_DEFINITIONS'] if x]
if 'INTERFACE_COMPILE_OPTIONS' in tgt.properties:
self.public_compile_opts += [x for x in tgt.properties['INTERFACE_COMPILE_OPTIONS'] if x]
if 'IMPORTED_CONFIGURATIONS' in tgt.properties:
cfgs += [x for x in tgt.properties['IMPORTED_CONFIGURATIONS'] if x]
cfg = cfgs[0]
if 'CONFIGURATIONS' in tgt.properties:
cfgs += [x for x in tgt.properties['CONFIGURATIONS'] if x]
cfg = cfgs[0]
is_debug = self.env.coredata.get_option(OptionKey('debug'))
if is_debug:
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:
libraries += [x for x in tgt.properties[f'IMPORTED_IMPLIB_{cfg}'] if x]
elif 'IMPORTED_IMPLIB' in tgt.properties:
libraries += [x for x in tgt.properties['IMPORTED_IMPLIB'] if x]
elif f'IMPORTED_LOCATION_{cfg}' in tgt.properties:
libraries += [x for x in tgt.properties[f'IMPORTED_LOCATION_{cfg}'] if x]
elif 'IMPORTED_LOCATION' in tgt.properties:
libraries += [x for x in tgt.properties['IMPORTED_LOCATION'] if x]
if 'LINK_LIBRARIES' in tgt.properties:
otherDeps += [x for x in tgt.properties['LINK_LIBRARIES'] if x]
if 'INTERFACE_LINK_LIBRARIES' in tgt.properties:
otherDeps += [x for x in tgt.properties['INTERFACE_LINK_LIBRARIES'] if x]
if f'IMPORTED_LINK_DEPENDENT_LIBRARIES_{cfg}' in tgt.properties:
otherDeps += [x for x in tgt.properties[f'IMPORTED_LINK_DEPENDENT_LIBRARIES_{cfg}'] if x]
elif 'IMPORTED_LINK_DEPENDENT_LIBRARIES' in tgt.properties:
otherDeps += [x for x in tgt.properties['IMPORTED_LINK_DEPENDENT_LIBRARIES'] if x]
for j in otherDeps:
if j in trace.targets:
to_process += [j]
elif reg_is_lib.match(j) or Path(j).exists():
libraries += [j]
for j in libraries:
if j not in self.link_libraries:
self.link_libraries += [j]
processed += [curr]
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')

View File

@ -0,0 +1,117 @@
# SPDX-License-Identifer: Apache-2.0
# Copyright 2021 The Meson development team
from .common import cmake_is_debug
from .. import mlog
from pathlib import Path
import re
import typing as T
if T.TYPE_CHECKING:
from .traceparser import CMakeTraceParser
from ..environment import Environment
from ..compilers import Compiler
class ResolvedTarget:
def __init__(self) -> None:
self.include_directories: T.List[str] = []
self.link_flags: T.List[str] = []
self.public_compile_opts: T.List[str] = []
self.libraries: T.List[str] = []
def resolve_cmake_trace_targets(target_name: str,
trace: 'CMakeTraceParser',
env: 'Environment',
*,
clib_compiler: T.Optional['Compiler'] = None,
not_found_warning: T.Callable[[str], None] = lambda x: None) -> ResolvedTarget:
res = ResolvedTarget()
targets = [target_name]
# recognise arguments we should pass directly to the linker
reg_is_lib = re.compile(r'^(-l[a-zA-Z0-9_]+|-l?pthread)$')
reg_is_maybe_bare_lib = re.compile(r'^[a-zA-Z0-9_]+$')
is_debug = cmake_is_debug(env)
processed_targets: T.List[str] = []
while len(targets) > 0:
curr = targets.pop(0)
# Skip already processed targets
if curr in processed_targets:
continue
if curr not in trace.targets:
if reg_is_lib.match(curr):
res.libraries += [curr]
elif Path(curr).is_absolute() and Path(curr).exists():
res.libraries += [curr]
elif env.machines.build.is_windows() and reg_is_maybe_bare_lib.match(curr) and clib_compiler is not None:
# On Windows, CMake library dependencies can be passed as bare library names,
# CMake brute-forces a combination of prefix/suffix combinations to find the
# right library. Assume any bare argument passed which is not also a CMake
# target must be a system library we should try to link against.
res.libraries += clib_compiler.find_library(curr, env, [])
else:
not_found_warning(curr)
continue
tgt = trace.targets[curr]
cfgs = []
cfg = ''
mlog.debug(tgt)
if 'INTERFACE_INCLUDE_DIRECTORIES' in tgt.properties:
res.include_directories += [x for x in tgt.properties['INTERFACE_INCLUDE_DIRECTORIES'] if x]
if 'INTERFACE_LINK_OPTIONS' in tgt.properties:
res.link_flags += [x for x in tgt.properties['INTERFACE_LINK_OPTIONS'] if x]
if 'INTERFACE_COMPILE_DEFINITIONS' in tgt.properties:
res.public_compile_opts += ['-D' + re.sub('^-D', '', x) for x in tgt.properties['INTERFACE_COMPILE_DEFINITIONS'] if x]
if 'INTERFACE_COMPILE_OPTIONS' in tgt.properties:
res.public_compile_opts += [x for x in tgt.properties['INTERFACE_COMPILE_OPTIONS'] if x]
if 'IMPORTED_CONFIGURATIONS' in tgt.properties:
cfgs = [x for x in tgt.properties['IMPORTED_CONFIGURATIONS'] if x]
cfg = cfgs[0]
if is_debug:
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:
res.libraries += [x for x in tgt.properties[f'IMPORTED_IMPLIB_{cfg}'] if x]
elif 'IMPORTED_IMPLIB' in tgt.properties:
res.libraries += [x for x in tgt.properties['IMPORTED_IMPLIB'] if x]
elif f'IMPORTED_LOCATION_{cfg}' in tgt.properties:
res.libraries += [x for x in tgt.properties[f'IMPORTED_LOCATION_{cfg}'] if x]
elif 'IMPORTED_LOCATION' in tgt.properties:
res.libraries += [x for x in tgt.properties['IMPORTED_LOCATION'] if x]
if 'LINK_LIBRARIES' in tgt.properties:
targets += [x for x in tgt.properties['LINK_LIBRARIES'] if x]
if 'INTERFACE_LINK_LIBRARIES' in tgt.properties:
targets += [x for x in tgt.properties['INTERFACE_LINK_LIBRARIES'] if x]
if f'IMPORTED_LINK_DEPENDENT_LIBRARIES_{cfg}' in tgt.properties:
targets += [x for x in tgt.properties[f'IMPORTED_LINK_DEPENDENT_LIBRARIES_{cfg}'] if x]
elif 'IMPORTED_LINK_DEPENDENT_LIBRARIES' in tgt.properties:
targets += [x for x in tgt.properties['IMPORTED_LINK_DEPENDENT_LIBRARIES'] if x]
processed_targets += [curr]
res.include_directories = sorted(set(res.include_directories))
res.link_flags = sorted(set(res.link_flags))
res.public_compile_opts = sorted(set(res.public_compile_opts))
res.libraries = sorted(set(res.libraries))
return res

View File

@ -15,7 +15,7 @@
from .base import ExternalDependency, DependencyException, DependencyTypeName
from ..mesonlib import is_windows, MesonException, OptionKey, PerMachine, stringlistify, extract_as_list
from ..mesondata import mesondata
from ..cmake import CMakeExecutor, CMakeTraceParser, CMakeException, CMakeToolchain, CMakeExecScope, check_cmake_args, CMakeTarget
from ..cmake import CMakeExecutor, CMakeTraceParser, CMakeException, CMakeToolchain, CMakeExecScope, check_cmake_args, CMakeTarget, resolve_cmake_trace_targets, cmake_is_debug
from .. import mlog
from pathlib import Path
import functools
@ -439,18 +439,6 @@ class CMakeDependency(ExternalDependency):
modules = self._map_module_list(modules, components)
autodetected_module_list = False
# Check if we need a DEBUG or RELEASE CMake dependencies
is_debug = False
if OptionKey('b_vscrt') in self.env.coredata.options:
is_debug = self.env.coredata.get_option(OptionKey('buildtype')) == 'debug'
if self.env.coredata.options[OptionKey('b_vscrt')].value in {'mdd', 'mtd'}:
is_debug = True
else:
# Don't directly assign to is_debug to make mypy happy
debug_opt = self.env.coredata.get_option(OptionKey('debug'))
assert isinstance(debug_opt, bool)
is_debug = debug_opt
# Try guessing a CMake target if none is provided
if len(modules) == 0:
for i in self.traceparser.targets:
@ -506,6 +494,7 @@ class CMakeDependency(ExternalDependency):
# - https://cmake.org/cmake/help/latest/command/target_link_libraries.html#overview (the last point in the section)
libs: T.List[str] = []
cfg_matches = True
is_debug = cmake_is_debug(self.env)
cm_tag_map = {'debug': is_debug, 'optimized': not is_debug, 'general': True}
for i in libs_raw:
if i.lower() in cm_tag_map:
@ -521,7 +510,12 @@ class CMakeDependency(ExternalDependency):
# Try to use old style variables if no module is specified
if len(libs) > 0:
self.compile_args = list(map(lambda x: f'-I{x}', incDirs)) + defs
self.link_args = libs
self.link_args = []
for j in libs:
rtgt = resolve_cmake_trace_targets(j, self.traceparser, self.env, clib_compiler=self.clib_compiler)
self.link_args += rtgt.libraries
self.compile_args += [f'-I{x}' for x in rtgt.include_directories]
self.compile_args += rtgt.public_compile_opts
mlog.debug(f'using old-style CMake variables for dependency {name}')
mlog.debug(f'Include Dirs: {incDirs}')
mlog.debug(f'Compiler Definitions: {defs}')
@ -536,13 +530,10 @@ class CMakeDependency(ExternalDependency):
# Set dependencies with CMake targets
# recognise arguments we should pass directly to the linker
reg_is_lib = re.compile(r'^(-l[a-zA-Z0-9_]+|-pthread|-delayload:[a-zA-Z0-9_\.]+|[a-zA-Z0-9_]+\.lib)$')
reg_is_maybe_bare_lib = re.compile(r'^[a-zA-Z0-9_]+$')
processed_targets = []
incDirs = []
compileDefinitions = []
compileOptions = []
libraries = []
for i, required in modules:
if i not in self.traceparser.targets:
if not required:
@ -552,92 +543,27 @@ class CMakeDependency(ExternalDependency):
'Try to explicitly specify one or more targets with the "modules" property.\n'
'Valid targets are:\n{}'.format(self._original_module_name(i), name, list(self.traceparser.targets.keys())))
targets = [i]
if not autodetected_module_list:
self.found_modules += [i]
while len(targets) > 0:
curr = targets.pop(0)
# Skip already processed targets
if curr in processed_targets:
continue
tgt = self.traceparser.targets[curr]
cfgs = []
cfg = ''
otherDeps = []
mlog.debug(tgt)
if 'INTERFACE_INCLUDE_DIRECTORIES' in tgt.properties:
incDirs += [x for x in tgt.properties['INTERFACE_INCLUDE_DIRECTORIES'] if x]
if 'INTERFACE_COMPILE_DEFINITIONS' in tgt.properties:
compileDefinitions += ['-D' + re.sub('^-D', '', x) for x in tgt.properties['INTERFACE_COMPILE_DEFINITIONS'] if x]
if 'INTERFACE_COMPILE_OPTIONS' in tgt.properties:
compileOptions += [x for x in tgt.properties['INTERFACE_COMPILE_OPTIONS'] if x]
if 'IMPORTED_CONFIGURATIONS' in tgt.properties:
cfgs = [x for x in tgt.properties['IMPORTED_CONFIGURATIONS'] if x]
cfg = cfgs[0]
if is_debug:
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:
libraries += [x for x in tgt.properties[f'IMPORTED_IMPLIB_{cfg}'] if x]
elif 'IMPORTED_IMPLIB' in tgt.properties:
libraries += [x for x in tgt.properties['IMPORTED_IMPLIB'] if x]
elif f'IMPORTED_LOCATION_{cfg}' in tgt.properties:
libraries += [x for x in tgt.properties[f'IMPORTED_LOCATION_{cfg}'] if x]
elif 'IMPORTED_LOCATION' in tgt.properties:
libraries += [x for x in tgt.properties['IMPORTED_LOCATION'] if x]
if 'INTERFACE_LINK_LIBRARIES' in tgt.properties:
otherDeps += [x for x in tgt.properties['INTERFACE_LINK_LIBRARIES'] if x]
if f'IMPORTED_LINK_DEPENDENT_LIBRARIES_{cfg}' in tgt.properties:
otherDeps += [x for x in tgt.properties[f'IMPORTED_LINK_DEPENDENT_LIBRARIES_{cfg}'] if x]
elif 'IMPORTED_LINK_DEPENDENT_LIBRARIES' in tgt.properties:
otherDeps += [x for x in tgt.properties['IMPORTED_LINK_DEPENDENT_LIBRARIES'] if x]
for j in otherDeps:
if j in self.traceparser.targets:
targets += [j]
elif reg_is_lib.match(j):
libraries += [j]
elif os.path.isabs(j) and os.path.exists(j):
libraries += [j]
elif self.env.machines.build.is_windows() and reg_is_maybe_bare_lib.match(j):
# On Windows, CMake library dependencies can be passed as bare library names,
# CMake brute-forces a combination of prefix/suffix combinations to find the
# right library. Assume any bare argument passed which is not also a CMake
# target must be a system library we should try to link against.
libraries += self.clib_compiler.find_library(j, self.env, [])
else:
mlog.warning('CMake: Dependency', mlog.bold(j), 'for', mlog.bold(name), 'target', mlog.bold(self._original_module_name(curr)), 'was not found')
processed_targets += [curr]
rtgt = resolve_cmake_trace_targets(i ,self.traceparser, self.env,
clib_compiler=self.clib_compiler,
not_found_warning=lambda x: mlog.warning('CMake: Dependency', mlog.bold(x), 'for', mlog.bold(name), 'was not found')
)
incDirs += rtgt.include_directories
compileOptions += rtgt.public_compile_opts
libraries += rtgt.libraries + rtgt.link_flags
# Make sure all elements in the lists are unique and sorted
incDirs = sorted(set(incDirs))
compileDefinitions = sorted(set(compileDefinitions))
compileOptions = sorted(set(compileOptions))
libraries = sorted(set(libraries))
mlog.debug(f'Include Dirs: {incDirs}')
mlog.debug(f'Compiler Definitions: {compileDefinitions}')
mlog.debug(f'Compiler Options: {compileOptions}')
mlog.debug(f'Libraries: {libraries}')
self.compile_args = compileOptions + compileDefinitions + [f'-I{x}' for x in incDirs]
self.compile_args = compileOptions + [f'-I{x}' for x in incDirs]
self.link_args = libraries
def _get_build_dir(self) -> Path:

View File

@ -0,0 +1,5 @@
find_package(ZLIB)
set(IMPORTEDOLDSTYLE_LIBRARIES ZLIB::ZLIB)
set(IMPORTEDOLDSTYLE_INCLUDE_DIRS ${CMAKE_CURRENT_LIST_DIR})
set(IMPORTEDOLDSTYLE_FOUND ON)

View File

@ -58,6 +58,15 @@ depm1 = dependency('SomethingLikeZLIB', required : true, components : 'required_
depm2 = dependency('SomethingLikeZLIB', required : true, components : 'required_comp', method : 'cmake', cmake_module_path : ['cmake'])
depm3 = dependency('SomethingLikeZLIB', required : true, components : ['required_comp'], cmake_module_path : 'cmake')
# Mix of imported targets and old style variables
depio1 = dependency('ImportedOldStyle', required : true, cmake_module_path : 'cmake')
# Try to actually link with depio1, since we are doing even more "fun" stuff there
exe4 = executable('zlibprog4', 'prog.c', dependencies : depio1)
test('zlibtest4', exe4)
# Test some edge cases with spaces, etc. (but only for CMake >= 3.15)
if cm_vers.version_compare('>=3.15')