Repository: conan-io/conan
Pull request #17582 (2025-01-15 23:26:31)

moving from target_link_libraries to property
Changelog: Omit
Docs: Omit

Just an internal first step towards better linking


Patch:
diff --git a/conan/tools/cmake/cmakedeps2/target_configuration.py b/conan/tools/cmake/cmakedeps2/target_configuration.py
index 454a4e8c9..f6ef07c95 100644
--- a/conan/tools/cmake/cmakedeps2/target_configuration.py
+++ b/conan/tools/cmake/cmakedeps2/target_configuration.py
@@ -148,7 +148,7 @@ class TargetConfigurationTemplate2:
 
         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 = ";".join(self._requires(info, components))
         defines = " ".join(info.defines)
         # TODO: Missing escaping?
         # TODO: Missing link language
@@ -207,9 +207,9 @@ class TargetConfigurationTemplate2:
                                                                defaultc)
                     comp_name = target_name or f"{pkg_name}::{defaultc}"
                     all_requires.append(comp_name)
-                all_requires = " ".join(all_requires)
+                all_requires = ";".join(all_requires)
             else:
-                all_requires = " ".join(libs.keys())
+                all_requires = ";".join(libs.keys())
             libs[root_target_name] = {"type": "INTERFACE",
                                       "requires": all_requires}
 
@@ -342,7 +342,8 @@ class TargetConfigurationTemplate2:
                               "{{lib_info["link_location"]}}")
         {% endif %}
         {% if lib_info.get("requires") %}
-        target_link_libraries({{lib}} INTERFACE {{lib_info["requires"]}})
+        set_target_properties({{lib}} PROPERTIES INTERFACE_LINK_LIBRARIES
+                              "{{config_wrapper(config, lib_info["requires"])}}")
         {% endif %}
         {% if lib_info.get("system_libs") %}
         target_link_libraries({{lib}} INTERFACE {{lib_info["system_libs"]}})
diff --git a/conans/client/graph/install_graph.py b/conans/client/graph/install_graph.py
index 2a23fd8c5..896aab9bd 100644
--- a/conans/client/graph/install_graph.py
+++ b/conans/client/graph/install_graph.py
@@ -576,8 +576,8 @@ class InstallGraph:
         missing_pkgs = "', '".join(list(sorted([str(pref.ref) for pref in missing_prefs])))
         if self._is_test_package:
             build_msg = "This is a **test_package** missing binary. You can use --build (for " \
-                        "all dependencies) or --build-test (exclusive for 'test_package' " \
-                        "dependencies) to define what can be built from sources"
+                        "all dependencies) or --build-test (exclusive for 'conan create' building " \
+                        "test_package' dependencies) to define what can be built from sources"
         else:
             if len(missing_prefs) >= 5:
                 build_str = "--build=missing"


Test patch:
diff --git a/test/functional/toolchains/cmake/cmakedeps/test_cmakedeps_new.py b/test/functional/toolchains/cmake/cmakedeps/test_cmakedeps_new.py
index 044865fe3..87509c064 100644
--- a/test/functional/toolchains/cmake/cmakedeps/test_cmakedeps_new.py
+++ b/test/functional/toolchains/cmake/cmakedeps/test_cmakedeps_new.py
@@ -218,36 +218,46 @@ class TestLibs:
             assert "Conan: Target declared imported STATIC library 'matrix::matrix'" in c.out
             assert "Conan: Target declared imported STATIC library 'engine::engine'" in c.out
 
-    def test_multilevel_shared(self):
+    @pytest.mark.parametrize("shared", [False, True])
+    def test_multilevel(self, shared):
         # TODO: make this shared fixtures in conftest for multi-level shared testing
         c = TestClient(default_server_user=True)
         c.run("new cmake_lib -d name=matrix -d version=0.1")
-        c.run(f"create . -o *:shared=True -c tools.cmake.cmakedeps:new={new_value}")
+        c.run(f"create . -o *:shared={shared} -c tools.cmake.cmakedeps:new={new_value}")
 
         c.save({}, clean_first=True)
         c.run("new cmake_lib -d name=engine -d version=0.1 -d requires=matrix/0.1")
-        c.run(f"create . -o *:shared=True -c tools.cmake.cmakedeps:new={new_value}")
+        c.run(f"create . -o *:shared={shared} -c tools.cmake.cmakedeps:new={new_value}")
 
         c.save({}, clean_first=True)
         c.run("new cmake_lib -d name=gamelib -d version=0.1 -d requires=engine/0.1")
-        c.run(f"create . -o *:shared=True -c tools.cmake.cmakedeps:new={new_value}")
+        c.run(f"create . -o *:shared={shared} -c tools.cmake.cmakedeps:new={new_value}")
 
         c.save({}, clean_first=True)
         c.run("new cmake_exe -d name=game -d version=0.1 -d requires=gamelib/0.1")
-        c.run(f"create . -o *:shared=True -c tools.cmake.cmakedeps:new={new_value}")
+        c.run(f"create . -o *:shared={shared} -c tools.cmake.cmakedeps:new={new_value}")
 
         assert "matrix/0.1: Hello World Release!"
         assert "engine/0.1: Hello World Release!"
         assert "gamelib/0.1: Hello World Release!"
         assert "game/0.1: Hello World Release!"
 
+        # Make sure that transitive headers are private, fails to include, traits work
+        game_cpp = c.load("src/game.cpp")
+        for header in ("matrix", "engine"):
+            new_game_cpp = f"#include <{header}.h>\n" + game_cpp
+            c.save({"src/game.cpp": new_game_cpp})
+            c.run(f"build . -o *:shared={shared} -c tools.cmake.cmakedeps:new={new_value}",
+                  assert_error=True)
+            assert f"{header}.h" in c.out
+
         # Make sure it works downloading to another cache
         c.run("upload * -r=default -c")
         c.run("remove * -c")
 
         c2 = TestClient(servers=c.servers)
         c2.run("new cmake_exe -d name=game -d version=0.1 -d requires=gamelib/0.1")
-        c2.run(f"create . -o *:shared=True -c tools.cmake.cmakedeps:new={new_value}")
+        c2.run(f"create . -o *:shared={shared} -c tools.cmake.cmakedeps:new={new_value}")
 
         assert "matrix/0.1: Hello World Release!"
         assert "engine/0.1: Hello World Release!"
@@ -304,7 +314,8 @@ class TestLibsIntegration:
         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 "target_link_libraries(pkg::pkg INTERFACE MyTargetDep)" in targets_cmake
+        assert 'set_target_properties(pkg::pkg PROPERTIES INTERFACE_LINK_LIBRARIES\n' \
+               '                      "$<$<CONFIG:RELEASE>:MyTargetDep>"' in targets_cmake
 
 
 class TestLibsLinkageTraits:
@@ -329,6 +340,28 @@ class TestLibsLinkageTraits:
         assert "engine/0.1: Hello World Release!"
         assert "game/0.1: Hello World Release!"
 
+    @pytest.mark.parametrize("shared", [False, True])
+    def test_transitive_headers(self, shared):
+        c = TestClient()
+        c.run("new cmake_lib -d name=matrix -d version=0.1")
+        c.run(f"create . -o *:shared={shared} -c tools.cmake.cmakedeps:new={new_value} -tf=")
+
+        c.save({}, clean_first=True)
+        c.run("new cmake_lib -d name=engine -d version=0.1 -d requires=matrix/0.1")
+        engine_h = c.load("include/engine.h")
+        engine_h = "#include <matrix.h>\n" + engine_h
+        c.save({"include/engine.h": engine_h})
+        conanfile = c.load("conanfile.py")
+        conanfile = conanfile.replace('self.requires("matrix/0.1")',
+                                      'self.requires("matrix/0.1", transitive_headers=True)')
+        c.save({"conanfile.py": conanfile})
+        c.run(f"create . -o *:shared={shared} -c tools.cmake.cmakedeps:new={new_value} -tf=")
+
+        c.save({}, clean_first=True)
+        c.run("new cmake_exe -d name=game -d version=0.1 -d requires=engine/0.1")
+        c.run(f"build . -o *:shared={shared} -c tools.cmake.cmakedeps:new={new_value}")
+        # it works
+
 
 @pytest.mark.tool("cmake")
 class TestLibsComponents: