Skip to content

Commit a6bd8fb

Browse files
authoredNov 22, 2024
new CMakeDeps defines components (conan-io#17302)
* new CMakeDeps defines components * avoid flaky tests * fix * skip private components * per-component info too
1 parent f5a15b5 commit a6bd8fb

File tree

6 files changed

+124
-6
lines changed

6 files changed

+124
-6
lines changed
 

‎conan/tools/cmake/cmakedeps2/cmakedeps.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,11 @@ def get_property(self, prop, dep, comp_name=None, check_type=None):
116116
except KeyError:
117117
# Here we are not using the cpp_info = deduce_cpp_info(dep) because it is not
118118
# necessary for the properties
119-
return dep.cpp_info.get_property(prop, check_type=check_type) if not comp_name \
120-
else dep.cpp_info.components[comp_name].get_property(prop, check_type=check_type)
119+
if not comp_name:
120+
return dep.cpp_info.get_property(prop, check_type=check_type)
121+
comp = dep.cpp_info.components.get(comp_name) # it is a default dict
122+
if comp is not None:
123+
return comp.get_property(prop, check_type=check_type)
121124

122125
def get_cmake_filename(self, dep, module_mode=None):
123126
"""Get the name of the file for the find_package(XXX)"""

‎conan/tools/cmake/cmakedeps2/config.py

+34-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,28 @@ def _context(self):
3333
# FIXME: Proper escaping of paths for CMake and relativization
3434
# FIXME: build_module_paths coming from last config only
3535
build_modules_paths = [f.replace("\\", "/") for f in build_modules_paths]
36-
return {"pkg_name": pkg_name,
36+
components = self._cmakedeps.get_property("cmake_components", self._conanfile,
37+
check_type=list)
38+
if components is None: # Lets compute the default components names
39+
components = []
40+
# This assumes that cmake_components is only defined with not multi .libs=[lib1, lib2]
41+
for name in self._conanfile.cpp_info.components:
42+
if name.startswith("_"): # Skip private components
43+
continue
44+
comp_components = self._cmakedeps.get_property("cmake_components", self._conanfile,
45+
name, check_type=list)
46+
if comp_components:
47+
components.extend(comp_components)
48+
else:
49+
cmakename = self._cmakedeps.get_property("cmake_target_name", self._conanfile,
50+
name)
51+
if cmakename and "::" in cmakename: # Remove package namespace
52+
cmakename = cmakename.split("::", 1)[1]
53+
components.append(cmakename or name)
54+
components = " ".join(components) if components else ""
55+
return {"filename": f,
56+
"components": components,
57+
"pkg_name": pkg_name,
3758
"targets_include_file": targets_include,
3859
"build_modules_paths": build_modules_paths}
3960

@@ -58,4 +79,16 @@ def _template(self):
5879
message(STATUS "Conan: Including build module from '{{build_module}}'")
5980
include("{{ build_module }}")
6081
{% endfor %}
82+
83+
{% if components %}
84+
set({{filename}}_PACKAGE_PROVIDED_COMPONENTS {{components}})
85+
foreach(comp {%raw%}${{%endraw%}{{filename}}_FIND_COMPONENTS})
86+
if(NOT ${comp} IN_LIST {{filename}}_PACKAGE_PROVIDED_COMPONENTS)
87+
if({%raw%}${{%endraw%}{{filename}}_FIND_REQUIRED_${comp}})
88+
message(STATUS "Conan: Error: '{{pkg_name}}' required COMPONENT '${comp}' not found")
89+
set({{filename}}_FOUND FALSE)
90+
endif()
91+
endif()
92+
endforeach()
93+
{% endif %}
6194
""")

‎conan/tools/cmake/cmakedeps2/targets.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ def _context(self):
3232
@property
3333
def _template(self):
3434
return textwrap.dedent("""\
35-
message(STATUS "Configuring Targets for {{ ref }}")
35+
message(STATUS "Conan: Configuring Targets for {{ ref }}")
3636
3737
# Load information for each installed configuration.
3838
file(GLOB _target_files "${CMAKE_CURRENT_LIST_DIR}/{{filename}}-Targets-*.cmake")

‎test/functional/toolchains/cmake/cmakedeps/test_cmakedeps_new.py

+82
Original file line numberDiff line numberDiff line change
@@ -1164,3 +1164,85 @@ def generate(self):
11641164
preset = "conan-default" if platform.system() == "Windows" else "conan-release"
11651165
c.run_command(f"cmake --preset {preset} ")
11661166
assert "Performing Test IT_COMPILES - Success" in c.out
1167+
1168+
1169+
class TestCMakeComponents:
1170+
1171+
@pytest.mark.tool("cmake")
1172+
@pytest.mark.parametrize("components, found", [("comp1", True), ("compX", False)])
1173+
def test_components(self, components, found):
1174+
c = TestClient()
1175+
dep = textwrap.dedent("""
1176+
from conan import ConanFile
1177+
class Pkg(ConanFile):
1178+
name = "dep"
1179+
version = "0.1"
1180+
def package_info(self):
1181+
self.cpp_info.set_property("cmake_components", ["comp1", "comp2"])
1182+
""")
1183+
c.save({"conanfile.py": dep})
1184+
c.run("create .")
1185+
1186+
consumer = textwrap.dedent("""
1187+
from conan import ConanFile
1188+
from conan.tools.cmake import CMake
1189+
class PkgConan(ConanFile):
1190+
settings = "os", "arch", "compiler", "build_type"
1191+
requires = "dep/0.1"
1192+
generators = "CMakeToolchain", "CMakeDeps"
1193+
def build(self):
1194+
deps = CMake(self)
1195+
deps.configure()
1196+
""")
1197+
1198+
cmakelist = textwrap.dedent(f"""\
1199+
cmake_minimum_required(VERSION 3.15)
1200+
project(Hello LANGUAGES NONE)
1201+
1202+
find_package(dep CONFIG REQUIRED COMPONENTS {components})
1203+
""")
1204+
1205+
c.save({"conanfile.py": consumer,
1206+
"CMakeLists.txt": cmakelist}, clean_first=True)
1207+
c.run(f"build . -c tools.cmake.cmakedeps:new={new_value}", assert_error=not found)
1208+
if not found:
1209+
assert f"Conan: Error: 'dep' required COMPONENT '{components}' not found" in c.out
1210+
1211+
def test_components_default_definition(self):
1212+
c = TestClient()
1213+
dep = textwrap.dedent("""
1214+
from conan import ConanFile
1215+
class Pkg(ConanFile):
1216+
name = "dep"
1217+
version = "0.1"
1218+
def package_info(self):
1219+
# private component that should be skipped
1220+
self.cpp_info.components["_private"].includedirs = ["include"]
1221+
self.cpp_info.components["c1"].set_property("cmake_target_name", "MyC1")
1222+
self.cpp_info.components["c2"].set_property("cmake_target_name", "Dep::MyC2")
1223+
self.cpp_info.components["c3"].includedirs = ["include"]
1224+
""")
1225+
c.save({"conanfile.py": dep})
1226+
c.run("create .")
1227+
c.run(f"install --requires=dep/0.1 -g CMakeDeps -c tools.cmake.cmakedeps:new={new_value}")
1228+
cmake = c.load("dep-config.cmake")
1229+
assert 'set(dep_PACKAGE_PROVIDED_COMPONENTS MyC1 MyC2 c3)' in cmake
1230+
1231+
def test_components_individual_names(self):
1232+
c = TestClient()
1233+
dep = textwrap.dedent("""
1234+
from conan import ConanFile
1235+
class Pkg(ConanFile):
1236+
name = "dep"
1237+
version = "0.1"
1238+
def package_info(self):
1239+
self.cpp_info.components["c1"].set_property("cmake_target_name", "MyC1")
1240+
self.cpp_info.components["c1"].set_property("cmake_components", ["MyCompC1"])
1241+
self.cpp_info.components["c2"].set_property("cmake_target_name", "Dep::MyC2")
1242+
self.cpp_info.components["c3"].includedirs = ["include"]
1243+
""")
1244+
c.save({"conanfile.py": dep})
1245+
c.run("create .")
1246+
c.run(f"install --requires=dep/0.1 -g CMakeDeps -c tools.cmake.cmakedeps:new={new_value}")
1247+
cmake = c.load("dep-config.cmake")
1248+
assert 'set(dep_PACKAGE_PROVIDED_COMPONENTS MyCompC1 MyC2 c3)' in cmake

‎test/integration/lockfile/test_ci.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ def test_single_config_centralized_change_dep(client_setup):
202202
c.run("install --requires=app1/0.1@ --lockfile=app1_b_changed.lock "
203203
"--lockfile-out=app1_b_integrated.lock "
204204
"--build=missing -s os=Windows")
205-
assert "pkga" not in c.out
205+
assert "pkga/" not in c.out
206206
c.assert_listed_binary({"pkgj/0.1": (pkgawin_01_id, "Cache"),
207207
"pkgb/0.2": ("79caa65bc5877c4ada84a2b454775f47a5045d59", "Cache"),
208208
"pkgc/0.1": ("67e4e9b17f41a4c71ff449eb29eb716b8f83767b", "Build"),

‎test/integration/lockfile/test_ci_revisions.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ def test_single_config_centralized_change_dep(client_setup):
208208
c.run("install --requires=app1/0.1@ --lockfile=app1_b_changed.lock "
209209
"--lockfile-out=app1_b_integrated.lock "
210210
"--build=missing -s os=Windows")
211-
assert "pkga" not in c.out
211+
assert "pkga/" not in c.out
212212
c.assert_listed_binary({"pkgj/0.1": (pkgawin_01_id, "Cache"),
213213
"pkgb/0.1": ("6142fb85ccd4e94afad85a8d01a87234eefa5600", "Cache"),
214214
"pkgc/0.1": ("93cfcbc8109eedf4211558258ff5a844fdb62cca", "Build"),

0 commit comments

Comments
 (0)
Please sign in to comment.