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):
51 self.cachedir = bb.data.getVar("CACHE", cooker.configuration.data, True)
52 self.cachefile = os.path.join(self.cachedir,"bb_cache.dat")
54 self.depends_cache = {}
58 if self.cachedir in [None, '']:
59 if cooker.cb is not None:
60 print "NOTE: Not using a cache. Set CACHE = <directory> to enable."
62 if cooker.cb is not None:
63 print "NOTE: Using cache in '%s'" % self.cachedir
65 os.stat( self.cachedir )
67 bb.mkdirhier( self.cachedir )
69 if (self.mtime(self.cachefile)):
71 p = pickle.Unpickler( file(self.cachefile,"rb"))
72 self.depends_cache, version_data = p.load()
73 if version_data['CACHE_VER'] != __cache_version__:
74 raise ValueError, 'Cache Version Mismatch'
75 if version_data['BITBAKE_VER'] != bb.__version__:
76 raise ValueError, 'Bitbake Version Mismatch'
77 except (ValueError, KeyError):
78 bb.note("Invalid cache found, rebuilding...")
79 self.depends_cache = {}
81 if self.depends_cache:
82 for fn in self.depends_cache.keys():
84 self.cacheValidUpdate(fn)
86 def getVar(self, var, fn, exp = 0):
88 Gets the value of a variable
89 (similar to getVar in the data class)
91 There are two scenarios:
92 1. We have cached data - serve from depends_cache[fn]
93 2. We're learning what data to cache - serve from data
94 backend but add a copy of the data to the cache.
98 return self.depends_cache[fn][var]
100 if not fn in self.depends_cache:
101 self.depends_cache[fn] = {}
103 if fn != self.data_fn:
104 # We're trying to access data in the cache which doesn't exist
105 # yet setData hasn't been called to setup the right access. Very bad.
106 bb.error("Parsing error data_fn %s and fn %s don't match" % (self.data_fn, fn))
108 result = bb.data.getVar(var, self.data, exp)
109 self.depends_cache[fn][var] = result
112 def setData(self, fn, data):
114 Called to prime bb_cache ready to learn which variables to cache.
115 Will be followed by calls to self.getVar which aren't cached
116 but can be fulfilled from self.data.
121 # Make sure __depends makes the depends_cache
122 self.getVar("__depends", fn, True)
123 self.depends_cache[fn]["CACHETIMESTAMP"] = bb.parse.cached_mtime(fn)
125 def loadDataFull(self, fn, cooker):
127 Return a complete set of data for fn.
128 To do this, we need to parse the file.
130 bb_data, skipped = self.load_bbfile(fn, cooker)
133 def loadData(self, fn, cooker):
135 Load a subset of data for fn.
136 If the cached data is valid we do nothing,
137 To do this, we need to parse the file and set the system
138 to record the variables accessed.
139 Return the cache status and whether the file was skipped when parsed
141 if self.cacheValid(fn):
142 if "SKIPPED" in self.depends_cache[fn]:
146 bb_data, skipped = self.load_bbfile(fn, cooker)
147 self.setData(fn, bb_data)
148 return False, skipped
150 def cacheValid(self, fn):
152 Is the cache valid for fn?
153 Fast version, no timestamps checked.
156 if self.cachedir in [None, '']:
162 def cacheValidUpdate(self, fn):
164 Is the cache valid for fn?
165 Make thorough (slower) checks including timestamps.
168 if self.cachedir in [None, '']:
171 # Check file still exists
172 if self.mtime(fn) == 0:
173 bb.debug(2, "Cache: %s not longer exists" % fn)
176 if fn in self.depends_cache:
177 del self.depends_cache[fn]
180 # File isn't in depends_cache
181 if not fn in self.depends_cache:
182 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)
194 # Check dependencies are still valid
195 depends = self.getVar("__depends", fn, True)
197 deps = depends.split(" ")
199 (f,old_mtime_s) = dep.split("@")
200 old_mtime = int(old_mtime_s)
201 new_mtime = bb.parse.cached_mtime(f)
202 if (new_mtime > old_mtime):
203 bb.debug(2, "Cache: %s's dependency %s changed" % (fn, f))
208 bb.debug(2, "Depends Cache: %s is clean" % fn)
209 if not fn in self.clean:
217 Called from the parser
219 if not fn in self.depends_cache:
220 self.depends_cache[fn] = {}
221 self.depends_cache[fn]["SKIPPED"] = "1"
223 def remove(self, fn):
225 Remove a fn from the cache
226 Called from the parser in error cases
228 bb.note("Removing %s from cache" % fn)
229 if fn in self.depends_cache:
230 del self.depends_cache[fn]
235 Called from the parser when complete (or exitting)
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())
275 bb_data = data.init_db(cooker.configuration.data)
277 parse.handle(bbfile, bb_data) # read .bb data
279 return bb_data, False
280 except bb.parse.SkipPackage:
289 The Objective: Cache the minimum amount of data possible yet get to the
290 stage of building packages (i.e. tryBuild) without reparsing any .bb files.
292 To do this, we intercept getVar calls and only cache the variables we see
293 being accessed. We rely on the cache getVar calls being made for all
294 variables bitbake might need to use to reach this stage. For each cached
295 file we need to track:
298 * The mtimes of all its dependencies
299 * Whether it caused a parse.SkipPackage exception
301 Files causing parsing errors are evicted from the cache.