2 # ex:ts=4:sw=4:sts=4:et
3 # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
5 BitBake 'Event' implementation
7 Caching of bitbake variables before task execution
9 # Copyright (C) 2006 Richard Purdie
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
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
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.
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.
38 import cPickle as pickle
41 print "NOTE: Importing cPickle failed. Falling back to a very slow implementation."
43 # __cache_version__ = "123"
44 __cache_version__ = "124" # changes the __depends structure
48 BitBake Cache implementation
50 def __init__(self, cooker):
53 self.cachedir = bb.data.getVar("CACHE", cooker.configuration.data, True)
55 self.depends_cache = {}
59 if self.cachedir in [None, '']:
60 self.has_cache = False
61 if cooker.cb is not None:
62 print "NOTE: Not using a cache. Set CACHE = <directory> to enable."
65 self.cachefile = os.path.join(self.cachedir,"bb_cache.dat")
67 if cooker.cb is not None:
68 print "NOTE: Using cache in '%s'" % self.cachedir
70 os.stat( self.cachedir )
72 bb.mkdirhier( self.cachedir )
74 if self.has_cache and (self.mtime(self.cachefile)):
76 p = pickle.Unpickler( file(self.cachefile,"rb"))
77 self.depends_cache, version_data = p.load()
78 if version_data['CACHE_VER'] != __cache_version__:
79 raise ValueError, 'Cache Version Mismatch'
80 if version_data['BITBAKE_VER'] != bb.__version__:
81 raise ValueError, 'Bitbake Version Mismatch'
82 except (ValueError, KeyError):
83 bb.note("Invalid cache found, rebuilding...")
84 self.depends_cache = {}
86 if self.depends_cache:
87 for fn in self.depends_cache.keys():
89 self.cacheValidUpdate(fn)
91 def getVar(self, var, fn, exp = 0):
93 Gets the value of a variable
94 (similar to getVar in the data class)
96 There are two scenarios:
97 1. We have cached data - serve from depends_cache[fn]
98 2. We're learning what data to cache - serve from data
99 backend but add a copy of the data to the cache.
103 return self.depends_cache[fn][var]
105 if not fn in self.depends_cache:
106 self.depends_cache[fn] = {}
108 if fn != self.data_fn:
109 # We're trying to access data in the cache which doesn't exist
110 # yet setData hasn't been called to setup the right access. Very bad.
111 bb.error("Parsing error data_fn %s and fn %s don't match" % (self.data_fn, fn))
113 result = bb.data.getVar(var, self.data, exp)
114 self.depends_cache[fn][var] = result
117 def setData(self, fn, data):
119 Called to prime bb_cache ready to learn which variables to cache.
120 Will be followed by calls to self.getVar which aren't cached
121 but can be fulfilled from self.data.
126 # Make sure __depends makes the depends_cache
127 self.getVar("__depends", fn, True)
128 self.depends_cache[fn]["CACHETIMESTAMP"] = bb.parse.cached_mtime(fn)
130 def loadDataFull(self, fn, cooker):
132 Return a complete set of data for fn.
133 To do this, we need to parse the file.
135 bb_data, skipped = self.load_bbfile(fn, cooker)
138 def loadData(self, fn, cooker):
140 Load a subset of data for fn.
141 If the cached data is valid we do nothing,
142 To do this, we need to parse the file and set the system
143 to record the variables accessed.
144 Return the cache status and whether the file was skipped when parsed
146 if self.cacheValid(fn):
147 if "SKIPPED" in self.depends_cache[fn]:
151 bb_data, skipped = self.load_bbfile(fn, cooker)
152 self.setData(fn, bb_data)
153 return False, skipped
155 def cacheValid(self, fn):
157 Is the cache valid for fn?
158 Fast version, no timestamps checked.
161 if not self.has_cache:
167 def cacheValidUpdate(self, fn):
169 Is the cache valid for fn?
170 Make thorough (slower) checks including timestamps.
173 if not self.has_cache:
176 # Check file still exists
177 if self.mtime(fn) == 0:
178 bb.debug(2, "Cache: %s not longer exists" % fn)
182 # File isn't in depends_cache
183 if not fn in self.depends_cache:
184 bb.debug(2, "Cache: %s is not cached" % fn)
188 # Check the file's timestamp
189 if bb.parse.cached_mtime(fn) > self.getVar("CACHETIMESTAMP", fn, True):
190 bb.debug(2, "Cache: %s changed" % fn)
194 # Check dependencies are still valid
195 depends = self.getVar("__depends", fn, True)
196 for f,old_mtime in depends:
197 new_mtime = bb.parse.cached_mtime(f)
198 if (new_mtime > old_mtime):
199 bb.debug(2, "Cache: %s's dependency %s changed" % (fn, f))
203 bb.debug(2, "Depends Cache: %s is clean" % fn)
204 if not fn in self.clean:
212 Called from the parser
214 if not fn in self.depends_cache:
215 self.depends_cache[fn] = {}
216 self.depends_cache[fn]["SKIPPED"] = "1"
218 def remove(self, fn):
220 Remove a fn from the cache
221 Called from the parser in error cases
223 bb.debug(1, "Removing %s from cache" % fn)
224 if fn in self.depends_cache:
225 del self.depends_cache[fn]
232 Called from the parser when complete (or exitting)
235 if not self.has_cache:
239 version_data['CACHE_VER'] = __cache_version__
240 version_data['BITBAKE_VER'] = bb.__version__
242 p = pickle.Pickler(file(self.cachefile, "wb" ), -1 )
243 p.dump([self.depends_cache, version_data])
245 def mtime(self, cachefile):
247 return os.stat(cachefile)[8]
251 def load_bbfile( self, bbfile , cooker):
253 Load and parse one .bb build file
254 Return the data and whether parsing resulted in the file being skipped
258 from bb import utils, data, parse, debug, event, fatal
260 topdir = data.getVar('TOPDIR', cooker.configuration.data)
262 topdir = os.path.abspath(os.getcwd())
264 data.setVar('TOPDIR', topdir, cooker.configuration)
265 bbfile = os.path.abspath(bbfile)
266 bbfile_loc = os.path.abspath(os.path.dirname(bbfile))
267 # expand tmpdir to include this topdir
268 data.setVar('TMPDIR', data.getVar('TMPDIR', cooker.configuration.data, 1) or "", cooker.configuration.data)
269 # set topdir to location of .bb file
271 #data.setVar('TOPDIR', topdir, cfg)
273 oldpath = os.path.abspath(os.getcwd())
274 if self.mtime(topdir):
276 bb_data = data.init_db(cooker.configuration.data)
278 parse.handle(bbfile, bb_data) # read .bb data
280 return bb_data, False
281 except bb.parse.SkipPackage:
290 The Objective: Cache the minimum amount of data possible yet get to the
291 stage of building packages (i.e. tryBuild) without reparsing any .bb files.
293 To do this, we intercept getVar calls and only cache the variables we see
294 being accessed. We rely on the cache getVar calls being made for all
295 variables bitbake might need to use to reach this stage. For each cached
296 file we need to track:
299 * The mtimes of all its dependencies
300 * Whether it caused a parse.SkipPackage exception
302 Files causing parsing errors are evicted from the cache.