cache.py: Catch truncated cache file errors (from trunk)
[bitbake.git] / 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__ = "125"
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         pv       = self.getVar('PV', file_name, True)
258         pr       = self.getVar('PR', file_name, True)
259         dp       = int(self.getVar('DEFAULT_PREFERENCE', file_name, True) or "0")
260         provides  = Set([pn] + (self.getVar("PROVIDES", file_name, True) or "").split())
261         depends   = bb.utils.explode_deps(self.getVar("DEPENDS", file_name, True) or "")
262         packages  = (self.getVar('PACKAGES', file_name, True) or "").split()
263         packages_dynamic = (self.getVar('PACKAGES_DYNAMIC', file_name, True) or "").split()
264         rprovides = (self.getVar("RPROVIDES", file_name, True) or "").split()
265
266         cacheData.task_queues[file_name] = self.getVar("_task_graph", file_name, True)
267         cacheData.task_deps[file_name] = self.getVar("_task_deps", file_name, True)
268
269         # build PackageName to FileName lookup table
270         if pn not in cacheData.pkg_pn:
271             cacheData.pkg_pn[pn] = []
272         cacheData.pkg_pn[pn].append(file_name)
273
274         cacheData.stamp[file_name] = self.getVar('STAMP', file_name, True)
275
276         # build FileName to PackageName lookup table
277         cacheData.pkg_fn[file_name] = pn
278         cacheData.pkg_pvpr[file_name] = (pv,pr)
279         cacheData.pkg_dp[file_name] = dp
280
281         # Build forward and reverse provider hashes
282         # Forward: virtual -> [filenames]
283         # Reverse: PN -> [virtuals]
284         if pn not in cacheData.pn_provides:
285             cacheData.pn_provides[pn] = Set()
286         cacheData.pn_provides[pn] |= provides
287
288         for provide in provides:
289             if provide not in cacheData.providers:
290                 cacheData.providers[provide] = []
291             cacheData.providers[provide].append(file_name)
292
293         cacheData.deps[file_name] = Set()
294         for dep in depends:
295             cacheData.all_depends.add(dep)
296             cacheData.deps[file_name].add(dep)
297
298         # Build reverse hash for PACKAGES, so runtime dependencies 
299         # can be be resolved (RDEPENDS, RRECOMMENDS etc.)
300         for package in packages:
301             if not package in cacheData.packages:
302                 cacheData.packages[package] = []
303             cacheData.packages[package].append(file_name)
304             rprovides += (self.getVar("RPROVIDES_%s" % package, file_name, 1) or "").split() 
305
306         for package in packages_dynamic:
307             if not package in cacheData.packages_dynamic:
308                 cacheData.packages_dynamic[package] = []
309             cacheData.packages_dynamic[package].append(file_name)
310
311         for rprovide in rprovides:
312             if not rprovide in cacheData.rproviders:
313                 cacheData.rproviders[rprovide] = []
314             cacheData.rproviders[rprovide].append(file_name)
315
316         # Build hash of runtime depends and rececommends
317
318         def add_dep(deplist, deps):
319             for dep in deps:
320                 if not dep in deplist:
321                     deplist[dep] = ""
322
323         if not file_name in cacheData.rundeps:
324             cacheData.rundeps[file_name] = {}
325         if not file_name in cacheData.runrecs:
326             cacheData.runrecs[file_name] = {}
327
328         for package in packages + [pn]:
329             if not package in cacheData.rundeps[file_name]:
330                 cacheData.rundeps[file_name][package] = {}
331             if not package in cacheData.runrecs[file_name]:
332                 cacheData.runrecs[file_name][package] = {}
333
334             add_dep(cacheData.rundeps[file_name][package], bb.utils.explode_deps(self.getVar('RDEPENDS', file_name, True) or ""))
335             add_dep(cacheData.runrecs[file_name][package], bb.utils.explode_deps(self.getVar('RRECOMMENDS', file_name, True) or ""))
336             add_dep(cacheData.rundeps[file_name][package], bb.utils.explode_deps(self.getVar("RDEPENDS_%s" % package, file_name, True) or ""))
337             add_dep(cacheData.runrecs[file_name][package], bb.utils.explode_deps(self.getVar("RRECOMMENDS_%s" % package, file_name, True) or ""))
338
339         # Collect files we may need for possible world-dep
340         # calculations
341         if not self.getVar('BROKEN', file_name, True) and not self.getVar('EXCLUDE_FROM_WORLD', file_name, True):
342             cacheData.possible_world.append(file_name)
343
344
345     def load_bbfile( self, bbfile , config):
346         """
347         Load and parse one .bb build file
348         Return the data and whether parsing resulted in the file being skipped
349         """
350
351         import bb
352         from bb import utils, data, parse, debug, event, fatal
353
354         # expand tmpdir to include this topdir
355         data.setVar('TMPDIR', data.getVar('TMPDIR', config, 1) or "", config)
356         bbfile_loc = os.path.abspath(os.path.dirname(bbfile))
357         oldpath = os.path.abspath(os.getcwd())
358         if self.mtime(bbfile_loc):
359             os.chdir(bbfile_loc)
360         bb_data = data.init_db(config)
361         try:
362             bb_data = parse.handle(bbfile, bb_data) # read .bb data
363             os.chdir(oldpath)
364             return bb_data, False
365         except bb.parse.SkipPackage:
366             os.chdir(oldpath)
367             return bb_data, True
368         except:
369             os.chdir(oldpath)
370             raise
371
372 def init(cooker):
373     """
374     The Objective: Cache the minimum amount of data possible yet get to the 
375     stage of building packages (i.e. tryBuild) without reparsing any .bb files.
376
377     To do this, we intercept getVar calls and only cache the variables we see 
378     being accessed. We rely on the cache getVar calls being made for all 
379     variables bitbake might need to use to reach this stage. For each cached 
380     file we need to track:
381
382     * Its mtime
383     * The mtimes of all its dependencies
384     * Whether it caused a parse.SkipPackage exception
385
386     Files causing parsing errors are evicted from the cache.
387
388     """
389     return Cache(cooker)
390
391
392
393 #============================================================================#
394 # CacheData
395 #============================================================================#
396 class CacheData:
397     """
398     The data structures we compile from the cached data
399     """
400
401     def __init__(self):
402         """
403         Direct cache variables
404         (from Cache.handle_data)
405         """
406         self.providers   = {}
407         self.rproviders = {}
408         self.packages = {}
409         self.packages_dynamic = {}
410         self.possible_world = []
411         self.pkg_pn = {}
412         self.pkg_fn = {}
413         self.pkg_pvpr = {}
414         self.pkg_dp = {}
415         self.pn_provides = {}
416         self.all_depends = Set()
417         self.deps = {}
418         self.rundeps = {}
419         self.runrecs = {}
420         self.task_queues = {}
421         self.task_deps = {}
422         self.stamp = {}
423         self.preferred = {}
424
425         """
426         Indirect Cache variables
427         (set elsewhere)
428         """
429         self.ignored_dependencies = []
430         self.world_target = Set()
431         self.bbfile_priority = {}
432         self.bbfile_config_priorities = []