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