Repository: conan-io/conan
Pull request #17941 (2025-03-11 19:21:00)

[bugfix] CMakeConfigDeps and transitive libs with frameworks
Changelog: omit
Docs: omit



Patch:
diff --git a/conan/internal/api/profile/profile_loader.py b/conan/internal/api/profile/profile_loader.py
index 5ed77b1f1..ddc19bb64 100644
--- a/conan/internal/api/profile/profile_loader.py
+++ b/conan/internal/api/profile/profile_loader.py
@@ -126,9 +126,9 @@ class ProfileLoader:
         # Always include the root Conan home "profiles" folder as secondary route for loading
         # imports and includes from jinja2 templates.
         loader_paths = [base_path, profiles_folder]
-        rtemplate = Environment(loader=FileSystemLoader(loader_paths)).from_string(text)
 
         try:
+            rtemplate = Environment(loader=FileSystemLoader(loader_paths)).from_string(text)
             text = rtemplate.render(context)
         except Exception as e:
             raise ConanException(f"Error while rendering the profile template file '{profile_path}'. "
diff --git a/conan/tools/cmake/cmakedeps2/target_configuration.py b/conan/tools/cmake/cmakedeps2/target_configuration.py
index 9f303c75f..3712da34e 100644
--- a/conan/tools/cmake/cmakedeps2/target_configuration.py
+++ b/conan/tools/cmake/cmakedeps2/target_configuration.py
@@ -393,7 +393,8 @@ class TargetConfigurationTemplate2:
         target_link_libraries({{lib}} INTERFACE {{lib_info["system_libs"]}})
         {% endif %}
         {% if lib_info.get("frameworks") %}
-        set_property(TARGET {{lib}} PROPERTY INTERFACE_LINK_LIBRARIES "{{config_wrapper(config, lib_info["frameworks"])}}")
+        set_property(TARGET {{lib}} APPEND PROPERTY INTERFACE_LINK_LIBRARIES
+                     "{{config_wrapper(config, lib_info["frameworks"])}}")
         {% endif %}
         {% if lib_info.get("package_framework") %}
         set_target_properties({{lib}} PROPERTIES


Test patch:
diff --git a/test/functional/toolchains/cmake/cmakedeps2/test_cmakeconfigdeps_frameworks.py b/test/functional/toolchains/cmake/cmakedeps2/test_cmakeconfigdeps_frameworks.py
index 9f98a402a..a3feee409 100644
--- a/test/functional/toolchains/cmake/cmakedeps2/test_cmakeconfigdeps_frameworks.py
+++ b/test/functional/toolchains/cmake/cmakedeps2/test_cmakeconfigdeps_frameworks.py
@@ -10,16 +10,21 @@ new_value = "will_break_next"
 
 
 @pytest.mark.parametrize("shared", [True, False])
-@pytest.mark.tool("cmake")
+@pytest.mark.tool("cmake", "3.27")
 @pytest.mark.skipif(platform.system() != "Darwin", reason="Only OSX")
 def test_osx_frameworks(shared):
     """
-    Testing custom package frameworks + system frameworks
+    Testing custom package frameworks + system frameworks + requirements
     """
+    client = TestClient()
+    client.run("new cmake_lib -d name=dep -d version=1.0")
+    client.run(f"create . -tf='' -o '&:shared={shared}'")
     cmakelists = textwrap.dedent("""
     cmake_minimum_required(VERSION 3.15)
     project(MyFramework CXX)
 
+    find_package(dep CONFIG REQUIRED)
+
     add_library(MyFramework frame.cpp frame.h)
 
     set_target_properties(MyFramework PROPERTIES
@@ -28,23 +33,34 @@ def test_osx_frameworks(shared):
       MACOSX_FRAMEWORK_IDENTIFIER MyFramework
       PUBLIC_HEADER frame.h
     )
+    target_link_libraries(MyFramework PRIVATE dep::dep)
+
+    if(BUILD_SHARED_LIBS)
+        target_link_libraries(MyFramework PRIVATE "-framework CoreFoundation")
+    endif()
 
     install(TARGETS MyFramework
       FRAMEWORK DESTINATION .)
     """)
     frame_cpp = textwrap.dedent("""
     #include "frame.h"
+    #include "dep.h"
     #include <iostream>
     #include <CoreFoundation/CoreFoundation.h>
 
     void greet() {
+        // CoreFoundation
         CFTypeRef keys[] = {CFSTR("key")};
         CFTypeRef values[] = {CFSTR("value")};
         CFDictionaryRef dict = CFDictionaryCreate(kCFAllocatorDefault, keys, values, sizeof(keys) / sizeof(keys[0]),
                         &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
         if (dict)
             CFRelease(dict);
+        // MyFramework
         std::cout << "Hello from MyFramework!" << std::endl;
+
+        // dep requirement
+        dep();
     }
     """)
     frame_h = textwrap.dedent("""
@@ -63,10 +79,19 @@ def test_osx_frameworks(shared):
         name = "frame"
         version = "1.0"
         settings = "os", "arch", "compiler", "build_type"
-        languages = ["C++"]
-        package_type = "{'static-library' if shared else 'shared-library'}"
-        exports_sources = ["frame.cpp", "frame.h", "CMakeLists.txt"]
-        generators = "CMakeToolchain"
+        options = {{"shared": [True, False], "fPIC": [True, False]}}
+        default_options = {{"shared": False, "fPIC": True}}
+        exports_sources = "frame.cpp", "frame.h", "CMakeLists.txt"
+        generators = "CMakeToolchain", "CMakeConfigDeps"
+        requires = "dep/1.0"
+
+        def config_options(self):
+            if self.settings.os == "Windows":
+                self.options.rm_safe("fPIC")
+
+        def configure(self):
+            if self.options.shared:
+                self.options.rm_safe("fPIC")
 
         def build(self):
             cmake = CMake(self)
@@ -123,7 +148,6 @@ def test_osx_frameworks(shared):
     add_executable(example main.cpp)
     target_link_libraries(example frame::frame)
     """)
-    client = TestClient()
     client.save({
         'test_package/main.cpp': test_main_cpp,
         'test_package/CMakeLists.txt': test_cmakelists,
@@ -132,6 +156,7 @@ def test_osx_frameworks(shared):
         'frame.cpp': frame_cpp,
         'frame.h': frame_h,
         'conanfile.py': conanfile
-    })
+    }, clean_first=True)
     client.run(f"create . -c tools.cmake.cmakedeps:new={new_value} -o '*:shared={shared}'")
     assert "Hello from MyFramework!" in client.out
+    assert "dep/1.0: Hello World" in client.out
diff --git a/test/integration/configuration/test_profile_jinja.py b/test/integration/configuration/test_profile_jinja.py
index dc3d3daf3..d8af563a1 100644
--- a/test/integration/configuration/test_profile_jinja.py
+++ b/test/integration/configuration/test_profile_jinja.py
@@ -266,9 +266,39 @@ class TestProfileDetectAPI:
 
 def test_profile_jinja_error():
     c = TestClient(light=True)
-    c.save({"profile1": "{% set kk = other() %}"})
+    profile = textwrap.dedent("""
+        {% set arr = [
+            {# comment that triggers the error #}
+            'smth'
+        ] %}""")
+    c.save({"profile1": profile,
+            "profile2": "include(profile1)",
+            "profile3": "include(profile2)",
+            "conanfile.txt": ""})
     c.run("profile show -pr=profile1", assert_error=True)
     assert "ERROR: Error while rendering the profile template file" in c.out
+    c.run("profile show -pr=profile2", assert_error=True)
+    assert "ERROR: Error reading 'profile2' profile: Error while " \
+           "rendering the profile template file " in c.out
+    assert "unexpected char '#' at 21" in c.out
+    c.run("profile show -pr=profile3", assert_error=True)
+    assert "ERROR: Error reading 'profile3' profile: Error reading 'profile2' profile: Error while " \
+           "rendering the profile template file " in c.out
+    assert "unexpected char '#' at 21" in c.out
+
+    # Also install messages
+    c.run("install . -pr=profile1", assert_error=True)
+    assert "ERROR: Error while rendering the profile template file" in c.out
+    assert "unexpected char '#' at 21" in c.out
+
+    c.run("install . -pr=profile2", assert_error=True)
+    assert "ERROR: Error reading 'profile2' profile: Error while " \
+           "rendering the profile template file " in c.out
+    assert "unexpected char '#' at 21" in c.out
+    c.run("install . -pr=profile3", assert_error=True)
+    assert "ERROR: Error reading 'profile3' profile: Error reading 'profile2' profile: Error while " \
+           "rendering the profile template file " in c.out
+    assert "unexpected char '#' at 21" in c.out
 
 
 def test_profile_macro_per_package():