diff --git a/.github/workflows/osx-tests.yml b/.github/workflows/osx-tests.yml index 13eab0aab97..6351a8e31fb 100644 --- a/.github/workflows/osx-tests.yml +++ b/.github/workflows/osx-tests.yml @@ -141,6 +141,7 @@ jobs: ~/Applications/CMake/3.15.7 ~/Applications/CMake/3.19.7 ~/Applications/CMake/3.23.5 + ~/Applications/CMake/3.27.9 ~/Applications/bazel/6.5.0 ~/Applications/bazel/7.4.1 ~/Applications/bazel/8.0.0 diff --git a/conan/tools/cmake/cmakedeps2/target_configuration.py b/conan/tools/cmake/cmakedeps2/target_configuration.py index be3967ffdc0..dd3b7670a11 100644 --- a/conan/tools/cmake/cmakedeps2/target_configuration.py +++ b/conan/tools/cmake/cmakedeps2/target_configuration.py @@ -34,17 +34,21 @@ def filename(self): return f"{f}-Targets{build}-{config}.cmake" def _requires(self, info, components): - result = [] + result = {} requires = info.parsed_requires() pkg_name = self._conanfile.ref.name + pkg_type = info.type + assert isinstance(pkg_type, PackageType), f"Pkg type {pkg_type} {type(pkg_type)}" transitive_reqs = self._cmakedeps.get_transitive_requires(self._conanfile) + if not requires and not components: # global cpp_info without components definition # require the pkgname::pkgname base (user defined) or INTERFACE base target - targets = [] for d in transitive_reqs.values(): dep_target = self._cmakedeps.get_property("cmake_target_name", d) - targets.append(dep_target or f"{d.ref.name}::{d.ref.name}") - return targets + dep_target = dep_target or f"{d.ref.name}::{d.ref.name}" + link = not (pkg_type is PackageType.SHARED and d.package_type is PackageType.SHARED) + result[dep_target] = link + return result for required_pkg, required_comp in requires: if required_pkg is None: # Points to a component of same package @@ -53,7 +57,9 @@ def _requires(self, info, components): dep_target = self._cmakedeps.get_property("cmake_target_name", self._conanfile, required_comp) dep_target = dep_target or f"{pkg_name}::{required_comp}" - result.append(dep_target) + link = not (pkg_type is PackageType.SHARED and + dep_comp.package_type is PackageType.SHARED) + result[dep_target] = link else: # Different package try: dep = transitive_reqs[required_pkg] @@ -73,12 +79,15 @@ def _requires(self, info, components): default_target = f"{required_pkg}::{required_comp}" dep_target = self._cmakedeps.get_property("cmake_target_name", dep, comp) dep_target = dep_target or default_target - result.append(dep_target) + link = not (pkg_type is PackageType.SHARED and + dep_comp.package_type is PackageType.SHARED) + result[dep_target] = link return result @property def _context(self): cpp_info = self._conanfile.cpp_info.deduce_full_cpp_info(self._conanfile) + assert isinstance(cpp_info.type, PackageType) pkg_name = self._conanfile.ref.name # fallback to consumer configuration if it doesn't have build_type config = self._conanfile.settings.get_safe("build_type", self._cmakedeps.configuration) @@ -150,10 +159,10 @@ def _get_cmake_lib(self, info, components, pkg_folder, pkg_folder_var): includedirs = ";".join(self._path(i, pkg_folder, pkg_folder_var) for i in info.includedirs) if info.includedirs else "" - requires = ";".join(self._requires(info, components)) + requires = self._requires(info, components) + assert isinstance(requires, dict) defines = " ".join(info.defines) # TODO: Missing escaping? - # TODO: Missing link language # FIXME: Filter by lib traits!!!!! if not self._require.headers: # If not depending on headers, paths and includedirs = defines = None @@ -171,7 +180,7 @@ def _get_cmake_lib(self, info, components, pkg_folder, pkg_folder_var): if info.frameworks: ConanOutput(scope=str(self._conanfile)).warning("frameworks not supported yet in new CMakeDeps generator") - if info.libs: + if info.libs: # TODO: to change to location if len(info.libs) != 1: raise ConanException(f"New CMakeDeps only allows 1 lib per component:\n" f"{self._conanfile}: {info.libs}") @@ -203,15 +212,14 @@ def _add_root_lib_target(self, libs, pkg_name, cpp_info): if libs and root_target_name not in libs: # Add a generic interface target for the package depending on the others if cpp_info.default_components is not None: - all_requires = [] + all_requires = {} for defaultc in cpp_info.default_components: target_name = self._cmakedeps.get_property("cmake_target_name", self._conanfile, defaultc) comp_name = target_name or f"{pkg_name}::{defaultc}" - all_requires.append(comp_name) - all_requires = ";".join(all_requires) + all_requires[comp_name] = True # It is an interface, full link else: - all_requires = ";".join(libs.keys()) + all_requires = {k: True for k in libs.keys()} libs[root_target_name] = {"type": "INTERFACE", "requires": all_requires} @@ -339,10 +347,29 @@ def _template(self): set_target_properties({{lib}} PROPERTIES IMPORTED_IMPLIB_{{config}} "{{lib_info["link_location"]}}") {% endif %} + {% if lib_info.get("requires") %} - set_target_properties({{lib}} PROPERTIES INTERFACE_LINK_LIBRARIES - "{{config_wrapper(config, lib_info["requires"])}}") + # Information of transitive dependencies + {% for require_target, link in lib_info["requires"].items() %} + # Requirement {{require_target}} => Full link: {{link}} + + {% if link %} + # set property allows to append, and lib_info[requires] will iterate + set_property(TARGET {{lib}} APPEND PROPERTY INTERFACE_LINK_LIBRARIES + "{{config_wrapper(config, require_target)}}") + {% else %} + if(${CMAKE_VERSION} VERSION_LESS "3.27") + message(FATAL_ERROR "The 'CMakeToolchain' generator only works with CMake >= 3.27") + endif() + # If the headers trait is not there, this will do nothing + target_link_libraries({{lib}} INTERFACE + $<COMPILE_ONLY:{{config_wrapper(config, require_target)}}> ) + set_property(TARGET {{lib}} APPEND PROPERTY IMPORTED_LINK_DEPENDENT_LIBRARIES_{{config}} + {{require_target}}) {% endif %} + {% endfor %} + {% endif %} + {% if lib_info.get("system_libs") %} target_link_libraries({{lib}} INTERFACE {{lib_info["system_libs"]}}) {% endif %} diff --git a/test/functional/toolchains/cmake/cmakedeps/test_cmakedeps_new.py b/test/functional/toolchains/cmake/cmakedeps/test_cmakedeps_new.py index 4ee67b740ec..64a993e5e82 100644 --- a/test/functional/toolchains/cmake/cmakedeps/test_cmakedeps_new.py +++ b/test/functional/toolchains/cmake/cmakedeps/test_cmakedeps_new.py @@ -219,7 +219,7 @@ def test_libs_transitive(self, transitive_libraries, shared): assert "Conan: Target declared imported STATIC library 'engine::engine'" in c.out # if not using cmake >= 3.23 the intermediate gamelib_test linkage fail - @pytest.mark.tool("cmake", "3.23") + @pytest.mark.tool("cmake", "3.27") @pytest.mark.parametrize("shared", [False, True]) def test_multilevel(self, shared): # TODO: make this shared fixtures in conftest for multi-level shared testing @@ -338,8 +338,8 @@ def package_info(self): c.run(f"install app -c tools.cmake.cmakedeps:new={new_value} -g CMakeDeps") targets_cmake = c.load("app/pkg-Targets-release.cmake") assert "find_dependency(MyDep REQUIRED CONFIG)" in targets_cmake - assert 'set_target_properties(pkg::pkg PROPERTIES INTERFACE_LINK_LIBRARIES\n' \ - ' "$<$<CONFIG:RELEASE>:MyTargetDep>"' in targets_cmake + assert 'set_property(TARGET pkg::pkg APPEND PROPERTY INTERFACE_LINK_LIBRARIES\n' \ + ' "$<$<CONFIG:RELEASE>:MyTargetDep>")' in targets_cmake class TestLibsLinkageTraits: @@ -364,6 +364,7 @@ def test_linkage_shared_static(self): assert "engine/0.1: Hello World Release!" assert "game/0.1: Hello World Release!" + @pytest.mark.tool("cmake", "3.27") @pytest.mark.parametrize("shared", [False, True]) def test_transitive_headers(self, shared): c = TestClient() diff --git a/test/integration/graph/test_replace_requires.py b/test/integration/graph/test_replace_requires.py index dbde54b949e..4b832ebac4a 100644 --- a/test/integration/graph/test_replace_requires.py +++ b/test/integration/graph/test_replace_requires.py @@ -451,8 +451,8 @@ class App(ConanFile): cmake = c.load("app/openssl-Targets-release.cmake") assert "find_dependency(ZLIB REQUIRED CONFIG)" in cmake assert "add_library(openssl::openssl STATIC IMPORTED)" in cmake - assert "set_target_properties(openssl::openssl PROPERTIES INTERFACE_LINK_LIBRARIES\n" \ - ' "$<$<CONFIG:RELEASE>:ZLIB::ZLIB>")' in cmake + assert "set_property(TARGET openssl::openssl APPEND PROPERTY INTERFACE_LINK_LIBRARIES\n" \ + ' "$<$<CONFIG:RELEASE>:ZLIB::ZLIB>")' in cmake @pytest.mark.parametrize("diamond", [True, False]) def test_openssl_components(self, diamond): @@ -523,8 +523,8 @@ class App(ConanFile): cmake = c.load("app/openssl-Targets-release.cmake") assert "find_dependency(ZLIB REQUIRED CONFIG)" in cmake assert "add_library(openssl::crypto STATIC IMPORTED)" in cmake - assert "set_target_properties(openssl::crypto PROPERTIES INTERFACE_LINK_LIBRARIES\n" \ - ' "$<$<CONFIG:RELEASE>:ZLIB::ZLIB>")' in cmake + assert "set_property(TARGET openssl::crypto APPEND PROPERTY INTERFACE_LINK_LIBRARIES\n" \ + ' "$<$<CONFIG:RELEASE>:ZLIB::ZLIB>")' in cmake @pytest.mark.parametrize("diamond", [True, False]) @pytest.mark.parametrize("explicit_requires", [True, False]) @@ -601,8 +601,8 @@ class App(ConanFile): assert "find_dependency(ZLIB REQUIRED CONFIG)" in cmake assert "add_library(openssl::openssl STATIC IMPORTED)" in cmake # It should access the generic zlib-ng target - assert "set_target_properties(openssl::openssl PROPERTIES INTERFACE_LINK_LIBRARIES\n" \ - ' "$<$<CONFIG:RELEASE>:zlib-ng::zlib-ng>")' in cmake + assert "set_property(TARGET openssl::openssl APPEND PROPERTY INTERFACE_LINK_LIBRARIES\n" \ + ' "$<$<CONFIG:RELEASE>:zlib-ng::zlib-ng>")' in cmake @pytest.mark.parametrize("diamond", [True, False]) @pytest.mark.parametrize("package_requires", [False, True]) @@ -682,8 +682,8 @@ class App(ConanFile): assert "add_library(openssl::crypto STATIC IMPORTED)" in cmake if package_requires: # The generic package requirement uses the package name zlib-ng - assert "set_target_properties(openssl::crypto PROPERTIES INTERFACE_LINK_LIBRARIES\n" \ - ' "$<$<CONFIG:RELEASE>:zlib-ng::zlib-ng>")' in cmake + assert "set_property(TARGET openssl::crypto APPEND PROPERTY INTERFACE_LINK_LIBRARIES\n" \ + ' "$<$<CONFIG:RELEASE>:zlib-ng::zlib-ng>")' in cmake else: - assert "set_target_properties(openssl::crypto PROPERTIES INTERFACE_LINK_LIBRARIES\n" \ - ' "$<$<CONFIG:RELEASE>:ZLIB::ZLIB>")' in cmake + assert "set_property(TARGET openssl::crypto APPEND PROPERTY INTERFACE_LINK_LIBRARIES\n" \ + ' "$<$<CONFIG:RELEASE>:ZLIB::ZLIB>")' in cmake