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