diff --git a/conan/tools/cmake/cmakeconfigdeps/cmakeconfigdeps.py b/conan/tools/cmake/cmakeconfigdeps/cmakeconfigdeps.py index 1a483bf0405..0533aa974b8 100644 --- a/conan/tools/cmake/cmakeconfigdeps/cmakeconfigdeps.py +++ b/conan/tools/cmake/cmakeconfigdeps/cmakeconfigdeps.py @@ -119,12 +119,16 @@ def _content(self): if require.direct: direct_deps.append((require, dep)) full_cpp_info = dep.cpp_info.deduce_full_cpp_info(dep) - config = ConfigTemplate2(self, require, dep, full_cpp_info) - ret[config.filename] = config.content() - config_version = ConfigVersionTemplate2(self, dep) + base_filename = self.get_cmake_filename(dep) + cmake_config_properties = self._get_cmake_config_properties(dep, full_cpp_info, + is_build_context=require.build) + config_version = ConfigVersionTemplate2(base_filename, dep.ref, cmake_config_properties) ret[config_version.filename] = config_version.content() - - targets = TargetsTemplate2(self, dep) + config = ConfigTemplate2(base_filename, dep.ref, self._conanfile, + full_cpp_info, cmake_config_properties, + is_build_context=require.build) + ret[config.filename] = config.content() + targets = TargetsTemplate2(base_filename, dep.ref) ret[targets.filename] = targets.content() target_configuration = TargetConfigurationTemplate2(self, dep, require, full_cpp_info) ret[target_configuration.filename] = target_configuration.content() @@ -195,6 +199,60 @@ def get_cmake_filename(self, dep): ret = self.get_property("cmake_file_name", dep) return ret or dep.ref.name + def _get_cmake_config_properties(self, dep, full_cpp_info=None, is_build_context=False): + conf_extra_variables = dep.conf.get("tools.cmake.cmaketoolchain:extra_variables", default={}, + check_type=dict) + dep_extra_variables = self.get_property("cmake_extra_variables", dep, check_type=dict) or {} + # The configuration variables have precedence over the dependency ones (those already appear on the toolchain files) + cmake_extra_variables = {dep: value for dep, value in dep_extra_variables.items() if + dep not in conf_extra_variables} + cmake_components = self.get_property("cmake_components", dep, check_type=list) + if cmake_components is None: + cmake_components = self._get_default_cmake_components(dep) + result = { + "cmake_config_version_compat": self.get_property("cmake_config_version_compat", dep), + "system_package_version": self.get_property("system_package_version", dep), + "cmake_build_modules": self.get_property("cmake_build_modules", dep, + check_type=list) or [], + "cmake_extra_variables": cmake_extra_variables, + "cmake_additional_variables_prefixes": self.get_property( + "cmake_additional_variables_prefixes", dep, check_type=list) or [], + "cmake_components": cmake_components, + "cmake_extra_dependencies": self.get_property("cmake_extra_dependencies", dep, + check_type=list) or [] + } + if full_cpp_info is not None and not is_build_context: + result["cmake_legacy_libraries"] = self._get_legacy_libraries(dep, full_cpp_info) + return result + + def _get_default_cmake_components(self, dep): + components = [] + # This assumes that cmake_components is only defined with not multi .libs=[lib1, lib2] + for name in dep.cpp_info.components: + if name.startswith("_"): # Skip private components + continue + comp_components = self.get_property("cmake_components", dep, name, check_type=list) + if comp_components: + components.extend(comp_components) + else: + cmakename = self.get_property("cmake_target_name", dep, name) + if cmakename and "::" in cmakename: # Remove package namespace + cmakename = cmakename.split("::", 1)[1] + components.append(cmakename or name) + return components + + def _get_legacy_libraries(self, dep, full_cpp_info): + pkg_name = dep.ref.name + libraries = [] + if full_cpp_info.has_components: + for component in full_cpp_info.components.keys(): + root_target_name = self.get_property("cmake_target_name", dep, comp_name=component) + libraries.append(root_target_name or f"{pkg_name}::{component}") + else: + root_target_name = self.get_property("cmake_target_name", dep) + libraries.append(root_target_name or f"{pkg_name}::{pkg_name}") + return " ".join(libraries) if libraries else "" + def _get_find_mode(self, dep): tmp = self.get_property("cmake_find_mode", dep) if tmp is None: diff --git a/conan/tools/cmake/cmakeconfigdeps/config.py b/conan/tools/cmake/cmakeconfigdeps/config.py index b0cc4928749..addd1bd0e75 100644 --- a/conan/tools/cmake/cmakeconfigdeps/config.py +++ b/conan/tools/cmake/cmakeconfigdeps/config.py @@ -11,11 +11,14 @@ class ConfigTemplate2: FooConfig.cmake foo-config.cmake """ - def __init__(self, cmakedeps, require, conanfile, full_cpp_info): - self._cmakedeps = cmakedeps - self._require = require - self._conanfile = conanfile + def __init__(self, filename, reference, consumer_conanfile, full_cpp_info, + cmake_config_properties, is_build_context=False): + self._filename = filename + self._reference = reference + self._consumer_conanfile = consumer_conanfile self._full_cpp_info = full_cpp_info + self._cmake_config_properties = cmake_config_properties + self._is_build_context = is_build_context def content(self): t = Template(self._template, trim_blocks=True, lstrip_blocks=True, @@ -24,57 +27,32 @@ def content(self): @property def filename(self): - f = self._cmakedeps.get_cmake_filename(self._conanfile) + f = self._filename return f"{f}-config.cmake" if f == f.lower() else f"{f}Config.cmake" @property def _context(self): - f = self._cmakedeps.get_cmake_filename(self._conanfile) + f = self._filename targets_include = f"{f}Targets.cmake" - pkg_name = self._conanfile.ref.name - build_modules_paths = self._cmakedeps.get_property("cmake_build_modules", self._conanfile, - check_type=list) or [] + build_modules_paths = self._cmake_config_properties.get("cmake_build_modules", []) # FIXME: Proper escaping of paths for CMake and relativization # FIXME: build_module_paths coming from last config only build_modules_paths = [f.replace("\\", "/") for f in build_modules_paths] - build_modules_paths = [relativize_path(p, self._cmakedeps._conanfile, + build_modules_paths = [relativize_path(p, self._consumer_conanfile, "${CMAKE_CURRENT_LIST_DIR}") for p in build_modules_paths] - components = self._cmakedeps.get_property("cmake_components", self._conanfile, - check_type=list) - if components is None: # Lets compute the default components names - components = [] - # This assumes that cmake_components is only defined with not multi .libs=[lib1, lib2] - for name in self._conanfile.cpp_info.components: - if name.startswith("_"): # Skip private components - continue - comp_components = self._cmakedeps.get_property("cmake_components", self._conanfile, - name, check_type=list) - if comp_components: - components.extend(comp_components) - else: - cmakename = self._cmakedeps.get_property("cmake_target_name", self._conanfile, - name) - if cmakename and "::" in cmakename: # Remove package namespace - cmakename = cmakename.split("::", 1)[1] - components.append(cmakename or name) + components = self._cmake_config_properties.get("cmake_components", []) components = " ".join(components) if components else "" result = {"filename": f, "components": components, - "pkg_name": pkg_name, + "pkg_name": self._reference.name, "targets_include_file": targets_include, "build_modules_paths": build_modules_paths} - conf_extra_variables = self._conanfile.conf.get("tools.cmake.cmaketoolchain:extra_variables", - default={}, check_type=dict) - dep_extra_variables = self._cmakedeps.get_property("cmake_extra_variables", self._conanfile, - check_type=dict) or {} - # The configuration variables have precedence over the dependency ones - extra_variables = {dep: value for dep, value in dep_extra_variables.items() - if dep not in conf_extra_variables} + dep_extra_variables = self._cmake_config_properties.get("cmake_extra_variables", {}) parsed_extra_variables = {} - for key, value in extra_variables.items(): + for key, value in dep_extra_variables.items(): parsed_extra_variables[key] = parse_extra_variable("cmake_extra_variables", key, value) result["extra_variables"] = parsed_extra_variables @@ -84,35 +62,21 @@ def _context(self): def _get_legacy_vars(self): # Auxiliary variables for legacy consumption and try_compile cases - pkg_name = self._conanfile.ref.name - prefixes = self._cmakedeps.get_property("cmake_additional_variables_prefixes", - self._conanfile, check_type=list) or [] - - f = self._cmakedeps.get_cmake_filename(self._conanfile) + prefixes = self._cmake_config_properties.get("cmake_additional_variables_prefixes", []) + f = self._filename prefixes = [f] + prefixes include_dirs = definitions = libraries = None - if not self._require.build: # To add global variables for try_compile and legacy + if not self._is_build_context: # To add global variables for try_compile and legacy aggregated_cppinfo = self._full_cpp_info.aggregated_components() # FIXME: Proper escaping of paths for CMake - incdirs = [i.replace("\\", "/") for i in aggregated_cppinfo.includedirs] - incdirs = [relativize_path(i, self._cmakedeps._conanfile, "${CMAKE_CURRENT_LIST_DIR}") - for i in incdirs] + incdirs = [relativize_path(i.replace("\\", "/"), self._consumer_conanfile, + "${CMAKE_CURRENT_LIST_DIR}") + for i in aggregated_cppinfo.includedirs] include_dirs = ";".join(incdirs) definitions = ";".join("-D" + cmake_escape_value(d) for d in aggregated_cppinfo.defines) - - libraries = [] - if self._full_cpp_info.has_components: - for component in self._full_cpp_info.components.keys(): - root_target_name = self._cmakedeps.get_property("cmake_target_name", - self._conanfile, - comp_name=component) - libraries.append(root_target_name or f"{pkg_name}::{component}") - else: - root_target_name = self._cmakedeps.get_property("cmake_target_name", self._conanfile) - libraries.append(root_target_name or f"{pkg_name}::{pkg_name}") - libraries = " ".join(libraries) if libraries else "" + libraries = self._cmake_config_properties.get("cmake_legacy_libraries", "") return {"additional_variables_prefixes": prefixes, - "version": self._conanfile.ref.version, + "version": self._reference.version, "include_dirs": include_dirs, "definitions": definitions, "libraries": libraries} diff --git a/conan/tools/cmake/cmakeconfigdeps/config_version.py b/conan/tools/cmake/cmakeconfigdeps/config_version.py index 51ae75d01a8..da49ad152db 100644 --- a/conan/tools/cmake/cmakeconfigdeps/config_version.py +++ b/conan/tools/cmake/cmakeconfigdeps/config_version.py @@ -10,9 +10,10 @@ class ConfigVersionTemplate2: """ foo-config-version.cmake """ - def __init__(self, cmakedeps, conanfile): - self._cmakedeps = cmakedeps - self._conanfile = conanfile + def __init__(self, filename, reference, properties): + self._filename = filename + self._properties = properties + self._reference = reference def content(self): t = Template(self._template, trim_blocks=True, lstrip_blocks=True, @@ -21,18 +22,18 @@ def content(self): @property def filename(self): - f = self._cmakedeps.get_cmake_filename(self._conanfile) + f = self._filename return f"{f}-config-version.cmake" if f == f.lower() else f"{f}ConfigVersion.cmake" @property def _context(self): - policy = self._cmakedeps.get_property("cmake_config_version_compat", self._conanfile) + policy = self._properties.get("cmake_config_version_compat") if policy is None: policy = "SameMajorVersion" if policy not in ("AnyNewerVersion", "SameMajorVersion", "SameMinorVersion", "ExactVersion"): - raise ConanException(f"Unknown cmake_config_version_compat={policy} in {self._conanfile}") - version = self._cmakedeps.get_property("system_package_version", self._conanfile) - version = version or self._conanfile.ref.version + raise ConanException(f"Unknown cmake_config_version_compat={policy} in {self._reference}") + version = self._properties.get("system_package_version") + version = version or self._reference.version return {"version": version, "policy": policy} diff --git a/conan/tools/cmake/cmakeconfigdeps/targets.py b/conan/tools/cmake/cmakeconfigdeps/targets.py index fba8092de2e..30467056a4e 100644 --- a/conan/tools/cmake/cmakeconfigdeps/targets.py +++ b/conan/tools/cmake/cmakeconfigdeps/targets.py @@ -8,9 +8,9 @@ class TargetsTemplate2: """ FooTargets.cmake """ - def __init__(self, cmakedeps, conanfile): - self._cmakedeps = cmakedeps - self._conanfile = conanfile + def __init__(self, filename, reference): + self._filename = filename + self._reference = reference def content(self): t = Template(self._template, trim_blocks=True, lstrip_blocks=True, @@ -19,14 +19,12 @@ def content(self): @property def filename(self): - f = self._cmakedeps.get_cmake_filename(self._conanfile) - return f"{f}Targets.cmake" + return f"{self._filename}Targets.cmake" @property def _context(self): - filename = self._cmakedeps.get_cmake_filename(self._conanfile) - ret = {"ref": str(self._conanfile.ref), - "filename": filename} + ret = {"filename": self._filename, + "ref": str(self._reference)} return ret @property diff --git a/test/functional/toolchains/cmake/cmakeconfigdeps/test_cmakeconfigdeps_new.py b/test/functional/toolchains/cmake/cmakeconfigdeps/test_cmakeconfigdeps_new.py index b3162f42ec4..55946f996fa 100644 --- a/test/functional/toolchains/cmake/cmakeconfigdeps/test_cmakeconfigdeps_new.py +++ b/test/functional/toolchains/cmake/cmakeconfigdeps/test_cmakeconfigdeps_new.py @@ -1776,7 +1776,7 @@ def package_info(self): client.save({"conanfile.py": consumer, "CMakeLists.txt": cmakelists}) client.run("build") - assert 'Conan: Configuring Targets for hello/1.0' in client.out + assert 'Conan: Configuring Targets for hello' in client.out # And this follows the expected found variable generation assert "Found HellO!" in client.out assert "Found hello!" not in client.out