blob: db02a0b0de4a5b7f6b7df56dff9b0140d1c858d5 [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
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500124 # pn can contain '_', e.g. gcc-cross-x86_64 and an override cannot
125 # hence we do this manually rather than use OVERRIDES
126 preferred_v = cfgData.getVar("PREFERRED_VERSION_pn-%s" % pn, True)
127 if not preferred_v:
128 preferred_v = cfgData.getVar("PREFERRED_VERSION_%s" % pn, True)
129 if not preferred_v:
130 preferred_v = cfgData.getVar("PREFERRED_VERSION", True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500131
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500132 if preferred_v:
133 m = re.match('(\d+:)*(.*)(_.*)*', preferred_v)
134 if m:
135 if m.group(1):
136 preferred_e = m.group(1)[:-1]
137 else:
138 preferred_e = None
139 preferred_v = m.group(2)
140 if m.group(3):
141 preferred_r = m.group(3)[1:]
142 else:
143 preferred_r = None
144 else:
145 preferred_e = None
146 preferred_r = None
147
148 for file_set in pkg_pn:
149 for f in file_set:
150 pe, pv, pr = dataCache.pkg_pepvpr[f]
151 if preferredVersionMatch(pe, pv, pr, preferred_e, preferred_v, preferred_r):
152 preferred_file = f
153 preferred_ver = (pe, pv, pr)
154 break
155 if preferred_file:
156 break;
157 if preferred_r:
158 pv_str = '%s-%s' % (preferred_v, preferred_r)
159 else:
160 pv_str = preferred_v
161 if not (preferred_e is None):
162 pv_str = '%s:%s' % (preferred_e, pv_str)
163 itemstr = ""
164 if item:
165 itemstr = " (for item %s)" % item
166 if preferred_file is None:
167 logger.info("preferred version %s of %s not available%s", pv_str, pn, itemstr)
168 available_vers = []
169 for file_set in pkg_pn:
170 for f in file_set:
171 pe, pv, pr = dataCache.pkg_pepvpr[f]
172 ver_str = pv
173 if pe:
174 ver_str = "%s:%s" % (pe, ver_str)
175 if not ver_str in available_vers:
176 available_vers.append(ver_str)
177 if available_vers:
178 available_vers.sort()
179 logger.info("versions of %s available: %s", pn, ' '.join(available_vers))
180 else:
181 logger.debug(1, "selecting %s as PREFERRED_VERSION %s of package %s%s", preferred_file, pv_str, pn, itemstr)
182
183 return (preferred_ver, preferred_file)
184
185
186def findLatestProvider(pn, cfgData, dataCache, file_set):
187 """
188 Return the highest version of the providers in file_set.
189 Take default preferences into account.
190 """
191 latest = None
192 latest_p = 0
193 latest_f = None
194 for file_name in file_set:
195 pe, pv, pr = dataCache.pkg_pepvpr[file_name]
196 dp = dataCache.pkg_dp[file_name]
197
198 if (latest is None) or ((latest_p == dp) and (utils.vercmp(latest, (pe, pv, pr)) < 0)) or (dp > latest_p):
199 latest = (pe, pv, pr)
200 latest_f = file_name
201 latest_p = dp
202
203 return (latest, latest_f)
204
205
206def findBestProvider(pn, cfgData, dataCache, pkg_pn = None, item = None):
207 """
208 If there is a PREFERRED_VERSION, find the highest-priority bbfile
209 providing that version. If not, find the latest version provided by
210 an bbfile in the highest-priority set.
211 """
212
213 sortpkg_pn = sortPriorities(pn, dataCache, pkg_pn)
214 # Find the highest priority provider with a PREFERRED_VERSION set
215 (preferred_ver, preferred_file) = findPreferredProvider(pn, cfgData, dataCache, sortpkg_pn, item)
216 # Find the latest version of the highest priority provider
217 (latest, latest_f) = findLatestProvider(pn, cfgData, dataCache, sortpkg_pn[0])
218
219 if preferred_file is None:
220 preferred_file = latest_f
221 preferred_ver = latest
222
223 return (latest, latest_f, preferred_ver, preferred_file)
224
225
226def _filterProviders(providers, item, cfgData, dataCache):
227 """
228 Take a list of providers and filter/reorder according to the
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500229 environment variables
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500230 """
231 eligible = []
232 preferred_versions = {}
233 sortpkg_pn = {}
234
235 # The order of providers depends on the order of the files on the disk
236 # up to here. Sort pkg_pn to make dependency issues reproducible rather
237 # than effectively random.
238 providers.sort()
239
240 # Collate providers by PN
241 pkg_pn = {}
242 for p in providers:
243 pn = dataCache.pkg_fn[p]
244 if pn not in pkg_pn:
245 pkg_pn[pn] = []
246 pkg_pn[pn].append(p)
247
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600248 logger.debug(1, "providers for %s are: %s", item, list(pkg_pn.keys()))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500249
250 # First add PREFERRED_VERSIONS
251 for pn in pkg_pn:
252 sortpkg_pn[pn] = sortPriorities(pn, dataCache, pkg_pn)
253 preferred_versions[pn] = findPreferredProvider(pn, cfgData, dataCache, sortpkg_pn[pn], item)
254 if preferred_versions[pn][1]:
255 eligible.append(preferred_versions[pn][1])
256
257 # Now add latest versions
258 for pn in sortpkg_pn:
259 if pn in preferred_versions and preferred_versions[pn][1]:
260 continue
261 preferred_versions[pn] = findLatestProvider(pn, cfgData, dataCache, sortpkg_pn[pn][0])
262 eligible.append(preferred_versions[pn][1])
263
264 if len(eligible) == 0:
265 logger.error("no eligible providers for %s", item)
266 return 0
267
268 # If pn == item, give it a slight default preference
269 # This means PREFERRED_PROVIDER_foobar defaults to foobar if available
270 for p in providers:
271 pn = dataCache.pkg_fn[p]
272 if pn != item:
273 continue
274 (newvers, fn) = preferred_versions[pn]
275 if not fn in eligible:
276 continue
277 eligible.remove(fn)
278 eligible = [fn] + eligible
279
280 return eligible
281
282
283def filterProviders(providers, item, cfgData, dataCache):
284 """
285 Take a list of providers and filter/reorder according to the
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500286 environment variables
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500287 Takes a "normal" target item
288 """
289
290 eligible = _filterProviders(providers, item, cfgData, dataCache)
291
292 prefervar = cfgData.getVar('PREFERRED_PROVIDER_%s' % item, True)
293 if prefervar:
294 dataCache.preferred[item] = prefervar
295
296 foundUnique = False
297 if item in dataCache.preferred:
298 for p in eligible:
299 pn = dataCache.pkg_fn[p]
300 if dataCache.preferred[item] == pn:
301 logger.verbose("selecting %s to satisfy %s due to PREFERRED_PROVIDERS", pn, item)
302 eligible.remove(p)
303 eligible = [p] + eligible
304 foundUnique = True
305 break
306
307 logger.debug(1, "sorted providers for %s are: %s", item, eligible)
308
309 return eligible, foundUnique
310
311def filterProvidersRunTime(providers, item, cfgData, dataCache):
312 """
313 Take a list of providers and filter/reorder according to the
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500314 environment variables
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500315 Takes a "runtime" target item
316 """
317
318 eligible = _filterProviders(providers, item, cfgData, dataCache)
319
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500320 # First try and match any PREFERRED_RPROVIDER entry
321 prefervar = cfgData.getVar('PREFERRED_RPROVIDER_%s' % item, True)
322 foundUnique = False
323 if prefervar:
324 for p in eligible:
325 pn = dataCache.pkg_fn[p]
326 if prefervar == pn:
327 logger.verbose("selecting %s to satisfy %s due to PREFERRED_RPROVIDER", pn, item)
328 eligible.remove(p)
329 eligible = [p] + eligible
330 foundUnique = True
331 numberPreferred = 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500332 break
333
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500334 # If we didn't find an RPROVIDER entry, try and infer the provider from PREFERRED_PROVIDER entries
335 # by looking through the provides of each eligible recipe and seeing if a PREFERRED_PROVIDER was set.
336 # This is most useful for virtual/ entries rather than having a RPROVIDER per entry.
337 if not foundUnique:
338 # Should use dataCache.preferred here?
339 preferred = []
340 preferred_vars = []
341 pns = {}
342 for p in eligible:
343 pns[dataCache.pkg_fn[p]] = p
344 for p in eligible:
345 pn = dataCache.pkg_fn[p]
346 provides = dataCache.pn_provides[pn]
347 for provide in provides:
348 prefervar = cfgData.getVar('PREFERRED_PROVIDER_%s' % provide, True)
349 #logger.debug(1, "checking PREFERRED_PROVIDER_%s (value %s) against %s", provide, prefervar, pns.keys())
350 if prefervar in pns and pns[prefervar] not in preferred:
351 var = "PREFERRED_PROVIDER_%s = %s" % (provide, prefervar)
352 logger.verbose("selecting %s to satisfy runtime %s due to %s", prefervar, item, var)
353 preferred_vars.append(var)
354 pref = pns[prefervar]
355 eligible.remove(pref)
356 eligible = [pref] + eligible
357 preferred.append(pref)
358 break
359
360 numberPreferred = len(preferred)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500361
362 if numberPreferred > 1:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500363 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. You could set PREFERRED_RPROVIDER_%s" % (item, preferred, preferred_vars, item))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500364
365 logger.debug(1, "sorted runtime providers for %s are: %s", item, eligible)
366
367 return eligible, numberPreferred
368
369regexp_cache = {}
370
371def getRuntimeProviders(dataCache, rdepend):
372 """
373 Return any providers of runtime dependency
374 """
375 rproviders = []
376
377 if rdepend in dataCache.rproviders:
378 rproviders += dataCache.rproviders[rdepend]
379
380 if rdepend in dataCache.packages:
381 rproviders += dataCache.packages[rdepend]
382
383 if rproviders:
384 return rproviders
385
386 # Only search dynamic packages if we can't find anything in other variables
387 for pattern in dataCache.packages_dynamic:
388 pattern = pattern.replace('+', "\+")
389 if pattern in regexp_cache:
390 regexp = regexp_cache[pattern]
391 else:
392 try:
393 regexp = re.compile(pattern)
394 except:
395 logger.error("Error parsing regular expression '%s'", pattern)
396 raise
397 regexp_cache[pattern] = regexp
398 if regexp.match(rdepend):
399 rproviders += dataCache.packages_dynamic[pattern]
400 logger.debug(1, "Assuming %s is a dynamic package, but it may not exist" % rdepend)
401
402 return rproviders
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500403
404
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600405def buildWorldTargetList(dataCache, task=None):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500406 """
407 Build package list for "bitbake world"
408 """
409 if dataCache.world_target:
410 return
411
412 logger.debug(1, "collating packages for \"world\"")
413 for f in dataCache.possible_world:
414 terminal = True
415 pn = dataCache.pkg_fn[f]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600416 if task and task not in dataCache.task_deps[f]['tasks']:
417 logger.debug(2, "World build skipping %s as task %s doesn't exist", f, task)
418 terminal = False
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500419
420 for p in dataCache.pn_provides[pn]:
421 if p.startswith('virtual/'):
422 logger.debug(2, "World build skipping %s due to %s provider starting with virtual/", f, p)
423 terminal = False
424 break
425 for pf in dataCache.providers[p]:
426 if dataCache.pkg_fn[pf] != pn:
427 logger.debug(2, "World build skipping %s due to both us and %s providing %s", f, pf, p)
428 terminal = False
429 break
430 if terminal:
431 dataCache.world_target.add(pn)