Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 63 additions & 5 deletions conan/tools/cmake/cmakeconfigdeps/cmakeconfigdeps.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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:
Expand Down
82 changes: 23 additions & 59 deletions conan/tools/cmake/cmakeconfigdeps/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can even abstract more in the future, like consumer_conanfile -> path_relativizator or the "folder" that it needs to relativize the paths. That way it is completely explicit that it is not getting any other information from that conanfile.

Can be done in the future, not necessary to do it now.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea!

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,
Expand All @@ -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
Expand All @@ -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}
Expand Down
17 changes: 9 additions & 8 deletions conan/tools/cmake/cmakeconfigdeps/config_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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}

Expand Down
14 changes: 6 additions & 8 deletions conan/tools/cmake/cmakeconfigdeps/targets.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Loading