Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CMakeDeps imported target multiconfig #11691

Merged
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

out_libraries arg was not used.

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
74 changes: 37 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,43 @@ 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 }}")
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 %}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved below, with the other components code to be easier.


########## 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 +126,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