From f1f071b805597e8c00815423ffe0f04cf326363f Mon Sep 17 00:00:00 2001 From: Luis Date: Fri, 20 Aug 2021 12:56:37 +0200 Subject: [PATCH 01/10] To explore, to validate --- conan/tools/cmake/cmakedeps/cmakedeps.py | 45 ++++++--- .../cmake/cmakedeps/templates/__init__.py | 62 +++++++------ .../tools/cmake/cmakedeps/templates/config.py | 16 +++- .../templates/target_configuration.py | 17 ++-- .../cmake/cmakedeps/templates/target_data.py | 20 ++-- .../cmake/cmakedeps/templates/targets.py | 17 +++- conan/tools/cmake/toolchain.py | 35 +++++-- conan/tools/cmake/utils.py | 6 +- .../test_cmakedeps_find_module_and_config.py | 91 +++++++++++++++++++ 9 files changed, 231 insertions(+), 78 deletions(-) create mode 100644 conans/test/functional/toolchains/cmake/cmakedeps/test_cmakedeps_find_module_and_config.py diff --git a/conan/tools/cmake/cmakedeps/cmakedeps.py b/conan/tools/cmake/cmakedeps/cmakedeps.py index 9ee9319c648..d14dfff1e29 100644 --- a/conan/tools/cmake/cmakedeps/cmakedeps.py +++ b/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 @@ -27,6 +28,9 @@ def __init__(self, conanfile): # a suffix. It is necessary in case of same require and build_require and will cause an error self.build_context_suffix = {} + # Patterns to be excluded + self.excluded_requirement_patterns = [] + check_using_build_profile(self._conanfile) # Enable/Disable checking if a component target exists or not @@ -75,26 +79,39 @@ def content(self): if dep.is_build_context and dep.ref.name not in self.build_context_activated: continue + # Skip from the consumer + if any(fnmatch(str(dep.ref), pattern) for pattern in self.excluded_requirement_patterns): + self._conanfile.output.info("CMakeDeps: Skipped '{}'".format(dep.ref)) + continue + + # Skip from the requirement if dep.new_cpp_info.get_property("skip_deps_file", "CMakeDeps"): # Skip the generation of config files for this node, it will be located externally continue - config_version = ConfigVersionTemplate(self, require, dep) - ret[config_version.filename] = config_version.render() + generate_find_module = dep.new_cpp_info.get_property("cmake_module_file_name", + "CMakeDeps") is not None + + for find_modules_mode in ([False, True] if generate_find_module else [False]): + if not find_modules_mode: + config_version = ConfigVersionTemplate(self, require, dep) + ret[config_version.filename] = config_version.render() + + data_target = ConfigDataTemplate(self, require, dep, find_modules_mode) + ret[data_target.filename] = data_target.render() - data_target = ConfigDataTemplate(self, require, dep) - ret[data_target.filename] = data_target.render() + target_configuration = TargetConfigurationTemplate(self, require, dep, + find_modules_mode) + ret[target_configuration.filename] = target_configuration.render() - target_configuration = TargetConfigurationTemplate(self, require, dep) - ret[target_configuration.filename] = target_configuration.render() + targets = TargetsTemplate(self, require, dep, find_modules_mode) + ret[targets.filename] = targets.render() - targets = TargetsTemplate(self, require, dep) - ret[targets.filename] = targets.render() + config = ConfigTemplate(self, require, dep, find_modules_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() - 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 diff --git a/conan/tools/cmake/cmakedeps/templates/__init__.py b/conan/tools/cmake/cmakedeps/templates/__init__.py index 7bd1a70c8a4..77891ef6d49 100644 --- a/conan/tools/cmake/cmakedeps/templates/__init__.py +++ b/conan/tools/cmake/cmakedeps/templates/__init__.py @@ -7,10 +7,11 @@ class CMakeDepsFileTemplate(object): - def __init__(self, cmakedeps, require, conanfile): + def __init__(self, cmakedeps, require, conanfile, find_modules_mode=False): self.cmakedeps = cmakedeps self.require = require self.conanfile = conanfile + self.find_modules_mode = find_modules_mode @property def pkg_name(self): @@ -18,11 +19,11 @@ def pkg_name(self): @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_modules_mode) + self.suffix @property def suffix(self): @@ -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_modules_mode) + + def get_target_namespace(self, req): + if self.find_modules_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_modules_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 diff --git a/conan/tools/cmake/cmakedeps/templates/config.py b/conan/tools/cmake/cmakedeps/templates/config.py index bfd457583db..a76b001459a 100644 --- a/conan/tools/cmake/cmakedeps/templates/config.py +++ b/conan/tools/cmake/cmakedeps/templates/config.py @@ -14,18 +14,24 @@ 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_modules_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): + targets_include = "" if not self.find_modules_mode else "modules-" + targets_include += "{}Targets.cmake".format(self.file_name) return {"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): @@ -38,7 +44,7 @@ def template(self): 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' + '}' }} ) diff --git a/conan/tools/cmake/cmakedeps/templates/target_configuration.py b/conan/tools/cmake/cmakedeps/templates/target_configuration.py index a3514eb240b..ff9fd15993d 100644 --- a/conan/tools/cmake/cmakedeps/templates/target_configuration.py +++ b/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 """ @@ -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_modules_mode else "modules-" + name += "{}Target-{}.cmake".format(self.file_name, self.cmakedeps.configuration.lower()) + return name @property def context(self): @@ -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 @@ -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 diff --git a/conan/tools/cmake/cmakedeps/templates/target_data.py b/conan/tools/cmake/cmakedeps/templates/target_data.py index 156fecb9b02..873179906db 100644 --- a/conan/tools/cmake/cmakedeps/templates/target_data.py +++ b/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 """ @@ -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_modules_mode else "modules-" + data_fname += "{}-{}".format(self.file_name, self.configuration.lower()) if self.arch: data_fname += "-{}".format(self.arch) data_fname += "-data.cmake" @@ -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 @@ -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_modules_mode)) elif direct_host: - ret = [get_file_name(r) for r in direct_host.values()] + ret = [get_file_name(r, self.find_modules_mode) for r in direct_host.values()] return ret diff --git a/conan/tools/cmake/cmakedeps/templates/targets.py b/conan/tools/cmake/cmakedeps/templates/targets.py index 59bdd3e1c16..f2788c81b74 100644 --- a/conan/tools/cmake/cmakedeps/templates/targets.py +++ b/conan/tools/cmake/cmakedeps/templates/targets.py @@ -13,13 +13,22 @@ class TargetsTemplate(CMakeDepsFileTemplate): @property def filename(self): - return "{}Targets.cmake".format(self.file_name) + name = "" if not self.find_modules_mode else "modules-" + name += self.file_name + "Targets.cmake" + return name @property def context(self): + data_pattern = "${_DIR}/" if not self.find_modules_mode else "${_DIR}/modules-" + data_pattern += "{}-*-data.cmake".format(self.file_name) + + target_pattern = "" if not self.find_modules_mode else "modules-" + 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 @@ -27,7 +36,7 @@ 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}) @@ -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}) diff --git a/conan/tools/cmake/toolchain.py b/conan/tools/cmake/toolchain.py index b1168ebc354..f7ff8ae094c 100644 --- a/conan/tools/cmake/toolchain.py +++ b/conan/tools/cmake/toolchain.py @@ -68,7 +68,16 @@ def get_rendered_content(self): context = self.values if context is None: return - return Template(self.template, trim_blocks=True, lstrip_blocks=True).render(**context) + + def cmake_value(value): + if isinstance(value, bool): + return "ON" if value else "OFF" + else: + return '"{}"'.format(value) + + template = Template(self.template, trim_blocks=True, lstrip_blocks=True) + template.environment.filters["cmake_value"] = cmake_value + return template.render(**context) def context(self): return {} @@ -386,10 +395,10 @@ class FindConfigFiles(Block): {% endif %} # To support the generators based on find_package() {% if cmake_module_path %} - set(CMAKE_MODULE_PATH "{{ cmake_module_path }}" ${CMAKE_MODULE_PATH}) + set(CMAKE_MODULE_PATH {{ cmake_module_path }} ${CMAKE_MODULE_PATH}) {% endif %} {% if cmake_prefix_path %} - set(CMAKE_PREFIX_PATH "{{ cmake_prefix_path }}" ${CMAKE_PREFIX_PATH}) + set(CMAKE_PREFIX_PATH {{ cmake_prefix_path }} ${CMAKE_PREFIX_PATH}) {% endif %} {% if android_prefix_path %} set(CMAKE_FIND_ROOT_PATH {{ android_prefix_path }} ${CMAKE_FIND_ROOT_PATH}) @@ -415,11 +424,23 @@ def context(self): host_req = self._conanfile.dependencies.host.values() find_names_needed = os_ in ('iOS', "watchOS", "tvOS") - find_names = [get_file_name(req) for req in host_req] if find_names_needed else [] + find_names = [get_file_name(req) + for req in host_req] if find_names_needed else [] + + # Read the buildirs + build_paths = [] + for req in host_req: + cppinfo = req.new_cpp_info.copy() + cppinfo.aggregate_components() + build_paths.extend([os.path.join(req.package_folder, + p.replace('\\', '/').replace('$', '\\$').replace('"', '\\"')) + for p in cppinfo.builddirs]) + + build_paths = " ".join(['"{}"'.format(b) for b in build_paths]) return {"find_package_prefer_config": find_package_prefer_config, - "cmake_prefix_path": "${CMAKE_CURRENT_LIST_DIR}", - "cmake_module_path": "${CMAKE_CURRENT_LIST_DIR}", + "cmake_prefix_path": "${CMAKE_CURRENT_LIST_DIR} " + build_paths, + "cmake_module_path": "${CMAKE_CURRENT_LIST_DIR} " + build_paths, "android_prefix_path": android_prefix, "find_names": find_names, "generators_folder": "${CMAKE_CURRENT_LIST_DIR}"} @@ -667,7 +688,7 @@ class CMakeToolchain(object): # Variables {% for it, value in variables.items() %} - set({{ it }} "{{ value }}" CACHE STRING "Variable {{ it }} conan-toolchain defined") + set({{ it }} {{ value|cmake_value }} CACHE STRING "Variable {{ it }} conan-toolchain defined") {% endfor %} # Variables per configuration {{ iterate_configs(variables_config, action='set') }} diff --git a/conan/tools/cmake/utils.py b/conan/tools/cmake/utils.py index 4f4e809f40f..2cab4a96654 100644 --- a/conan/tools/cmake/utils.py +++ b/conan/tools/cmake/utils.py @@ -4,10 +4,14 @@ def is_multi_configuration(generator): return "Visual" in generator or "Xcode" in generator or "Multi-Config" in generator -def get_file_name(conanfile): +def get_file_name(conanfile, find_module_mode=False): """Get the name of the file for the find_package(XXX)""" # This is used by the CMakeToolchain to adjust the XXX_DIR variables and the CMakeDeps. Both # to know the file name that will have the XXX-config.cmake files. + if find_module_mode: + ret = conanfile.new_cpp_info.get_property("cmake_module_file_name", "CMakeDeps") + if ret: + return ret ret = conanfile.new_cpp_info.get_property("cmake_file_name", "CMakeDeps") if not ret: ret = conanfile.cpp_info.get_filename("cmake_find_package_multi", default_name=False) diff --git a/conans/test/functional/toolchains/cmake/cmakedeps/test_cmakedeps_find_module_and_config.py b/conans/test/functional/toolchains/cmake/cmakedeps/test_cmakedeps_find_module_and_config.py new file mode 100644 index 00000000000..9ea50a56d06 --- /dev/null +++ b/conans/test/functional/toolchains/cmake/cmakedeps/test_cmakedeps_find_module_and_config.py @@ -0,0 +1,91 @@ +import textwrap + +import pytest + +from conans.test.assets.cmake import gen_cmakelists +from conans.test.assets.genconanfile import GenConanfile +from conans.test.assets.sources import gen_function_cpp, gen_function_h +from conans.test.utils.tools import TestClient + + +@pytest.fixture(scope="module") +def client(): + t = TestClient() + cpp = gen_function_cpp(name="mydep") + h = gen_function_h(name="mydep") + cmake = gen_cmakelists(libname="mydep", libsources=["mydep.cpp"]) + conanfile = textwrap.dedent(""" + import os + from conans import ConanFile + from conan.tools.cmake import CMake + + class Conan(ConanFile): + name = "mydep" + version = "1.0" + settings = "os", "arch", "compiler", "build_type" + exports_sources = "*.cpp", "*.h", "CMakeLists.txt" + generators = "CMakeToolchain" + + def build(self): + cmake = CMake(self) + cmake.configure() + cmake.build() + + def package(self): + self.copy("*.h", dst="include") + self.copy("*.lib", dst="lib", keep_path=False) + self.copy("*.dll", dst="bin", keep_path=False) + self.copy("*.dylib*", dst="lib", keep_path=False) + self.copy("*.so", dst="lib", keep_path=False) + self.copy("*.a", dst="lib", keep_path=False) + + def package_info(self): + self.cpp_info.set_property("cmake_file_name", "MyDep") + self.cpp_info.set_property("cmake_target_name", "MyDepTarget") + + self.cpp_info.set_property("cmake_module_file_name", "mi_dependencia") + self.cpp_info.set_property("cmake_module_target_name", "mi_dependencia_target") + + self.cpp_info.components["crispin"].libs = ["mydep"] + self.cpp_info.components["crispin"].set_property("cmake_target_name", "MyCrispinTarget") + self.cpp_info.components["crispin"].set_property("cmake_module_target_name", "mi_crispin_target") + """) + + t.save({"conanfile.py": conanfile, + "mydep.cpp": cpp, + "mydep.h": h, + "CMakeLists.txt": cmake}) + + t.run("create .") + return t + + +@pytest.mark.tool_cmake +def test_reuse_with_modules_and_config(client): + cpp = gen_function_cpp(name="main") + cmake = """ + set(CMAKE_CXX_COMPILER_WORKS 1) + set(CMAKE_CXX_ABI_COMPILED 1) + set(CMAKE_C_COMPILER_WORKS 1) + set(CMAKE_C_ABI_COMPILED 1) + + cmake_minimum_required(VERSION 3.15) + project(project CXX) + + add_executable(myapp main.cpp) + find_package(MyDep) # This one will find the config + target_link_libraries(myapp MyDepTarget::MyCrispinTarget) + + add_executable(myapp2 main.cpp) + find_package(mi_dependencia) # This one will find the module + target_link_libraries(myapp2 mi_dependencia_target::mi_crispin_target) + + """ + conanfile = GenConanfile().with_name("myapp")\ + .with_cmake_build().with_exports_sources("*.cpp", "*.txt").with_require("mydep/1.0") + client.save({"conanfile.py": conanfile, + "main.cpp": cpp, + "CMakeLists.txt": cmake}) + + client.run("install . -if=install") + client.run("build . -if=install") From 735ad3fea1c351bd30dad38b5cdb819a44912dac Mon Sep 17 00:00:00 2001 From: Luis Date: Fri, 20 Aug 2021 13:47:52 +0200 Subject: [PATCH 02/10] Fix names --- conan/tools/cmake/cmakedeps/templates/target_configuration.py | 2 +- conan/tools/cmake/cmakedeps/templates/targets.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/conan/tools/cmake/cmakedeps/templates/target_configuration.py b/conan/tools/cmake/cmakedeps/templates/target_configuration.py index ff9fd15993d..16f564fd60d 100644 --- a/conan/tools/cmake/cmakedeps/templates/target_configuration.py +++ b/conan/tools/cmake/cmakedeps/templates/target_configuration.py @@ -14,7 +14,7 @@ class TargetConfigurationTemplate(CMakeDepsFileTemplate): @property def filename(self): name = "" if not self.find_modules_mode else "modules-" - name += "{}Target-{}.cmake".format(self.file_name, self.cmakedeps.configuration.lower()) + name += "{}-Target-{}.cmake".format(self.file_name, self.cmakedeps.configuration.lower()) return name @property diff --git a/conan/tools/cmake/cmakedeps/templates/targets.py b/conan/tools/cmake/cmakedeps/templates/targets.py index f2788c81b74..68416ebe800 100644 --- a/conan/tools/cmake/cmakedeps/templates/targets.py +++ b/conan/tools/cmake/cmakedeps/templates/targets.py @@ -19,7 +19,7 @@ def filename(self): @property def context(self): - data_pattern = "${_DIR}/" if not self.find_modules_mode else "${_DIR}/modules-" + data_pattern = "${_DIR}/" if not self.find_modules_mode else "${_DIR}/modules-" data_pattern += "{}-*-data.cmake".format(self.file_name) target_pattern = "" if not self.find_modules_mode else "modules-" From 682b7fe8512561198f94627ef0347a70bef85eb1 Mon Sep 17 00:00:00 2001 From: Luis Date: Fri, 20 Aug 2021 13:59:42 +0200 Subject: [PATCH 03/10] Rename --- conan/tools/cmake/cmakedeps/cmakedeps.py | 12 ++++++------ conan/tools/cmake/cmakedeps/templates/__init__.py | 12 ++++++------ conan/tools/cmake/cmakedeps/templates/config.py | 4 ++-- .../cmakedeps/templates/target_configuration.py | 2 +- conan/tools/cmake/cmakedeps/templates/target_data.py | 6 +++--- conan/tools/cmake/cmakedeps/templates/targets.py | 6 +++--- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/conan/tools/cmake/cmakedeps/cmakedeps.py b/conan/tools/cmake/cmakedeps/cmakedeps.py index d14dfff1e29..00a17c92495 100644 --- a/conan/tools/cmake/cmakedeps/cmakedeps.py +++ b/conan/tools/cmake/cmakedeps/cmakedeps.py @@ -92,22 +92,22 @@ def content(self): generate_find_module = dep.new_cpp_info.get_property("cmake_module_file_name", "CMakeDeps") is not None - for find_modules_mode in ([False, True] if generate_find_module else [False]): - if not find_modules_mode: + for find_module_mode in ([False, True] if generate_find_module else [False]): + if not find_module_mode: config_version = ConfigVersionTemplate(self, require, dep) ret[config_version.filename] = config_version.render() - data_target = ConfigDataTemplate(self, require, dep, find_modules_mode) + data_target = ConfigDataTemplate(self, require, dep, find_module_mode) ret[data_target.filename] = data_target.render() target_configuration = TargetConfigurationTemplate(self, require, dep, - find_modules_mode) + find_module_mode) ret[target_configuration.filename] = target_configuration.render() - targets = TargetsTemplate(self, require, dep, find_modules_mode) + targets = TargetsTemplate(self, require, dep, find_module_mode) ret[targets.filename] = targets.render() - config = ConfigTemplate(self, require, dep, find_modules_mode) + 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. diff --git a/conan/tools/cmake/cmakedeps/templates/__init__.py b/conan/tools/cmake/cmakedeps/templates/__init__.py index 77891ef6d49..cb72da28498 100644 --- a/conan/tools/cmake/cmakedeps/templates/__init__.py +++ b/conan/tools/cmake/cmakedeps/templates/__init__.py @@ -7,11 +7,11 @@ class CMakeDepsFileTemplate(object): - def __init__(self, cmakedeps, require, conanfile, find_modules_mode=False): + def __init__(self, cmakedeps, require, conanfile, find_module_mode=False): self.cmakedeps = cmakedeps self.require = require self.conanfile = conanfile - self.find_modules_mode = find_modules_mode + self.find_module_mode = find_module_mode @property def pkg_name(self): @@ -23,7 +23,7 @@ def target_namespace(self): @property def file_name(self): - return get_file_name(self.conanfile, self.find_modules_mode) + self.suffix + return get_file_name(self.conanfile, self.find_module_mode) + self.suffix @property def suffix(self): @@ -76,10 +76,10 @@ def config_suffix(self): return "_{}".format(self.configuration.upper()) if self.configuration else "" def get_file_name(self): - return get_file_name(self.conanfile, find_module_mode=self.find_modules_mode) + return get_file_name(self.conanfile, find_module_mode=self.find_module_mode) def get_target_namespace(self, req): - if self.find_modules_mode: + if self.find_module_mode: ret = req.new_cpp_info.get_property("cmake_module_target_name", "CMakeDeps") if ret: return ret @@ -96,7 +96,7 @@ def get_component_alias(self, req, 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_modules_mode: + if self.find_module_mode: ret = req.new_cpp_info.components[comp_name].get_property("cmake_module_target_name", "CMakeDeps") if ret: diff --git a/conan/tools/cmake/cmakedeps/templates/config.py b/conan/tools/cmake/cmakedeps/templates/config.py index a76b001459a..d631ba6ca19 100644 --- a/conan/tools/cmake/cmakedeps/templates/config.py +++ b/conan/tools/cmake/cmakedeps/templates/config.py @@ -14,7 +14,7 @@ class ConfigTemplate(CMakeDepsFileTemplate): @property def filename(self): - if self.find_modules_mode: + if self.find_module_mode: return "Find{}.cmake".format(self.file_name) else: if self.file_name == self.file_name.lower(): @@ -24,7 +24,7 @@ def filename(self): @property def context(self): - targets_include = "" if not self.find_modules_mode else "modules-" + targets_include = "" if not self.find_module_mode else "module-" targets_include += "{}Targets.cmake".format(self.file_name) return {"file_name": self.file_name, "pkg_name": self.pkg_name, diff --git a/conan/tools/cmake/cmakedeps/templates/target_configuration.py b/conan/tools/cmake/cmakedeps/templates/target_configuration.py index 16f564fd60d..6e427d89335 100644 --- a/conan/tools/cmake/cmakedeps/templates/target_configuration.py +++ b/conan/tools/cmake/cmakedeps/templates/target_configuration.py @@ -13,7 +13,7 @@ class TargetConfigurationTemplate(CMakeDepsFileTemplate): @property def filename(self): - name = "" if not self.find_modules_mode else "modules-" + name = "" if not self.find_module_mode else "module-" name += "{}-Target-{}.cmake".format(self.file_name, self.cmakedeps.configuration.lower()) return name diff --git a/conan/tools/cmake/cmakedeps/templates/target_data.py b/conan/tools/cmake/cmakedeps/templates/target_data.py index 873179906db..b10c50720a5 100644 --- a/conan/tools/cmake/cmakedeps/templates/target_data.py +++ b/conan/tools/cmake/cmakedeps/templates/target_data.py @@ -15,7 +15,7 @@ class ConfigDataTemplate(CMakeDepsFileTemplate): @property def filename(self): - data_fname = "" if not self.find_modules_mode else "modules-" + 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) @@ -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, self.find_modules_mode)) + ret.append(get_file_name(req, self.find_module_mode)) elif direct_host: - ret = [get_file_name(r, self.find_modules_mode) for r in direct_host.values()] + ret = [get_file_name(r, self.find_module_mode) for r in direct_host.values()] return ret diff --git a/conan/tools/cmake/cmakedeps/templates/targets.py b/conan/tools/cmake/cmakedeps/templates/targets.py index 68416ebe800..118cb572f94 100644 --- a/conan/tools/cmake/cmakedeps/templates/targets.py +++ b/conan/tools/cmake/cmakedeps/templates/targets.py @@ -13,16 +13,16 @@ class TargetsTemplate(CMakeDepsFileTemplate): @property def filename(self): - name = "" if not self.find_modules_mode else "modules-" + 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_modules_mode else "${_DIR}/modules-" + 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_modules_mode else "modules-" + target_pattern = "" if not self.find_module_mode else "module-" target_pattern += "{}-Target-*.cmake".format(self.file_name) ret = {"pkg_name": self.pkg_name, From bc0656ecebbf358b4c5a1dfef138986f61b165a5 Mon Sep 17 00:00:00 2001 From: Luis Date: Fri, 20 Aug 2021 14:14:31 +0200 Subject: [PATCH 04/10] fix tests --- .../toolchains/cmake/cmakedeps/test_cmakedeps_components.py | 4 ++-- conans/test/unittests/tools/cmake/test_cmakedeps.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/conans/test/functional/toolchains/cmake/cmakedeps/test_cmakedeps_components.py b/conans/test/functional/toolchains/cmake/cmakedeps/test_cmakedeps_components.py index bb39cc5e5fb..a3ec6c303fd 100644 --- a/conans/test/functional/toolchains/cmake/cmakedeps/test_cmakedeps_components.py +++ b/conans/test/functional/toolchains/cmake/cmakedeps/test_cmakedeps_components.py @@ -61,7 +61,7 @@ def test_cmakedeps_app(self): t = TestClient(cache_folder=self.cache_folder) t.save({'conanfile.py': self.app}) t.run("install . -g CMakeDeps") - config = t.load("middleTarget-release.cmake") + config = t.load("middle-Target-release.cmake") self.assertIn('top::cmp1', config) self.assertNotIn("top::top", config) @@ -76,7 +76,7 @@ def test_cmakedeps_multi(self): content = t.load('middle-config.cmake') self.assertIn("find_dependency(${_DEPENDENCY} REQUIRED NO_MODULE)", content) - content = t.load('middleTarget-release.cmake') + content = t.load('middle-Target-release.cmake') self.assertNotIn("top::top", content) self.assertNotIn("top::cmp2", content) self.assertIn("top::cmp1", content) diff --git a/conans/test/unittests/tools/cmake/test_cmakedeps.py b/conans/test/unittests/tools/cmake/test_cmakedeps.py index c25d127d43f..ce05d2136bd 100644 --- a/conans/test/unittests/tools/cmake/test_cmakedeps.py +++ b/conans/test/unittests/tools/cmake/test_cmakedeps.py @@ -45,7 +45,7 @@ def test_cpp_info_name_cmakedeps(using_properties): cmakedeps = CMakeDeps(conanfile) files = cmakedeps.content - assert "TARGET MySuperPkg1::MySuperPkg1" in files["ComplexFileName1Target-release.cmake"] + assert "TARGET MySuperPkg1::MySuperPkg1" in files["ComplexFileName1-Target-release.cmake"] assert 'set(OriginalDepName_INCLUDE_DIRS_RELEASE ' \ '"${OriginalDepName_PACKAGE_FOLDER_RELEASE}/include")' \ in files["ComplexFileName1-release-x86-data.cmake"] @@ -87,7 +87,7 @@ def test_cpp_info_name_cmakedeps_components(using_properties): cmakedeps = CMakeDeps(conanfile) files = cmakedeps.content - assert "TARGET GlobakPkgName1::MySuperPkg1" in files["ComplexFileName1Target-debug.cmake"] + assert "TARGET GlobakPkgName1::MySuperPkg1" in files["ComplexFileName1-Target-debug.cmake"] assert 'set(OriginalDepName_INCLUDE_DIRS_DEBUG ' \ '"${OriginalDepName_PACKAGE_FOLDER_DEBUG}/include")' \ in files["ComplexFileName1-debug-x64-data.cmake"] @@ -165,7 +165,7 @@ def test_component_name_same_package(): cmakedeps = CMakeDeps(conanfile) files = cmakedeps.content - target_cmake = files["mypkgTarget-release.cmake"] + target_cmake = files["mypkg-Target-release.cmake"] assert "$<$:${mypkg_mypkg_INCLUDE_DIRS_RELEASE}> APPEND)" in target_cmake data_cmake = files["mypkg-release-x86-data.cmake"] From a9c11478141b27e4a92d090616e3cb91a1988149 Mon Sep 17 00:00:00 2001 From: Luis Date: Mon, 30 Aug 2021 12:33:30 +0200 Subject: [PATCH 05/10] Fix win tests --- conan/tools/cmake/toolchain.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/conan/tools/cmake/toolchain.py b/conan/tools/cmake/toolchain.py index f7ff8ae094c..23925533c00 100644 --- a/conan/tools/cmake/toolchain.py +++ b/conan/tools/cmake/toolchain.py @@ -436,7 +436,9 @@ def context(self): p.replace('\\', '/').replace('$', '\\$').replace('"', '\\"')) for p in cppinfo.builddirs]) - build_paths = " ".join(['"{}"'.format(b) for b in build_paths]) + build_paths = " ".join(['"{}"'.format(b.replace('\\', '/') + .replace('$', '\\$') + .replace('"', '\\"')) for b in build_paths]) return {"find_package_prefer_config": find_package_prefer_config, "cmake_prefix_path": "${CMAKE_CURRENT_LIST_DIR} " + build_paths, From 9b64bf3c4bc5decb9e40e05dd1a3a2ae865ed330 Mon Sep 17 00:00:00 2001 From: Luis Date: Mon, 30 Aug 2021 13:14:16 +0200 Subject: [PATCH 06/10] Added tests for new cmake_find_mode --- conan/tools/cmake/cmakedeps/cmakedeps.py | 51 ++++++++------- .../test_cmakedeps_find_module_and_config.py | 3 + .../cmake/cmakedeps/test_cmakedeps.py | 2 +- .../test_cmakedeps_find_module_and_config.py | 63 +++++++++++++++++++ 4 files changed, 97 insertions(+), 22 deletions(-) create mode 100644 conans/test/integration/toolchains/cmake/cmakedeps/test_cmakedeps_find_module_and_config.py diff --git a/conan/tools/cmake/cmakedeps/cmakedeps.py b/conan/tools/cmake/cmakedeps/cmakedeps.py index 00a17c92495..0b2d79dc3c8 100644 --- a/conan/tools/cmake/cmakedeps/cmakedeps.py +++ b/conan/tools/cmake/cmakedeps/cmakedeps.py @@ -12,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): @@ -84,34 +89,38 @@ def content(self): self._conanfile.output.info("CMakeDeps: Skipped '{}'".format(dep.ref)) continue + 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 dep.new_cpp_info.get_property("skip_deps_file", "CMakeDeps"): + if cmake_find_mode == FIND_MODE_NONE: # Skip the generation of config files for this node, it will be located externally continue - generate_find_module = dep.new_cpp_info.get_property("cmake_module_file_name", - "CMakeDeps") is not None + 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) - for find_module_mode in ([False, True] if generate_find_module else [False]): - if not find_module_mode: - config_version = ConfigVersionTemplate(self, require, dep) - ret[config_version.filename] = config_version.render() + return ret - data_target = ConfigDataTemplate(self, require, dep, find_module_mode) - ret[data_target.filename] = data_target.render() + 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() - target_configuration = TargetConfigurationTemplate(self, require, dep, - find_module_mode) - ret[target_configuration.filename] = target_configuration.render() + data_target = ConfigDataTemplate(self, require, dep, find_module_mode) + ret[data_target.filename] = data_target.render() - targets = TargetsTemplate(self, require, dep, find_module_mode) - ret[targets.filename] = targets.render() + target_configuration = TargetConfigurationTemplate(self, require, dep, find_module_mode) + ret[target_configuration.filename] = target_configuration.render() - 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() + targets = TargetsTemplate(self, require, dep, find_module_mode) + ret[targets.filename] = targets.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() diff --git a/conans/test/functional/toolchains/cmake/cmakedeps/test_cmakedeps_find_module_and_config.py b/conans/test/functional/toolchains/cmake/cmakedeps/test_cmakedeps_find_module_and_config.py index 9ea50a56d06..cb16621ba57 100644 --- a/conans/test/functional/toolchains/cmake/cmakedeps/test_cmakedeps_find_module_and_config.py +++ b/conans/test/functional/toolchains/cmake/cmakedeps/test_cmakedeps_find_module_and_config.py @@ -40,6 +40,9 @@ def package(self): self.copy("*.a", dst="lib", keep_path=False) def package_info(self): + + self.cpp_info.set_property("cmake_find_mode", "both") + self.cpp_info.set_property("cmake_file_name", "MyDep") self.cpp_info.set_property("cmake_target_name", "MyDepTarget") diff --git a/conans/test/integration/toolchains/cmake/cmakedeps/test_cmakedeps.py b/conans/test/integration/toolchains/cmake/cmakedeps/test_cmakedeps.py index e2656d034bb..2da2ef31b35 100644 --- a/conans/test/integration/toolchains/cmake/cmakedeps/test_cmakedeps.py +++ b/conans/test/integration/toolchains/cmake/cmakedeps/test_cmakedeps.py @@ -16,7 +16,7 @@ def test_package_from_system(): .with_settings("os", "arch", "build_type", "compiler")) dep2 += """ def package_info(self): - self.cpp_info.set_property("skip_deps_file", True) + self.cpp_info.set_property("cmake_find_mode", "None") self.cpp_info.set_property("cmake_file_name", "custom_dep2") """ diff --git a/conans/test/integration/toolchains/cmake/cmakedeps/test_cmakedeps_find_module_and_config.py b/conans/test/integration/toolchains/cmake/cmakedeps/test_cmakedeps_find_module_and_config.py new file mode 100644 index 00000000000..6f4257fc77f --- /dev/null +++ b/conans/test/integration/toolchains/cmake/cmakedeps/test_cmakedeps_find_module_and_config.py @@ -0,0 +1,63 @@ +import os +import textwrap + +import pytest + +from conan.tools.cmake.cmakedeps.cmakedeps import FIND_MODE_CONFIG, FIND_MODE_MODULE, FIND_MODE_BOTH, \ + FIND_MODE_NONE +from conans.test.assets.genconanfile import GenConanfile +from conans.test.utils.tools import TestClient + + +@pytest.mark.parametrize("cmake_find_mode", [FIND_MODE_CONFIG, FIND_MODE_MODULE, + FIND_MODE_BOTH, FIND_MODE_NONE, None]) +def test_reuse_with_modules_and_config(cmake_find_mode): + t = TestClient() + conanfile = textwrap.dedent(""" + import os + from conans import ConanFile + + class Conan(ConanFile): + name = "mydep" + version = "1.0" + settings = "os", "arch", "compiler", "build_type" + + def package_info(self): + {} + + """) + + if cmake_find_mode is not None: + s = 'self.cpp_info.set_property("cmake_find_mode", "{}")'.format(cmake_find_mode) + conanfile = conanfile.format(s) + t.save({"conanfile.py": conanfile}) + t.run("create .") + + conanfile = GenConanfile().with_name("myapp").with_require("mydep/1.0")\ + .with_generator("CMakeDeps")\ + .with_settings("build_type", "os", "arch", "compiler") + t.save({"conanfile.py": conanfile}) + + t.run("install . -if=install") + + ifolder = os.path.join(t.current_folder, "install") + + def exists_config(ifolder): + return os.path.exists(os.path.join(ifolder, "mydep-config.cmake")) + + def exists_module(ifolder): + return os.path.exists(os.path.join(ifolder, "Findmydep.cmake")) + + if cmake_find_mode == FIND_MODE_CONFIG or cmake_find_mode is None: + # None is default "config" + assert exists_config(ifolder) + assert not exists_module(ifolder) + elif cmake_find_mode == FIND_MODE_MODULE: + assert not exists_config(ifolder) + assert exists_module(ifolder) + elif cmake_find_mode == FIND_MODE_BOTH: + assert exists_config(ifolder) + assert exists_module(ifolder) + elif cmake_find_mode == FIND_MODE_NONE: + assert not exists_config(ifolder) + assert not exists_module(ifolder) From 2a078fbd7e24a16ddad08033ef6dd4fd7e91089c Mon Sep 17 00:00:00 2001 From: Luis Date: Mon, 30 Aug 2021 13:22:57 +0200 Subject: [PATCH 07/10] Fixing win test --- .../toolchains/cmake/test_cmakedeps_custom_configs.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/conans/test/functional/toolchains/cmake/test_cmakedeps_custom_configs.py b/conans/test/functional/toolchains/cmake/test_cmakedeps_custom_configs.py index 8ac644066c7..7ea78608fe5 100644 --- a/conans/test/functional/toolchains/cmake/test_cmakedeps_custom_configs.py +++ b/conans/test/functional/toolchains/cmake/test_cmakedeps_custom_configs.py @@ -84,9 +84,9 @@ def test_generator_multi(self): self.client.run("install .. %s -o hello:shared=True" % settings) self.client.run("install .. %s -o hello:shared=False" % settings) self.assertTrue(os.path.isfile(os.path.join(self.client.current_folder, - "helloTarget-releaseshared.cmake"))) + "hello-Target-releaseshared.cmake"))) self.assertTrue(os.path.isfile(os.path.join(self.client.current_folder, - "helloTarget-release.cmake"))) + "hello-Target-release.cmake"))) self.client.run_command('cmake .. -G "Visual Studio 15 Win64"') self.client.run_command('cmake --build . --config Release') @@ -177,7 +177,7 @@ def test_generator_multi(self): with self.client.chdir(build_directory): self.client.run("install .. %s" % settings) self.assertTrue(os.path.isfile(os.path.join(self.client.current_folder, - "helloTarget-myrelease.cmake"))) + "hello-Target-myrelease.cmake"))) self.client.run_command('cmake .. -G "Visual Studio 15 Win64"') self.client.run_command('cmake --build . --config MyRelease') From 3f99570415a4b33d65b6a0c9f4a955ef291cbdd6 Mon Sep 17 00:00:00 2001 From: Luis Date: Tue, 31 Aug 2021 08:37:21 +0200 Subject: [PATCH 08/10] opt-in for toolchain and test --- conan/tools/cmake/toolchain.py | 14 ++++-- .../toolchains/cmake/test_cmaketoolchain.py | 44 +++++++++++++++++++ 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/conan/tools/cmake/toolchain.py b/conan/tools/cmake/toolchain.py index 23925533c00..74738acfbe9 100644 --- a/conan/tools/cmake/toolchain.py +++ b/conan/tools/cmake/toolchain.py @@ -436,9 +436,12 @@ def context(self): p.replace('\\', '/').replace('$', '\\$').replace('"', '\\"')) for p in cppinfo.builddirs]) - build_paths = " ".join(['"{}"'.format(b.replace('\\', '/') - .replace('$', '\\$') - .replace('"', '\\"')) for b in build_paths]) + if self._toolchain.find_builddirs: + build_paths = " ".join(['"{}"'.format(b.replace('\\', '/') + .replace('$', '\\$') + .replace('"', '\\"')) for b in build_paths]) + else: + build_paths = "" return {"find_package_prefer_config": find_package_prefer_config, "cmake_prefix_path": "${CMAKE_CURRENT_LIST_DIR} " + build_paths, @@ -727,6 +730,9 @@ def __init__(self, conanfile, generator=None): ("rpath", SkipRPath), ("shared", SharedLibBock)]) + # Set the CMAKE_MODULE_PATH and CMAKE_PREFIX_PATH to the deps .builddirs + self.find_builddirs = False + check_using_build_profile(self._conanfile) def _context(self): @@ -739,7 +745,7 @@ def _context(self): "variables_config": self.variables.configuration_types, "preprocessor_definitions": self.preprocessor_definitions, "preprocessor_definitions_config": self.preprocessor_definitions.configuration_types, - "conan_blocks": blocks, + "conan_blocks": blocks } return ctxt_toolchain diff --git a/conans/test/integration/toolchains/cmake/test_cmaketoolchain.py b/conans/test/integration/toolchains/cmake/test_cmaketoolchain.py index 8e674005f45..a4646aaf553 100644 --- a/conans/test/integration/toolchains/cmake/test_cmaketoolchain.py +++ b/conans/test/integration/toolchains/cmake/test_cmaketoolchain.py @@ -1,4 +1,7 @@ import textwrap +import os + +import pytest from conans.test.assets.genconanfile import GenConanfile from conans.test.utils.tools import TestClient @@ -188,3 +191,44 @@ def test_cross_build_conf(): assert "set(CMAKE_SYSTEM_NAME Custom)" in toolchain assert "set(CMAKE_SYSTEM_VERSION 42)" in toolchain assert "set(CMAKE_SYSTEM_PROCESSOR myarm)" in toolchain + + +@pytest.mark.parametrize("find_builddir", [True, False]) +def test_find_builddirs(find_builddir): + client = TestClient() + conanfile = textwrap.dedent(""" + import os + from conans import ConanFile + from conan.tools.cmake import CMakeToolchain + + class Conan(ConanFile): + settings = "os", "arch", "compiler", "build_type" + + def package_info(self): + self.cpp_info.builddirs = ["/path/to/builddir"] + """) + client.save({"conanfile.py": conanfile}) + client.run("create . dep/1.0@") + + conanfile = textwrap.dedent(""" + import os + from conans import ConanFile + from conan.tools.cmake import CMakeToolchain + + class Conan(ConanFile): + name = "mydep" + version = "1.0" + settings = "os", "arch", "compiler", "build_type" + requires = "dep/1.0@" + + def generate(self): + cmake = CMakeToolchain(self) + cmake.find_builddirs = {} + cmake.generate() + """).format(str(find_builddir)) + + client.save({"conanfile.py": conanfile}) + client.run("install . ") + with open(os.path.join(client.current_folder, "conan_toolchain.cmake")) as f: + contents = f.read() + assert ("/path/to/builddir" in contents) == find_builddir From 1c81c2abdf3fb8db32fe5f4b4d42805a067a3ce9 Mon Sep 17 00:00:00 2001 From: Luis Date: Tue, 31 Aug 2021 12:45:37 +0200 Subject: [PATCH 09/10] review --- conan/tools/cmake/cmakedeps/cmakedeps.py | 8 -------- conan/tools/cmake/toolchain.py | 2 +- .../toolchains/cmake/test_cmaketoolchain.py | 14 ++++++++++---- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/conan/tools/cmake/cmakedeps/cmakedeps.py b/conan/tools/cmake/cmakedeps/cmakedeps.py index 0b2d79dc3c8..2e26b657c5a 100644 --- a/conan/tools/cmake/cmakedeps/cmakedeps.py +++ b/conan/tools/cmake/cmakedeps/cmakedeps.py @@ -33,9 +33,6 @@ def __init__(self, conanfile): # a suffix. It is necessary in case of same require and build_require and will cause an error self.build_context_suffix = {} - # Patterns to be excluded - self.excluded_requirement_patterns = [] - check_using_build_profile(self._conanfile) # Enable/Disable checking if a component target exists or not @@ -84,11 +81,6 @@ def content(self): if dep.is_build_context and dep.ref.name not in self.build_context_activated: continue - # Skip from the consumer - if any(fnmatch(str(dep.ref), pattern) for pattern in self.excluded_requirement_patterns): - self._conanfile.output.info("CMakeDeps: Skipped '{}'".format(dep.ref)) - continue - 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 diff --git a/conan/tools/cmake/toolchain.py b/conan/tools/cmake/toolchain.py index 74738acfbe9..6fccf4719f2 100644 --- a/conan/tools/cmake/toolchain.py +++ b/conan/tools/cmake/toolchain.py @@ -731,7 +731,7 @@ def __init__(self, conanfile, generator=None): ("shared", SharedLibBock)]) # Set the CMAKE_MODULE_PATH and CMAKE_PREFIX_PATH to the deps .builddirs - self.find_builddirs = False + self.find_builddirs = True check_using_build_profile(self._conanfile) diff --git a/conans/test/integration/toolchains/cmake/test_cmaketoolchain.py b/conans/test/integration/toolchains/cmake/test_cmaketoolchain.py index a4646aaf553..95aec16afea 100644 --- a/conans/test/integration/toolchains/cmake/test_cmaketoolchain.py +++ b/conans/test/integration/toolchains/cmake/test_cmaketoolchain.py @@ -193,7 +193,7 @@ def test_cross_build_conf(): assert "set(CMAKE_SYSTEM_PROCESSOR myarm)" in toolchain -@pytest.mark.parametrize("find_builddir", [True, False]) +@pytest.mark.parametrize("find_builddir", [True, False, None]) def test_find_builddirs(find_builddir): client = TestClient() conanfile = textwrap.dedent(""" @@ -223,12 +223,18 @@ class Conan(ConanFile): def generate(self): cmake = CMakeToolchain(self) - cmake.find_builddirs = {} + {} cmake.generate() - """).format(str(find_builddir)) + """) + + if find_builddir is not None: + conanfile = conanfile.format('cmake.find_builddirs = {}'.format(str(find_builddir))) client.save({"conanfile.py": conanfile}) client.run("install . ") with open(os.path.join(client.current_folder, "conan_toolchain.cmake")) as f: contents = f.read() - assert ("/path/to/builddir" in contents) == find_builddir + if find_builddir is True or find_builddir is None: + assert "/path/to/builddir" in contents + else: + assert "/path/to/builddir" not in contents From ab102df9c5fd920d95d5548c45ee800d747fedc1 Mon Sep 17 00:00:00 2001 From: Luis Date: Tue, 31 Aug 2021 13:03:46 +0200 Subject: [PATCH 10/10] findpackagehandlestardardargs --- conan/tools/cmake/cmakedeps/templates/config.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/conan/tools/cmake/cmakedeps/templates/config.py b/conan/tools/cmake/cmakedeps/templates/config.py index d631ba6ca19..22df2cb01f4 100644 --- a/conan/tools/cmake/cmakedeps/templates/config.py +++ b/conan/tools/cmake/cmakedeps/templates/config.py @@ -26,7 +26,9 @@ def filename(self): def context(self): targets_include = "" if not self.find_module_mode else "module-" targets_include += "{}Targets.cmake".format(self.file_name) - return {"file_name": 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, @@ -38,11 +40,23 @@ 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}/{{ targets_include_file }}) include(CMakeFindDependencyMacro)