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