trunk/bitbake/bin/bitbake:
[bitbake.git] / lib / bb / cache.py
1 #!/usr/bin/env python
2 # ex:ts=4:sw=4:sts=4:et
3 # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
4 """
5 BitBake 'Event' implementation
6
7 Caching of bitbake variables before task execution
8
9 # Copyright (C) 2006        Richard Purdie
10
11 # but small sections based on code from bin/bitbake:
12 # Copyright (C) 2003, 2004  Chris Larson
13 # Copyright (C) 2003, 2004  Phil Blundell
14 # Copyright (C) 2003 - 2005 Michael 'Mickey' Lauer
15 # Copyright (C) 2005        Holger Hans Peter Freyther
16 # Copyright (C) 2005        ROAD GmbH
17
18 This program is free software; you can redistribute it and/or modify it under
19 the terms of the GNU General Public License as published by the Free Software
20 Foundation; either version 2 of the License, or (at your option) any later
21 version.
22
23 This program is distributed in the hope that it will be useful, but WITHOUT
24 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
25 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
26
27 You should have received a copy of the GNU General Public License along with
28 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
29 Place, Suite 330, Boston, MA 02111-1307 USA. 
30
31 """
32
33 import os, re
34 import bb.data
35 import bb.utils
36
37 try:
38     import cPickle as pickle
39 except ImportError:
40     import pickle
41     print "NOTE: Importing cPickle failed. Falling back to a very slow implementation."
42
43 __cache_version__ = "125"
44
45 class Cache:
46     """
47     BitBake Cache implementation
48     """
49     def __init__(self, cooker):
50
51
52         self.cachedir = bb.data.getVar("CACHE", cooker.configuration.data, True)
53         self.clean = {}
54         self.depends_cache = {}
55         self.data = None
56         self.data_fn = None
57
58         if self.cachedir in [None, '']:
59             self.has_cache = False
60             if cooker.cb is not None:
61                 print "NOTE: Not using a cache. Set CACHE = <directory> to enable."
62         else:
63             self.has_cache = True
64             self.cachefile = os.path.join(self.cachedir,"bb_cache.dat")
65             
66             if cooker.cb is not None:
67                 print "NOTE: Using cache in '%s'" % self.cachedir
68             try:
69                 os.stat( self.cachedir )
70             except OSError:
71                 bb.mkdirhier( self.cachedir )
72
73         if self.has_cache and (self.mtime(self.cachefile)):
74             try:
75                 p = pickle.Unpickler( file(self.cachefile,"rb"))
76                 self.depends_cache, version_data = p.load()
77                 if version_data['CACHE_VER'] != __cache_version__:
78                     raise ValueError, 'Cache Version Mismatch'
79                 if version_data['BITBAKE_VER'] != bb.__version__:
80                     raise ValueError, 'Bitbake Version Mismatch'
81             except (ValueError, KeyError):
82                 bb.note("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.error("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, cooker):
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, cooker.configuration.data)
135         return bb_data
136
137     def loadData(self, fn, cooker):
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, cooker.configuration.data)
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.debug(2, "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.debug(2, "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.debug(2, "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             new_mtime = bb.parse.cached_mtime(f)
197             if (new_mtime > old_mtime):
198                 bb.debug(2, "Cache: %s's dependency %s changed" % (fn, f))
199                 self.remove(fn)
200                 return False
201
202         bb.debug(2, "Depends Cache: %s is clean" % fn)
203         if not fn in self.clean:
204             self.clean[fn] = ""
205
206         return True
207
208     def skip(self, fn):
209         """
210         Mark a fn as skipped
211         Called from the parser
212         """
213         if not fn in self.depends_cache:
214             self.depends_cache[fn] = {}
215         self.depends_cache[fn]["SKIPPED"] = "1"
216
217     def remove(self, fn):
218         """
219         Remove a fn from the cache
220         Called from the parser in error cases
221         """
222         bb.debug(1, "Removing %s from cache" % fn)
223         if fn in self.depends_cache:
224             del self.depends_cache[fn]
225         if fn in self.clean:
226             del self.clean[fn]
227
228     def sync(self):
229         """
230         Save the cache
231         Called from the parser when complete (or exitting)
232         """
233
234         if not self.has_cache:
235             return
236
237         version_data = {}
238         version_data['CACHE_VER'] = __cache_version__
239         version_data['BITBAKE_VER'] = bb.__version__
240
241         p = pickle.Pickler(file(self.cachefile, "wb" ), -1 )
242         p.dump([self.depends_cache, version_data])
243
244     def mtime(self, cachefile):
245         try:
246             return os.stat(cachefile)[8]
247         except OSError:
248             return 0
249
250     def load_bbfile( self, bbfile , config):
251         """
252         Load and parse one .bb build file
253         Return the data and whether parsing resulted in the file being skipped
254         """
255
256         import bb
257         from bb import utils, data, parse, debug, event, fatal
258
259         # expand tmpdir to include this topdir
260         data.setVar('TMPDIR', data.getVar('TMPDIR', config, 1) or "", config)
261         bbfile_loc = os.path.abspath(os.path.dirname(bbfile))
262         oldpath = os.path.abspath(os.getcwd())
263         if self.mtime(bbfile_loc):
264             os.chdir(bbfile_loc)
265         bb_data = data.init_db(config)
266         try:
267             bb_data = parse.handle(bbfile, bb_data) # read .bb data
268             os.chdir(oldpath)
269             return bb_data, False
270         except bb.parse.SkipPackage:
271             os.chdir(oldpath)
272             return bb_data, True
273         except:
274             os.chdir(oldpath)
275             raise
276
277 def init(cooker):
278     """
279     The Objective: Cache the minimum amount of data possible yet get to the 
280     stage of building packages (i.e. tryBuild) without reparsing any .bb files.
281
282     To do this, we intercept getVar calls and only cache the variables we see 
283     being accessed. We rely on the cache getVar calls being made for all 
284     variables bitbake might need to use to reach this stage. For each cached 
285     file we need to track:
286
287     * Its mtime
288     * The mtimes of all its dependencies
289     * Whether it caused a parse.SkipPackage exception
290
291     Files causing parsing errors are evicted from the cache.
292
293     """
294     return Cache(cooker)
295