runqueue.py: Detect builds of tasks with overlapping providers and warn (will become...
[vuplus_bitbake] / lib / bb / cache.py
1 # ex:ts=4:sw=4:sts=4:et
2 # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
3 #
4 # BitBake 'Event' implementation
5 #
6 # Caching of bitbake variables before task execution
7
8 # Copyright (C) 2006        Richard Purdie
9
10 # but small sections based on code from bin/bitbake:
11 # Copyright (C) 2003, 2004  Chris Larson
12 # Copyright (C) 2003, 2004  Phil Blundell
13 # Copyright (C) 2003 - 2005 Michael 'Mickey' Lauer
14 # Copyright (C) 2005        Holger Hans Peter Freyther
15 # Copyright (C) 2005        ROAD GmbH
16 #
17 # This program is free software; you can redistribute it and/or modify
18 # it under the terms of the GNU General Public License version 2 as
19 # published by the Free Software Foundation.
20 #
21 # This program is distributed in the hope that it will be useful,
22 # but WITHOUT ANY WARRANTY; without even the implied warranty of
23 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24 # GNU General Public License for more details.
25 #
26 # You should have received a copy of the GNU General Public License along
27 # with this program; if not, write to the Free Software Foundation, Inc.,
28 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
29
30
31 import os, re
32 import bb.data
33 import bb.utils
34 from sets import Set
35
36 try:
37     import cPickle as pickle
38 except ImportError:
39     import pickle
40     bb.msg.note(1, bb.msg.domain.Cache, "Importing cPickle failed. Falling back to a very slow implementation.")
41
42 __cache_version__ = "127"
43
44 class Cache:
45     """
46     BitBake Cache implementation
47     """
48     def __init__(self, cooker):
49
50
51         self.cachedir = bb.data.getVar("CACHE", cooker.configuration.data, True)
52         self.clean = {}
53         self.depends_cache = {}
54         self.data = None
55         self.data_fn = None
56
57         if self.cachedir in [None, '']:
58             self.has_cache = False
59             bb.msg.note(1, bb.msg.domain.Cache, "Not using a cache. Set CACHE = <directory> to enable.")
60         else:
61             self.has_cache = True
62             self.cachefile = os.path.join(self.cachedir,"bb_cache.dat")
63             
64             bb.msg.debug(1, bb.msg.domain.Cache, "Using cache in '%s'" % self.cachedir)
65             try:
66                 os.stat( self.cachedir )
67             except OSError:
68                 bb.mkdirhier( self.cachedir )
69
70         if self.has_cache and (self.mtime(self.cachefile)):
71             try:
72                 p = pickle.Unpickler( file(self.cachefile,"rb"))
73                 self.depends_cache, version_data = p.load()
74                 if version_data['CACHE_VER'] != __cache_version__:
75                     raise ValueError, 'Cache Version Mismatch'
76                 if version_data['BITBAKE_VER'] != bb.__version__:
77                     raise ValueError, 'Bitbake Version Mismatch'
78             except EOFError:
79                 bb.msg.note(1, bb.msg.domain.Cache, "Truncated cache found, rebuilding...")
80                 self.depends_cache = {}
81             except (ValueError, KeyError):
82                 bb.msg.note(1, bb.msg.domain.Cache, "Invalid cache found, rebuilding...")
83                 self.depends_cache = {}
84
85         if self.depends_cache:
86             for fn in self.depends_cache.keys():
87                 self.clean[fn] = ""
88                 self.cacheValidUpdate(fn)
89
90     def getVar(self, var, fn, exp = 0):
91         """
92         Gets the value of a variable
93         (similar to getVar in the data class)
94         
95         There are two scenarios:
96           1. We have cached data - serve from depends_cache[fn]
97           2. We're learning what data to cache - serve from data 
98              backend but add a copy of the data to the cache.
99         """
100
101         if fn in self.clean:
102             return self.depends_cache[fn][var]
103
104         if not fn in self.depends_cache:
105             self.depends_cache[fn] = {}
106
107         if fn != self.data_fn:
108             # We're trying to access data in the cache which doesn't exist
109             # yet setData hasn't been called to setup the right access. Very bad.
110             bb.msg.error(bb.msg.domain.Cache, "Parsing error data_fn %s and fn %s don't match" % (self.data_fn, fn))
111
112         result = bb.data.getVar(var, self.data, exp)
113         self.depends_cache[fn][var] = result
114         return result
115
116     def setData(self, fn, data):
117         """
118         Called to prime bb_cache ready to learn which variables to cache.
119         Will be followed by calls to self.getVar which aren't cached
120         but can be fulfilled from self.data.
121         """
122         self.data_fn = fn
123         self.data = data
124
125         # Make sure __depends makes the depends_cache
126         self.getVar("__depends", fn, True)
127         self.depends_cache[fn]["CACHETIMESTAMP"] = bb.parse.cached_mtime(fn)
128
129     def loadDataFull(self, fn, cfgData):
130         """
131         Return a complete set of data for fn.
132         To do this, we need to parse the file.
133         """
134         bb_data, skipped = self.load_bbfile(fn, cfgData)
135         return bb_data
136
137     def loadData(self, fn, cfgData):
138         """
139         Load a subset of data for fn.
140         If the cached data is valid we do nothing,
141         To do this, we need to parse the file and set the system
142         to record the variables accessed.
143         Return the cache status and whether the file was skipped when parsed
144         """
145         if self.cacheValid(fn):
146             if "SKIPPED" in self.depends_cache[fn]:
147                 return True, True
148             return True, False
149
150         bb_data, skipped = self.load_bbfile(fn, cfgData)
151         self.setData(fn, bb_data)
152         return False, skipped
153
154     def cacheValid(self, fn):
155         """
156         Is the cache valid for fn?
157         Fast version, no timestamps checked.
158         """
159         # Is cache enabled?
160         if not self.has_cache:
161             return False
162         if fn in self.clean:
163             return True
164         return False
165
166     def cacheValidUpdate(self, fn):
167         """
168         Is the cache valid for fn?
169         Make thorough (slower) checks including timestamps.
170         """
171         # Is cache enabled?
172         if not self.has_cache:
173             return False
174
175         # Check file still exists
176         if self.mtime(fn) == 0:
177             bb.msg.debug(2, bb.msg.domain.Cache, "Cache: %s not longer exists" % fn)
178             self.remove(fn)
179             return False
180
181         # File isn't in depends_cache
182         if not fn in self.depends_cache:
183             bb.msg.debug(2, bb.msg.domain.Cache, "Cache: %s is not cached" % fn)
184             self.remove(fn)
185             return False
186
187         # Check the file's timestamp
188         if bb.parse.cached_mtime(fn) > self.getVar("CACHETIMESTAMP", fn, True):
189             bb.msg.debug(2, bb.msg.domain.Cache, "Cache: %s changed" % fn)
190             self.remove(fn)
191             return False
192
193         # Check dependencies are still valid
194         depends = self.getVar("__depends", fn, True)
195         for f,old_mtime in depends:
196             # Check if file still exists
197             if self.mtime(f) == 0:
198                 return False
199
200             new_mtime = bb.parse.cached_mtime(f)
201             if (new_mtime > old_mtime):
202                 bb.msg.debug(2, bb.msg.domain.Cache, "Cache: %s's dependency %s changed" % (fn, f))
203                 self.remove(fn)
204                 return False
205
206         bb.msg.debug(2, bb.msg.domain.Cache, "Depends Cache: %s is clean" % fn)
207         if not fn in self.clean:
208             self.clean[fn] = ""
209
210         return True
211
212     def skip(self, fn):
213         """
214         Mark a fn as skipped
215         Called from the parser
216         """
217         if not fn in self.depends_cache:
218             self.depends_cache[fn] = {}
219         self.depends_cache[fn]["SKIPPED"] = "1"
220
221     def remove(self, fn):
222         """
223         Remove a fn from the cache
224         Called from the parser in error cases
225         """
226         bb.msg.debug(1, bb.msg.domain.Cache, "Removing %s from cache" % fn)
227         if fn in self.depends_cache:
228             del self.depends_cache[fn]
229         if fn in self.clean:
230             del self.clean[fn]
231
232     def sync(self):
233         """
234         Save the cache
235         Called from the parser when complete (or exiting)
236         """
237
238         if not self.has_cache:
239             return
240
241         version_data = {}
242         version_data['CACHE_VER'] = __cache_version__
243         version_data['BITBAKE_VER'] = bb.__version__
244
245         p = pickle.Pickler(file(self.cachefile, "wb" ), -1 )
246         p.dump([self.depends_cache, version_data])
247
248     def mtime(self, cachefile):
249         return bb.parse.cached_mtime_noerror(cachefile)
250
251     def handle_data(self, file_name, cacheData):
252         """
253         Save data we need into the cache 
254         """
255
256         pn       = self.getVar('PN', file_name, True)
257         pe       = self.getVar('PE', file_name, True) or "0"
258         pv       = self.getVar('PV', file_name, True)
259         pr       = self.getVar('PR', file_name, True)
260         dp       = int(self.getVar('DEFAULT_PREFERENCE', file_name, True) or "0")
261         provides  = Set([pn] + (self.getVar("PROVIDES", file_name, True) or "").split())
262         depends   = bb.utils.explode_deps(self.getVar("DEPENDS", file_name, True) or "")
263         packages  = (self.getVar('PACKAGES', file_name, True) or "").split()
264         packages_dynamic = (self.getVar('PACKAGES_DYNAMIC', file_name, True) or "").split()
265         rprovides = (self.getVar("RPROVIDES", file_name, True) or "").split()
266
267         cacheData.task_queues[file_name] = self.getVar("_task_graph", file_name, True)
268         cacheData.task_deps[file_name] = self.getVar("_task_deps", file_name, True)
269
270         # build PackageName to FileName lookup table
271         if pn not in cacheData.pkg_pn:
272             cacheData.pkg_pn[pn] = []
273         cacheData.pkg_pn[pn].append(file_name)
274
275         cacheData.stamp[file_name] = self.getVar('STAMP', file_name, True)
276
277         # build FileName to PackageName lookup table
278         cacheData.pkg_fn[file_name] = pn
279         cacheData.pkg_pepvpr[file_name] = (pe,pv,pr)
280         cacheData.pkg_dp[file_name] = dp
281
282         # Build forward and reverse provider hashes
283         # Forward: virtual -> [filenames]
284         # Reverse: PN -> [virtuals]
285         if pn not in cacheData.pn_provides:
286             cacheData.pn_provides[pn] = Set()
287         cacheData.pn_provides[pn] |= provides
288
289         cacheData.fn_provides[file_name] = Set()
290         for provide in provides:
291             if provide not in cacheData.providers:
292                 cacheData.providers[provide] = []
293             cacheData.providers[provide].append(file_name)
294             cacheData.fn_provides[file_name].add(provide)
295
296         cacheData.deps[file_name] = Set()
297         for dep in depends:
298             cacheData.all_depends.add(dep)
299             cacheData.deps[file_name].add(dep)
300
301         # Build reverse hash for PACKAGES, so runtime dependencies 
302         # can be be resolved (RDEPENDS, RRECOMMENDS etc.)
303         for package in packages:
304             if not package in cacheData.packages:
305                 cacheData.packages[package] = []
306             cacheData.packages[package].append(file_name)
307             rprovides += (self.getVar("RPROVIDES_%s" % package, file_name, 1) or "").split() 
308
309         for package in packages_dynamic:
310             if not package in cacheData.packages_dynamic:
311                 cacheData.packages_dynamic[package] = []
312             cacheData.packages_dynamic[package].append(file_name)
313
314         for rprovide in rprovides:
315             if not rprovide in cacheData.rproviders:
316                 cacheData.rproviders[rprovide] = []
317             cacheData.rproviders[rprovide].append(file_name)
318
319         # Build hash of runtime depends and rececommends
320
321         def add_dep(deplist, deps):
322             for dep in deps:
323                 if not dep in deplist:
324                     deplist[dep] = ""
325
326         if not file_name in cacheData.rundeps:
327             cacheData.rundeps[file_name] = {}
328         if not file_name in cacheData.runrecs:
329             cacheData.runrecs[file_name] = {}
330
331         for package in packages + [pn]:
332             if not package in cacheData.rundeps[file_name]:
333                 cacheData.rundeps[file_name][package] = {}
334             if not package in cacheData.runrecs[file_name]:
335                 cacheData.runrecs[file_name][package] = {}
336
337             add_dep(cacheData.rundeps[file_name][package], bb.utils.explode_deps(self.getVar('RDEPENDS', file_name, True) or ""))
338             add_dep(cacheData.runrecs[file_name][package], bb.utils.explode_deps(self.getVar('RRECOMMENDS', file_name, True) or ""))
339             add_dep(cacheData.rundeps[file_name][package], bb.utils.explode_deps(self.getVar("RDEPENDS_%s" % package, file_name, True) or ""))
340             add_dep(cacheData.runrecs[file_name][package], bb.utils.explode_deps(self.getVar("RRECOMMENDS_%s" % package, file_name, True) or ""))
341
342         # Collect files we may need for possible world-dep
343         # calculations
344         if not self.getVar('BROKEN', file_name, True) and not self.getVar('EXCLUDE_FROM_WORLD', file_name, True):
345             cacheData.possible_world.append(file_name)
346
347
348     def load_bbfile( self, bbfile , config):
349         """
350         Load and parse one .bb build file
351         Return the data and whether parsing resulted in the file being skipped
352         """
353
354         import bb
355         from bb import utils, data, parse, debug, event, fatal
356
357         # expand tmpdir to include this topdir
358         data.setVar('TMPDIR', data.getVar('TMPDIR', config, 1) or "", config)
359         bbfile_loc = os.path.abspath(os.path.dirname(bbfile))
360         oldpath = os.path.abspath(os.getcwd())
361         if self.mtime(bbfile_loc):
362             os.chdir(bbfile_loc)
363         bb_data = data.init_db(config)
364         try:
365             bb_data = parse.handle(bbfile, bb_data) # read .bb data
366             os.chdir(oldpath)
367             return bb_data, False
368         except bb.parse.SkipPackage:
369             os.chdir(oldpath)
370             return bb_data, True
371         except:
372             os.chdir(oldpath)
373             raise
374
375 def init(cooker):
376     """
377     The Objective: Cache the minimum amount of data possible yet get to the 
378     stage of building packages (i.e. tryBuild) without reparsing any .bb files.
379
380     To do this, we intercept getVar calls and only cache the variables we see 
381     being accessed. We rely on the cache getVar calls being made for all 
382     variables bitbake might need to use to reach this stage. For each cached 
383     file we need to track:
384
385     * Its mtime
386     * The mtimes of all its dependencies
387     * Whether it caused a parse.SkipPackage exception
388
389     Files causing parsing errors are evicted from the cache.
390
391     """
392     return Cache(cooker)
393
394
395
396 #============================================================================#
397 # CacheData
398 #============================================================================#
399 class CacheData:
400     """
401     The data structures we compile from the cached data
402     """
403
404     def __init__(self):
405         """
406         Direct cache variables
407         (from Cache.handle_data)
408         """
409         self.providers   = {}
410         self.rproviders = {}
411         self.packages = {}
412         self.packages_dynamic = {}
413         self.possible_world = []
414         self.pkg_pn = {}
415         self.pkg_fn = {}
416         self.pkg_pepvpr = {}
417         self.pkg_dp = {}
418         self.pn_provides = {}
419         self.fn_provides = {}
420         self.all_depends = Set()
421         self.deps = {}
422         self.rundeps = {}
423         self.runrecs = {}
424         self.task_queues = {}
425         self.task_deps = {}
426         self.stamp = {}
427         self.preferred = {}
428
429         """
430         Indirect Cache variables
431         (set elsewhere)
432         """
433         self.ignored_dependencies = []
434         self.world_target = Set()
435         self.bbfile_priority = {}
436         self.bbfile_config_priorities = []