providers.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. # 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. import re
  24. import logging
  25. from bb import data, utils
  26. from collections import defaultdict
  27. import bb
  28. logger = logging.getLogger("BitBake.Provider")
  29. class NoProvider(bb.BBHandledException):
  30. """Exception raised when no provider of a build dependency can be found"""
  31. class NoRProvider(bb.BBHandledException):
  32. """Exception raised when no provider of a runtime dependency can be found"""
  33. class MultipleRProvider(bb.BBHandledException):
  34. """Exception raised when multiple providers of a runtime dependency can be found"""
  35. def findProviders(cfgData, dataCache, pkg_pn = None):
  36. """
  37. Convenience function to get latest and preferred providers in pkg_pn
  38. """
  39. if not pkg_pn:
  40. pkg_pn = dataCache.pkg_pn
  41. # Need to ensure data store is expanded
  42. localdata = data.createCopy(cfgData)
  43. bb.data.update_data(localdata)
  44. bb.data.expandKeys(localdata)
  45. preferred_versions = {}
  46. latest_versions = {}
  47. for pn in pkg_pn:
  48. (last_ver, last_file, pref_ver, pref_file) = findBestProvider(pn, localdata, dataCache, pkg_pn)
  49. preferred_versions[pn] = (pref_ver, pref_file)
  50. latest_versions[pn] = (last_ver, last_file)
  51. return (latest_versions, preferred_versions)
  52. def allProviders(dataCache):
  53. """
  54. Find all providers for each pn
  55. """
  56. all_providers = defaultdict(list)
  57. for (fn, pn) in dataCache.pkg_fn.items():
  58. ver = dataCache.pkg_pepvpr[fn]
  59. all_providers[pn].append((ver, fn))
  60. return all_providers
  61. def sortPriorities(pn, dataCache, pkg_pn = None):
  62. """
  63. Reorder pkg_pn by file priority and default preference
  64. """
  65. if not pkg_pn:
  66. pkg_pn = dataCache.pkg_pn
  67. files = pkg_pn[pn]
  68. priorities = {}
  69. for f in files:
  70. priority = dataCache.bbfile_priority[f]
  71. preference = dataCache.pkg_dp[f]
  72. if priority not in priorities:
  73. priorities[priority] = {}
  74. if preference not in priorities[priority]:
  75. priorities[priority][preference] = []
  76. priorities[priority][preference].append(f)
  77. tmp_pn = []
  78. for pri in sorted(priorities):
  79. tmp_pref = []
  80. for pref in sorted(priorities[pri]):
  81. tmp_pref.extend(priorities[pri][pref])
  82. tmp_pn = [tmp_pref] + tmp_pn
  83. return tmp_pn
  84. def preferredVersionMatch(pe, pv, pr, preferred_e, preferred_v, preferred_r):
  85. """
  86. Check if the version pe,pv,pr is the preferred one.
  87. If there is preferred version defined and ends with '%', then pv has to start with that version after removing the '%'
  88. """
  89. if (pr == preferred_r or preferred_r == None):
  90. if (pe == preferred_e or preferred_e == None):
  91. if preferred_v == pv:
  92. return True
  93. if preferred_v != None and preferred_v.endswith('%') and pv.startswith(preferred_v[:len(preferred_v)-1]):
  94. return True
  95. return False
  96. def findPreferredProvider(pn, cfgData, dataCache, pkg_pn = None, item = None):
  97. """
  98. Find the first provider in pkg_pn with a PREFERRED_VERSION set.
  99. """
  100. preferred_file = None
  101. preferred_ver = None
  102. localdata = data.createCopy(cfgData)
  103. localdata.setVar('OVERRIDES', "%s:pn-%s:%s" % (data.getVar('OVERRIDES', localdata), pn, pn))
  104. bb.data.update_data(localdata)
  105. preferred_v = localdata.getVar('PREFERRED_VERSION', True)
  106. if preferred_v:
  107. m = re.match('(\d+:)*(.*)(_.*)*', preferred_v)
  108. if m:
  109. if m.group(1):
  110. preferred_e = m.group(1)[:-1]
  111. else:
  112. preferred_e = None
  113. preferred_v = m.group(2)
  114. if m.group(3):
  115. preferred_r = m.group(3)[1:]
  116. else:
  117. preferred_r = None
  118. else:
  119. preferred_e = None
  120. preferred_r = None
  121. for file_set in pkg_pn:
  122. for f in file_set:
  123. pe, pv, pr = dataCache.pkg_pepvpr[f]
  124. if preferredVersionMatch(pe, pv, pr, preferred_e, preferred_v, preferred_r):
  125. preferred_file = f
  126. preferred_ver = (pe, pv, pr)
  127. break
  128. if preferred_file:
  129. break;
  130. if preferred_r:
  131. pv_str = '%s-%s' % (preferred_v, preferred_r)
  132. else:
  133. pv_str = preferred_v
  134. if not (preferred_e is None):
  135. pv_str = '%s:%s' % (preferred_e, pv_str)
  136. itemstr = ""
  137. if item:
  138. itemstr = " (for item %s)" % item
  139. if preferred_file is None:
  140. logger.info("preferred version %s of %s not available%s", pv_str, pn, itemstr)
  141. available_vers = []
  142. for file_set in pkg_pn:
  143. for f in file_set:
  144. pe, pv, pr = dataCache.pkg_pepvpr[f]
  145. ver_str = pv
  146. if pe:
  147. ver_str = "%s:%s" % (pe, ver_str)
  148. if not ver_str in available_vers:
  149. available_vers.append(ver_str)
  150. if available_vers:
  151. available_vers.sort()
  152. logger.info("versions of %s available: %s", pn, ' '.join(available_vers))
  153. else:
  154. logger.debug(1, "selecting %s as PREFERRED_VERSION %s of package %s%s", preferred_file, pv_str, pn, itemstr)
  155. return (preferred_ver, preferred_file)
  156. def findLatestProvider(pn, cfgData, dataCache, file_set):
  157. """
  158. Return the highest version of the providers in file_set.
  159. Take default preferences into account.
  160. """
  161. latest = None
  162. latest_p = 0
  163. latest_f = None
  164. for file_name in file_set:
  165. pe, pv, pr = dataCache.pkg_pepvpr[file_name]
  166. dp = dataCache.pkg_dp[file_name]
  167. if (latest is None) or ((latest_p == dp) and (utils.vercmp(latest, (pe, pv, pr)) < 0)) or (dp > latest_p):
  168. latest = (pe, pv, pr)
  169. latest_f = file_name
  170. latest_p = dp
  171. return (latest, latest_f)
  172. def findBestProvider(pn, cfgData, dataCache, pkg_pn = None, item = None):
  173. """
  174. If there is a PREFERRED_VERSION, find the highest-priority bbfile
  175. providing that version. If not, find the latest version provided by
  176. an bbfile in the highest-priority set.
  177. """
  178. sortpkg_pn = sortPriorities(pn, dataCache, pkg_pn)
  179. # Find the highest priority provider with a PREFERRED_VERSION set
  180. (preferred_ver, preferred_file) = findPreferredProvider(pn, cfgData, dataCache, sortpkg_pn, item)
  181. # Find the latest version of the highest priority provider
  182. (latest, latest_f) = findLatestProvider(pn, cfgData, dataCache, sortpkg_pn[0])
  183. if preferred_file is None:
  184. preferred_file = latest_f
  185. preferred_ver = latest
  186. return (latest, latest_f, preferred_ver, preferred_file)
  187. def _filterProviders(providers, item, cfgData, dataCache):
  188. """
  189. Take a list of providers and filter/reorder according to the
  190. environment variables and previous build results
  191. """
  192. eligible = []
  193. preferred_versions = {}
  194. sortpkg_pn = {}
  195. # The order of providers depends on the order of the files on the disk
  196. # up to here. Sort pkg_pn to make dependency issues reproducible rather
  197. # than effectively random.
  198. providers.sort()
  199. # Collate providers by PN
  200. pkg_pn = {}
  201. for p in providers:
  202. pn = dataCache.pkg_fn[p]
  203. if pn not in pkg_pn:
  204. pkg_pn[pn] = []
  205. pkg_pn[pn].append(p)
  206. logger.debug(1, "providers for %s are: %s", item, pkg_pn.keys())
  207. # First add PREFERRED_VERSIONS
  208. for pn in pkg_pn:
  209. sortpkg_pn[pn] = sortPriorities(pn, dataCache, pkg_pn)
  210. preferred_versions[pn] = findPreferredProvider(pn, cfgData, dataCache, sortpkg_pn[pn], item)
  211. if preferred_versions[pn][1]:
  212. eligible.append(preferred_versions[pn][1])
  213. # Now add latest versions
  214. for pn in sortpkg_pn:
  215. if pn in preferred_versions and preferred_versions[pn][1]:
  216. continue
  217. preferred_versions[pn] = findLatestProvider(pn, cfgData, dataCache, sortpkg_pn[pn][0])
  218. eligible.append(preferred_versions[pn][1])
  219. if len(eligible) == 0:
  220. logger.error("no eligible providers for %s", item)
  221. return 0
  222. # If pn == item, give it a slight default preference
  223. # This means PREFERRED_PROVIDER_foobar defaults to foobar if available
  224. for p in providers:
  225. pn = dataCache.pkg_fn[p]
  226. if pn != item:
  227. continue
  228. (newvers, fn) = preferred_versions[pn]
  229. if not fn in eligible:
  230. continue
  231. eligible.remove(fn)
  232. eligible = [fn] + eligible
  233. return eligible
  234. def filterProviders(providers, item, cfgData, dataCache):
  235. """
  236. Take a list of providers and filter/reorder according to the
  237. environment variables and previous build results
  238. Takes a "normal" target item
  239. """
  240. eligible = _filterProviders(providers, item, cfgData, dataCache)
  241. prefervar = cfgData.getVar('PREFERRED_PROVIDER_%s' % item, True)
  242. if prefervar:
  243. dataCache.preferred[item] = prefervar
  244. foundUnique = False
  245. if item in dataCache.preferred:
  246. for p in eligible:
  247. pn = dataCache.pkg_fn[p]
  248. if dataCache.preferred[item] == pn:
  249. logger.verbose("selecting %s to satisfy %s due to PREFERRED_PROVIDERS", pn, item)
  250. eligible.remove(p)
  251. eligible = [p] + eligible
  252. foundUnique = True
  253. break
  254. logger.debug(1, "sorted providers for %s are: %s", item, eligible)
  255. return eligible, foundUnique
  256. def filterProvidersRunTime(providers, item, cfgData, dataCache):
  257. """
  258. Take a list of providers and filter/reorder according to the
  259. environment variables and previous build results
  260. Takes a "runtime" target item
  261. """
  262. eligible = _filterProviders(providers, item, cfgData, dataCache)
  263. # Should use dataCache.preferred here?
  264. preferred = []
  265. preferred_vars = []
  266. pns = {}
  267. for p in eligible:
  268. pns[dataCache.pkg_fn[p]] = p
  269. for p in eligible:
  270. pn = dataCache.pkg_fn[p]
  271. provides = dataCache.pn_provides[pn]
  272. for provide in provides:
  273. prefervar = cfgData.getVar('PREFERRED_PROVIDER_%s' % provide, True)
  274. #logger.debug(1, "checking PREFERRED_PROVIDER_%s (value %s) against %s", provide, prefervar, pns.keys())
  275. if prefervar in pns and pns[prefervar] not in preferred:
  276. var = "PREFERRED_PROVIDER_%s = %s" % (provide, prefervar)
  277. logger.verbose("selecting %s to satisfy runtime %s due to %s", prefervar, item, var)
  278. preferred_vars.append(var)
  279. pref = pns[prefervar]
  280. eligible.remove(pref)
  281. eligible = [pref] + eligible
  282. preferred.append(pref)
  283. break
  284. numberPreferred = len(preferred)
  285. if numberPreferred > 1:
  286. 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)
  287. logger.debug(1, "sorted runtime providers for %s are: %s", item, eligible)
  288. return eligible, numberPreferred
  289. regexp_cache = {}
  290. def getRuntimeProviders(dataCache, rdepend):
  291. """
  292. Return any providers of runtime dependency
  293. """
  294. rproviders = []
  295. if rdepend in dataCache.rproviders:
  296. rproviders += dataCache.rproviders[rdepend]
  297. if rdepend in dataCache.packages:
  298. rproviders += dataCache.packages[rdepend]
  299. if rproviders:
  300. return rproviders
  301. # Only search dynamic packages if we can't find anything in other variables
  302. for pattern in dataCache.packages_dynamic:
  303. pattern = pattern.replace('+', "\+")
  304. if pattern in regexp_cache:
  305. regexp = regexp_cache[pattern]
  306. else:
  307. try:
  308. regexp = re.compile(pattern)
  309. except:
  310. logger.error("Error parsing regular expression '%s'", pattern)
  311. raise
  312. regexp_cache[pattern] = regexp
  313. if regexp.match(rdepend):
  314. rproviders += dataCache.packages_dynamic[pattern]
  315. logger.debug(1, "Assuming %s is a dynamic package, but it may not exist" % rdepend)
  316. return rproviders