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