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