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"
47 BitBake Cache implementation
49 def __init__(self, cooker):
52 self.cachedir = bb.data.getVar("CACHE", cooker.configuration.data, True)
54 self.depends_cache = {}
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."
64 self.cachefile = os.path.join(self.cachedir,"bb_cache.dat")
66 if cooker.cb is not None:
67 print "NOTE: Using cache in '%s'" % self.cachedir
69 os.stat( self.cachedir )
71 bb.mkdirhier( self.cachedir )
73 if self.has_cache and (self.mtime(self.cachefile)):
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 = {}
85 if self.depends_cache:
86 for fn in self.depends_cache.keys():
88 self.cacheValidUpdate(fn)
90 def getVar(self, var, fn, exp = 0):
92 Gets the value of a variable
93 (similar to getVar in the data class)
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.
102 return self.depends_cache[fn][var]
104 if not fn in self.depends_cache:
105 self.depends_cache[fn] = {}
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))
112 result = bb.data.getVar(var, self.data, exp)
113 self.depends_cache[fn][var] = result
116 def setData(self, fn, data):
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.
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)
129 def loadDataFull(self, fn, cooker):
131 Return a complete set of data for fn.
132 To do this, we need to parse the file.
134 bb_data, skipped = self.load_bbfile(fn, cooker)
137 def loadData(self, fn, cooker):
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
145 if self.cacheValid(fn):
146 if "SKIPPED" in self.depends_cache[fn]:
150 bb_data, skipped = self.load_bbfile(fn, cooker)
151 self.setData(fn, bb_data)
152 return False, skipped
154 def cacheValid(self, fn):
156 Is the cache valid for fn?
157 Fast version, no timestamps checked.
160 if not self.has_cache:
166 def cacheValidUpdate(self, fn):
168 Is the cache valid for fn?
169 Make thorough (slower) checks including timestamps.
172 if not self.has_cache:
175 # Check file still exists
176 if self.mtime(fn) == 0:
177 bb.debug(2, "Cache: %s not longer exists" % fn)
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)
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)
193 # Check dependencies are still valid
194 depends = self.getVar("__depends", fn, True)
196 deps = depends.split(" ")
198 (f,old_mtime_s) = dep.split("@")
199 old_mtime = int(old_mtime_s)
200 new_mtime = bb.parse.cached_mtime(f)
201 if (new_mtime > old_mtime):
202 bb.debug(2, "Cache: %s's dependency %s changed" % (fn, f))
206 bb.debug(2, "Depends Cache: %s is clean" % fn)
207 if not fn in self.clean:
215 Called from the parser
217 if not fn in self.depends_cache:
218 self.depends_cache[fn] = {}
219 self.depends_cache[fn]["SKIPPED"] = "1"
221 def remove(self, fn):
223 Remove a fn from the cache
224 Called from the parser in error cases
226 bb.debug(1, "Removing %s from cache" % fn)
227 if fn in self.depends_cache:
228 del self.depends_cache[fn]
235 Called from the parser when complete (or exitting)
238 if not self.has_cache:
242 version_data['CACHE_VER'] = __cache_version__
243 version_data['BITBAKE_VER'] = bb.__version__
245 p = pickle.Pickler(file(self.cachefile, "wb" ), -1 )
246 p.dump([self.depends_cache, version_data])
248 def mtime(self, cachefile):
250 return os.stat(cachefile)[8]
254 def load_bbfile( self, bbfile , cooker):
256 Load and parse one .bb build file
257 Return the data and whether parsing resulted in the file being skipped
261 from bb import utils, data, parse, debug, event, fatal
263 topdir = data.getVar('TOPDIR', cooker.configuration.data)
265 topdir = os.path.abspath(os.getcwd())
267 data.setVar('TOPDIR', topdir, cooker.configuration)
268 bbfile = os.path.abspath(bbfile)
269 bbfile_loc = os.path.abspath(os.path.dirname(bbfile))
270 # expand tmpdir to include this topdir
271 data.setVar('TMPDIR', data.getVar('TMPDIR', cooker.configuration.data, 1) or "", cooker.configuration.data)
272 # set topdir to location of .bb file
274 #data.setVar('TOPDIR', topdir, cfg)
276 oldpath = os.path.abspath(os.getcwd())
278 bb_data = data.init_db(cooker.configuration.data)
280 parse.handle(bbfile, bb_data) # read .bb data
282 return bb_data, False
283 except bb.parse.SkipPackage:
292 The Objective: Cache the minimum amount of data possible yet get to the
293 stage of building packages (i.e. tryBuild) without reparsing any .bb files.
295 To do this, we intercept getVar calls and only cache the variables we see
296 being accessed. We rely on the cache getVar calls being made for all
297 variables bitbake might need to use to reach this stage. For each cached
298 file we need to track:
301 * The mtimes of all its dependencies
302 * Whether it caused a parse.SkipPackage exception
304 Files causing parsing errors are evicted from the cache.