Skip to content

Commit

Permalink
CMakeDeps/CMakeToolchain: Several improvements for open issues (#9455)
Browse files Browse the repository at this point in the history
* To explore, to validate

* Fix names

* Rename

* fix tests

* Fix win tests

* Added tests for new cmake_find_mode

* Fixing win test

* opt-in for toolchain and test

* review

* findpackagehandlestardardargs
  • Loading branch information
lasote committed Sep 1, 2021
1 parent addd8ab commit aae78ea
Show file tree
Hide file tree
Showing 15 changed files with 381 additions and 89 deletions.
46 changes: 32 additions & 14 deletions conan/tools/cmake/cmakedeps/cmakedeps.py
@@ -1,4 +1,5 @@
import os
from fnmatch import fnmatch

from conan.tools._check_build_profile import check_using_build_profile
from conan.tools.cmake.cmakedeps.templates.config import ConfigTemplate
Expand All @@ -11,6 +12,11 @@
from conans.util.files import save


FIND_MODE_MODULE = "module"
FIND_MODE_CONFIG = "config"
FIND_MODE_NONE = "none"
FIND_MODE_BOTH = "both"

class CMakeDeps(object):

def __init__(self, conanfile):
Expand Down Expand Up @@ -75,26 +81,38 @@ def content(self):
if dep.is_build_context and dep.ref.name not in self.build_context_activated:
continue

if dep.new_cpp_info.get_property("skip_deps_file", "CMakeDeps"):
cmake_find_mode = dep.new_cpp_info.get_property("cmake_find_mode", "CMakeDeps") or FIND_MODE_CONFIG
cmake_find_mode = cmake_find_mode.lower()
# Skip from the requirement
if cmake_find_mode == FIND_MODE_NONE:
# Skip the generation of config files for this node, it will be located externally
continue

if cmake_find_mode in (FIND_MODE_CONFIG, FIND_MODE_BOTH):
self._generate_files(require, dep, ret, find_module_mode=False)

if cmake_find_mode in (FIND_MODE_MODULE, FIND_MODE_BOTH):
self._generate_files(require, dep, ret, find_module_mode=True)

return ret

def _generate_files(self, require, dep, ret, find_module_mode):
if not find_module_mode:
config_version = ConfigVersionTemplate(self, require, dep)
ret[config_version.filename] = config_version.render()

data_target = ConfigDataTemplate(self, require, dep)
ret[data_target.filename] = data_target.render()
data_target = ConfigDataTemplate(self, require, dep, find_module_mode)
ret[data_target.filename] = data_target.render()

target_configuration = TargetConfigurationTemplate(self, require, dep)
ret[target_configuration.filename] = target_configuration.render()
target_configuration = TargetConfigurationTemplate(self, require, dep, find_module_mode)
ret[target_configuration.filename] = target_configuration.render()

targets = TargetsTemplate(self, require, dep)
ret[targets.filename] = targets.render()
targets = TargetsTemplate(self, require, dep, find_module_mode)
ret[targets.filename] = targets.render()

config = ConfigTemplate(self, require, dep)
# Check if the XXConfig.cmake exists to keep the first generated configuration
# to only include the build_modules from the first conan install. The rest of the
# file is common for the different configurations.
if not os.path.exists(config.filename):
ret[config.filename] = config.render()
return ret
config = ConfigTemplate(self, require, dep, find_module_mode)
# Check if the XXConfig.cmake exists to keep the first generated configuration
# to only include the build_modules from the first conan install. The rest of the
# file is common for the different configurations.
if not os.path.exists(config.filename):
ret[config.filename] = config.render()
62 changes: 34 additions & 28 deletions conan/tools/cmake/cmakedeps/templates/__init__.py
Expand Up @@ -7,22 +7,23 @@

class CMakeDepsFileTemplate(object):

def __init__(self, cmakedeps, require, conanfile):
def __init__(self, cmakedeps, require, conanfile, find_module_mode=False):
self.cmakedeps = cmakedeps
self.require = require
self.conanfile = conanfile
self.find_module_mode = find_module_mode

@property
def pkg_name(self):
return self.conanfile.ref.name + self.suffix

@property
def target_namespace(self):
return get_target_namespace(self.conanfile) + self.suffix
return self.get_target_namespace(self.conanfile) + self.suffix

@property
def file_name(self):
return get_file_name(self.conanfile) + self.suffix
return get_file_name(self.conanfile, self.find_module_mode) + self.suffix

@property
def suffix(self):
Expand Down Expand Up @@ -74,29 +75,34 @@ def arch(self):
def config_suffix(self):
return "_{}".format(self.configuration.upper()) if self.configuration else ""

def get_target_namespace(self):
return get_target_namespace(self.conanfile)

def get_file_name(self):
return get_file_name(self.conanfile)


def get_target_namespace(req):
ret = req.new_cpp_info.get_property("cmake_target_name", "CMakeDeps")
if not ret:
ret = req.cpp_info.get_name("cmake_find_package_multi", default_name=False)
return ret or req.ref.name


def get_component_alias(req, comp_name):
if comp_name not in req.new_cpp_info.components:
# foo::foo might be referencing the root cppinfo
if req.ref.name == comp_name:
return get_target_namespace(req)
raise ConanException("Component '{name}::{cname}' not found in '{name}' "
"package requirement".format(name=req.ref.name, cname=comp_name))
ret = req.new_cpp_info.components[comp_name].get_property("cmake_target_name", "CMakeDeps")
if not ret:
ret = req.cpp_info.components[comp_name].get_name("cmake_find_package_multi",
default_name=False)
return ret or comp_name
return get_file_name(self.conanfile, find_module_mode=self.find_module_mode)

def get_target_namespace(self, req):
if self.find_module_mode:
ret = req.new_cpp_info.get_property("cmake_module_target_name", "CMakeDeps")
if ret:
return ret

ret = req.new_cpp_info.get_property("cmake_target_name", "CMakeDeps")
if not ret:
ret = req.cpp_info.get_name("cmake_find_package_multi", default_name=False)
return ret or req.ref.name

def get_component_alias(self, req, comp_name):
if comp_name not in req.new_cpp_info.components:
# foo::foo might be referencing the root cppinfo
if req.ref.name == comp_name:
return self.get_target_namespace(req)
raise ConanException("Component '{name}::{cname}' not found in '{name}' "
"package requirement".format(name=req.ref.name, cname=comp_name))
if self.find_module_mode:
ret = req.new_cpp_info.components[comp_name].get_property("cmake_module_target_name",
"CMakeDeps")
if ret:
return ret
ret = req.new_cpp_info.components[comp_name].get_property("cmake_target_name", "CMakeDeps")
if not ret:
ret = req.cpp_info.components[comp_name].get_name("cmake_find_package_multi",
default_name=False)
return ret or comp_name
32 changes: 26 additions & 6 deletions conan/tools/cmake/cmakedeps/templates/config.py
Expand Up @@ -14,31 +14,51 @@ class ConfigTemplate(CMakeDepsFileTemplate):

@property
def filename(self):
if self.file_name == self.file_name.lower():
return "{}-config.cmake".format(self.file_name)
if self.find_module_mode:
return "Find{}.cmake".format(self.file_name)
else:
return "{}Config.cmake".format(self.file_name)
if self.file_name == self.file_name.lower():
return "{}-config.cmake".format(self.file_name)
else:
return "{}Config.cmake".format(self.file_name)

@property
def context(self):
return {"file_name": self.file_name,
targets_include = "" if not self.find_module_mode else "module-"
targets_include += "{}Targets.cmake".format(self.file_name)
return {"is_module": self.find_module_mode,
"version": self.conanfile.ref.version,
"file_name": self.file_name,
"pkg_name": self.pkg_name,
"config_suffix": self.config_suffix,
"target_namespace": self.target_namespace,
"check_components_exist": self.cmakedeps.check_components_exist}
"check_components_exist": self.cmakedeps.check_components_exist,
"targets_include_file": targets_include}

@property
def template(self):
return textwrap.dedent("""\
########## MACROS ###########################################################################
#############################################################################################
# Requires CMake > 3.15
if(${CMAKE_VERSION} VERSION_LESS "3.15")
message(FATAL_ERROR "The 'CMakeDeps' generator only works with CMake >= 3.15")
endif()
{% if is_module %}
include(FindPackageHandleStandardArgs)
set({{ pkg_name }}_FOUND 1)
set({{ pkg_name }}_VERSION "{{ version }}")
find_package_handle_standard_args({{ pkg_name }}
REQUIRED_VARS {{ pkg_name }}_VERSION
VERSION_VAR {{ pkg_name }}_VERSION)
mark_as_advanced({{ pkg_name }}_FOUND {{ pkg_name }}_VERSION)
{% endif %}
include(${CMAKE_CURRENT_LIST_DIR}/cmakedeps_macros.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/{{ file_name }}Targets.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/{{ targets_include_file }})
include(CMakeFindDependencyMacro)
foreach(_DEPENDENCY {{ '${' + pkg_name + '_FIND_DEPENDENCY_NAMES' + '}' }} )
Expand Down
17 changes: 8 additions & 9 deletions conan/tools/cmake/cmakedeps/templates/target_configuration.py
@@ -1,7 +1,6 @@
import textwrap

from conan.tools.cmake.cmakedeps.templates import CMakeDepsFileTemplate, get_component_alias, \
get_target_namespace
from conan.tools.cmake.cmakedeps.templates import CMakeDepsFileTemplate

"""
Expand All @@ -14,8 +13,9 @@ class TargetConfigurationTemplate(CMakeDepsFileTemplate):

@property
def filename(self):
return "{}Target-{}.cmake".format(self.file_name,
self.cmakedeps.configuration.lower())
name = "" if not self.find_module_mode else "module-"
name += "{}-Target-{}.cmake".format(self.file_name, self.cmakedeps.configuration.lower())
return name

@property
def context(self):
Expand Down Expand Up @@ -147,7 +147,7 @@ def get_required_components_names(self):
ret = []
sorted_comps = self.conanfile.new_cpp_info.get_sorted_components()
for comp_name, comp in sorted_comps.items():
ret.append(get_component_alias(self.conanfile, comp_name))
ret.append(self.get_component_alias(self.conanfile, comp_name))
ret.reverse()
return ret

Expand All @@ -163,16 +163,15 @@ def get_deps_targets_names(self):
for dep_name, component_name in self.conanfile.new_cpp_info.required_components:
if not dep_name:
# Internal dep (no another component)
dep_name = get_target_namespace(self.conanfile)
req = self.conanfile
else:
req = self.conanfile.dependencies.host[dep_name]
dep_name = get_target_namespace(req)

component_name = get_component_alias(req, component_name)
dep_name = self.get_target_namespace(req)
component_name = self.get_component_alias(req, component_name)
ret.append("{}::{}".format(dep_name, component_name))
elif self.conanfile.dependencies.direct_host:
# Regular external "conanfile.requires" declared, not cpp_info requires
ret = ["{p}::{p}".format(p=get_target_namespace(r))
ret = ["{p}::{p}".format(p=self.get_target_namespace(r))
for r in self.conanfile.dependencies.direct_host.values()]
return ret
20 changes: 10 additions & 10 deletions conan/tools/cmake/cmakedeps/templates/target_data.py
@@ -1,8 +1,7 @@
import os
import textwrap

from conan.tools.cmake.cmakedeps.templates import CMakeDepsFileTemplate, get_component_alias, \
get_target_namespace
from conan.tools.cmake.cmakedeps.templates import CMakeDepsFileTemplate
from conan.tools.cmake.utils import get_file_name

"""
Expand All @@ -16,7 +15,8 @@ class ConfigDataTemplate(CMakeDepsFileTemplate):

@property
def filename(self):
data_fname = "{}-{}".format(self.file_name, self.configuration.lower())
data_fname = "" if not self.find_module_mode else "module-"
data_fname += "{}-{}".format(self.file_name, self.configuration.lower())
if self.arch:
data_fname += "-{}".format(self.arch)
data_fname += "-data.cmake"
Expand Down Expand Up @@ -122,14 +122,14 @@ def get_required_components_cpp(self):
if "::" in require: # Points to a component of a different package
pkg, cmp_name = require.split("::")
req = self.conanfile.dependencies.direct_host[pkg]
public_comp_deps.append("{}::{}".format(get_target_namespace(req),
get_component_alias(req, cmp_name)))
public_comp_deps.append("{}::{}".format(self.get_target_namespace(req),
self.get_component_alias(req, cmp_name)))
else: # Points to a component of same package
public_comp_deps.append("{}::{}".format(self.target_namespace,
get_component_alias(self.conanfile,
require)))
self.get_component_alias(self.conanfile,
require)))
deps_cpp_cmake.public_deps = " ".join(public_comp_deps)
component_rename = get_component_alias(self.conanfile, comp_name)
component_rename = self.get_component_alias(self.conanfile, comp_name)
ret.append((component_rename, deps_cpp_cmake))
ret.reverse()
return ret
Expand All @@ -143,9 +143,9 @@ def _get_dependency_filenames(self):
for dep_name, _ in self.conanfile.new_cpp_info.required_components:
if dep_name and dep_name not in ret: # External dep
req = direct_host[dep_name]
ret.append(get_file_name(req))
ret.append(get_file_name(req, self.find_module_mode))
elif direct_host:
ret = [get_file_name(r) for r in direct_host.values()]
ret = [get_file_name(r, self.find_module_mode) for r in direct_host.values()]

return ret

Expand Down
17 changes: 13 additions & 4 deletions conan/tools/cmake/cmakedeps/templates/targets.py
Expand Up @@ -13,21 +13,30 @@ class TargetsTemplate(CMakeDepsFileTemplate):

@property
def filename(self):
return "{}Targets.cmake".format(self.file_name)
name = "" if not self.find_module_mode else "module-"
name += self.file_name + "Targets.cmake"
return name

@property
def context(self):
data_pattern = "${_DIR}/" if not self.find_module_mode else "${_DIR}/module-"
data_pattern += "{}-*-data.cmake".format(self.file_name)

target_pattern = "" if not self.find_module_mode else "module-"
target_pattern += "{}-Target-*.cmake".format(self.file_name)

ret = {"pkg_name": self.pkg_name,
"target_namespace": self.target_namespace,
"file_name": self.file_name}
"data_pattern": data_pattern,
"target_pattern": target_pattern}
return ret

@property
def template(self):
return textwrap.dedent("""\
# Load the debug and release variables
get_filename_component(_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
file(GLOB DATA_FILES "${_DIR}/{{ file_name }}-*-data.cmake")
file(GLOB DATA_FILES "{{data_pattern}}")
foreach(f ${DATA_FILES})
include(${f})
Expand All @@ -48,7 +57,7 @@ def template(self):
# Load the debug and release library finders
get_filename_component(_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
file(GLOB CONFIG_FILES "${_DIR}/{{ file_name }}Target-*.cmake")
file(GLOB CONFIG_FILES "${_DIR}/{{ target_pattern }}")
foreach(f ${CONFIG_FILES})
include(${f})
Expand Down

0 comments on commit aae78ea

Please sign in to comment.