fc6f75d732dfe0005582d6dd39583526a939a5ad
[vuplus_bitbake] / 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__ = "123"
44
45 class Cache:
46     """
47     BitBake Cache implementation
48     """
49     def __init__(self, cooker):
50
51         self.cachedir = bb.data.getVar("CACHE", cooker.configuration.data, True)
52         self.cachefile = os.path.join(self.cachedir,"bb_cache.dat")
53         self.clean = {}
54         self.depends_cache = {}
55         self.data = None
56         self.data_fn = None
57
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."
61         else:
62             if cooker.cb is not None:
63                 print "NOTE: Using cache in '%s'" % self.cachedir
64             try:
65                 os.stat( self.cachedir )
66             except OSError:
67                 bb.mkdirhier( self.cachedir )
68
69         if (self.mtime(self.cachefile)):
70             try:
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 = {}
80
81         if self.depends_cache:
82             for fn in self.depends_cache.keys():
83                 self.clean[fn] = ""
84                 self.cacheValidUpdate(fn)
85
86     def getVar(self, var, fn, exp = 0):
87         """
88         Gets the value of a variable
89         (similar to getVar in the data class)
90         
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.
95         """
96
97         if fn in self.clean:
98             return self.depends_cache[fn][var]
99
100         if not fn in self.depends_cache:
101             self.depends_cache[fn] = {}
102
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))
107
108         result = bb.data.getVar(var, self.data, exp)
109         self.depends_cache[fn][var] = result
110         return result
111
112     def setData(self, fn, data):
113         """
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.
117         """
118         self.data_fn = fn
119         self.data = data
120
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)
124
125     def loadDataFull(self, fn, cooker):
126         """
127         Return a complete set of data for fn.
128         To do this, we need to parse the file.
129         """
130         bb_data, skipped = self.load_bbfile(fn, cooker)
131         return bb_data
132
133     def loadData(self, fn, cooker):
134         """
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
140         """
141         if self.cacheValid(fn):
142             if "SKIPPED" in self.depends_cache[fn]:
143                 return True, True
144             return True, False
145
146         bb_data, skipped = self.load_bbfile(fn, cooker)
147         self.setData(fn, bb_data)
148         return False, skipped
149
150     def cacheValid(self, fn):
151         """
152         Is the cache valid for fn?
153         Fast version, no timestamps checked.
154         """
155         # Is cache enabled?
156         if self.cachedir in [None, '']:
157             return False
158         if fn in self.clean:
159             return True
160         return False
161
162     def cacheValidUpdate(self, fn):
163         """
164         Is the cache valid for fn?
165         Make thorough (slower) checks including timestamps.
166         """
167         # Is cache enabled?
168         if self.cachedir in [None, '']:
169             return False
170
171         # Check file still exists
172         if self.mtime(fn) == 0:
173             bb.debug(2, "Cache: %s not longer exists" % fn)
174             self.remove(fn)
175             return False
176
177         # File isn't in depends_cache
178         if not fn in self.depends_cache:
179             bb.debug(2, "Cache: %s is not cached" % fn)
180             self.remove(fn)
181             return False
182
183         # Check the file's timestamp
184         if bb.parse.cached_mtime(fn) > self.getVar("CACHETIMESTAMP", fn, True):
185             bb.debug(2, "Cache: %s changed" % fn)
186             self.remove(fn)
187             return False
188
189         # Check dependencies are still valid
190         depends = self.getVar("__depends", fn, True)
191         if depends:
192             deps = depends.split(" ")
193             for dep in deps:
194                 (f,old_mtime_s) = dep.split("@")
195                 old_mtime = int(old_mtime_s)
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         version_data = {}
235         version_data['CACHE_VER'] = __cache_version__
236         version_data['BITBAKE_VER'] = bb.__version__
237
238         p = pickle.Pickler(file(self.cachefile, "wb" ), -1 )
239         p.dump([self.depends_cache, version_data])
240
241     def mtime(self, cachefile):
242         try:
243             return os.stat(cachefile)[8]
244         except OSError:
245             return 0
246
247     def load_bbfile( self, bbfile , cooker):
248         """
249         Load and parse one .bb build file
250         Return the data and whether parsing resulted in the file being skipped
251         """
252
253         import bb
254         from bb import utils, data, parse, debug, event, fatal
255
256         topdir = data.getVar('TOPDIR', cooker.configuration.data)
257         if not topdir:
258             topdir = os.path.abspath(os.getcwd())
259             # set topdir to here
260             data.setVar('TOPDIR', topdir, cooker.configuration)
261         bbfile = os.path.abspath(bbfile)
262         bbfile_loc = os.path.abspath(os.path.dirname(bbfile))
263         # expand tmpdir to include this topdir
264         data.setVar('TMPDIR', data.getVar('TMPDIR', cooker.configuration.data, 1) or "", cooker.configuration.data)
265         # set topdir to location of .bb file
266         topdir = bbfile_loc
267         #data.setVar('TOPDIR', topdir, cfg)
268         # go there
269         oldpath = os.path.abspath(os.getcwd())
270         os.chdir(topdir)
271         bb_data = data.init_db(cooker.configuration.data)
272         try:
273             parse.handle(bbfile, bb_data) # read .bb data
274             os.chdir(oldpath)
275             return bb_data, False
276         except bb.parse.SkipPackage:
277             os.chdir(oldpath)
278             return bb_data, True
279         except:
280             os.chdir(oldpath)
281             raise
282
283 def init(cooker):
284     """
285     The Objective: Cache the minimum amount of data possible yet get to the 
286     stage of building packages (i.e. tryBuild) without reparsing any .bb files.
287
288     To do this, we intercept getVar calls and only cache the variables we see 
289     being accessed. We rely on the cache getVar calls being made for all 
290     variables bitbake might need to use to reach this stage. For each cached 
291     file we need to track:
292
293     * Its mtime
294     * The mtimes of all its dependencies
295     * Whether it caused a parse.SkipPackage exception
296
297     Files causing parsing errors are evicted from the cache.
298
299     """
300     return Cache(cooker)
301