-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Cmake find mode copy provided #18816
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop2
Are you sure you want to change the base?
Changes from all commits
519b681
bd3b1ab
1b25809
e190bf8
7c12eda
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,18 @@ | ||
import os | ||
import re | ||
import shutil | ||
import textwrap | ||
|
||
from jinja2 import Template | ||
|
||
from conan.api.output import Color, ConanOutput | ||
from conan.errors import ConanException | ||
from conan.internal import check_duplicated_generator | ||
from conan.internal.api.install.generators import relativize_path | ||
from conan.internal.model.dependencies import get_transitive_requires | ||
from conan.internal.model.conanfile_interface import ConanFileInterface | ||
from conan.internal.model.conan_file import ConanFile | ||
from conan.internal.model.cpp_info import CppInfo | ||
from conan.internal.model.requires import Requirement | ||
from conan.tools.cmake.cmakedeps2.config import ConfigTemplate2 | ||
from conan.tools.cmake.cmakedeps2.config_version import ConfigVersionTemplate2 | ||
from conan.tools.cmake.cmakedeps2.target_configuration import TargetConfigurationTemplate2 | ||
|
@@ -20,6 +24,7 @@ | |
FIND_MODE_CONFIG = "config" | ||
FIND_MODE_NONE = "none" | ||
FIND_MODE_BOTH = "both" | ||
FIND_MODE_COPY = "copy" | ||
|
||
|
||
class CMakeDeps2: | ||
|
@@ -68,19 +73,62 @@ def _content(self): | |
|
||
if require.direct: | ||
direct_deps.append((require, dep)) | ||
config = ConfigTemplate2(self, dep) | ||
ret[config.filename] = config.content() | ||
config_version = ConfigVersionTemplate2(self, dep) | ||
ret[config_version.filename] = config_version.content() | ||
|
||
targets = TargetsTemplate2(self, dep) | ||
ret[targets.filename] = targets.content() | ||
target_configuration = TargetConfigurationTemplate2(self, dep, require) | ||
ret[target_configuration.filename] = target_configuration.content() | ||
if cmake_find_mode == FIND_MODE_COPY: | ||
ConanOutput(self._conanfile.ref).info("Copying project provided CMake configuration files...") | ||
config_filename = ConfigTemplate2(self, dep).filename | ||
version_filename = ConfigVersionTemplate2(self, dep).filename | ||
targets_filename = TargetsTemplate2(self, dep).filename | ||
target_configuration_filename = self._project_provided_target_configuration_filename(dep) | ||
# The version and config files are the ones that matter and are typically different when provided by the project. | ||
ret[config_filename] = self._read_project_provided_cmake_file(config_filename, dep) | ||
ret[version_filename] = self._read_project_provided_cmake_file(version_filename, dep) | ||
ret[targets_filename] = self._read_project_provided_cmake_file(targets_filename, dep) | ||
ret[target_configuration_filename] = self._read_project_provided_cmake_file(target_configuration_filename, dep) | ||
deduced_cpp_info : CppInfo = dep.cpp_info.deduce_full_cpp_info(self._conanfile) | ||
# Generate CMake files have their ${IMPORT_PREFIX} tied to the parent folder relative to the location of the file, so we need to copy over our headers and libs | ||
# so that they load correctly. | ||
ConanOutput(self._conanfile.ref).info(f"Installing libs found in {deduced_cpp_info.location} and includes in {deduced_cpp_info.includedirs} to IMPORT_PREFIX location") | ||
lib_folder = os.path.join("..", "lib", self.configuration) | ||
os.makedirs(lib_folder, exist_ok=True) | ||
shutil.copy(deduced_cpp_info.location, lib_folder) | ||
shutil.copytree(deduced_cpp_info.includedir, os.path.join("..", "include"), dirs_exist_ok=True) | ||
# Copy the component libraries. | ||
for name, lib in deduced_cpp_info.components.items(): | ||
shutil.copy(lib.location, lib_folder) | ||
Comment on lines
+90
to
+97
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A generator shouldn't be copying artifacts like headers or libs around, that is more the responsibility of a deployer. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would you recommend moving this somewhere else in the code base, or just not doing this step entirely? This is how I attempted to make the files relocatable using the ${_IMPORT_PREFIX} which points to parent directory of the folder. Another alternative might be to edit this path so that include/lib references for each target point back to the conan package. |
||
else: | ||
config = ConfigTemplate2(self, dep) | ||
ret[config.filename] = config.content() | ||
config_version = ConfigVersionTemplate2(self, dep) | ||
ret[config_version.filename] = config_version.content() | ||
|
||
targets = TargetsTemplate2(self, dep) | ||
ret[targets.filename] = targets.content() | ||
target_configuration = TargetConfigurationTemplate2(self, dep, require) | ||
ret[target_configuration.filename] = target_configuration.content() | ||
|
||
self._print_help(direct_deps) | ||
return ret | ||
|
||
def _project_provided_target_configuration_filename(self, dep): | ||
# This function produces a slightly different Target-<config>.cmake filename | ||
# than the template here. When these files are generated in a project using | ||
# the expected EXPORT keywords, there is no dash between the project name and | ||
# Targets. I will not change this in the conan template class, as that could break | ||
# user's packages. | ||
return f"{self.get_cmake_filename(dep)}Targets-{self.configuration.lower()}.cmake" | ||
|
||
def _read_project_provided_cmake_file(self, cmake_file, dep): | ||
# Just return first hit. There won't be multiple matching files in a given package. | ||
for dirpath, dirnames, filenames in os.walk(dep.package_folder): | ||
for filename in filenames: | ||
if cmake_file in filename: | ||
ConanOutput(self._conanfile.ref).info(f"Match found: {dirpath + filename}") | ||
with open(os.path.join(dirpath, filename), 'r') as file: | ||
return file.read() | ||
|
||
# If we got here, the file was not found. | ||
raise FileNotFoundError(f"The file {cmake_file} was not available to be copied.") | ||
|
||
def _print_help(self, direct_deps): | ||
if direct_deps: | ||
msg = ["CMakeDeps necessary find_package() and targets for your CMakeLists.txt"] | ||
|
@@ -147,7 +195,7 @@ def get_cmake_filename(self, dep, module_mode=None): | |
def _get_find_mode(self, dep): | ||
""" | ||
:param dep: requirement | ||
:return: "none" or "config" or "module" or "both" or "config" when not set | ||
:return: "none" or "config" or "module" or "both" or "config" or "copy" when not set | ||
""" | ||
tmp = self.get_property("cmake_find_mode", dep) | ||
if tmp is None: | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem with this is that the files that projects provide might have different names, the "Targets" files is mostly a convention, but not mandatory. This will not work in many cases, and it is necessary a more general approach
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we provide the ability to pass in the corresponding names of these files then? I tried to use mostly existing code to generate these filenames.
Or just leverage the existing conan variables such as
cmake_file_name
etc?