forked from conan-io/conan
/
dependencies.py
171 lines (133 loc) · 5.47 KB
/
dependencies.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
from collections import OrderedDict
from conans.model.conanfile_interface import ConanFileInterface
from conans.model.ref import ConanFileReference
class Requirement(object):
def __init__(self, ref, build=False, direct=True, test=False):
# By default this is a generic library requirement
self.ref = ref
self.build = build # This dependent node is a build tool that is executed at build time only
self.direct = direct
self.test = test
def __repr__(self):
return repr(self.__dict__)
def __hash__(self):
return hash((self.ref.name, self.build))
def __eq__(self, other):
return self.ref.name == other.ref.name and self.build == other.build
def __ne__(self, other):
return not self.__eq__(other)
class UserRequirementsDict(object):
""" user facing dict to allow access of dependencies by name
"""
def __init__(self, data, require_filter=None):
self._data = data # dict-like
self._require_filter = require_filter # dict {trait: value} for requirements
def filter(self, require_filter):
def filter_fn(require):
for k, v in require_filter.items():
if getattr(require, k) != v:
return False
return True
data = OrderedDict((k, v) for k, v in self._data.items() if filter_fn(k))
return UserRequirementsDict(data, require_filter)
def __bool__(self):
return bool(self._data)
__nonzero__ = __bool__
def _get_require(self, ref, **kwargs):
assert isinstance(ref, str)
if "/" in ref:
ref = ConanFileReference.loads(ref)
else:
ref = ConanFileReference(ref, "unknown", "unknown", "unknown", validate=False)
if self._require_filter:
kwargs.update(self._require_filter)
r = Requirement(ref, **kwargs)
return r
def get(self, ref, **kwargs):
r = self._get_require(ref, **kwargs)
return self._data.get(r)
def __getitem__(self, name):
r = self._get_require(name)
return self._data[r]
def __delitem__(self, name):
r = self._get_require(name)
del self._data[r]
def items(self):
return self._data.items()
def values(self):
return self._data.values()
class ConanFileDependencies(UserRequirementsDict):
@staticmethod
def from_node(node):
# TODO: This construction will be easier in 2.0
build, test, host = [], [], []
for edge in node.dependencies:
if edge.build_require:
if not edge.require.force_host_context:
build.append(edge.dst)
else:
test.append(edge.dst)
else:
host.append(edge.dst)
d = OrderedDict()
def expand(nodes, is_build, is_test):
all_nodes = set(nodes)
for n in nodes:
conanfile = ConanFileInterface(n.conanfile)
d[Requirement(n.ref, build=is_build, test=is_test)] = conanfile
next_nodes = nodes
while next_nodes:
new_nodes = []
for next_node in next_nodes:
for e in next_node.dependencies:
if not e.build_require and not e.private and e.dst not in all_nodes:
new_nodes.append(e.dst)
all_nodes.add(e.dst)
next_nodes = new_nodes
for n in next_nodes:
conanfile = ConanFileInterface(n.conanfile)
d[Requirement(n.ref, build=is_build, test=is_test, direct=False)] = conanfile
expand(host, is_build=False, is_test=False)
expand(build, is_build=True, is_test=False)
expand(test, is_build=False, is_test=True)
return ConanFileDependencies(d)
def filter(self, require_filter):
# FIXME: Copy of hte above, to return ConanFileDependencies class object
def filter_fn(require):
for k, v in require_filter.items():
if getattr(require, k) != v:
return False
return True
data = OrderedDict((k, v) for k, v in self._data.items() if filter_fn(k))
return ConanFileDependencies(data, require_filter)
@property
def topological_sort(self):
# Return first independent nodes, final ones are the more direct deps
result = OrderedDict()
opened = self._data.copy()
while opened:
opened_values = set(opened.values())
new_opened = OrderedDict()
for req, conanfile in opened.items():
deps_in_opened = any(d in opened_values for d in conanfile.dependencies.values())
if deps_in_opened:
new_opened[req] = conanfile # keep it for next iteration
else:
result[req] = conanfile # No dependencies in open set!
opened = new_opened
return ConanFileDependencies(result)
@property
def direct_host(self):
return self.filter({"build": False, "direct": True, "test": False})
@property
def direct_build(self):
return self.filter({"build": True, "direct": True})
@property
def host(self):
return self.filter({"build": False, "test": False})
@property
def test(self):
return self.filter({"build": False, "test": True})
@property
def build(self):
return self.filter({"build": True})