bitbake/lib/bb/cache.py:
[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 class Cache:
44     """
45     BitBake Cache implementation
46     """
47     def __init__(self, cooker):
48
49         self.cachedir = bb.data.getVar("CACHE", cooker.configuration.data, True)
50         self.cachefile = os.path.join(self.cachedir,"bb_cache.dat")
51         self.clean = {}
52         self.depends_cache = {}
53         self.data = None
54         self.data_fn = None
55
56         if self.cachedir in [None, '']:
57             if cooker.cb is not None:
58                 print "NOTE: Not using a cache. Set CACHE = <directory> to enable."
59         else:
60             if cooker.cb is not None:
61                 print "NOTE: Using cache in '%s'" % self.cachedir
62             try:
63                 os.stat( self.cachedir )
64             except OSError:
65                 bb.mkdirhier( self.cachedir )
66
67         if (self.mtime(self.cachefile)):
68             p = pickle.Unpickler( file(self.cachefile,"rb"))
69             self.depends_cache = p.load()
70         if self.depends_cache:
71             for fn in self.depends_cache.keys():
72                 self.clean[fn] = ""
73                 self.cacheValidUpdate(fn)
74
75     def getVar(self, var, fn, exp = 0):
76         """
77         Gets the value of a variable
78         (similar to getVar in the data class)
79         
80         There are two scenarios:
81           1. We have cached data - serve from depends_cache[fn]
82           2. We're learning what data to cache - serve from data 
83              backend but add a copy of the data to the cache.
84         """
85
86         if fn in self.clean:
87             return self.depends_cache[fn][var]
88
89         if not fn in self.depends_cache:
90             self.depends_cache[fn] = {}
91
92         if fn != self.data_fn:
93             # We're trying to access data in the cache which doesn't exist
94             # yet setData hasn't been called to setup the right access. Very bad.
95             bb.error("Parsing error data_fn %s and fn %s don't match" % (self.data_fn, fn))
96
97         result = bb.data.getVar(var, self.data, exp)
98         self.depends_cache[fn][var] = result
99         return result
100
101     def setData(self, fn, data):
102         """
103         Called to prime bb_cache ready to learn which variables to cache.
104         Will be followed by calls to self.getVar which aren't cached
105         but can be fulfilled from self.data.
106         """
107         self.data_fn = fn
108         self.data = data
109
110         # Make sure __depends makes the depends_cache
111         self.getVar("__depends", fn, True)
112         self.depends_cache[fn]["CACHETIMESTAMP"] = bb.parse.cached_mtime(fn)
113
114     def loadDataFull(self, fn, cooker):
115         """
116         Return a complete set of data for fn.
117         To do this, we need to parse the file.
118         """
119         bb_data, skipped = self.load_bbfile(fn, cooker)
120         return bb_data, False
121
122     def loadData(self, fn, cooker):
123         """
124         Load a subset of data for fn.
125         If the cached data is valid we do nothing,
126         To do this, we need to parse the file and set the system
127         to record the variables accessed.
128         Return the cache status and whether the file was skipped when parsed
129         """
130         if self.cacheValid(fn):
131             if "SKIPPED" in self.depends_cache[fn]:
132                 return True, True
133             return True, False
134
135         bb_data, skipped = self.load_bbfile(fn, cooker)
136         self.setData(fn, bb_data)
137         return False, skipped
138
139     def cacheValid(self, fn):
140         """
141         Is the cache valid for fn?
142         Fast version, no timestamps checked.
143         """
144         # Is cache enabled?
145         if self.cachedir in [None, '']:
146             return False
147         if fn in self.clean:
148             return True
149         return False
150
151     def cacheValidUpdate(self, fn):
152         """
153         Is the cache valid for fn?
154         Make thorough (slower) checks including timestamps.
155         """
156         # Is cache enabled?
157         if self.cachedir in [None, '']:
158             return False
159
160         # File isn't in depends_cache
161         if not fn in self.depends_cache:
162             bb.note("Cache: %s is not cached" % fn)
163             if fn in self.clean:
164                 del self.clean[fn]
165             return False
166
167         # Check the file's timestamp
168         if bb.parse.cached_mtime(fn) > self.getVar("CACHETIMESTAMP", fn, True):
169             bb.note("Cache: %s changed" % fn)
170             if fn in self.clean:
171                 del self.clean[fn]
172             return False
173
174         # Check dependencies are still valid
175         depends = self.getVar("__depends", fn, True)
176         if depends:
177             deps = depends.split(" ")
178             for dep in deps:
179                 (f,old_mtime_s) = dep.split("@")
180                 old_mtime = int(old_mtime_s)
181                 new_mtime = bb.parse.cached_mtime(f)
182                 if (new_mtime > old_mtime):
183                     bb.note("Cache: %s's dependency %s changed" % (fn, f))
184                     if fn in self.clean:
185                         del self.clean[fn]
186                     return False
187
188         #bb.note("Depends Cache: %s is clean" % fn)
189         if not fn in self.clean:
190             self.clean[fn] = ""
191
192         return True
193
194     def skip(self, fn):
195         """
196         Mark a fn as skipped
197         Called from the parser
198         """
199         if not fn in self.depends_cache:
200             self.depends_cache[fn] = {}
201         self.depends_cache[fn]["SKIPPED"] = "1"
202
203     def remove(self, fn):
204         """
205         Remove a fn from the cache
206         Called from the parser in error cases
207         """
208         bb.note("Removing %s from cache" % fn)
209         if fn in self.depends_cache:
210             del self.depends_cache[fn]
211
212     def sync(self):
213         """
214         Save the cache
215         Called from the parser when complete (or exitting)
216         """
217         p = pickle.Pickler(file(self.cachefile, "wb" ), -1 )
218         p.dump(self.depends_cache)
219
220     def mtime(self, cachefile):
221         try:
222             return os.stat(cachefile)[8]
223         except OSError:
224             return 0
225
226     def load_bbfile( self, bbfile , cooker):
227         """
228         Load and parse one .bb build file
229         Return the data and whether parsing resulted in the file being skipped
230         """
231
232         import bb
233         from bb import utils, data, parse, debug, event, fatal
234
235         topdir = data.getVar('TOPDIR', cooker.configuration.data)
236         if not topdir:
237             topdir = os.path.abspath(os.getcwd())
238             # set topdir to here
239             data.setVar('TOPDIR', topdir, cooker.configuration)
240         bbfile = os.path.abspath(bbfile)
241         bbfile_loc = os.path.abspath(os.path.dirname(bbfile))
242         # expand tmpdir to include this topdir
243         data.setVar('TMPDIR', data.getVar('TMPDIR', cooker.configuration.data, 1) or "", cooker.configuration.data)
244         # set topdir to location of .bb file
245         topdir = bbfile_loc
246         #data.setVar('TOPDIR', topdir, cfg)
247         # go there
248         oldpath = os.path.abspath(os.getcwd())
249         os.chdir(topdir)
250         bb_data = data.init_db(cooker.configuration.data)
251         try:
252             parse.handle(bbfile, bb_data) # read .bb data
253             os.chdir(oldpath)
254             return bb_data, False
255         except bb.parse.SkipPackage:
256             os.chdir(oldpath)
257             return bb_data, True
258         except:
259             os.chdir(oldpath)
260             raise
261
262 def init(cooker):
263     """
264     The Objective: Cache the minimum amount of data possible yet get to the 
265     stage of building packages (i.e. tryBuild) without reparsing any .bb files.
266
267     To do this, we intercept getVar calls and only cache the variables we see 
268     being accessed. We rely on the cache getVar calls being made for all 
269     variables bitbake might need to use to reach this stage. For each cached 
270     file we need to track:
271
272     * Its mtime
273     * The mtimes of all its dependencies
274     * Whether it caused a parse.SkipPackage exception
275
276     Files causing parsing errors are evicted from the cache.
277
278     """
279     return Cache(cooker)
280