providers.py: No longer weight providers on the basis of a package being "already...
[bitbake.git] / lib / bb / providers.py
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