Repository: conan-io/conan
Pull request #18561 (2025-06-28 21:07:57)

fix incorrect inputs for relativize paths generator
Changelog: Fix: Avoid incorrect absolute path inputs in ``-of`` for relativize paths in generators
Docs: Omit

Close https://github.com/conan-io/conan/issues/18560



Patch:
diff --git a/conan/internal/api/install/generators.py b/conan/internal/api/install/generators.py
index 4e7093875..0ee676a81 100644
--- a/conan/internal/api/install/generators.py
+++ b/conan/internal/api/install/generators.py
@@ -254,7 +254,7 @@ def relativize_path(path, conanfile, placeholder, normalize=True):
         return path
     try:
         common_path = os.path.commonpath([path, conanfile.generators_folder, base_common_folder])
-        if common_path == base_common_folder:
+        if common_path.replace("\\", "/") == base_common_folder.replace("\\", "/"):
             rel_path = os.path.relpath(path, conanfile.generators_folder)
             new_path = os.path.join(placeholder, rel_path)
             return new_path.replace("\\", "/") if normalize else new_path
diff --git a/conan/tools/cmake/cmakedeps2/target_configuration.py b/conan/tools/cmake/cmakedeps2/target_configuration.py
index cfc7a1e3d..9b7bdc033 100644
--- a/conan/tools/cmake/cmakedeps2/target_configuration.py
+++ b/conan/tools/cmake/cmakedeps2/target_configuration.py
@@ -78,9 +78,11 @@ class TargetConfigurationTemplate2:
                     dep_comp = dep.cpp_info.components.get(required_comp)
                     if dep_comp is None:
                         # It must be the interface pkgname::pkgname target
-                        assert required_pkg == required_comp, \
-                            (f"{self._conanfile}: Required package '{required_pkg}' != "
-                             f"Required component: {required_comp} for not found component")
+                        if required_pkg != required_comp:
+                            msg = (f"{self._conanfile} recipe cpp_info did .requires to "
+                                   f"'{required_pkg}::{required_comp}' but component "
+                                   f"'{required_comp}' not found in {required_pkg}")
+                            raise ConanException(msg)
                         comp = None
                         default_target = f"{dep.ref.name}::{dep.ref.name}"  # replace_requires
                         link = pkg_type is not PackageType.SHARED


Test patch:
diff --git a/test/functional/command/test_install_deploy.py b/test/functional/command/test_install_deploy.py
index e18b99c70..b01ab3a32 100644
--- a/test/functional/command/test_install_deploy.py
+++ b/test/functional/command/test_install_deploy.py
@@ -601,3 +601,21 @@ def test_deploy_relative_paths():
     c.run("install consumer --build=missing --deployer=mydeploy.py")
     data = c.load("consumer/some/sub/folders/generators/pkg-release-data.cmake")
     assert 'set(pkg_PACKAGE_FOLDER_RELEASE "${CMAKE_CURRENT_LIST_DIR}/../installed/pkg")' in data
+
+
+@pytest.mark.parametrize("absolute_path", [True, False])
+def test_deploy_output_absolute(absolute_path):
+    # https://github.com/conan-io/conan/issues/18560
+    c = TestClient()
+    c.save({"pkg/conanfile.py": GenConanfile("pkg", "0.1").with_package_file("myfile.txt", "c"),
+            "consumer/conanfile.txt": "[requires]\npkg/0.1"})
+    c.run("create pkg")
+
+    # It is important to use the forward /myout in Windows, this was the breaking input
+    out_path = f"{c.current_folder}/myout" if absolute_path else "myout"
+    c.run("install consumer/conanfile.txt -s arch=x86_64 --deployer=full_deploy -g CMakeDeps "
+          f'-of="{out_path}"')
+    assert c.load("myout/full_deploy/host/pkg/0.1/myfile.txt") == "c"
+    data = c.load("myout/pkg-release-x86_64-data.cmake")
+    assert ('set(pkg_PACKAGE_FOLDER_RELEASE '
+            '"${CMAKE_CURRENT_LIST_DIR}/full_deploy/host/pkg/0.1")') in data
diff --git a/test/integration/toolchains/cmake/cmakedeps2/test_cmakedeps.py b/test/integration/toolchains/cmake/cmakedeps2/test_cmakedeps.py
index 6639dc44f..887d7b055 100644
--- a/test/integration/toolchains/cmake/cmakedeps2/test_cmakedeps.py
+++ b/test/integration/toolchains/cmake/cmakedeps2/test_cmakedeps.py
@@ -1,8 +1,6 @@
 import re
 import textwrap
 
-import pytest
-
 from conan.test.assets.genconanfile import GenConanfile
 from conan.test.utils.tools import TestClient
 
@@ -85,8 +83,10 @@ def test_cmakedeps_transitive_paths():
     cmake_paths = c.load("conan_cmakedeps_paths.cmake")
     assert re.search(r"list\(PREPEND CMAKE_PROGRAM_PATH \".*/libb.*/p/binb\"\)", cmake_paths)
     assert not re.search(r"list\(PREPEND CMAKE_PROGRAM_PATH /bina\"", cmake_paths)
-    assert re.search(r"list\(PREPEND CMAKE_LIBRARY_PATH \".*/libb.*/p/libb\" \".*/liba.*/p/liba\"\)", cmake_paths)
-    assert re.search(r"list\(PREPEND CMAKE_INCLUDE_PATH \".*/libb.*/p/includeb\" \".*/liba.*/p/includea\"\)", cmake_paths)
+    assert re.search(r"list\(PREPEND CMAKE_LIBRARY_PATH \".*/libb.*/p/libb\" \".*/liba.*/p/liba\"\)",
+                     cmake_paths)
+    assert re.search(r"list\(PREPEND CMAKE_INCLUDE_PATH \".*/libb.*/p/includeb\" "
+                     r"\".*/liba.*/p/includea\"\)", cmake_paths)
 
 
 def test_cmakedeps_deployer_relative_paths():
@@ -253,7 +253,7 @@ def test_autolink_pragma():
                                                        .with_generator("CMakeDeps")})
     c.run("create . --name=pkg --version=0.1")
     assert "CMakeDeps: cmake_set_interface_link_directories is legacy, not necessary" in c.out
-    c.run("create . --name=pkg --version=0.1 -c tools.cmake.cmakedeps:new=will_break_next")
+    c.run(f"create . --name=pkg --version=0.1 -c tools.cmake.cmakedeps:new={new_value}")
     assert "CMakeConfigDeps: cmake_set_interface_link_directories deprecated and invalid. " \
            "The package 'package_info()' must correctly define the (CPS) information" in c.out
 
@@ -271,7 +271,7 @@ def test_consuming_cpp_info_with_components_dependency_from_same_package():
             "test_package/conanfile.py": GenConanfile().with_settings("build_type")
                                                        .with_test("pass")
                                                        .with_generator("CMakeDeps")})
-    c.run("create . --name=pkg --version=0.1 -c tools.cmake.cmakedeps:new=will_break_next")
+    c.run(f"create . --name=pkg --version=0.1 -c tools.cmake.cmakedeps:new={new_value}")
     # it doesn't break
     assert "find_package(pkg)" in c.out
 
@@ -300,12 +300,34 @@ def test_consuming_cpp_info_with_components_dependency_from_other_package():
                                                            .with_test("pass")
                                                            .with_generator("CMakeDeps")})
     c.run("create dep")
-    c.run("create pkg --name=pkg --version=0.1 -c tools.cmake.cmakedeps:new=will_break_next")
+    c.run(f"create pkg --name=pkg --version=0.1 -c tools.cmake.cmakedeps:new={new_value}")
     # it doesn't break
     assert "find_package(pkg)" in c.out
 
 
-def test_consuming_cpp_info_transitively_by_requiring_root_component_in_another_component_from_another_package():
+def test_error_incorrect_component():
+    # https://github.com/conan-io/conan/issues/18554
+    c = TestClient()
+    conanfile = textwrap.dedent("""
+        from conan import ConanFile
+        class Pkg(ConanFile):
+            requires = "dep/0.1"
+            def package_info(self):
+                self.cpp_info.requires = ['dep::lib']
+        """)
+    c.save({"dep/conanfile.py": GenConanfile("dep", "0.1"),
+            "pkg/conanfile.py": conanfile,
+            "pkg/test_package/conanfile.py": GenConanfile().with_settings("build_type")
+                                                           .with_generator("CMakeDeps")
+                                                           .with_test("pass")})
+    c.run("create dep")
+    c.run(f"create pkg --name=pkg --version=0.1 -c tools.cmake.cmakedeps:new={new_value}",
+          assert_error=True)
+    assert ("ERROR: Error in generator 'CMakeDeps': pkg/0.1 recipe cpp_info did .requires to "
+            "'dep::lib' but component 'lib' not found in dep") in c.out
+
+
+def test_consuming_cpp_info_transitively_by_requiring_root_component():
     c = TestClient()
     dependent_conanfile = textwrap.dedent("""
         from conan import ConanFile
@@ -339,8 +361,9 @@ def test_consuming_cpp_info_transitively_by_requiring_root_component_in_another_
     c.save({"dependent/conanfile.py": dependent_conanfile,
             "main/conanfile.py": conanfile,
             "main/test_package/conanfile.py":test_package})
-    c.run("create ./dependent/ --name=dependent --version=0.1 -c tools.cmake.cmakedeps:new=will_break_next")
-    c.run("create ./main/ --name=pkg --version=0.1 -c tools.cmake.cmakedeps:new=will_break_next")
+    c.run("create ./dependent/ --name=dependent --version=0.1 "
+          f"-c tools.cmake.cmakedeps:new={new_value}")
+    c.run(f"create ./main/ --name=pkg --version=0.1 -c tools.cmake.cmakedeps:new={new_value}")
 
 
 def test_cmake_find_mode_deprecated():