blob: 637e1fab96f624060af936b09cba4e0da176860c [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001# ex:ts=4:sw=4:sts=4:et
2# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
3#
4# Copyright (C) 2003, 2004 Chris Larson
5# Copyright (C) 2003, 2004 Phil Blundell
6# Copyright (C) 2003 - 2005 Michael 'Mickey' Lauer
7# Copyright (C) 2005 Holger Hans Peter Freyther
8# Copyright (C) 2005 ROAD GmbH
9# Copyright (C) 2006 Richard Purdie
10#
11# This program is free software; you can redistribute it and/or modify
12# it under the terms of the GNU General Public License version 2 as
13# published by the Free Software Foundation.
14#
15# This program is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18# GNU General Public License for more details.
19#
20# You should have received a copy of the GNU General Public License along
21# with this program; if not, write to the Free Software Foundation, Inc.,
22# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23
24import re
25import logging
26from bb import data, utils
27from collections import defaultdict
28import bb
29
30logger = logging.getLogger("BitBake.Provider")
31
32class NoProvider(bb.BBHandledException):
33 """Exception raised when no provider of a build dependency can be found"""
34
35class NoRProvider(bb.BBHandledException):
36 """Exception raised when no provider of a runtime dependency can be found"""
37
38class MultipleRProvider(bb.BBHandledException):
39 """Exception raised when multiple providers of a runtime dependency can be found"""
40
41def findProviders(cfgData, dataCache, pkg_pn = None):
42 """
43 Convenience function to get latest and preferred providers in pkg_pn
44 """
45
46 if not pkg_pn:
47 pkg_pn = dataCache.pkg_pn
48
49 # Need to ensure data store is expanded
50 localdata = data.createCopy(cfgData)
51 bb.data.update_data(localdata)
52 bb.data.expandKeys(localdata)
53
54 preferred_versions = {}
55 latest_versions = {}
56
57 for pn in pkg_pn:
58 (last_ver, last_file, pref_ver, pref_file) = findBestProvider(pn, localdata, dataCache, pkg_pn)
59 preferred_versions[pn] = (pref_ver, pref_file)
60 latest_versions[pn] = (last_ver, last_file)
61
62 return (latest_versions, preferred_versions)
63
64
65def allProviders(dataCache):
66 """
67 Find all providers for each pn
68 """
69 all_providers = defaultdict(list)
70 for (fn, pn) in dataCache.pkg_fn.items():
71 ver = dataCache.pkg_pepvpr[fn]
72 all_providers[pn].append((ver, fn))
73 return all_providers
74
75
76def sortPriorities(pn, dataCache, pkg_pn = None):
77 """
78 Reorder pkg_pn by file priority and default preference
79 """
80
81 if not pkg_pn:
82 pkg_pn = dataCache.pkg_pn
83
84 files = pkg_pn[pn]
85 priorities = {}
86 for f in files:
87 priority = dataCache.bbfile_priority[f]
88 preference = dataCache.pkg_dp[f]
89 if priority not in priorities:
90 priorities[priority] = {}
91 if preference not in priorities[priority]:
92 priorities[priority][preference] = []
93 priorities[priority][preference].append(f)
94 tmp_pn = []
95 for pri in sorted(priorities):
96 tmp_pref = []
97 for pref in sorted(priorities[pri]):
98 tmp_pref.extend(priorities[pri][pref])
99 tmp_pn = [tmp_pref] + tmp_pn
100
101 return tmp_pn
102
103def preferredVersionMatch(pe, pv, pr, preferred_e, preferred_v, preferred_r):
104 """
105 Check if the version pe,pv,pr is the preferred one.
106 If there is preferred version defined and ends with '%', then pv has to start with that version after removing the '%'
107 """
108 if (pr == preferred_r or preferred_r == None):
109 if (pe == preferred_e or preferred_e == None):
110 if preferred_v == pv:
111 return True
112 if preferred_v != None and preferred_v.endswith('%') and pv.startswith(preferred_v[:len(preferred_v)-1]):
113 return True
114 return False
115
116def findPreferredProvider(pn, cfgData, dataCache, pkg_pn = None, item = None):
117 """
118 Find the first provider in pkg_pn with a PREFERRED_VERSION set.
119 """
120
121 preferred_file = None
122 preferred_ver = None
123
124 localdata = data.createCopy(cfgData)
125 localdata.setVar('OVERRIDES', "%s:pn-%s:%s" % (data.getVar('OVERRIDES', localdata), pn, pn))
126 bb.data.update_data(localdata)
127
128 preferred_v = localdata.getVar('PREFERRED_VERSION', True)
129 if preferred_v:
130 m = re.match('(\d+:)*(.*)(_.*)*', preferred_v)
131 if m:
132 if m.group(1):
133 preferred_e = m.group(1)[:-1]
134 else:
135 preferred_e = None
136 preferred_v = m.group(2)
137 if m.group(3):
138 preferred_r = m.group(3)[1:]
139 else:
140 preferred_r = None
141 else:
142 preferred_e = None
143 preferred_r = None
144
145 for file_set in pkg_pn:
146 for f in file_set:
147 pe, pv, pr = dataCache.pkg_pepvpr[f]
148 if preferredVersionMatch(pe, pv, pr, preferred_e, preferred_v, preferred_r):
149 preferred_file = f
150 preferred_ver = (pe, pv, pr)
151 break
152 if preferred_file:
153 break;
154 if preferred_r:
155 pv_str = '%s-%s' % (preferred_v, preferred_r)
156 else:
157 pv_str = preferred_v
158 if not (preferred_e is None):
159 pv_str = '%s:%s' % (preferred_e, pv_str)
160 itemstr = ""
161 if item:
162 itemstr = " (for item %s)" % item
163 if preferred_file is None:
164 logger.info("preferred version %s of %s not available%s", pv_str, pn, itemstr)
165 available_vers = []
166 for file_set in pkg_pn:
167 for f in file_set:
168 pe, pv, pr = dataCache.pkg_pepvpr[f]
169 ver_str = pv
170 if pe:
171 ver_str = "%s:%s" % (pe, ver_str)
172 if not ver_str in available_vers:
173 available_vers.append(ver_str)
174 if available_vers:
175 available_vers.sort()
176 logger.info("versions of %s available: %s", pn, ' '.join(available_vers))
177 else:
178 logger.debug(1, "selecting %s as PREFERRED_VERSION %s of package %s%s", preferred_file, pv_str, pn, itemstr)
179
180 return (preferred_ver, preferred_file)
181
182
183def findLatestProvider(pn, cfgData, dataCache, file_set):
184 """
185 Return the highest version of the providers in file_set.
186 Take default preferences into account.
187 """
188 latest = None
189 latest_p = 0
190 latest_f = None
191 for file_name in file_set:
192 pe, pv, pr = dataCache.pkg_pepvpr[file_name]
193 dp = dataCache.pkg_dp[file_name]
194
195 if (latest is None) or ((latest_p == dp) and (utils.vercmp(latest, (pe, pv, pr)) < 0)) or (dp > latest_p):
196 latest = (pe, pv, pr)
197 latest_f = file_name
198 latest_p = dp
199
200 return (latest, latest_f)
201
202
203def findBestProvider(pn, cfgData, dataCache, pkg_pn = None, item = None):
204 """
205 If there is a PREFERRED_VERSION, find the highest-priority bbfile
206 providing that version. If not, find the latest version provided by
207 an bbfile in the highest-priority set.
208 """
209
210 sortpkg_pn = sortPriorities(pn, dataCache, pkg_pn)
211 # Find the highest priority provider with a PREFERRED_VERSION set
212 (preferred_ver, preferred_file) = findPreferredProvider(pn, cfgData, dataCache, sortpkg_pn, item)
213 # Find the latest version of the highest priority provider
214 (latest, latest_f) = findLatestProvider(pn, cfgData, dataCache, sortpkg_pn[0])
215
216 if preferred_file is None:
217 preferred_file = latest_f
218 preferred_ver = latest
219
220 return (latest, latest_f, preferred_ver, preferred_file)
221
222
223def _filterProviders(providers, item, cfgData, dataCache):
224 """
225 Take a list of providers and filter/reorder according to the
226 environment variables and previous build results
227 """
228 eligible = []
229 preferred_versions = {}
230 sortpkg_pn = {}
231
232 # The order of providers depends on the order of the files on the disk
233 # up to here. Sort pkg_pn to make dependency issues reproducible rather
234 # than effectively random.
235 providers.sort()
236
237 # Collate providers by PN
238 pkg_pn = {}
239 for p in providers:
240 pn = dataCache.pkg_fn[p]
241 if pn not in pkg_pn:
242 pkg_pn[pn] = []
243 pkg_pn[pn].append(p)
244
245 logger.debug(1, "providers for %s are: %s", item, pkg_pn.keys())
246
247 # First add PREFERRED_VERSIONS
248 for pn in pkg_pn:
249 sortpkg_pn[pn] = sortPriorities(pn, dataCache, pkg_pn)
250 preferred_versions[pn] = findPreferredProvider(pn, cfgData, dataCache, sortpkg_pn[pn], item)
251 if preferred_versions[pn][1]:
252 eligible.append(preferred_versions[pn][1])
253
254 # Now add latest versions
255 for pn in sortpkg_pn:
256 if pn in preferred_versions and preferred_versions[pn][1]:
257 continue
258 preferred_versions[pn] = findLatestProvider(pn, cfgData, dataCache, sortpkg_pn[pn][0])
259 eligible.append(preferred_versions[pn][1])
260
261 if len(eligible) == 0:
262 logger.error("no eligible providers for %s", item)
263 return 0
264
265 # If pn == item, give it a slight default preference
266 # This means PREFERRED_PROVIDER_foobar defaults to foobar if available
267 for p in providers:
268 pn = dataCache.pkg_fn[p]
269 if pn != item:
270 continue
271 (newvers, fn) = preferred_versions[pn]
272 if not fn in eligible:
273 continue
274 eligible.remove(fn)
275 eligible = [fn] + eligible
276
277 return eligible
278
279
280def filterProviders(providers, item, cfgData, dataCache):
281 """
282 Take a list of providers and filter/reorder according to the
283 environment variables and previous build results
284 Takes a "normal" target item
285 """
286
287 eligible = _filterProviders(providers, item, cfgData, dataCache)
288
289 prefervar = cfgData.getVar('PREFERRED_PROVIDER_%s' % item, True)
290 if prefervar:
291 dataCache.preferred[item] = prefervar
292
293 foundUnique = False
294 if item in dataCache.preferred:
295 for p in eligible:
296 pn = dataCache.pkg_fn[p]
297 if dataCache.preferred[item] == pn:
298 logger.verbose("selecting %s to satisfy %s due to PREFERRED_PROVIDERS", pn, item)
299 eligible.remove(p)
300 eligible = [p] + eligible
301 foundUnique = True
302 break
303
304 logger.debug(1, "sorted providers for %s are: %s", item, eligible)
305
306 return eligible, foundUnique
307
308def filterProvidersRunTime(providers, item, cfgData, dataCache):
309 """
310 Take a list of providers and filter/reorder according to the
311 environment variables and previous build results
312 Takes a "runtime" target item
313 """
314
315 eligible = _filterProviders(providers, item, cfgData, dataCache)
316
317 # Should use dataCache.preferred here?
318 preferred = []
319 preferred_vars = []
320 pns = {}
321 for p in eligible:
322 pns[dataCache.pkg_fn[p]] = p
323 for p in eligible:
324 pn = dataCache.pkg_fn[p]
325 provides = dataCache.pn_provides[pn]
326 for provide in provides:
327 prefervar = cfgData.getVar('PREFERRED_PROVIDER_%s' % provide, True)
328 #logger.debug(1, "checking PREFERRED_PROVIDER_%s (value %s) against %s", provide, prefervar, pns.keys())
329 if prefervar in pns and pns[prefervar] not in preferred:
330 var = "PREFERRED_PROVIDER_%s = %s" % (provide, prefervar)
331 logger.verbose("selecting %s to satisfy runtime %s due to %s", prefervar, item, var)
332 preferred_vars.append(var)
333 pref = pns[prefervar]
334 eligible.remove(pref)
335 eligible = [pref] + eligible
336 preferred.append(pref)
337 break
338
339 numberPreferred = len(preferred)
340
341 if numberPreferred > 1:
342 logger.error("Trying to resolve runtime dependency %s resulted in conflicting PREFERRED_PROVIDER entries being found.\nThe providers found were: %s\nThe PREFERRED_PROVIDER entries resulting in this conflict were: %s", item, preferred, preferred_vars)
343
344 logger.debug(1, "sorted runtime providers for %s are: %s", item, eligible)
345
346 return eligible, numberPreferred
347
348regexp_cache = {}
349
350def getRuntimeProviders(dataCache, rdepend):
351 """
352 Return any providers of runtime dependency
353 """
354 rproviders = []
355
356 if rdepend in dataCache.rproviders:
357 rproviders += dataCache.rproviders[rdepend]
358
359 if rdepend in dataCache.packages:
360 rproviders += dataCache.packages[rdepend]
361
362 if rproviders:
363 return rproviders
364
365 # Only search dynamic packages if we can't find anything in other variables
366 for pattern in dataCache.packages_dynamic:
367 pattern = pattern.replace('+', "\+")
368 if pattern in regexp_cache:
369 regexp = regexp_cache[pattern]
370 else:
371 try:
372 regexp = re.compile(pattern)
373 except:
374 logger.error("Error parsing regular expression '%s'", pattern)
375 raise
376 regexp_cache[pattern] = regexp
377 if regexp.match(rdepend):
378 rproviders += dataCache.packages_dynamic[pattern]
379 logger.debug(1, "Assuming %s is a dynamic package, but it may not exist" % rdepend)
380
381 return rproviders