Add proper GPLv2 headers to all BitBake files
[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
19 # it under the terms of the GNU General Public License version 2 as
20 # published by the Free Software Foundation.
21 #
22 # This program is distributed in the hope that it will be useful,
23 # but WITHOUT ANY WARRANTY; without even the implied warranty of
24 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25 # GNU General Public License for more details.
26 #
27 # You should have received a copy of the GNU General Public License along
28 # with this program; if not, write to the Free Software Foundation, Inc.,
29 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
30
31
32 import os, re
33 import bb.data
34 import bb.utils
35 from sets import Set
36
37 try:
38     import cPickle as pickle
39 except ImportError:
40     import pickle
41     bb.msg.note(1, bb.msg.domain.Cache, "Importing cPickle failed. Falling back to a very slow implementation.")
42
43 __cache_version__ = "125"
44
45 class Cache:
46     """
47     BitBake Cache implementation
48     """
49     def __init__(self, cooker):
50
51
52         self.cachedir = bb.data.getVar("CACHE", cooker.configuration.data, True)
53         self.clean = {}
54         self.depends_cache = {}
55         self.data = None
56         self.data_fn = None
57
58         if self.cachedir in [None, '']:
59             self.has_cache = False
60             bb.msg.note(1, bb.msg.domain.Cache, "Not using a cache. Set CACHE = <directory> to enable.")
61         else:
62             self.has_cache = True
63             self.cachefile = os.path.join(self.cachedir,"bb_cache.dat")
64             
65             bb.msg.debug(1, bb.msg.domain.Cache, "Using cache in '%s'" % self.cachedir)
66             try:
67                 os.stat( self.cachedir )
68             except OSError:
69                 bb.mkdirhier( self.cachedir )
70
71         if self.has_cache and (self.mtime(self.cachefile)):
72             try:
73                 p = pickle.Unpickler( file(self.cachefile,"rb"))
74                 self.depends_cache, version_data = p.load()
75                 if version_data['CACHE_VER'] != __cache_version__:
76                     raise ValueError, 'Cache Version Mismatch'
77                 if version_data['BITBAKE_VER'] != bb.__version__:
78                     raise ValueError, 'Bitbake Version Mismatch'
79             except (ValueError, KeyError):
80                 bb.msg.note(1, bb.msg.domain.Cache, "Invalid cache found, rebuilding...")
81                 self.depends_cache = {}
82
83         if self.depends_cache:
84             for fn in self.depends_cache.keys():
85                 self.clean[fn] = ""
86                 self.cacheValidUpdate(fn)
87
88     def getVar(self, var, fn, exp = 0):
89         """
90         Gets the value of a variable
91         (similar to getVar in the data class)
92         
93         There are two scenarios:
94           1. We have cached data - serve from depends_cache[fn]
95           2. We're learning what data to cache - serve from data 
96              backend but add a copy of the data to the cache.
97         """
98
99         if fn in self.clean:
100             return self.depends_cache[fn][var]
101
102         if not fn in self.depends_cache:
103             self.depends_cache[fn] = {}
104
105         if fn != self.data_fn:
106             # We're trying to access data in the cache which doesn't exist
107             # yet setData hasn't been called to setup the right access. Very bad.
108             bb.msg.error(bb.msg.domain.Cache, "Parsing error data_fn %s and fn %s don't match" % (self.data_fn, fn))
109
110         result = bb.data.getVar(var, self.data, exp)
111         self.depends_cache[fn][var] = result
112         return result
113
114     def setData(self, fn, data):
115         """
116         Called to prime bb_cache ready to learn which variables to cache.
117         Will be followed by calls to self.getVar which aren't cached
118         but can be fulfilled from self.data.
119         """
120         self.data_fn = fn
121         self.data = data
122
123         # Make sure __depends makes the depends_cache
124         self.getVar("__depends", fn, True)
125         self.depends_cache[fn]["CACHETIMESTAMP"] = bb.parse.cached_mtime(fn)
126
127     def loadDataFull(self, fn, cfgData):
128         """
129         Return a complete set of data for fn.
130         To do this, we need to parse the file.
131         """
132         bb_data, skipped = self.load_bbfile(fn, cfgData)
133         return bb_data
134
135     def loadData(self, fn, cfgData):
136         """
137         Load a subset of data for fn.
138         If the cached data is valid we do nothing,
139         To do this, we need to parse the file and set the system
140         to record the variables accessed.
141         Return the cache status and whether the file was skipped when parsed
142         """
143         if self.cacheValid(fn):
144             if "SKIPPED" in self.depends_cache[fn]:
145                 return True, True
146             return True, False
147
148         bb_data, skipped = self.load_bbfile(fn, cfgData)
149         self.setData(fn, bb_data)
150         return False, skipped
151
152     def cacheValid(self, fn):
153         """
154         Is the cache valid for fn?
155         Fast version, no timestamps checked.
156         """
157         # Is cache enabled?
158         if not self.has_cache:
159             return False
160         if fn in self.clean:
161             return True
162         return False
163
164     def cacheValidUpdate(self, fn):
165         """
166         Is the cache valid for fn?
167         Make thorough (slower) checks including timestamps.
168         """
169         # Is cache enabled?
170         if not self.has_cache:
171             return False
172
173         # Check file still exists
174         if self.mtime(fn) == 0:
175             bb.msg.debug(2, bb.msg.domain.Cache, "Cache: %s not longer exists" % fn)
176             self.remove(fn)
177             return False
178
179         # File isn't in depends_cache
180         if not fn in self.depends_cache:
181             bb.msg.debug(2, bb.msg.domain.Cache, "Cache: %s is not cached" % fn)
182             self.remove(fn)
183             return False
184
185         # Check the file's timestamp
186         if bb.parse.cached_mtime(fn) > self.getVar("CACHETIMESTAMP", fn, True):
187             bb.msg.debug(2, bb.msg.domain.Cache, "Cache: %s changed" % fn)
188             self.remove(fn)
189             return False
190
191         # Check dependencies are still valid
192         depends = self.getVar("__depends", fn, True)
193         for f,old_mtime in depends:
194             # Check if file still exists
195             if self.mtime(f) == 0:
196                 return False
197
198             new_mtime = bb.parse.cached_mtime(f)
199             if (new_mtime > old_mtime):
200                 bb.msg.debug(2, bb.msg.domain.Cache, "Cache: %s's dependency %s changed" % (fn, f))
201                 self.remove(fn)
202                 return False
203
204         bb.msg.debug(2, bb.msg.domain.Cache, "Depends Cache: %s is clean" % fn)
205         if not fn in self.clean:
206             self.clean[fn] = ""
207
208         return True
209
210     def skip(self, fn):
211         """
212         Mark a fn as skipped
213         Called from the parser
214         """
215         if not fn in self.depends_cache:
216             self.depends_cache[fn] = {}
217         self.depends_cache[fn]["SKIPPED"] = "1"
218
219     def remove(self, fn):
220         """
221         Remove a fn from the cache
222         Called from the parser in error cases
223         """
224         bb.msg.debug(1, bb.msg.domain.Cache, "Removing %s from cache" % fn)
225         if fn in self.depends_cache:
226             del self.depends_cache[fn]
227         if fn in self.clean:
228             del self.clean[fn]
229
230     def sync(self):
231         """
232         Save the cache
233         Called from the parser when complete (or exiting)
234         """
235
236         if not self.has_cache:
237             return
238
239         version_data = {}
240         version_data['CACHE_VER'] = __cache_version__
241         version_data['BITBAKE_VER'] = bb.__version__
242
243         p = pickle.Pickler(file(self.cachefile, "wb" ), -1 )
244         p.dump([self.depends_cache, version_data])
245
246     def mtime(self, cachefile):
247         return bb.parse.cached_mtime_noerror(cachefile)
248
249     def handle_data(self, file_name, cacheData):
250         """
251         Save data we need into the cache 
252         """
253
254         pn       = self.getVar('PN', file_name, True)
255         pv       = self.getVar('PV', file_name, True)
256         pr       = self.getVar('PR', file_name, True)
257         dp       = int(self.getVar('DEFAULT_PREFERENCE', file_name, True) or "0")
258         provides  = Set([pn] + (self.getVar("PROVIDES", file_name, True) or "").split())
259         depends   = bb.utils.explode_deps(self.getVar("DEPENDS", file_name, True) or "")
260         packages  = (self.getVar('PACKAGES', file_name, True) or "").split()
261         packages_dynamic = (self.getVar('PACKAGES_DYNAMIC', file_name, True) or "").split()
262         rprovides = (self.getVar("RPROVIDES", file_name, True) or "").split()
263
264         cacheData.task_queues[file_name] = self.getVar("_task_graph", file_name, True)
265         cacheData.task_deps[file_name] = self.getVar("_task_deps", file_name, True)
266
267         # build PackageName to FileName lookup table
268         if pn not in cacheData.pkg_pn:
269             cacheData.pkg_pn[pn] = []
270         cacheData.pkg_pn[pn].append(file_name)
271
272         cacheData.stamp[file_name] = self.getVar('STAMP', file_name, True)
273
274         # build FileName to PackageName lookup table
275         cacheData.pkg_fn[file_name] = pn
276         cacheData.pkg_pvpr[file_name] = (pv,pr)
277         cacheData.pkg_dp[file_name] = dp
278
279         # Build forward and reverse provider hashes
280         # Forward: virtual -> [filenames]
281         # Reverse: PN -> [virtuals]
282         if pn not in cacheData.pn_provides:
283             cacheData.pn_provides[pn] = Set()
284         cacheData.pn_provides[pn] |= provides
285
286         for provide in provides:
287             if provide not in cacheData.providers:
288                 cacheData.providers[provide] = []
289             cacheData.providers[provide].append(file_name)
290
291         cacheData.deps[file_name] = Set()
292         for dep in depends:
293             cacheData.all_depends.add(dep)
294             cacheData.deps[file_name].add(dep)
295
296         # Build reverse hash for PACKAGES, so runtime dependencies 
297         # can be be resolved (RDEPENDS, RRECOMMENDS etc.)
298         for package in packages:
299             if not package in cacheData.packages:
300                 cacheData.packages[package] = []
301             cacheData.packages[package].append(file_name)
302             rprovides += (self.getVar("RPROVIDES_%s" % package, file_name, 1) or "").split() 
303
304         for package in packages_dynamic:
305             if not package in cacheData.packages_dynamic:
306                 cacheData.packages_dynamic[package] = []
307             cacheData.packages_dynamic[package].append(file_name)
308
309         for rprovide in rprovides:
310             if not rprovide in cacheData.rproviders:
311                 cacheData.rproviders[rprovide] = []
312             cacheData.rproviders[rprovide].append(file_name)
313
314         # Build hash of runtime depends and rececommends
315
316         def add_dep(deplist, deps):
317             for dep in deps:
318                 if not dep in deplist:
319                     deplist[dep] = ""
320
321         if not file_name in cacheData.rundeps:
322             cacheData.rundeps[file_name] = {}
323         if not file_name in cacheData.runrecs:
324             cacheData.runrecs[file_name] = {}
325
326         for package in packages + [pn]:
327             if not package in cacheData.rundeps[file_name]:
328                 cacheData.rundeps[file_name][package] = {}
329             if not package in cacheData.runrecs[file_name]:
330                 cacheData.runrecs[file_name][package] = {}
331
332             add_dep(cacheData.rundeps[file_name][package], bb.utils.explode_deps(self.getVar('RDEPENDS', file_name, True) or ""))
333             add_dep(cacheData.runrecs[file_name][package], bb.utils.explode_deps(self.getVar('RRECOMMENDS', file_name, True) or ""))
334             add_dep(cacheData.rundeps[file_name][package], bb.utils.explode_deps(self.getVar("RDEPENDS_%s" % package, file_name, True) or ""))
335             add_dep(cacheData.runrecs[file_name][package], bb.utils.explode_deps(self.getVar("RRECOMMENDS_%s" % package, file_name, True) or ""))
336
337         # Collect files we may need for possible world-dep
338         # calculations
339         if not self.getVar('BROKEN', file_name, True) and not self.getVar('EXCLUDE_FROM_WORLD', file_name, True):
340             cacheData.possible_world.append(file_name)
341
342
343     def load_bbfile( self, bbfile , config):
344         """
345         Load and parse one .bb build file
346         Return the data and whether parsing resulted in the file being skipped
347         """
348
349         import bb
350         from bb import utils, data, parse, debug, event, fatal
351
352         # expand tmpdir to include this topdir
353         data.setVar('TMPDIR', data.getVar('TMPDIR', config, 1) or "", config)
354         bbfile_loc = os.path.abspath(os.path.dirname(bbfile))
355         oldpath = os.path.abspath(os.getcwd())
356         if self.mtime(bbfile_loc):
357             os.chdir(bbfile_loc)
358         bb_data = data.init_db(config)
359         try:
360             bb_data = parse.handle(bbfile, bb_data) # read .bb data
361             os.chdir(oldpath)
362             return bb_data, False
363         except bb.parse.SkipPackage:
364             os.chdir(oldpath)
365             return bb_data, True
366         except:
367             os.chdir(oldpath)
368             raise
369
370 def init(cooker):
371     """
372     The Objective: Cache the minimum amount of data possible yet get to the 
373     stage of building packages (i.e. tryBuild) without reparsing any .bb files.
374
375     To do this, we intercept getVar calls and only cache the variables we see 
376     being accessed. We rely on the cache getVar calls being made for all 
377     variables bitbake might need to use to reach this stage. For each cached 
378     file we need to track:
379
380     * Its mtime
381     * The mtimes of all its dependencies
382     * Whether it caused a parse.SkipPackage exception
383
384     Files causing parsing errors are evicted from the cache.
385
386     """
387     return Cache(cooker)
388
389
390
391 #============================================================================#
392 # CacheData
393 #============================================================================#
394 class CacheData:
395     """
396     The data structures we compile from the cached data
397     """
398
399     def __init__(self):
400         """
401         Direct cache variables
402         (from Cache.handle_data)
403         """
404         self.providers   = {}
405         self.rproviders = {}
406         self.packages = {}
407         self.packages_dynamic = {}
408         self.possible_world = []
409         self.pkg_pn = {}
410         self.pkg_fn = {}
411         self.pkg_pvpr = {}
412         self.pkg_dp = {}
413         self.pn_provides = {}
414         self.all_depends = Set()
415         self.deps = {}
416         self.rundeps = {}
417         self.runrecs = {}
418         self.task_queues = {}
419         self.task_deps = {}
420         self.stamp = {}
421         self.preferred = {}
422
423         """
424         Indirect Cache variables
425         (set elsewhere)
426         """
427         self.ignored_dependencies = []
428         self.world_target = Set()
429         self.bbfile_priority = {}
430         self.bbfile_config_priorities = []