summaryrefslogtreecommitdiffstats
path: root/meta/lib
diff options
context:
space:
mode:
authorPaul Eggleton <paul.eggleton@linux.intel.com>2012-01-19 10:32:12 (GMT)
committerRichard Purdie <richard.purdie@linuxfoundation.org>2012-01-19 14:59:23 (GMT)
commit967de59f35acef7fb258524973473f3d154e4a37 (patch)
tree400ee21c98f7590b5df97f05aecb56f3d72cbf3d /meta/lib
parent11e5d7bc814a265d338dbb06d82e7533c2187be2 (diff)
downloadpoky-967de59f35acef7fb258524973473f3d154e4a37.tar.gz
buildhistory_analysis: include related fields in output
Sometimes, when a value changes in the buildhistory it is useful to know when a related (but not necessarily itself monitored) value changes as it can help explain the change. For example, when the list of installed packages for an image changes it could be caused by a change to one of the image-related variables. Related field changes are recorded as sub-items of each change. Currently the only way to visualise these is via the buildhistory-diff tool, so an example would be: Changes to images/qemux86/eglibc/core-image-minimal (installed-package-names.txt): locale-base-de-de was added procps was added * IMAGE_LINGUAS: added "de-de" * IMAGE_INSTALL: added "procps" Here we see that two additional packages have been added to the image, and looking at the related changes to the two variables IMAGE_INSTALL and IMAGE_LINGUAS we have the explanation as to why. (From OE-Core rev: 2c5b90c6d1b1091779602ebe03e84674eb63ea83) Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'meta/lib')
-rw-r--r--meta/lib/oe/buildhistory_analysis.py89
1 files changed, 64 insertions, 25 deletions
diff --git a/meta/lib/oe/buildhistory_analysis.py b/meta/lib/oe/buildhistory_analysis.py
index 627467c..103cfb4 100644
--- a/meta/lib/oe/buildhistory_analysis.py
+++ b/meta/lib/oe/buildhistory_analysis.py
@@ -23,22 +23,41 @@ monitor_fields = ['RDEPENDS', 'RRECOMMENDS', 'PACKAGES', 'FILELIST', 'PKGSIZE',
23monitor_numeric_threshold = 20 23monitor_numeric_threshold = 20
24# Image files to monitor (note that image-info.txt is handled separately) 24# Image files to monitor (note that image-info.txt is handled separately)
25img_monitor_files = ['installed-package-names.txt', 'files-in-image.txt'] 25img_monitor_files = ['installed-package-names.txt', 'files-in-image.txt']
26# Related context fields for reporting (note: PE, PV & PR are always reported for monitored package fields)
27related_fields = {}
28related_fields['RDEPENDS'] = ['DEPENDS']
29related_fields['RRECOMMENDS'] = ['DEPENDS']
30related_fields['FILELIST'] = ['FILES']
31related_fields['PKGSIZE'] = ['FILELIST']
32related_fields['files-in-image.txt'] = ['installed-package-names.txt', 'USER_CLASSES', 'IMAGE_CLASSES', 'ROOTFS_POSTPROCESS_COMMAND', 'IMAGE_POSTPROCESS_COMMAND']
33related_fields['installed-package-names.txt'] = ['IMAGE_FEATURES', 'IMAGE_LINGUAS', 'IMAGE_INSTALL', 'BAD_RECOMMENDATIONS']
34
26 35
27class ChangeRecord: 36class ChangeRecord:
28 def __init__(self, path, fieldname, oldvalue, newvalue): 37 def __init__(self, path, fieldname, oldvalue, newvalue, monitored):
29 self.path = path 38 self.path = path
30 self.fieldname = fieldname 39 self.fieldname = fieldname
31 self.oldvalue = oldvalue 40 self.oldvalue = oldvalue
32 self.newvalue = newvalue 41 self.newvalue = newvalue
42 self.monitored = monitored
43 self.related = []
33 self.filechanges = None 44 self.filechanges = None
34 45
35 def __str__(self): 46 def __str__(self):
47 return self._str_internal(True)
48
49 def _str_internal(self, pathprefix):
50 if pathprefix:
51 prefix = '%s: ' % self.path
52 else:
53 prefix = ''
54
36 if self.fieldname in list_fields: 55 if self.fieldname in list_fields:
37 aitems = self.oldvalue.split() 56 aitems = self.oldvalue.split()
38 bitems = self.newvalue.split() 57 bitems = self.newvalue.split()
39 removed = list(set(aitems) - set(bitems)) 58 removed = list(set(aitems) - set(bitems))
40 added = list(set(bitems) - set(aitems)) 59 added = list(set(bitems) - set(aitems))
41 return '%s: %s:%s%s' % (self.path, self.fieldname, ' removed "%s"' % ' '.join(removed) if removed else '', ' added "%s"' % ' '.join(added) if added else '') 60 out = '%s:%s%s' % (self.fieldname, ' removed "%s"' % ' '.join(removed) if removed else '', ' added "%s"' % ' '.join(added) if added else '')
42 elif self.fieldname in numeric_fields: 61 elif self.fieldname in numeric_fields:
43 aval = int(self.oldvalue or 0) 62 aval = int(self.oldvalue or 0)
44 bval = int(self.newvalue or 0) 63 bval = int(self.newvalue or 0)
@@ -46,9 +65,11 @@ class ChangeRecord:
46 percentchg = ((bval - aval) / float(aval)) * 100 65 percentchg = ((bval - aval) / float(aval)) * 100
47 else: 66 else:
48 percentchg = 100 67 percentchg = 100
49 return '%s: %s changed from %s to %s (%s%d%%)' % (self.path, self.fieldname, self.oldvalue or "''", self.newvalue or "''", '+' if percentchg > 0 else '', percentchg) 68 out = '%s changed from %s to %s (%s%d%%)' % (self.fieldname, self.oldvalue or "''", self.newvalue or "''", '+' if percentchg > 0 else '', percentchg)
50 elif self.fieldname in img_monitor_files: 69 elif self.fieldname in img_monitor_files:
51 out = 'Changes to %s (%s):\n ' % (self.path, self.fieldname) 70 if pathprefix:
71 prefix = 'Changes to %s ' % self.path
72 out = '(%s):\n ' % self.fieldname
52 if self.filechanges: 73 if self.filechanges:
53 out += '\n '.join(['%s' % i for i in self.filechanges]) 74 out += '\n '.join(['%s' % i for i in self.filechanges])
54 else: 75 else:
@@ -57,10 +78,15 @@ class ChangeRecord:
57 diff = difflib.unified_diff(alines, blines, self.fieldname, self.fieldname, lineterm='') 78 diff = difflib.unified_diff(alines, blines, self.fieldname, self.fieldname, lineterm='')
58 out += '\n '.join(list(diff)) 79 out += '\n '.join(list(diff))
59 out += '\n --' 80 out += '\n --'
60 return out
61 else: 81 else:
62 return '%s: %s changed from "%s" to "%s"' % (self.path, self.self.fieldname, self.oldvalue, self.newvalue) 82 out = '%s changed from "%s" to "%s"' % (self.fieldname, self.oldvalue, self.newvalue)
63 83
84 if self.related:
85 for chg in self.related:
86 for line in chg._str_internal(False).splitlines():
87 out += '\n * %s' % line
88
89 return '%s%s' % (prefix, out)
64 90
65class FileChange: 91class FileChange:
66 changetype_add = 'A' 92 changetype_add = 'A'
@@ -199,21 +225,20 @@ def compare_dict_blobs(path, ablob, bblob, report_all):
199 changes = [] 225 changes = []
200 keys = list(set(adict.keys()) | set(bdict.keys())) 226 keys = list(set(adict.keys()) | set(bdict.keys()))
201 for key in keys: 227 for key in keys:
202 if report_all or key in monitor_fields: 228 astr = adict.get(key, '')
203 astr = adict.get(key, '') 229 bstr = bdict.get(key, '')
204 bstr = bdict.get(key, '') 230 if astr != bstr:
205 if astr != bstr: 231 if (not report_all) and key in numeric_fields:
206 if (not report_all) and key in numeric_fields: 232 aval = int(astr or 0)
207 aval = int(astr or 0) 233 bval = int(bstr or 0)
208 bval = int(bstr or 0) 234 if aval != 0:
209 if aval != 0: 235 percentchg = ((bval - aval) / float(aval)) * 100
210 percentchg = ((bval - aval) / float(aval)) * 100 236 else:
211 else: 237 percentchg = 100
212 percentchg = 100 238 if percentchg < monitor_numeric_threshold:
213 if percentchg < monitor_numeric_threshold: 239 continue
214 continue 240 chg = ChangeRecord(path, key, astr, bstr, key in monitor_fields)
215 chg = ChangeRecord(path, key, astr, bstr) 241 changes.append(chg)
216 changes.append(chg)
217 return changes 242 return changes
218 243
219 244
@@ -236,7 +261,7 @@ def process_changes(repopath, revision1, revision2 = 'HEAD', report_all = False)
236 blines = d.b_blob.data_stream.read().splitlines() 261 blines = d.b_blob.data_stream.read().splitlines()
237 filechanges = compare_file_lists(alines,blines) 262 filechanges = compare_file_lists(alines,blines)
238 if filechanges: 263 if filechanges:
239 chg = ChangeRecord(path, filename, None, None) 264 chg = ChangeRecord(path, filename, None, None, True)
240 chg.filechanges = filechanges 265 chg.filechanges = filechanges
241 changes.append(chg) 266 changes.append(chg)
242 elif filename == 'installed-package-names.txt': 267 elif filename == 'installed-package-names.txt':
@@ -244,13 +269,27 @@ def process_changes(repopath, revision1, revision2 = 'HEAD', report_all = False)
244 blines = d.b_blob.data_stream.read().splitlines() 269 blines = d.b_blob.data_stream.read().splitlines()
245 filechanges = compare_lists(alines,blines) 270 filechanges = compare_lists(alines,blines)
246 if filechanges: 271 if filechanges:
247 chg = ChangeRecord(path, filename, None, None) 272 chg = ChangeRecord(path, filename, None, None, True)
248 chg.filechanges = filechanges 273 chg.filechanges = filechanges
249 changes.append(chg) 274 changes.append(chg)
250 else: 275 else:
251 chg = ChangeRecord(path, filename, d.a_blob.data_stream.read(), d.b_blob.data_stream.read()) 276 chg = ChangeRecord(path, filename, d.a_blob.data_stream.read(), d.b_blob.data_stream.read(), True)
252 changes.append(chg) 277 changes.append(chg)
253 elif filename == 'image-info.txt': 278 elif filename == 'image-info.txt':
254 changes.extend(compare_dict_blobs(path, d.a_blob, d.b_blob, report_all)) 279 changes.extend(compare_dict_blobs(path, d.a_blob, d.b_blob, report_all))
255 280
256 return changes 281 # Link related changes
282 for chg in changes:
283 if chg.monitored:
284 for chg2 in changes:
285 # (Check dirname in the case of fields from recipe info files)
286 if chg.path == chg2.path or os.path.dirname(chg.path) == chg2.path:
287 if chg2.fieldname in related_fields.get(chg.fieldname, []):
288 chg.related.append(chg2)
289 elif chg.path.startswith('packages/') and chg2.fieldname in ['PE', 'PV', 'PR']:
290 chg.related.append(chg2)
291
292 if report_all:
293 return changes
294 else:
295 return [chg for chg in changes if chg.monitored]