Skip to content
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

Extended cpp_info model object to new components concept #5242

Closed
wants to merge 52 commits into from
Closed
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
e743bd2
Added tests and first POC component class with recursive libs retrieval
danimtb May 20, 2019
7073291
Removed ordered dict class and moved to component class
danimtb May 21, 2019
66289a7
Propagate lib and exe to cpp_info and maintain current behavior or libs
danimtb May 21, 2019
07f90a7
Removed multilevel components and recursivity
danimtb May 21, 2019
25a2776
implemented inheritance of includedir directory
danimtb May 21, 2019
a100bc2
comment
danimtb May 22, 2019
9643fea
Merge branch 'develop' into feature/5090
danimtb May 28, 2019
e43b442
Directory propagation and integration test
danimtb May 28, 2019
14dc504
Introduced DirList and completed tests
danimtb May 28, 2019
b00a93c
small changes for paths
danimtb May 29, 2019
06e2117
Merge branch 'develop' into feature/5090
danimtb Jun 13, 2019
9b53334
Removed DirList and added system_deps
danimtb Jun 13, 2019
61981a2
fix exes
danimtb Jun 14, 2019
5509638
fixed deprecation warning in tests
danimtb Jun 17, 2019
f6df365
Fixed cpp_info libs value in json output
danimtb Jun 17, 2019
e88b6f1
fix test
danimtb Jun 17, 2019
6c8bd40
Add description to _get_paths()
danimtb Jun 19, 2019
831172c
removed prints
danimtb Jun 19, 2019
058ed4e
Add exes test
danimtb Jun 19, 2019
0d9ba39
Test wrong cpp_info raises on create
danimtb Jun 19, 2019
db8b873
Added system_deps test
danimtb Jun 19, 2019
b098c12
Adds complete test
danimtb Jun 19, 2019
6ab1bdb
Add system_deps global behavior
danimtb Jun 19, 2019
2f82336
Merge branch 'develop' into feature/5090
danimtb Jun 19, 2019
d1d2ffc
Indent text
danimtb Jun 20, 2019
be74c0d
Review and move libs properties. Use function to get components sorted
danimtb Jun 20, 2019
1e6e1fd
Renamed .deps to .components
danimtb Jun 20, 2019
6a2e2f7
simplified get paths
danimtb Jun 20, 2019
b32e3b9
Fix link order and added test
danimtb Jun 20, 2019
4bb2db2
Update conans/test/unittests/model/build_info_test.py
danimtb Jun 20, 2019
4b49026
Update conans/test/unittests/model/build_info_test.py
danimtb Jun 20, 2019
62a9782
Update conans/test/integration/package_info_test.py
danimtb Jun 20, 2019
b756503
Update conans/test/integration/package_info_test.py
danimtb Jun 20, 2019
aef1a51
asserts
danimtb Jun 20, 2019
b085211
fix path repetition
danimtb Jun 20, 2019
ebd80e3
remove endline
danimtb Jun 20, 2019
63f8235
move default values dict
danimtb Jun 20, 2019
2c3e3d7
Use is none
danimtb Jun 20, 2019
c01ba50
Completed test
danimtb Jun 21, 2019
d150060
Added deps_cpp_info.system_deps global
danimtb Jun 21, 2019
f61e2d9
simplified link order property, added test and fixed other ones
danimtb Jun 21, 2019
0dbb943
get paths in order too
danimtb Jun 21, 2019
c3da770
docstring
danimtb Jun 21, 2019
195665c
improve system_deps test
danimtb Jun 21, 2019
ff6fca3
merge methods
danimtb Jun 21, 2019
1c95d9b
Added components to the cpp_info json output
danimtb Jun 21, 2019
6cdda0f
loop check with test
danimtb Jun 24, 2019
93f6c8d
5090 fixes
lasote Jun 25, 2019
5f5f172
Merge pull request #4 from lasote/feature/5090_fixes
danimtb Jun 25, 2019
1036bd8
improved message
danimtb Jun 25, 2019
9696a4a
remove cppflags from component class
danimtb Jun 25, 2019
6fae2f8
Merge branch 'develop' into feature/5090
danimtb Jun 27, 2019
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
5 changes: 3 additions & 2 deletions conans/client/recorder/action_recorder.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
def _cpp_info_to_dict(cpp_info):
doc = {}
for it, value in vars(cpp_info).items():
if it.startswith("_") or not value:
if (it.startswith("_") and it != "_libs") or not value:
continue

if it == "configs":
Expand All @@ -35,7 +35,8 @@ def _cpp_info_to_dict(cpp_info):
doc["configs"] = configs_data
continue

doc[it] = value
key = "libs" if it == "_libs" else it
doc[key] = value
return doc


Expand Down
278 changes: 253 additions & 25 deletions conans/model/build_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@

import deprecation

from conans.errors import ConanException

DEFAULT_INCLUDE = "include"
DEFAULT_LIB = "lib"
DEFAULT_BIN = "bin"
DEFAULT_RES = "res"
DEFAULT_SHARE = "share"
DEFAULT_BUILD = ""


class _CppInfo(object):
Expand All @@ -16,14 +19,17 @@ class _CppInfo(object):
specific systems will be produced from this info
"""
def __init__(self):
self.name = None
self._system_deps = []
self.includedirs = [] # Ordered list of include paths
self.srcdirs = [] # Ordered list of source paths
self.libdirs = [] # Directories to find libraries
self.resdirs = [] # Directories to find resources, data, etc
self.bindirs = [] # Directories to find executables and shared libs
self.builddirs = []
self.rootpaths = []
self.libs = [] # The libs to link against
self._libs = [] # The libs to link against
self._exes = []
self.defines = [] # preprocessor definitions
self.cflags = [] # pure C flags
self.cxxflags = [] # C++ compilation flags
Expand All @@ -41,49 +47,158 @@ def __init__(self):
self.description = None # Description of the conan package
# When package is editable, filter_empty=False, so empty dirs are maintained
self.filter_empty = True
self._components = OrderedDict()

@property
def _sorted_components(self):
"""
Sorted components from less dependent to the most one
:return: ordered list of components
"""
# Sort first elements with less items in .deps attribute
comps = sorted(self._components.values(), key=lambda component: len(component.deps))
# Save name of unsorted elements
unsorted_names = [comp.name for comp in comps]

sorted_comps = []
while unsorted_names:
for comp in comps:
# If element is already sorted, continue
if comp.name not in unsorted_names:
continue
# If element does not have deps or all of its deps are already sorted, sort this
# element and remove it from the unsorted list
elif not comp.deps or not [dep for dep in comp.deps if dep in unsorted_names]:
sorted_comps.append(comp)
unsorted_names.remove(comp.name)
return sorted_comps

@property
def libs(self):
if self._components:
result = []
for component in self._sorted_components:
for sys_dep in component.system_deps:
if sys_dep and sys_dep not in result:
result.append(sys_dep)
if component.lib:
result.append(component.lib)
return result
else:
return self._libs

@libs.setter
def libs(self, libs):
assert isinstance(libs, list), "'libs' attribute should be a list of strings"
if self._components:
raise ConanException("Setting first level libs is not supported when Components are "
"already in use")
self._libs = libs

@property
def exes(self):
if self._components:
return [component.exe for component in self._sorted_components if component.exe]
else:
return self._exes

@exes.setter
def exes(self, exes):
assert isinstance(exes, list), "'exes' attribute should be a list of strings"
if self._components:
raise ConanException("Setting first level exes is not supported when Components are "
"already in use")
self._exes = exes

@property
def system_deps(self):
if self._components:
result = []
for component in self._sorted_components:
if component.system_deps:
for system_dep in component.system_deps:
if system_dep and system_dep not in result:
result.append(system_dep)
return result
else:
return self._system_deps

@system_deps.setter
def system_deps(self, system_deps):
assert isinstance(system_deps, list), "'system_deps' attribute should be a list of strings"
if self._components:
raise ConanException("Setting first level system_deps is not supported when Components "
"are already in use")
self._system_deps = system_deps

def __getitem__(self, key):
if self._libs:
raise ConanException("Usage of Components with '.libs' values is not allowed")
if key not in self._components.keys():
self._components[key] = Component(self, key)
return self._components[key]

def _filter_paths(self, paths):
abs_paths = [os.path.join(self.rootpath, p)
if not os.path.isabs(p) else p for p in paths]
abs_paths = [os.path.join(self.rootpath, p) for p in paths]
if self.filter_empty:
return [p for p in abs_paths if os.path.isdir(p)]
else:
return abs_paths

def _get_paths(self, path_name):
"""
Get the absolute paths either composing the lists from components or from the global
variables. Also filter the values checking if the folders exist or not and avoid repeated
values.
:param path_name: name of the path variable to get (include_paths, res_paths...)
:return: List of absolute paths
"""
result = []

if self._components:
for dep_value in self._components.values():
abs_paths = self._filter_paths(getattr(dep_value, "%s_paths" % path_name))
for path in abs_paths:
if path not in result:
result.append(path)
else:
result = self._filter_paths(getattr(self, "%sdirs" % path_name))
return result

@property
def include_paths(self):
if self._include_paths is None:
self._include_paths = self._filter_paths(self.includedirs)
if not self._include_paths:
self._include_paths = self._get_paths("include")
return self._include_paths

@property
def lib_paths(self):
if self._lib_paths is None:
self._lib_paths = self._filter_paths(self.libdirs)
if not self._lib_paths:
self._lib_paths = self._get_paths("lib")
return self._lib_paths

@property
def src_paths(self):
if self._src_paths is None:
self._src_paths = self._filter_paths(self.srcdirs)
if not self._src_paths:
self._src_paths = self._get_paths("src")
return self._src_paths

@property
def bin_paths(self):
if self._bin_paths is None:
self._bin_paths = self._filter_paths(self.bindirs)
if not self._bin_paths:
self._bin_paths = self._get_paths("bin")
return self._bin_paths

@property
def build_paths(self):
if self._build_paths is None:
self._build_paths = self._filter_paths(self.builddirs)
if not self._build_paths:
self._build_paths = self._get_paths("build")
return self._build_paths

@property
def res_paths(self):
if self._res_paths is None:
self._res_paths = self._filter_paths(self.resdirs)
if not self._res_paths:
self._res_paths = self._get_paths("res")
return self._res_paths

# Compatibility for 'cppflags' (old style property to allow decoration)
Expand All @@ -107,32 +222,143 @@ class CppInfo(_CppInfo):
def __init__(self, root_folder):
super(CppInfo, self).__init__()
self.rootpath = root_folder # the full path of the package in which the conans is found
self.includedirs.append(DEFAULT_INCLUDE)
self.libdirs.append(DEFAULT_LIB)
self.bindirs.append(DEFAULT_BIN)
self.resdirs.append(DEFAULT_RES)
self.builddirs.append("")
self._default_dirs_values = {
"includedirs": [DEFAULT_INCLUDE],
"libdirs": [DEFAULT_LIB],
"bindirs": [DEFAULT_BIN],
"resdirs": [DEFAULT_RES],
"builddirs": [DEFAULT_BUILD],
"srcdirs": []
}
self.includedirs.extend(self._default_dirs_values["includedirs"])
self.libdirs.extend(self._default_dirs_values["libdirs"])
self.bindirs.extend(self._default_dirs_values["bindirs"])
self.resdirs.extend(self._default_dirs_values["resdirs"])
self.builddirs.extend(self._default_dirs_values["builddirs"])
# public_deps is needed to accumulate list of deps for cmake targets
self.public_deps = []
self.configs = {}

def _check_dirs_values(self):
msg_template = "Using Components and global '{}' values ('{}') is not supported"
for dir_name in self._default_dirs_values:
dirs_value = getattr(self, dir_name)
if dirs_value is not None and dirs_value != self._default_dirs_values[dir_name]:
raise ConanException(msg_template.format(dir_name, dirs_value))

def _clear_dirs_values(self):
for dir_name in self._default_dirs_values:
if getattr(self, dir_name) == self._default_dirs_values[dir_name]:
self.__dict__[dir_name] = None

def __getitem__(self, key):
if self._libs or self._exes:
raise ConanException("Usage of Components with '.libs' or '.exes' values is not allowed")
self._clear_dirs_values()
self._check_dirs_values()
if key not in self._components:
self._components[key] = Component(key, self.rootpath)
return self._components[key]

@property
def components(self):
return self._components

def __getattr__(self, config):

def _get_cpp_info():
result = _CppInfo()
result.rootpath = self.rootpath
result.sysroot = self.sysroot
result.includedirs.append(DEFAULT_INCLUDE)
result.libdirs.append(DEFAULT_LIB)
result.bindirs.append(DEFAULT_BIN)
result.resdirs.append(DEFAULT_RES)
result.builddirs.append("")
result.includedirs.extend(self._default_dirs_values["includedirs"])
result.libdirs.extend(self._default_dirs_values["libdirs"])
result.bindirs.extend(self._default_dirs_values["bindirs"])
result.resdirs.extend(self._default_dirs_values["resdirs"])
result.builddirs.extend(self._default_dirs_values["builddirs"])
return result

return self.configs.setdefault(config, _get_cpp_info())


class Component(object):

def __init__(self, name, root_folder):
self._rootpath = root_folder
self.name = name
self.deps = []
self._lib = None
self._exe = None
self.system_deps = []
self.includedirs = [DEFAULT_INCLUDE]
self.libdirs = [DEFAULT_LIB]
self.resdirs = [DEFAULT_RES]
self.bindirs = [DEFAULT_BIN]
self.builddirs = [DEFAULT_BUILD]
self.srcdirs = []
self.defines = []
self.cflags = []
self.cppflags = []
self.cxxflags = []
self.sharedlinkflags = []
self.exelinkflags = []
self._filter_empty = True

def _filter_paths(self, paths):
abs_paths = [os.path.join(self._rootpath, p) for p in paths]
if self._filter_empty:
return [p for p in abs_paths if os.path.isdir(p)]
else:
return abs_paths

@property
def lib(self):
return self._lib

@lib.setter
def lib(self, name):
assert isinstance(name, str), "'lib' attribute should be a string"
if self._exe:
raise ConanException("'.exe' is already set for this Component")
self._lib = name

@property
def exe(self):
return self._exe

@exe.setter
def exe(self, name):
assert isinstance(name, str), "'exe' attribute should be a string"
if self._lib:
raise ConanException("'.lib' is already set for this Component")
self._exe = name

@property
def include_paths(self):
return self._filter_paths(self.includedirs)

@property
def lib_paths(self):
return self._filter_paths(self.libdirs)

@property
def bin_paths(self):
return self._filter_paths(self.bindirs)

@property
def build_paths(self):
return self._filter_paths(self.builddirs)

@property
def res_paths(self):
return self._filter_paths(self.resdirs)

@property
def src_paths(self):
return self._filter_paths(self.srcdirs)


class _BaseDepsCppInfo(_CppInfo):

def __init__(self):
super(_BaseDepsCppInfo, self).__init__()

Expand All @@ -148,6 +374,7 @@ def merge_lists(seq1, seq2):
self.resdirs = merge_lists(self.resdirs, dep_cpp_info.res_paths)
self.builddirs = merge_lists(self.builddirs, dep_cpp_info.build_paths)
self.libs = merge_lists(self.libs, dep_cpp_info.libs)
self.exes = merge_lists(self.exes, dep_cpp_info.exes)
self.rootpaths.append(dep_cpp_info.rootpath)

# Note these are in reverse order
Expand Down Expand Up @@ -222,3 +449,4 @@ def update_deps_cpp_info(self, dep_cpp_info):
assert isinstance(dep_cpp_info, DepsCppInfo)
for pkg_name, cpp_info in dep_cpp_info.dependencies:
self.update(cpp_info, pkg_name)

Loading