Skip to content

Commit

Permalink
CMakeDeps imported target multiconfig (#11691)
Browse files Browse the repository at this point in the history
Changelog: omit
Docs: omit

CMakeDeps imported target multiconfig
  • Loading branch information
jcar87 committed Jul 28, 2022
2 parents b52e7f3 + 9340ce1 commit dd4e9fa
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 60 deletions.
23 changes: 14 additions & 9 deletions conan/tools/cmake/cmakedeps/templates/macros.py
Expand Up @@ -41,7 +41,7 @@ def template(self):
endif()
endmacro()
function(conan_package_library_targets libraries package_libdir deps out_libraries out_libraries_target config_suffix package_name)
function(conan_package_library_targets libraries package_libdir deps out_libraries_target config package_name)
set(_out_libraries "")
set(_out_libraries_target "")
set(_CONAN_ACTUAL_TARGETS "")
Expand All @@ -51,20 +51,20 @@ def template(self):
NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH)
if(CONAN_FOUND_LIBRARY)
message(VERBOSE "Conan: Library ${_LIBRARY_NAME} found ${CONAN_FOUND_LIBRARY}")
list(APPEND _out_libraries ${CONAN_FOUND_LIBRARY})
# Create a micro-target for each lib/a found
# Allow only some characters for the target name
string(REGEX REPLACE "[^A-Za-z0-9.+_-]" "_" _LIBRARY_NAME ${_LIBRARY_NAME})
set(_LIB_NAME CONAN_LIB::${package_name}_${_LIBRARY_NAME}${config_suffix})
set(_LIB_NAME CONAN_LIB::${package_name}_${_LIBRARY_NAME})
if(NOT TARGET ${_LIB_NAME})
# Create a micro-target for each lib/a found
add_library(${_LIB_NAME} UNKNOWN IMPORTED)
set_target_properties(${_LIB_NAME} PROPERTIES IMPORTED_LOCATION ${CONAN_FOUND_LIBRARY})
list(APPEND _CONAN_ACTUAL_TARGETS ${_LIB_NAME})
else()
message(VERBOSE "Conan: Skipping already existing target: ${_LIB_NAME}")
endif()
# Enable configuration only when it is available to avoid missing configs
set_property(TARGET ${_LIB_NAME} APPEND PROPERTY IMPORTED_CONFIGURATIONS ${config})
# Link library file
set_target_properties(${_LIB_NAME} PROPERTIES IMPORTED_LOCATION_${config} ${CONAN_FOUND_LIBRARY})
list(APPEND _CONAN_ACTUAL_TARGETS ${_LIB_NAME})
list(APPEND _out_libraries_target ${_LIB_NAME})
message(VERBOSE "Conan: Found: ${CONAN_FOUND_LIBRARY}")
else()
Expand All @@ -76,10 +76,15 @@ def template(self):
# Add all dependencies to all targets
string(REPLACE " " ";" deps_list "${deps}")
foreach(_CONAN_ACTUAL_TARGET ${_CONAN_ACTUAL_TARGETS})
set_property(TARGET ${_CONAN_ACTUAL_TARGET} PROPERTY INTERFACE_LINK_LIBRARIES "${deps_list}" APPEND)
set_property(TARGET ${_CONAN_ACTUAL_TARGET} PROPERTY INTERFACE_LINK_LIBRARIES $<$<CONFIG:${config}>:${deps_list}> APPEND)
endforeach()
# ONLY FOR DEBUGGING PURPOSES
foreach(_CONAN_ACTUAL_TARGET ${_CONAN_ACTUAL_TARGETS})
get_target_property(linked_libs ${_CONAN_ACTUAL_TARGET} INTERFACE_LINK_LIBRARIES)
message(VERBOSE "Target Properties: ${_CONAN_ACTUAL_TARGET} INTERFACE_LINK_LIBRARIES ='${linked_libs}'")
endforeach()
set(${out_libraries} ${_out_libraries} PARENT_SCOPE)
set(${out_libraries_target} ${_out_libraries_target} PARENT_SCOPE)
endfunction()
Expand Down
75 changes: 38 additions & 37 deletions conan/tools/cmake/cmakedeps/templates/target_configuration.py
Expand Up @@ -31,6 +31,7 @@ def context(self):
return {"pkg_name": self.pkg_name,
"root_target_name": self.root_target_name,
"config_suffix": self.config_suffix,
"config": self.configuration.upper(),
"deps_targets_names": ";".join(deps_targets_names),
"components_names": components_names,
"configuration": self.cmakedeps.configuration,
Expand Down Expand Up @@ -60,64 +61,44 @@ def template(self):
conan_find_apple_frameworks({{ pkg_name }}_FRAMEWORKS_FOUND{{ config_suffix }} "{{ '${' }}{{ pkg_name }}_FRAMEWORKS{{ config_suffix }}}" "{{ '${' }}{{ pkg_name }}_FRAMEWORK_DIRS{{ config_suffix }}}")
# Gather all the libraries that should be linked to the targets (do not touch existing variables)
set(_{{ pkg_name }}_DEPENDENCIES{{ config_suffix }} "{{ '${' }}{{ pkg_name }}_FRAMEWORKS_FOUND{{ config_suffix }}} {{ '${' }}{{ pkg_name }}_SYSTEM_LIBS{{ config_suffix }}} {{ deps_targets_names }}")
# FIXME: Why is needed to pass all the dependencies to link with the micro-targets?
set(_{{ pkg_name }}_DEPENDENCIES{{ config_suffix }} "{{ deps_targets_names }}")
set({{ pkg_name }}_LIBRARIES_TARGETS{{ config_suffix }} "") # Will be filled later
set({{ pkg_name }}_LIBRARIES{{ config_suffix }} "") # Will be filled later
conan_package_library_targets("{{ '${' }}{{ pkg_name }}_LIBS{{ config_suffix }}}" # libraries
"{{ '${' }}{{ pkg_name }}_LIB_DIRS{{ config_suffix }}}" # package_libdir
"{{ '${' }}_{{ pkg_name }}_DEPENDENCIES{{ config_suffix }}}" # deps
{{ pkg_name }}_LIBRARIES{{ config_suffix }} # out_libraries
{{ pkg_name }}_LIBRARIES_TARGETS{{ config_suffix }} # out_libraries_targets
"{{ config_suffix }}" # config_suffix
{{ pkg_name }}_LIBRARIES_TARGETS # out_libraries_targets
"{{ config }}" # DEBUG, RELEASE ...
"{{ pkg_name }}") # package_name
# The XXXX_LIBRARIES_RELEASE/DEBUG is used for the module (FindXXX.cmake)
foreach(_FRAMEWORK {{ '${' }}{{ pkg_name }}_FRAMEWORKS_FOUND{{ config_suffix }}})
list(APPEND {{ pkg_name }}_LIBRARIES_TARGETS{{ config_suffix }} ${_FRAMEWORK})
list(APPEND {{ pkg_name }}_LIBRARIES{{ config_suffix }} ${_FRAMEWORK})
endforeach()
foreach(_SYSTEM_LIB {{ '${' }}{{ pkg_name }}_SYSTEM_LIBS{{ config_suffix }}})
list(APPEND {{ pkg_name }}_LIBRARIES_TARGETS{{ config_suffix }} ${_SYSTEM_LIB})
list(APPEND {{ pkg_name }}_LIBRARIES{{ config_suffix }} ${_SYSTEM_LIB})
endforeach()
# We need to add our requirements too
set({{ pkg_name }}_LIBRARIES_TARGETS{{ config_suffix }} {{ '"${' }}{{ pkg_name }}_LIBRARIES_TARGETS{{ config_suffix }}{{ '};' }}{{ deps_targets_names }}")
set({{ pkg_name }}_LIBRARIES{{ config_suffix }} {{ '"${' }}{{ pkg_name }}_LIBRARIES{{ config_suffix }}{{ '};' }}{{ deps_targets_names }}")
set({{ pkg_name }}_LIBRARIES{{ config_suffix }} "")
list(APPEND {{ pkg_name }}_LIBRARIES{{ config_suffix }} {{ deps_targets_names }})
# FIXME: What is the result of this for multi-config? All configs adding themselves to path?
set(CMAKE_MODULE_PATH {{ '${' }}{{ pkg_name }}_BUILD_DIRS{{ config_suffix }}} {{ '${' }}CMAKE_MODULE_PATH})
set(CMAKE_PREFIX_PATH {{ '${' }}{{ pkg_name }}_BUILD_DIRS{{ config_suffix }}} {{ '${' }}CMAKE_PREFIX_PATH})
{%- for comp_variable_name, comp_target_name in components_names %}
########## COMPONENT {{ comp_target_name }} FIND LIBRARIES & FRAMEWORKS / DYNAMIC VARS #############
set({{ pkg_name }}_{{ comp_variable_name }}_FRAMEWORKS_FOUND{{ config_suffix }} "")
conan_find_apple_frameworks({{ pkg_name }}_{{ comp_variable_name }}_FRAMEWORKS_FOUND{{ config_suffix }} "{{ '${'+pkg_name+'_'+comp_variable_name+'_FRAMEWORKS'+config_suffix+'}' }}" "{{ '${'+pkg_name+'_'+comp_variable_name+'_FRAMEWORK_DIRS'+config_suffix+'}' }}")
set({{ pkg_name }}_{{ comp_variable_name }}_LIB_TARGETS{{ config_suffix }} "")
set({{ pkg_name }}_{{ comp_variable_name }}_NOT_USED{{ config_suffix }} "")
set({{ pkg_name }}_{{ comp_variable_name }}_LIBS_FRAMEWORKS_DEPS{{ config_suffix }} {{ '${'+pkg_name+'_'+comp_variable_name+'_FRAMEWORKS_FOUND'+config_suffix+'}' }} {{ '${'+pkg_name+'_'+comp_variable_name+'_SYSTEM_LIBS'+config_suffix+'}' }} {{ '${'+pkg_name+'_'+comp_variable_name+'_DEPENDENCIES'+config_suffix+'}' }})
conan_package_library_targets("{{ '${'+pkg_name+'_'+comp_variable_name+'_LIBS'+config_suffix+'}' }}"
"{{ '${'+pkg_name+'_'+comp_variable_name+'_LIB_DIRS'+config_suffix+'}' }}"
"{{ '${'+pkg_name+'_'+comp_variable_name+'_LIBS_FRAMEWORKS_DEPS'+config_suffix+'}' }}"
{{ pkg_name }}_{{ comp_variable_name }}_NOT_USED{{ config_suffix }}
{{ pkg_name }}_{{ comp_variable_name }}_LIB_TARGETS{{ config_suffix }}
"{{ config_suffix }}"
"{{ pkg_name }}_{{ comp_variable_name }}")
set({{ pkg_name }}_{{ comp_variable_name }}_LINK_LIBS{{ config_suffix }} {{ '${'+pkg_name+'_'+comp_variable_name+'_LIB_TARGETS'+config_suffix+'}' }} {{ '${'+pkg_name+'_'+comp_variable_name+'_LIBS_FRAMEWORKS_DEPS'+config_suffix+'}' }})
{%- endfor %}
{% if not components_names %}
########## GLOBAL TARGET PROPERTIES {{ configuration }} ########################################
set_property(TARGET {{root_target_name}}
PROPERTY INTERFACE_LINK_LIBRARIES
$<$<CONFIG:{{configuration}}>:${{'{'}}{{pkg_name}}_OBJECTS{{config_suffix}}}
${{'{'}}{{pkg_name}}_LIBRARIES_TARGETS{{config_suffix}}}> APPEND)
$<$<CONFIG:{{configuration}}>:${{'{'}}{{pkg_name}}_OBJECTS{{config_suffix}}}>
${{'{'}}{{pkg_name}}_LIBRARIES_TARGETS}
$<$<CONFIG:{{configuration}}>:${{'{'}}_{{pkg_name}}_DEPENDENCIES{{config_suffix}}}>
$<$<CONFIG:{{configuration}}>:${{'{'}}{{pkg_name}}_FRAMEWORKS_FOUND{{config_suffix}}}>
$<$<CONFIG:{{configuration}}>:${{'{'}}{{pkg_name}}_SYSTEM_LIBS{{config_suffix}}}>
APPEND)
set_property(TARGET {{root_target_name}}
PROPERTY INTERFACE_LINK_OPTIONS
Expand Down Expand Up @@ -146,10 +127,30 @@ def template(self):
{%- for comp_variable_name, comp_target_name in components_names %}
########## COMPONENT {{ comp_target_name }} TARGET PROPERTIES ######################################
set_property(TARGET {{ comp_target_name }} PROPERTY INTERFACE_LINK_LIBRARIES
$<$<CONFIG:{{ configuration }}>:{{tvalue(pkg_name, comp_variable_name, 'OBJECTS', config_suffix)}}
{{tvalue(pkg_name, comp_variable_name, 'LINK_LIBS', config_suffix)}}> APPEND)
########## COMPONENT {{ comp_target_name }} FIND LIBRARIES & FRAMEWORKS / DYNAMIC VARS #############
set({{ pkg_name }}_{{ comp_variable_name }}_FRAMEWORKS_FOUND{{ config_suffix }} "")
conan_find_apple_frameworks({{ pkg_name }}_{{ comp_variable_name }}_FRAMEWORKS_FOUND{{ config_suffix }} "{{ '${'+pkg_name+'_'+comp_variable_name+'_FRAMEWORKS'+config_suffix+'}' }}" "{{ '${'+pkg_name+'_'+comp_variable_name+'_FRAMEWORK_DIRS'+config_suffix+'}' }}")
set({{ pkg_name }}_{{ comp_variable_name }}_LIBRARIES_TARGETS "")
conan_package_library_targets("{{ '${'+pkg_name+'_'+comp_variable_name+'_LIBS'+config_suffix+'}' }}"
"{{ '${'+pkg_name+'_'+comp_variable_name+'_LIB_DIRS'+config_suffix+'}' }}"
"{{ '${'+pkg_name+'_'+comp_variable_name+'_DEPENDENCIES'+config_suffix+'}' }}"
{{ pkg_name }}_{{ comp_variable_name }}_LIBRARIES_TARGETS
"{{ config }}" # DEBUG, RELEASE...
"{{ pkg_name }}_{{ comp_variable_name }}")
########## COMPONENT {{ comp_target_name }} TARGET PROPERTIES #####################################
set_property(TARGET {{comp_target_name}}
PROPERTY INTERFACE_LINK_LIBRARIES
$<$<CONFIG:{{configuration}}>:${{'{'}}{{pkg_name}}_{{comp_variable_name}}_OBJECTS{{config_suffix}}}>
${{'{'}}{{pkg_name}}_{{comp_variable_name}}_LIBRARIES_TARGETS}
$<$<CONFIG:{{configuration}}>:${{'{'}}{{pkg_name}}_{{comp_variable_name}}_DEPENDENCIES{{config_suffix}}}>
$<$<CONFIG:{{configuration}}>:${{'{'}}{{pkg_name}}_{{comp_variable_name}}_FRAMEWORKS_FOUND{{config_suffix}}}>
$<$<CONFIG:{{configuration}}>:${{'{'}}{{pkg_name}}_{{comp_variable_name}}_SYSTEM_LIBS{{config_suffix}}}>
APPEND)
set_property(TARGET {{ comp_target_name }} PROPERTY INTERFACE_LINK_OPTIONS
$<$<CONFIG:{{ configuration }}>:{{tvalue(pkg_name, comp_variable_name, 'LINKER_FLAGS', config_suffix)}}> APPEND)
set_property(TARGET {{ comp_target_name }} PROPERTY INTERFACE_INCLUDE_DIRECTORIES
Expand Down
Expand Up @@ -153,13 +153,14 @@ def package_info(self):
if build_type == "Release":
assert "System libs release: %s" % library_name in client.out
assert "Libraries to Link release: lib1" in client.out
target_libs = "$<$<CONFIG:Release>:;CONAN_LIB::Test_lib1_RELEASE;sys1;>"
else:
assert "System libs debug: %s" % library_name in client.out
assert "Libraries to Link debug: lib1" in client.out
target_libs = "$<$<CONFIG:Debug>:;CONAN_LIB::Test_lib1_DEBUG;sys1d;>"

assert "Target libs: %s" % target_libs in client.out
# FIXME: This assert is ugly, empty configs comes from empty _OBJECTS, FRAMEWORKS etc
target_libs = f"$<$<CONFIG:{build_type}>:>;CONAN_LIB::Test_lib1;$<$<CONFIG:{build_type}>" \
f":>;$<$<CONFIG:{build_type}>:>;$<$<CONFIG:{build_type}>:{library_name}>"
assert target_libs in client.out


@pytest.mark.tool_cmake
Expand Down
Expand Up @@ -204,7 +204,9 @@ def build(self):

t.save({"conanfile.py": conanfile, "CMakeLists.txt": cmakelists})
t.run("create . --build missing -s build_type=Release")
assert 'component libs: $<$<CONFIG:Release>:;system_lib_component>' in t.out
# FIXME: This assert is ugly, empty configs comes from empty _OBJECTS, FRAMEWORKS etc
assert 'component libs: $<$<CONFIG:Release>:>;$<$<CONFIG:Release>:>;$<$<CONFIG:Release>:>' \
';$<$<CONFIG:Release>:system_lib_component>' in t.out
assert ('component options: '
'$<$<CONFIG:Release>:'
'$<$<STREQUAL:$<TARGET_PROPERTY:TYPE>,SHARED_LIBRARY>:>;'
Expand Down
@@ -1,5 +1,6 @@
import os
import platform
import re
import textwrap

import pytest
Expand Down Expand Up @@ -257,12 +258,19 @@ def _get_link_order_from_cmake(content):

def _get_link_order_from_xcode(content):
libs = []
start_key = '-headerpad_max_install_names",'

# Find the right Release block in the XCode file
results = re.finditer('/\* Release \*/ = {', content)
for r in results:
release_section = content[r.start():].split("name = Release;", 1)[0]
if "-headerpad_max_install_names" in release_section:
break
else:
raise Exception("Cannot find the Release block linking the expected libraries")

start_key = '-Wl,-headerpad_max_install_names'
end_key = ');'
libs_content = content.split(start_key, 1)[1].split(end_key, 1)[0]
if libs_content == '"$(inherited)"':
# FIXME: Dirty hack, sometimes the library list is not at the 1 but at 2
libs_content = content.split(start_key, 1)[2].split(end_key, 1)[0]
libs_content = release_section.split(start_key, 1)[1].split(end_key, 1)[0]
libs_unstripped = libs_content.split(",")
for lib in libs_unstripped:
if ".a" in lib:
Expand Down Expand Up @@ -303,7 +311,10 @@ def _run_and_get_lib_order(t, generator):
if generator == "Xcode":
t.run_command("cmake . -G Xcode -DCMAKE_VERBOSE_MAKEFILE:BOOL=True"
" -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake")
# This is building by default the Debug configuration that contains nothing, so it works
t.run_command("cmake --build .")
# This is building the release and fails because invented system libraries are missing
t.run_command("cmake --build . --config Release", assert_error=True)
# Get the actual link order from the CMake call
libs = _get_link_order_from_xcode(t.load(os.path.join('executable.xcodeproj',
'project.pbxproj')))
Expand Down
Expand Up @@ -104,14 +104,15 @@ def package_info(self):
client.run("install hello/1.0@ -g CMakeDeps -s arch=x86_64 -s build_type=Release")
with open(os.path.join(client.current_folder, "hello-Target-release.cmake")) as f:
content = f.read()
assert """set_property(TARGET hello::say PROPERTY INTERFACE_LINK_LIBRARIES
$<$<CONFIG:Release>:${hello_hello_say_OBJECTS_RELEASE}
${hello_hello_say_LINK_LIBS_RELEASE}> APPEND)""" in content
assert """set_property(TARGET hello::say
PROPERTY INTERFACE_LINK_LIBRARIES
$<$<CONFIG:Release>:${hello_hello_say_OBJECTS_RELEASE}>
${hello_hello_say_LIBRARIES_TARGETS}""" in content
# If there are componets, there is not a global cpp so this is not generated
assert """set_property(TARGET hello::hello
PROPERTY INTERFACE_LINK_LIBRARIES
$<$<CONFIG:Release>:${hello_OBJECTS_RELEASE}
${hello_LIBRARIES_TARGETS_RELEASE}> APPEND)""" not in content
$<$<CONFIG:Release>:${hello_OBJECTS_RELEASE}>
${hello_LIBRARIES_TARGETS}""" not in content
# But the global target is linked with the targets from the components
assert "target_link_libraries(hello::hello INTERFACE hello::say)" in content

Expand Down

0 comments on commit dd4e9fa

Please sign in to comment.