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