Skip to content

Commit

Permalink
Specify build modules by the generator in cpp_info (#8232)
Browse files Browse the repository at this point in the history
* build_modules specific for generator

* use str defaultdict

* remove asserts

* fix merge

* fix components

* add generator_name

* minor fixes

* unify merge_dicts

* Allow old list behavior for cmake build modules

* test build modules old behavior

* fix import

* Update conans/model/build_info.py

Co-authored-by: James <james@conan.io>

* review

* test class

* fix imports

* use custom_message()

* fix

* Update conans/model/build_info.py

Co-authored-by: Javier G. Sogo <jgsogo@gmail.com>

* Add conan v2 mode warnings

* typo

* use list

* fix import

Co-authored-by: James <james@conan.io>
Co-authored-by: Javier G. Sogo <jgsogo@gmail.com>
  • Loading branch information
3 people committed Jan 11, 2021
1 parent f185fac commit 074b652
Show file tree
Hide file tree
Showing 11 changed files with 209 additions and 52 deletions.
20 changes: 10 additions & 10 deletions conans/client/generators/cmake.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,12 @@


class DepsCppCmake(object):
def __init__(self, cpp_info):
def __init__(self, cpp_info, generator_name):
def join_paths(paths):
"""
Paths are doubled quoted, and escaped (but spaces)
e.g: set(LIBFOO_INCLUDE_DIRS "/path/to/included/dir" "/path/to/included/dir2")
"""

return "\n\t\t\t".join('"%s"'
% p.replace('\\', '/').replace('$', '\\$').replace('"', '\\"')
for p in paths)
Expand Down Expand Up @@ -63,11 +62,12 @@ def join_paths_single_var(values):
self.exelinkflags_list = join_flags(";", cpp_info.exelinkflags)

self.rootpath = join_paths([cpp_info.rootpath])
self.build_modules_paths = join_paths([path for path in cpp_info.build_modules_paths if
path.endswith(".cmake")])
self.build_modules_paths = join_paths(cpp_info.build_modules_paths.get(generator_name, []))


class CMakeGenerator(Generator):
name = "cmake"

@property
def filename(self):
return BUILD_INFO_CMAKE
Expand All @@ -79,13 +79,13 @@ def content(self):

# Per requirement variables
for _, dep_cpp_info in self.deps_build_info.dependencies:
dep_name = dep_cpp_info.get_name("cmake")
deps = DepsCppCmake(dep_cpp_info)
dep_name = dep_cpp_info.get_name(self.name)
deps = DepsCppCmake(dep_cpp_info, self.name)
dep_flags = cmake_dependency_vars(dep_name, deps=deps)
sections.append(dep_flags)

for config, cpp_info in dep_cpp_info.configs.items():
deps = DepsCppCmake(cpp_info)
deps = DepsCppCmake(cpp_info, self.name)
dep_flags = cmake_dependency_vars(dep_name, deps=deps, build_type=config)
sections.append(dep_flags)

Expand All @@ -96,17 +96,17 @@ def content(self):
sections.append(cmake_settings_info(self.conanfile.settings))
all_flags = cmake_dependencies(dependencies=self.deps_build_info.deps)
sections.append(all_flags)
deps = DepsCppCmake(self.deps_build_info)
deps = DepsCppCmake(self.deps_build_info, self.name)
all_flags = cmake_global_vars(deps=deps)
sections.append(all_flags)

for config, cpp_info in self.deps_build_info.configs.items():
deps = DepsCppCmake(cpp_info)
deps = DepsCppCmake(cpp_info, self.name)
dep_flags = cmake_global_vars(deps=deps, build_type=config)
sections.append(dep_flags)

# TARGETS
sections.extend(generate_targets_section(self.deps_build_info.dependencies, "cmake"))
sections.extend(generate_targets_section(self.deps_build_info.dependencies, self.name))

# MACROS
sections.append(cmake_macros)
Expand Down
6 changes: 3 additions & 3 deletions conans/client/generators/cmake_find_package.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ def _get_components(self, pkg_name, cpp_info):
components = super(CMakeFindPackageGenerator, self)._get_components(pkg_name, cpp_info)
ret = []
for comp_genname, comp, comp_requires_gennames in components:
deps_cpp_cmake = DepsCppCmake(comp)
deps_cpp_cmake = DepsCppCmake(comp, self.name)
deps_cpp_cmake.public_deps = " ".join(
["{}::{}".format(*it) for it in comp_requires_gennames])
ret.append((comp_genname, deps_cpp_cmake))
Expand All @@ -253,7 +253,7 @@ def _find_for_dep(self, pkg_name, pkg_findname, pkg_filename, cpp_info):
# Note these are in reversed order, from more dependent to less dependent
pkg_components = " ".join(["{p}::{c}".format(p=pkg_findname, c=comp_findname) for
comp_findname, _ in reversed(components)])
pkg_info = DepsCppCmake(cpp_info)
pkg_info = DepsCppCmake(cpp_info, self.name)
global_target_variables = target_template.format(name=pkg_findname, deps=pkg_info,
build_type_suffix="",
deps_names=deps_names)
Expand Down Expand Up @@ -283,7 +283,7 @@ def _find_for_dep(self, pkg_name, pkg_findname, pkg_filename, cpp_info):
dep_cpp_info = extend(dep_cpp_info, build_type.lower())

# The find_libraries_block, all variables for the package, and creation of targets
deps = DepsCppCmake(dep_cpp_info)
deps = DepsCppCmake(dep_cpp_info, self.name)
find_libraries_block = target_template.format(name=pkg_findname, deps=deps,
build_type_suffix="",
deps_names=deps_names)
Expand Down
4 changes: 2 additions & 2 deletions conans/client/generators/cmake_find_package_multi.py
Original file line number Diff line number Diff line change
Expand Up @@ -309,14 +309,14 @@ def content(self):

# If any config matches the build_type one, add it to the cpp_info
dep_cpp_info = extend(cpp_info, build_type.lower())
deps = DepsCppCmake(dep_cpp_info)
deps = DepsCppCmake(dep_cpp_info, self.name)
find_lib = target_template.format(name=pkg_findname, deps=deps,
build_type_suffix=build_type_suffix,
deps_names=deps_names)
ret["{}Target-{}.cmake".format(pkg_filename, self.configuration.lower())] = find_lib
else:
cpp_info = extend(cpp_info, build_type.lower())
pkg_info = DepsCppCmake(cpp_info)
pkg_info = DepsCppCmake(cpp_info, self.name)
components = self._get_components(pkg_name, cpp_info)
# Note these are in reversed order, from more dependent to less dependent
pkg_components = " ".join(["{p}::{c}".format(p=pkg_findname, c=comp_findname) for
Expand Down
14 changes: 8 additions & 6 deletions conans/client/generators/cmake_multi.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
cmake_package_info, cmake_user_info_vars,
generate_targets_section, CMakeCommonMacros)
from conans.model import Generator
from conans.model.build_info import CppInfo
from conans.model.build_info import CppInfo, merge_dicts


def extend(cpp_info, config):
Expand All @@ -14,6 +14,7 @@ def extend(cpp_info, config):
if config_info:
def add_lists(seq1, seq2):
return seq1 + [s for s in seq2 if s not in seq1]

result = CppInfo(str(config_info), config_info.rootpath)
result.filter_empty = cpp_info.filter_empty
result.includedirs = add_lists(cpp_info.includedirs, config_info.includedirs)
Expand All @@ -28,12 +29,13 @@ def add_lists(seq1, seq2):
result.sharedlinkflags = cpp_info.sharedlinkflags + config_info.sharedlinkflags
result.exelinkflags = cpp_info.exelinkflags + config_info.exelinkflags
result.system_libs = add_lists(cpp_info.system_libs, config_info.system_libs)
result.build_modules = add_lists(cpp_info.build_modules, config_info.build_modules)
result.build_modules = merge_dicts(cpp_info.build_modules, config_info.build_modules)
return result
return cpp_info


class CMakeMultiGenerator(Generator):
name = "cmake_multi"

@property
def content(self):
Expand All @@ -51,10 +53,10 @@ def _content_type(self, build_type):

# Per requirement variables
for _, dep_cpp_info in self.deps_build_info.dependencies:
dep_name = dep_cpp_info.get_name("cmake_multi")
dep_name = dep_cpp_info.get_name(self.name)
# Only the specific of the build_type
dep_cpp_info = extend(dep_cpp_info, build_type)
deps = DepsCppCmake(dep_cpp_info)
deps = DepsCppCmake(dep_cpp_info, self.name)
dep_flags = cmake_dependency_vars(dep_name, deps=deps, build_type=build_type)
sections.append(dep_flags)

Expand All @@ -65,7 +67,7 @@ def _content_type(self, build_type):
sections.append(all_flags)

dep_cpp_info = extend(self.deps_build_info, build_type)
deps = DepsCppCmake(dep_cpp_info)
deps = DepsCppCmake(dep_cpp_info, self.name)
all_flags = cmake_global_vars(deps=deps, build_type=build_type)
sections.append(all_flags)

Expand All @@ -84,7 +86,7 @@ def _content_multi(self):
version=self.conanfile.version))

# TARGETS
sections.extend(generate_targets_section(self.deps_build_info.dependencies, "cmake_multi"))
sections.extend(generate_targets_section(self.deps_build_info.dependencies, self.name))
# MACROS
sections.append(cmake_macros_multi)

Expand Down
8 changes: 5 additions & 3 deletions conans/client/generators/cmake_paths.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@


class CMakePathsGenerator(Generator):
name = "cmake_paths"

@property
def filename(self):
Expand All @@ -15,16 +16,17 @@ def content(self):
# in a package could have been "patched" with the `cmake.patch_config_paths()`
# replacing absolute paths with CONAN_XXX_ROOT variables.
for _, dep_cpp_info in self.deps_build_info.dependencies:
var_name = "CONAN_{}_ROOT".format(dep_cpp_info.get_name("cmake_paths").upper())
lines.append('set({} {})'.format(var_name, DepsCppCmake(dep_cpp_info).rootpath))
var_name = "CONAN_{}_ROOT".format(dep_cpp_info.get_name(self.name).upper())
lines.append('set({} {})'.format(var_name, DepsCppCmake(dep_cpp_info,
self.name).rootpath))

# We want to prioritize the FindXXX.cmake files:
# 1. First the files found in the packages
# 2. The previously set (by default CMAKE_MODULE_PATH is empty)
# 3. The "install_folder" ones, in case there is no FindXXX.cmake, try with the install dir
# if the user used the "cmake_find_package" will find the auto-generated
# 4. The CMake installation dir/Modules ones.
deps = DepsCppCmake(self.deps_build_info)
deps = DepsCppCmake(self.deps_build_info, self.name)
lines.append("set(CMAKE_MODULE_PATH {deps.build_paths} ${{CMAKE_MODULE_PATH}} "
"${{CMAKE_CURRENT_LIST_DIR}})".format(deps=deps))
lines.append("set(CMAKE_PREFIX_PATH {deps.build_paths} ${{CMAKE_PREFIX_PATH}} "
Expand Down
80 changes: 74 additions & 6 deletions conans/model/build_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,62 @@ def __copy__(self):
return the_copy


class BuildModulesDict(dict):
"""
A dictionary with append and extend for cmake build modules to keep it backwards compatible
with the list interface
"""

def __getitem__(self, key):
if key not in self.keys():
super(BuildModulesDict, self).__setitem__(key, list())
return super(BuildModulesDict, self).__getitem__(key)

def _append(self, item):
if item.endswith(".cmake"):
self["cmake"].append(item)
self["cmake_multi"].append(item)
self["cmake_find_package"].append(item)
self["cmake_find_package_multi"].append(item)

def append(self, item):
conan_v2_behavior("Use 'self.cpp_info.build_modules[\"<generator>\"].append(\"{item}\")' "
'instead'.format(item=item))
self._append(item)

def extend(self, items):
conan_v2_behavior("Use 'self.cpp_info.build_modules[\"<generator>\"].extend({items})' "
"instead".format(items=items))
for item in items:
self._append(item)

@classmethod
def from_list(cls, build_modules):
the_dict = BuildModulesDict()
the_dict.extend(build_modules)
return the_dict


def dict_to_abs_paths(the_dict, rootpath):
new_dict = {}
for generator, values in the_dict.items():
new_dict[generator] = [os.path.join(rootpath, p) if not os.path.isabs(p) else p
for p in values]
return new_dict


def merge_dicts(d1, d2):
def merge_lists(seq1, seq2):
return [s for s in seq1 if s not in seq2] + seq2
result = d1.copy()
for k, v in d2.items():
if k not in d1.keys():
result[k] = v
else:
result[k] = merge_lists(d1[k], d2[k])
return result


class _CppInfo(object):
""" Object that stores all the necessary information to build in C/C++.
It is intended to be system independent, translation to
Expand All @@ -60,7 +116,7 @@ def __init__(self):
self.cxxflags = [] # C++ compilation flags
self.sharedlinkflags = [] # linker flags
self.exelinkflags = [] # linker flags
self.build_modules = []
self.build_modules = BuildModulesDict() # FIXME: This should be just a plain dict
self.filenames = {} # name of filename to create for various generators
self.rootpath = ""
self.sysroot = ""
Expand Down Expand Up @@ -89,8 +145,12 @@ def _filter_paths(self, paths):
@property
def build_modules_paths(self):
if self._build_modules_paths is None:
self._build_modules_paths = [os.path.join(self.rootpath, p) if not os.path.isabs(p)
else p for p in self.build_modules]
if isinstance(self.build_modules, list): # FIXME: This should be just a plain dict
conan_v2_behavior("Use 'self.cpp_info.build_modules[\"<generator>\"] = "
"{the_list}' instead".format(the_list=self.build_modules))
self.build_modules = BuildModulesDict.from_list(self.build_modules)
tmp = dict_to_abs_paths(BuildModulesDict(self.build_modules), self.rootpath)
self._build_modules_paths = tmp
return self._build_modules_paths

@property
Expand Down Expand Up @@ -320,6 +380,7 @@ def __init__(self):
super(_BaseDepsCppInfo, self).__init__()

def update(self, dep_cpp_info):

def merge_lists(seq1, seq2):
return [s for s in seq1 if s not in seq2] + seq2

Expand All @@ -333,7 +394,7 @@ def merge_lists(seq1, seq2):
self.frameworkdirs = merge_lists(self.frameworkdirs, dep_cpp_info.framework_paths)
self.libs = merge_lists(self.libs, dep_cpp_info.libs)
self.frameworks = merge_lists(self.frameworks, dep_cpp_info.frameworks)
self.build_modules = merge_lists(self.build_modules, dep_cpp_info.build_modules_paths)
self.build_modules = merge_dicts(self.build_modules, dep_cpp_info.build_modules_paths)
self.requires = merge_lists(self.requires, dep_cpp_info.requires)
self.rootpaths.append(dep_cpp_info.rootpath)

Expand Down Expand Up @@ -401,7 +462,7 @@ def __init__(self, cpp_info):
self._res_paths = None
self._src_paths = None
self._framework_paths = None
self._build_module_paths = None
self._build_modules_paths = None
self._sorted_components = None
self._check_component_requires()

Expand Down Expand Up @@ -488,7 +549,14 @@ def _get_sorted_components(self):

@property
def build_modules_paths(self):
return self._aggregated_paths("build_modules")
if self._build_modules_paths is not None:
return self._build_modules_paths
paths = self._cpp_info.build_modules_paths
if self._cpp_info.components:
for component in self._get_sorted_components().values():
paths = merge_dicts(paths, component.build_modules_paths)
self._build_modules_paths = paths
return self._build_modules_paths

@property
def include_paths(self):
Expand Down
4 changes: 2 additions & 2 deletions conans/test/functional/generators/cmake_find_package_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,7 @@ def package_info(self):
""")
# This is a module that defines some functionality
find_module = textwrap.dedent("""
function(conan_message MESSAGE_OUTPUT)
function(custom_message MESSAGE_OUTPUT)
message(${ARGV${0}})
endfunction()
""")
Expand Down Expand Up @@ -422,7 +422,7 @@ def build(self):
cmake_minimum_required(VERSION 3.0)
project(test)
find_package(test)
conan_message("Printing using a external module!")
custom_message("Printing using a external module!")
""")
client.save({"conanfile.py": consumer, "CMakeLists.txt": cmakelists})
client.run("create .")
Expand Down

0 comments on commit 074b652

Please sign in to comment.