diff options
Diffstat (limited to 'bitbake')
-rw-r--r-- | bitbake/lib/bb/cache.py | 13 | ||||
-rw-r--r-- | bitbake/lib/bb/checksum.py | 90 | ||||
-rw-r--r-- | bitbake/lib/bb/cooker.py | 2 | ||||
-rw-r--r-- | bitbake/lib/bb/fetch2/__init__.py | 85 | ||||
-rw-r--r-- | bitbake/lib/bb/siggen.py | 24 |
5 files changed, 211 insertions, 3 deletions
diff --git a/bitbake/lib/bb/cache.py b/bitbake/lib/bb/cache.py index 36e6356f51..dea2a80616 100644 --- a/bitbake/lib/bb/cache.py +++ b/bitbake/lib/bb/cache.py | |||
@@ -43,7 +43,7 @@ except ImportError: | |||
43 | logger.info("Importing cPickle failed. " | 43 | logger.info("Importing cPickle failed. " |
44 | "Falling back to a very slow implementation.") | 44 | "Falling back to a very slow implementation.") |
45 | 45 | ||
46 | __cache_version__ = "143" | 46 | __cache_version__ = "144" |
47 | 47 | ||
48 | def getCacheFile(path, filename, data_hash): | 48 | def getCacheFile(path, filename, data_hash): |
49 | return os.path.join(path, filename + "." + data_hash) | 49 | return os.path.join(path, filename + "." + data_hash) |
@@ -76,9 +76,13 @@ class RecipeInfoCommon(object): | |||
76 | for task in tasks) | 76 | for task in tasks) |
77 | 77 | ||
78 | @classmethod | 78 | @classmethod |
79 | def flaglist(cls, flag, varlist, metadata): | 79 | def flaglist(cls, flag, varlist, metadata, squash=False): |
80 | return dict((var, metadata.getVarFlag(var, flag, True)) | 80 | out_dict = dict((var, metadata.getVarFlag(var, flag, True)) |
81 | for var in varlist) | 81 | for var in varlist) |
82 | if squash: | ||
83 | return dict((k,v) for (k,v) in out_dict.iteritems() if v) | ||
84 | else: | ||
85 | return out_dict | ||
82 | 86 | ||
83 | @classmethod | 87 | @classmethod |
84 | def getvar(cls, var, metadata): | 88 | def getvar(cls, var, metadata): |
@@ -128,6 +132,7 @@ class CoreRecipeInfo(RecipeInfoCommon): | |||
128 | self.stamp = self.getvar('STAMP', metadata) | 132 | self.stamp = self.getvar('STAMP', metadata) |
129 | self.stamp_base = self.flaglist('stamp-base', self.tasks, metadata) | 133 | self.stamp_base = self.flaglist('stamp-base', self.tasks, metadata) |
130 | self.stamp_extrainfo = self.flaglist('stamp-extra-info', self.tasks, metadata) | 134 | self.stamp_extrainfo = self.flaglist('stamp-extra-info', self.tasks, metadata) |
135 | self.file_checksums = self.flaglist('file-checksums', self.tasks, metadata, True) | ||
131 | self.packages_dynamic = self.listvar('PACKAGES_DYNAMIC', metadata) | 136 | self.packages_dynamic = self.listvar('PACKAGES_DYNAMIC', metadata) |
132 | self.depends = self.depvar('DEPENDS', metadata) | 137 | self.depends = self.depvar('DEPENDS', metadata) |
133 | self.provides = self.depvar('PROVIDES', metadata) | 138 | self.provides = self.depvar('PROVIDES', metadata) |
@@ -154,6 +159,7 @@ class CoreRecipeInfo(RecipeInfoCommon): | |||
154 | cachedata.stamp = {} | 159 | cachedata.stamp = {} |
155 | cachedata.stamp_base = {} | 160 | cachedata.stamp_base = {} |
156 | cachedata.stamp_extrainfo = {} | 161 | cachedata.stamp_extrainfo = {} |
162 | cachedata.file_checksums = {} | ||
157 | cachedata.fn_provides = {} | 163 | cachedata.fn_provides = {} |
158 | cachedata.pn_provides = defaultdict(list) | 164 | cachedata.pn_provides = defaultdict(list) |
159 | cachedata.all_depends = [] | 165 | cachedata.all_depends = [] |
@@ -185,6 +191,7 @@ class CoreRecipeInfo(RecipeInfoCommon): | |||
185 | cachedata.stamp[fn] = self.stamp | 191 | cachedata.stamp[fn] = self.stamp |
186 | cachedata.stamp_base[fn] = self.stamp_base | 192 | cachedata.stamp_base[fn] = self.stamp_base |
187 | cachedata.stamp_extrainfo[fn] = self.stamp_extrainfo | 193 | cachedata.stamp_extrainfo[fn] = self.stamp_extrainfo |
194 | cachedata.file_checksums[fn] = self.file_checksums | ||
188 | 195 | ||
189 | provides = [self.pn] | 196 | provides = [self.pn] |
190 | for provide in self.provides: | 197 | for provide in self.provides: |
diff --git a/bitbake/lib/bb/checksum.py b/bitbake/lib/bb/checksum.py new file mode 100644 index 0000000000..514ff0b1e6 --- /dev/null +++ b/bitbake/lib/bb/checksum.py | |||
@@ -0,0 +1,90 @@ | |||
1 | # Local file checksum cache implementation | ||
2 | # | ||
3 | # Copyright (C) 2012 Intel Corporation | ||
4 | # | ||
5 | # This program is free software; you can redistribute it and/or modify | ||
6 | # it under the terms of the GNU General Public License version 2 as | ||
7 | # published by the Free Software Foundation. | ||
8 | # | ||
9 | # This program is distributed in the hope that it will be useful, | ||
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | # GNU General Public License for more details. | ||
13 | # | ||
14 | # You should have received a copy of the GNU General Public License along | ||
15 | # with this program; if not, write to the Free Software Foundation, Inc., | ||
16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
17 | |||
18 | import os | ||
19 | import stat | ||
20 | import bb.utils | ||
21 | import logging | ||
22 | from bb.cache import MultiProcessCache | ||
23 | |||
24 | logger = logging.getLogger("BitBake.Cache") | ||
25 | |||
26 | try: | ||
27 | import cPickle as pickle | ||
28 | except ImportError: | ||
29 | import pickle | ||
30 | logger.info("Importing cPickle failed. " | ||
31 | "Falling back to a very slow implementation.") | ||
32 | |||
33 | |||
34 | # mtime cache (non-persistent) | ||
35 | # based upon the assumption that files do not change during bitbake run | ||
36 | class FileMtimeCache(object): | ||
37 | cache = {} | ||
38 | |||
39 | def cached_mtime(self, f): | ||
40 | if f not in self.cache: | ||
41 | self.cache[f] = os.stat(f)[stat.ST_MTIME] | ||
42 | return self.cache[f] | ||
43 | |||
44 | def cached_mtime_noerror(self, f): | ||
45 | if f not in self.cache: | ||
46 | try: | ||
47 | self.cache[f] = os.stat(f)[stat.ST_MTIME] | ||
48 | except OSError: | ||
49 | return 0 | ||
50 | return self.cache[f] | ||
51 | |||
52 | def update_mtime(self, f): | ||
53 | self.cache[f] = os.stat(f)[stat.ST_MTIME] | ||
54 | return self.cache[f] | ||
55 | |||
56 | def clear(self): | ||
57 | self.cache.clear() | ||
58 | |||
59 | # Checksum + mtime cache (persistent) | ||
60 | class FileChecksumCache(MultiProcessCache): | ||
61 | cache_file_name = "local_file_checksum_cache.dat" | ||
62 | CACHE_VERSION = 1 | ||
63 | |||
64 | def __init__(self): | ||
65 | self.mtime_cache = FileMtimeCache() | ||
66 | MultiProcessCache.__init__(self) | ||
67 | |||
68 | def get_checksum(self, f): | ||
69 | entry = self.cachedata[0].get(f) | ||
70 | cmtime = self.mtime_cache.cached_mtime(f) | ||
71 | if entry: | ||
72 | (mtime, hashval) = entry | ||
73 | if cmtime == mtime: | ||
74 | return hashval | ||
75 | else: | ||
76 | bb.debug(2, "file %s changed mtime, recompute checksum" % f) | ||
77 | |||
78 | hashval = bb.utils.md5_file(f) | ||
79 | self.cachedata_extras[0][f] = (cmtime, hashval) | ||
80 | return hashval | ||
81 | |||
82 | def merge_data(self, source, dest): | ||
83 | for h in source[0]: | ||
84 | if h in dest: | ||
85 | (smtime, _) = source[0][h] | ||
86 | (dmtime, _) = dest[0][h] | ||
87 | if smtime > dmtime: | ||
88 | dest[0][h] = source[0][h] | ||
89 | else: | ||
90 | dest[0][h] = source[0][h] | ||
diff --git a/bitbake/lib/bb/cooker.py b/bitbake/lib/bb/cooker.py index dea0aadbee..8ad4922651 100644 --- a/bitbake/lib/bb/cooker.py +++ b/bitbake/lib/bb/cooker.py | |||
@@ -1570,6 +1570,7 @@ class CookerParser(object): | |||
1570 | def init(): | 1570 | def init(): |
1571 | Parser.cfg = self.cfgdata | 1571 | Parser.cfg = self.cfgdata |
1572 | multiprocessing.util.Finalize(None, bb.codeparser.parser_cache_save, args=(self.cfgdata,), exitpriority=1) | 1572 | multiprocessing.util.Finalize(None, bb.codeparser.parser_cache_save, args=(self.cfgdata,), exitpriority=1) |
1573 | multiprocessing.util.Finalize(None, bb.fetch.fetcher_parse_save, args=(self.cfgdata,), exitpriority=1) | ||
1573 | 1574 | ||
1574 | self.feeder_quit = multiprocessing.Queue(maxsize=1) | 1575 | self.feeder_quit = multiprocessing.Queue(maxsize=1) |
1575 | self.parser_quit = multiprocessing.Queue(maxsize=self.num_processes) | 1576 | self.parser_quit = multiprocessing.Queue(maxsize=self.num_processes) |
@@ -1618,6 +1619,7 @@ class CookerParser(object): | |||
1618 | sync.start() | 1619 | sync.start() |
1619 | multiprocessing.util.Finalize(None, sync.join, exitpriority=-100) | 1620 | multiprocessing.util.Finalize(None, sync.join, exitpriority=-100) |
1620 | bb.codeparser.parser_cache_savemerge(self.cooker.configuration.data) | 1621 | bb.codeparser.parser_cache_savemerge(self.cooker.configuration.data) |
1622 | bb.fetch.fetcher_parse_done(self.cooker.configuration.data) | ||
1621 | 1623 | ||
1622 | def load_cached(self): | 1624 | def load_cached(self): |
1623 | for filename, appends in self.fromcache: | 1625 | for filename, appends in self.fromcache: |
diff --git a/bitbake/lib/bb/fetch2/__init__.py b/bitbake/lib/bb/fetch2/__init__.py index 0b976c4079..d4b6c3ec39 100644 --- a/bitbake/lib/bb/fetch2/__init__.py +++ b/bitbake/lib/bb/fetch2/__init__.py | |||
@@ -8,6 +8,7 @@ BitBake build tools. | |||
8 | """ | 8 | """ |
9 | 9 | ||
10 | # Copyright (C) 2003, 2004 Chris Larson | 10 | # Copyright (C) 2003, 2004 Chris Larson |
11 | # Copyright (C) 2012 Intel Corporation | ||
11 | # | 12 | # |
12 | # This program is free software; you can redistribute it and/or modify | 13 | # This program is free software; you can redistribute it and/or modify |
13 | # it under the terms of the GNU General Public License version 2 as | 14 | # it under the terms of the GNU General Public License version 2 as |
@@ -30,9 +31,11 @@ import os, re | |||
30 | import logging | 31 | import logging |
31 | import urllib | 32 | import urllib |
32 | import bb.persist_data, bb.utils | 33 | import bb.persist_data, bb.utils |
34 | import bb.checksum | ||
33 | from bb import data | 35 | from bb import data |
34 | 36 | ||
35 | __version__ = "2" | 37 | __version__ = "2" |
38 | _checksum_cache = bb.checksum.FileChecksumCache() | ||
36 | 39 | ||
37 | logger = logging.getLogger("BitBake.Fetcher") | 40 | logger = logging.getLogger("BitBake.Fetcher") |
38 | 41 | ||
@@ -233,10 +236,18 @@ def fetcher_init(d): | |||
233 | else: | 236 | else: |
234 | raise FetchError("Invalid SRCREV cache policy of: %s" % srcrev_policy) | 237 | raise FetchError("Invalid SRCREV cache policy of: %s" % srcrev_policy) |
235 | 238 | ||
239 | _checksum_cache.init_cache(d) | ||
240 | |||
236 | for m in methods: | 241 | for m in methods: |
237 | if hasattr(m, "init"): | 242 | if hasattr(m, "init"): |
238 | m.init(d) | 243 | m.init(d) |
239 | 244 | ||
245 | def fetcher_parse_save(d): | ||
246 | _checksum_cache.save_extras(d) | ||
247 | |||
248 | def fetcher_parse_done(d): | ||
249 | _checksum_cache.save_merge(d) | ||
250 | |||
240 | def fetcher_compare_revisions(d): | 251 | def fetcher_compare_revisions(d): |
241 | """ | 252 | """ |
242 | Compare the revisions in the persistant cache with current values and | 253 | Compare the revisions in the persistant cache with current values and |
@@ -553,6 +564,80 @@ def srcrev_internal_helper(ud, d, name): | |||
553 | 564 | ||
554 | return rev | 565 | return rev |
555 | 566 | ||
567 | |||
568 | def get_checksum_file_list(d): | ||
569 | """ Get a list of files checksum in SRC_URI | ||
570 | |||
571 | Returns the all resolved local path of all local file entries in | ||
572 | SRC_URI as a space-separated string | ||
573 | """ | ||
574 | fetch = Fetch([], d) | ||
575 | |||
576 | dl_dir = d.getVar('DL_DIR', True) | ||
577 | filelist = [] | ||
578 | for u in fetch.urls: | ||
579 | ud = fetch.ud[u] | ||
580 | |||
581 | if isinstance(ud.method, local.Local): | ||
582 | ud.setup_localpath(d) | ||
583 | f = ud.localpath | ||
584 | if f.startswith(dl_dir): | ||
585 | # The local fetcher's behaviour is to return a path under DL_DIR if it couldn't find the file anywhere else | ||
586 | if os.path.exists(f): | ||
587 | bb.warn("Getting checksum for %s SRC_URI entry %s: file not found except in DL_DIR" % (d.getVar('PN', True), os.path.basename(f))) | ||
588 | else: | ||
589 | bb.warn("Unable to get checksum for %s SRC_URI entry %s: file could not be found" % (d.getVar('PN', True), os.path.basename(f))) | ||
590 | continue | ||
591 | filelist.append(f) | ||
592 | |||
593 | return " ".join(filelist) | ||
594 | |||
595 | |||
596 | def get_file_checksums(filelist, pn): | ||
597 | """Get a list of the checksums for a list of local files | ||
598 | |||
599 | Returns the checksums for a list of local files, caching the results as | ||
600 | it proceeds | ||
601 | |||
602 | """ | ||
603 | |||
604 | def checksum_file(f): | ||
605 | try: | ||
606 | checksum = _checksum_cache.get_checksum(f) | ||
607 | except OSError as e: | ||
608 | import traceback | ||
609 | bb.warn("Unable to get checksum for %s SRC_URI entry %s: %s" % (pn, os.path.basename(f), e)) | ||
610 | return None | ||
611 | return checksum | ||
612 | |||
613 | checksums = [] | ||
614 | for pth in filelist.split(): | ||
615 | checksum = None | ||
616 | if '*' in pth: | ||
617 | # Handle globs | ||
618 | import glob | ||
619 | for f in glob.glob(pth): | ||
620 | checksum = checksum_file(f) | ||
621 | if checksum: | ||
622 | checksums.append((f, checksum)) | ||
623 | elif os.path.isdir(pth): | ||
624 | # Handle directories | ||
625 | for root, dirs, files in os.walk(pth): | ||
626 | for name in files: | ||
627 | fullpth = os.path.join(root, name) | ||
628 | checksum = checksum_file(fullpth) | ||
629 | if checksum: | ||
630 | checksums.append((fullpth, checksum)) | ||
631 | else: | ||
632 | checksum = checksum_file(pth) | ||
633 | |||
634 | if checksum: | ||
635 | checksums.append((pth, checksum)) | ||
636 | |||
637 | checksums.sort() | ||
638 | return checksums | ||
639 | |||
640 | |||
556 | class FetchData(object): | 641 | class FetchData(object): |
557 | """ | 642 | """ |
558 | A class which represents the fetcher state for a given URI. | 643 | A class which represents the fetcher state for a given URI. |
diff --git a/bitbake/lib/bb/siggen.py b/bitbake/lib/bb/siggen.py index 5a0b80e8a9..daf56770f9 100644 --- a/bitbake/lib/bb/siggen.py +++ b/bitbake/lib/bb/siggen.py | |||
@@ -60,6 +60,7 @@ class SignatureGeneratorBasic(SignatureGenerator): | |||
60 | self.taskhash = {} | 60 | self.taskhash = {} |
61 | self.taskdeps = {} | 61 | self.taskdeps = {} |
62 | self.runtaskdeps = {} | 62 | self.runtaskdeps = {} |
63 | self.file_checksum_values = {} | ||
63 | self.gendeps = {} | 64 | self.gendeps = {} |
64 | self.lookupcache = {} | 65 | self.lookupcache = {} |
65 | self.pkgnameextract = re.compile("(?P<fn>.*)\..*") | 66 | self.pkgnameextract = re.compile("(?P<fn>.*)\..*") |
@@ -152,6 +153,7 @@ class SignatureGeneratorBasic(SignatureGenerator): | |||
152 | k = fn + "." + task | 153 | k = fn + "." + task |
153 | data = dataCache.basetaskhash[k] | 154 | data = dataCache.basetaskhash[k] |
154 | self.runtaskdeps[k] = [] | 155 | self.runtaskdeps[k] = [] |
156 | self.file_checksum_values[k] = {} | ||
155 | recipename = dataCache.pkg_fn[fn] | 157 | recipename = dataCache.pkg_fn[fn] |
156 | for dep in sorted(deps, key=clean_basepath): | 158 | for dep in sorted(deps, key=clean_basepath): |
157 | depname = dataCache.pkg_fn[self.pkgnameextract.search(dep).group('fn')] | 159 | depname = dataCache.pkg_fn[self.pkgnameextract.search(dep).group('fn')] |
@@ -161,6 +163,12 @@ class SignatureGeneratorBasic(SignatureGenerator): | |||
161 | bb.fatal("%s is not in taskhash, caller isn't calling in dependency order?", dep) | 163 | bb.fatal("%s is not in taskhash, caller isn't calling in dependency order?", dep) |
162 | data = data + self.taskhash[dep] | 164 | data = data + self.taskhash[dep] |
163 | self.runtaskdeps[k].append(dep) | 165 | self.runtaskdeps[k].append(dep) |
166 | |||
167 | if task in dataCache.file_checksums[fn]: | ||
168 | checksums = bb.fetch2.get_file_checksums(dataCache.file_checksums[fn][task], recipename) | ||
169 | for (f,cs) in checksums: | ||
170 | self.file_checksum_values[k][f] = cs | ||
171 | data = data + cs | ||
164 | h = hashlib.md5(data).hexdigest() | 172 | h = hashlib.md5(data).hexdigest() |
165 | self.taskhash[k] = h | 173 | self.taskhash[k] = h |
166 | #d.setVar("BB_TASKHASH_task-%s" % task, taskhash[task]) | 174 | #d.setVar("BB_TASKHASH_task-%s" % task, taskhash[task]) |
@@ -197,6 +205,7 @@ class SignatureGeneratorBasic(SignatureGenerator): | |||
197 | 205 | ||
198 | if runtime and k in self.taskhash: | 206 | if runtime and k in self.taskhash: |
199 | data['runtaskdeps'] = self.runtaskdeps[k] | 207 | data['runtaskdeps'] = self.runtaskdeps[k] |
208 | data['file_checksum_values'] = self.file_checksum_values[k] | ||
200 | data['runtaskhashes'] = {} | 209 | data['runtaskhashes'] = {} |
201 | for dep in data['runtaskdeps']: | 210 | for dep in data['runtaskdeps']: |
202 | data['runtaskhashes'][dep] = self.taskhash[dep] | 211 | data['runtaskhashes'][dep] = self.taskhash[dep] |
@@ -304,6 +313,18 @@ def compare_sigfiles(a, b): | |||
304 | for dep in changed: | 313 | for dep in changed: |
305 | print "Variable %s value changed from %s to %s" % (dep, a_data['varvals'][dep], b_data['varvals'][dep]) | 314 | print "Variable %s value changed from %s to %s" % (dep, a_data['varvals'][dep], b_data['varvals'][dep]) |
306 | 315 | ||
316 | changed, added, removed = dict_diff(a_data['file_checksum_values'], b_data['file_checksum_values']) | ||
317 | if changed: | ||
318 | for f in changed: | ||
319 | print "Checksum for file %s changed from %s to %s" % (f, a_data['file_checksum_values'][f], b_data['file_checksum_values'][f]) | ||
320 | if added: | ||
321 | for f in added: | ||
322 | print "Dependency on checksum of file %s was added" % (f) | ||
323 | if removed: | ||
324 | for f in removed: | ||
325 | print "Dependency on checksum of file %s was removed" % (f) | ||
326 | |||
327 | |||
307 | if 'runtaskhashes' in a_data and 'runtaskhashes' in b_data: | 328 | if 'runtaskhashes' in a_data and 'runtaskhashes' in b_data: |
308 | a = clean_basepaths(a_data['runtaskhashes']) | 329 | a = clean_basepaths(a_data['runtaskhashes']) |
309 | b = clean_basepaths(b_data['runtaskhashes']) | 330 | b = clean_basepaths(b_data['runtaskhashes']) |
@@ -353,6 +374,9 @@ def dump_sigfile(a): | |||
353 | if 'runtaskdeps' in a_data: | 374 | if 'runtaskdeps' in a_data: |
354 | print "Tasks this task depends on: %s" % (a_data['runtaskdeps']) | 375 | print "Tasks this task depends on: %s" % (a_data['runtaskdeps']) |
355 | 376 | ||
377 | if 'file_checksum_values' in a_data: | ||
378 | print "This task depends on the checksums of files: %s" % (a_data['file_checksum_values']) | ||
379 | |||
356 | if 'runtaskhashes' in a_data: | 380 | if 'runtaskhashes' in a_data: |
357 | for dep in a_data['runtaskhashes']: | 381 | for dep in a_data['runtaskhashes']: |
358 | print "Hash for dependent task %s is %s" % (dep, a_data['runtaskhashes'][dep]) | 382 | print "Hash for dependent task %s is %s" % (dep, a_data['runtaskhashes'][dep]) |