providers.py: Fix regressions
[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
74     localdata = data.createCopy(cfgData)
75     bb.data.setVar('OVERRIDES', "pn-%s:%s:%s" % (pn, pn, data.getVar('OVERRIDES', localdata)), localdata)
76     bb.data.update_data(localdata)
77
78     preferred_v = bb.data.getVar('PREFERRED_VERSION_%s' % pn, localdata, True)
79     if preferred_v:
80         m = re.match('(\d+:)*(.*)(_.*)*', preferred_v)
81         if m:
82             if m.group(1):
83                 preferred_e = int(m.group(1)[:-1])
84             else:
85                 preferred_e = None
86             preferred_v = m.group(2)
87             if m.group(3):
88                 preferred_r = m.group(3)[1:]
89             else:
90                 preferred_r = None
91         else:
92             preferred_e = None
93             preferred_r = None
94
95         for file_set in pkg_pn:
96             for f in file_set:
97                 pe,pv,pr = dataCache.pkg_pepvpr[f]
98                 if preferred_v == pv and (preferred_r == pr or preferred_r == None) and (preferred_e == pe or preferred_e == None):
99                     preferred_file = f
100                     preferred_ver = (pe, pv, pr)
101                     break
102             if preferred_file:
103                 break;
104         if preferred_r:
105             pv_str = '%s-%s' % (preferred_v, preferred_r)
106         else:
107             pv_str = preferred_v
108         if not (preferred_e is None):
109             pv_str = '%s:%s' % (preferred_e, pv_str)
110         itemstr = ""
111         if item:
112             itemstr = " (for item %s)" % item
113         if preferred_file is None:
114             bb.msg.note(1, bb.msg.domain.Provider, "preferred version %s of %s not available%s" % (pv_str, pn, itemstr))
115         else:
116             bb.msg.debug(1, bb.msg.domain.Provider, "selecting %s as PREFERRED_VERSION %s of package %s%s" % (preferred_file, pv_str, pn, itemstr))
117
118     return (preferred_v, preferred_file)
119
120
121 def findLatestProvider(pn, cfgData, dataCache, file_set):
122     """
123     Return the highest version of the providers in file_set.
124     Take default preferences into account.
125     """
126     latest = None
127     latest_p = 0
128     latest_f = None
129     for file_name in file_set:
130         pe,pv,pr = dataCache.pkg_pepvpr[file_name]
131         dp = dataCache.pkg_dp[file_name]
132
133         if (latest is None) or ((latest_p == dp) and (utils.vercmp(latest, (pe, pv, pr)) < 0)) or (dp > latest_p):
134             latest = (pe, pv, pr)
135             latest_f = file_name
136             latest_p = dp
137
138     return (latest, latest_f)
139
140
141 def findBestProvider(pn, cfgData, dataCache, pkg_pn = None, item = None):
142     """
143     If there is a PREFERRED_VERSION, find the highest-priority bbfile
144     providing that version.  If not, find the latest version provided by
145     an bbfile in the highest-priority set.
146     """
147
148     sortpkg_pn = sortPriorities(pn, dataCache, pkg_pn)
149     # Find the highest priority provider with a PREFERRED_VERSION set
150     (preferred_ver, preferred_file) = findPreferredProvider(pn, cfgData, dataCache, sortpkg_pn, item)
151     # Find the latest version of the highest priority provider
152     (latest, latest_f) = findLatestProvider(pn, cfgData, dataCache, sortpkg_pn[0])
153
154     if preferred_file is None:
155         preferred_file = latest_f
156         preferred_ver = latest
157
158     return (latest, latest_f, preferred_ver, preferred_file)
159
160
161 def _filterProviders(providers, item, cfgData, dataCache):
162     """
163     Take a list of providers and filter/reorder according to the 
164     environment variables and previous build results
165     """
166     eligible = []
167     preferred_versions = {}
168     sortpkg_pn = {}
169
170     # The order of providers depends on the order of the files on the disk 
171     # up to here. Sort pkg_pn to make dependency issues reproducible rather
172     # than effectively random.
173     providers.sort()
174
175     # Collate providers by PN
176     pkg_pn = {}
177     for p in providers:
178         pn = dataCache.pkg_fn[p]
179         if pn not in pkg_pn:
180             pkg_pn[pn] = []
181         pkg_pn[pn].append(p)
182
183     bb.msg.debug(1, bb.msg.domain.Provider, "providers for %s are: %s" % (item, pkg_pn.keys()))
184
185     # First add PREFERRED_VERSIONS
186     for pn in pkg_pn.keys():
187         sortpkg_pn[pn] = sortPriorities(pn, dataCache, pkg_pn)
188         preferred_versions[pn] = findPreferredProvider(pn, cfgData, dataCache, sortpkg_pn[pn], item)
189         if preferred_versions[pn][1]:
190             eligible.append(preferred_versions[pn][1])
191
192     # Now add latest verisons
193     for pn in pkg_pn.keys():
194         if pn in preferred_versions and preferred_versions[pn][1]:
195             continue
196         preferred_versions[pn] = findLatestProvider(pn, cfgData, dataCache, sortpkg_pn[pn][0])
197         eligible.append(preferred_versions[pn][1])
198
199     if len(eligible) == 0:
200         bb.msg.error(bb.msg.domain.Provider, "no eligible providers for %s" % item)
201         return 0
202
203     # If pn == item, give it a slight default preference
204     # This means PREFERRED_PROVIDER_foobar defaults to foobar if available
205     for p in providers:
206         pn = dataCache.pkg_fn[p]
207         if pn != item:
208             continue
209         (newvers, fn) = preferred_versions[pn]
210         if not fn in eligible:
211             continue
212         eligible.remove(fn)
213         eligible = [fn] + eligible
214
215     # look to see if one of them is already staged, or marked as preferred.
216     # if so, bump it to the head of the queue
217     for p in providers:
218         pn = dataCache.pkg_fn[p]
219         pe, pv, pr = dataCache.pkg_pepvpr[p]
220
221         stamp = '%s.do_populate_staging' % dataCache.stamp[p]
222         if os.path.exists(stamp):
223             (newvers, fn) = preferred_versions[pn]
224             if not fn in eligible:
225                 # package was made ineligible by already-failed check
226                 continue
227             oldver = "%s-%s" % (pv, pr)
228             if pe > 0:
229                 oldver = "%s:%s" % (pe, oldver)
230             newver = "%s-%s" % (newvers[1], newvers[2])
231             if newvers[0] > 0:
232                 newver = "%s:%s" % (newvers[0], newver)
233             if (newver != oldver):
234                 extra_chat = "%s (%s) already staged but upgrading to %s to satisfy %s" % (pn, oldver, newver, item)
235             else:
236                 extra_chat = "Selecting already-staged %s (%s) to satisfy %s" % (pn, oldver, item)
237
238             bb.msg.note(2, bb.msg.domain.Provider, "%s" % extra_chat)
239             eligible.remove(fn)
240             eligible = [fn] + eligible
241             break
242
243     return eligible
244
245
246 def filterProviders(providers, item, cfgData, dataCache):
247     """
248     Take a list of providers and filter/reorder according to the 
249     environment variables and previous build results
250     Takes a "normal" target item
251     """
252
253     eligible = _filterProviders(providers, item, cfgData, dataCache)
254
255     prefervar = bb.data.getVar('PREFERRED_PROVIDER_%s' % item, cfgData, 1)
256     if prefervar:
257         dataCache.preferred[item] = prefervar
258
259     foundUnique = False
260     if item in dataCache.preferred:
261         for p in eligible:
262             pn = dataCache.pkg_fn[p]
263             if dataCache.preferred[item] == pn:
264                 bb.msg.note(2, bb.msg.domain.Provider, "selecting %s to satisfy %s due to PREFERRED_PROVIDERS" % (pn, item))
265                 eligible.remove(p)
266                 eligible = [p] + eligible
267                 foundUnique = True
268                 break
269
270     bb.msg.debug(1, bb.msg.domain.Provider, "sorted providers for %s are: %s" % (item, eligible))
271
272     return eligible, foundUnique
273
274 def filterProvidersRunTime(providers, item, cfgData, dataCache):
275     """
276     Take a list of providers and filter/reorder according to the 
277     environment variables and previous build results
278     Takes a "runtime" target item
279     """
280
281     eligible = _filterProviders(providers, item, cfgData, dataCache)
282
283     # Should use dataCache.preferred here?
284     preferred = []
285     for p in eligible:
286         pn = dataCache.pkg_fn[p]
287         provides = dataCache.pn_provides[pn]
288         for provide in provides:
289             prefervar = bb.data.getVar('PREFERRED_PROVIDER_%s' % provide, cfgData, 1)
290             if prefervar == pn:
291                 bb.msg.note(2, bb.msg.domain.Provider, "selecting %s to satisfy runtime %s due to PREFERRED_PROVIDERS" % (pn, item))
292                 eligible.remove(p)
293                 eligible = [p] + eligible
294                 preferred.append(p)
295                 break
296
297     numberPreferred = len(preferred)
298
299     bb.msg.debug(1, bb.msg.domain.Provider, "sorted providers for %s are: %s" % (item, eligible))
300
301     return eligible, numberPreferred
302
303 def getRuntimeProviders(dataCache, rdepend):
304     """
305     Return any providers of runtime dependency
306     """
307     rproviders = []
308
309     if rdepend in dataCache.rproviders:
310        rproviders += dataCache.rproviders[rdepend]
311
312     if rdepend in dataCache.packages:
313         rproviders += dataCache.packages[rdepend]
314
315     if rproviders:
316         return rproviders
317
318     # Only search dynamic packages if we can't find anything in other variables
319     for pattern in dataCache.packages_dynamic:
320         regexp = re.compile(pattern)
321         if regexp.match(rdepend):
322             rproviders += dataCache.packages_dynamic[pattern]
323
324     return rproviders