-
Notifications
You must be signed in to change notification settings - Fork 949
/
new_build_info.py
282 lines (238 loc) · 11.5 KB
/
new_build_info.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
import copy
import os
from collections import OrderedDict
from conans.model.build_info import DefaultOrderedDict, CppInfoDefaultValues
_DIRS_VAR_NAMES = ["includedirs", "srcdirs", "libdirs", "resdirs", "bindirs", "builddirs",
"frameworkdirs", "objects"]
_FIELD_VAR_NAMES = ["system_libs", "frameworks", "libs", "defines", "cflags", "cxxflags",
"sharedlinkflags", "exelinkflags"]
_ALL_NAMES = _DIRS_VAR_NAMES + _FIELD_VAR_NAMES
class _NewComponent(object):
def __init__(self, with_defaults=False):
# ###### PROPERTIES
self._generator_properties = None
# ###### DIRECTORIES
self.includedirs = None # Ordered list of include paths
self.srcdirs = None # Ordered list of source paths
self.libdirs = None # Directories to find libraries
self.resdirs = None # Directories to find resources, data, etc
self.bindirs = None # Directories to find executables and shared libs
self.builddirs = None
self.frameworkdirs = None
# ##### FIELDS
self.system_libs = None # Ordered list of system libraries
self.frameworks = None # Macos .framework
self.libs = None # The libs to link against
self.defines = None # preprocessor definitions
self.cflags = None # pure C flags
self.cxxflags = None # C++ compilation flags
self.sharedlinkflags = None # linker flags
self.exelinkflags = None # linker flags
self.objects = None # objects to link
self.sysroot = None
self.requires = None
if with_defaults:
self.includedirs = ["include"]
self.libdirs = ["lib"]
self.bindirs = ["bin"]
@property
def required_component_names(self):
""" Names of the required components of the same package (not scoped with ::)"""
if self.requires is None:
return []
return [r for r in self.requires if "::" not in r]
def set_property(self, property_name, value):
if self._generator_properties is None:
self._generator_properties = {}
self._generator_properties[property_name] = value
def get_property(self, property_name):
if self._generator_properties is None:
return None
try:
return self._generator_properties[property_name]
except KeyError:
pass
def get_init(self, attribute, default):
item = getattr(self, attribute)
if item is not None:
return item
setattr(self, attribute, default)
return default
class NewCppInfo(object):
def __init__(self, with_defaults=False):
self.components = DefaultOrderedDict(lambda: _NewComponent(with_defaults))
# Main package is a component with None key
self.components[None] = _NewComponent(with_defaults)
self._aggregated = None # A _NewComponent object with all the components aggregated
def __getattr__(self, attr):
return getattr(self.components[None], attr)
def __setattr__(self, attr, value):
if attr == "components":
super(NewCppInfo, self).__setattr__(attr, value)
else:
setattr(self.components[None], attr, value)
@property
def has_components(self):
return len(self.components) > 1
@property
def component_names(self):
return filter(None, self.components.keys())
def merge(self, other):
"""Merge 'other' into self. 'other' can be an old cpp_info object
Used to merge Layout source + build cpp objects info (editables)
:type other: NewCppInfo
"""
def merge_list(o, d):
d.extend(e for e in o if e not in d)
for varname in _ALL_NAMES:
other_values = getattr(other, varname)
if other_values is not None:
current_values = self.components[None].get_init(varname, [])
merge_list(other_values, current_values)
if self.sysroot is None and other.sysroot:
self.sysroot = other.sysroot
if other.requires:
current_values = self.components[None].get_init("requires", [])
merge_list(other.requires, current_values)
if other._generator_properties:
current_values = self.components[None].get_init("_generator_properties", {})
current_values.update(other._generator_properties)
# COMPONENTS
for cname, c in other.components.items():
if cname is None:
continue
for varname in _ALL_NAMES:
other_values = getattr(c, varname)
if other_values is not None:
current_values = self.components[cname].get_init(varname, [])
merge_list(other_values, current_values)
if c.requires:
current_values = self.components[cname].get_init("requires", [])
merge_list(c.requires, current_values)
if c._generator_properties:
current_values = self.components[cname].get_init("_generator_properties", {})
current_values.update(c._generator_properties)
def set_relative_base_folder(self, folder):
"""Prepend the folder to all the directories"""
for component in self.components.values():
for varname in _DIRS_VAR_NAMES:
origin = getattr(component, varname)
if origin is not None:
origin[:] = [os.path.join(folder, el) for el in origin]
if component._generator_properties is not None:
updates = {}
for prop_name, value in component._generator_properties.items():
if prop_name == "cmake_build_modules":
if isinstance(value, list):
updates[prop_name] = [os.path.join(folder, v) for v in value]
else:
updates[prop_name] = os.path.join(folder, value)
component._generator_properties.update(updates)
def get_sorted_components(self):
"""Order the components taking into account if they depend on another component in the
same package (not scoped with ::). First less dependant
return: {component_name: component}
"""
processed = [] # Names of the components ordered
# FIXME: Cache the sort
while (len(self.components) - 1) > len(processed):
for name, c in self.components.items():
if name is None:
continue
req_processed = [n for n in c.required_component_names if n not in processed]
if not req_processed and name not in processed:
processed.append(name)
return OrderedDict([(cname, self.components[cname]) for cname in processed])
def aggregated_components(self):
"""Aggregates all the components as global values, returning a new NewCppInfo"""
if self._aggregated is None:
if self.has_components:
result = _NewComponent()
for n in _ALL_NAMES: # Initialize all values, from None => []
setattr(result, n, []) # TODO: This is a bit dirty
# Reversed to make more dependant first
for name, component in reversed(self.get_sorted_components().items()):
for n in _ALL_NAMES:
if getattr(component, n):
dest = result.get_init(n, [])
dest.extend([i for i in getattr(component, n) if i not in dest])
# NOTE: The properties are not aggregated because they might refer only to the
# component like "cmake_target_name" describing the target name FOR THE component
# not the namespace.
if component.requires:
current_values = result.get_init("requires", [])
current_values.extend(component.requires)
# We copy the properties from the root object, even if we have components
result._generator_properties = copy.copy(self._generator_properties)
# FIXME: What to do about sysroot?
else:
result = copy.copy(self.components[None])
self._aggregated = NewCppInfo()
self._aggregated.components[None] = result
return self._aggregated
@property
def required_components(self):
"""Returns a list of tuples with (require, component_name) required by the package
If the require is internal (to another component), the require will be None"""
# FIXME: Cache the value
ret = []
for key, comp in self.components.items():
ret.extend([r.split("::") for r in comp.requires if "::" in r and r not in ret])
ret.extend([(None, r) for r in comp.requires if "::" not in r and r not in ret])
return ret
def clear_none(self):
"""A field with None meaning is 'not declared' but for consumers, that is irrelevant, an
empty list is easier to handle and makes perfect sense."""
for c in self.components.values():
for varname in _ALL_NAMES:
if getattr(c, varname) is None:
setattr(c, varname, [])
if c.requires is None:
c.requires = []
if self.sysroot is None:
self.sysroot = ""
if self._generator_properties is None:
self._generator_properties = {}
def __str__(self):
ret = []
for cname, c in self.components.items():
for n in _ALL_NAMES:
ret.append("Component: '{}' "
"Var: '{}' "
"Value: '{}'".format(cname, n, getattr(c, n)))
return "\n".join(ret)
def from_old_cppinfo(old):
ret = NewCppInfo()
ret.merge(old)
ret.clear_none()
return ret
def fill_old_cppinfo(origin, old_cpp):
"""Copy the values from a new cpp info object to an old one but prioritizing it,
if the value is not None, then override the declared in the conanfile.cpp_info => (dest)"""
if origin.has_components:
# If the user declared components, reset the global values
origin.components[None] = _NewComponent()
# COMPONENTS
for cname, c in origin.components.items():
if cname is None:
continue
for varname in _ALL_NAMES:
value = getattr(c, varname)
if value is not None:
# Override the self.cpp_info component value
setattr(old_cpp.components[cname], varname, copy.copy(value))
if c.requires is not None:
old_cpp.components[cname].requires = copy.copy(c.requires)
if c._generator_properties is not None:
old_cpp.components[cname]._generator_properties = copy.copy(c._generator_properties)
else:
for varname in _ALL_NAMES:
value = getattr(origin, varname)
if value is not None:
# Override the self.cpp_info value
setattr(old_cpp, varname, copy.copy(value))
if origin._generator_properties is not None:
old_cpp._generator_properties = copy.copy(origin._generator_properties)
# We change the defaults so the new components the user is going to declare in package_info
# have also defaults, not only the declared in the `self.cpp.package`
old_cpp._default_values = CppInfoDefaultValues(includedir="include", libdir="lib", bindir="bin")