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