providers.py: Improve runtime PREFERRED_PROVIDERS warning messages to be more user...
[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     # look to see if one of them is already staged, or marked as preferred.
217     # if so, bump it to the head of the queue
218     for p in providers:
219         pn = dataCache.pkg_fn[p]
220         pe, pv, pr = dataCache.pkg_pepvpr[p]
221
222         stamp = '%s.do_populate_staging' % dataCache.stamp[p]
223         if os.path.exists(stamp):
224             (newvers, fn) = preferred_versions[pn]
225             if not fn in eligible:
226                 # package was made ineligible by already-failed check
227                 continue
228             oldver = "%s-%s" % (pv, pr)
229             if pe > 0:
230                 oldver = "%s:%s" % (pe, oldver)
231             newver = "%s-%s" % (newvers[1], newvers[2])
232             if newvers[0] > 0:
233                 newver = "%s:%s" % (newvers[0], newver)
234             if (newver != oldver):
235                 extra_chat = "%s (%s) already staged but upgrading to %s to satisfy %s" % (pn, oldver, newver, item)
236             else:
237                 extra_chat = "Selecting already-staged %s (%s) to satisfy %s" % (pn, oldver, item)
238
239             bb.msg.note(2, bb.msg.domain.Provider, "%s" % extra_chat)
240             eligible.remove(fn)
241             eligible = [fn] + eligible
242             break
243
244     return eligible
245
246
247 def filterProviders(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 "normal" target item
252     """
253
254     eligible = _filterProviders(providers, item, cfgData, dataCache)
255
256     prefervar = bb.data.getVar('PREFERRED_PROVIDER_%s' % item, cfgData, 1)
257     if prefervar:
258         dataCache.preferred[item] = prefervar
259
260     foundUnique = False
261     if item in dataCache.preferred:
262         for p in eligible:
263             pn = dataCache.pkg_fn[p]
264             if dataCache.preferred[item] == pn:
265                 bb.msg.note(2, bb.msg.domain.Provider, "selecting %s to satisfy %s due to PREFERRED_PROVIDERS" % (pn, item))
266                 eligible.remove(p)
267                 eligible = [p] + eligible
268                 foundUnique = True
269                 break
270
271     bb.msg.debug(1, bb.msg.domain.Provider, "sorted providers for %s are: %s" % (item, eligible))
272
273     return eligible, foundUnique
274
275 def filterProvidersRunTime(providers, item, cfgData, dataCache):
276     """
277     Take a list of providers and filter/reorder according to the 
278     environment variables and previous build results
279     Takes a "runtime" target item
280     """
281
282     eligible = _filterProviders(providers, item, cfgData, dataCache)
283
284     # Should use dataCache.preferred here?
285     preferred = []
286     preferred_vars = []
287     for p in eligible:
288         pn = dataCache.pkg_fn[p]
289         provides = dataCache.pn_provides[pn]
290         for provide in provides:
291             prefervar = bb.data.getVar('PREFERRED_PROVIDER_%s' % provide, cfgData, 1)
292             if prefervar == pn:
293                 var = "PREFERRED_PROVIDERS_%s = %s" % (provide, prefervar)
294                 bb.msg.note(2, bb.msg.domain.Provider, "selecting %s to satisfy runtime %s due to %s" % (pn, item, var))
295                 preferred_vars.append(var)
296                 eligible.remove(p)
297                 eligible = [p] + eligible
298                 preferred.append(p)
299                 break
300
301     numberPreferred = len(preferred)
302
303     if numberPreferred > 1:
304         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))
305
306     bb.msg.debug(1, bb.msg.domain.Provider, "sorted providers for %s are: %s" % (item, eligible))
307
308     return eligible, numberPreferred
309
310 def getRuntimeProviders(dataCache, rdepend):
311     """
312     Return any providers of runtime dependency
313     """
314     rproviders = []
315
316     if rdepend in dataCache.rproviders:
317        rproviders += dataCache.rproviders[rdepend]
318
319     if rdepend in dataCache.packages:
320         rproviders += dataCache.packages[rdepend]
321
322     if rproviders:
323         return rproviders
324
325     # Only search dynamic packages if we can't find anything in other variables
326     for pattern in dataCache.packages_dynamic:
327         regexp = re.compile(pattern)
328         if regexp.match(rdepend):
329             rproviders += dataCache.packages_dynamic[pattern]
330
331     return rproviders