providers.py 11 KB

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