diff options
Diffstat (limited to 'meta/lib/oe')
-rw-r--r-- | meta/lib/oe/buildhistory_analysis.py | 65 |
1 files changed, 62 insertions, 3 deletions
diff --git a/meta/lib/oe/buildhistory_analysis.py b/meta/lib/oe/buildhistory_analysis.py index 5b28774c98..2d6fa1779e 100644 --- a/meta/lib/oe/buildhistory_analysis.py +++ b/meta/lib/oe/buildhistory_analysis.py | |||
@@ -213,6 +213,7 @@ class FileChange: | |||
213 | changetype_perms = 'P' | 213 | changetype_perms = 'P' |
214 | changetype_ownergroup = 'O' | 214 | changetype_ownergroup = 'O' |
215 | changetype_link = 'L' | 215 | changetype_link = 'L' |
216 | changetype_move = 'M' | ||
216 | 217 | ||
217 | def __init__(self, path, changetype, oldvalue = None, newvalue = None): | 218 | def __init__(self, path, changetype, oldvalue = None, newvalue = None): |
218 | self.path = path | 219 | self.path = path |
@@ -251,10 +252,11 @@ class FileChange: | |||
251 | return '%s changed owner/group from %s to %s' % (self.path, self.oldvalue, self.newvalue) | 252 | return '%s changed owner/group from %s to %s' % (self.path, self.oldvalue, self.newvalue) |
252 | elif self.changetype == self.changetype_link: | 253 | elif self.changetype == self.changetype_link: |
253 | return '%s changed symlink target from %s to %s' % (self.path, self.oldvalue, self.newvalue) | 254 | return '%s changed symlink target from %s to %s' % (self.path, self.oldvalue, self.newvalue) |
255 | elif self.changetype == self.changetype_move: | ||
256 | return '%s moved to %s' % (self.path, self.oldvalue) | ||
254 | else: | 257 | else: |
255 | return '%s changed (unknown)' % self.path | 258 | return '%s changed (unknown)' % self.path |
256 | 259 | ||
257 | |||
258 | def blob_to_dict(blob): | 260 | def blob_to_dict(blob): |
259 | alines = [line for line in blob.data_stream.read().decode('utf-8').splitlines()] | 261 | alines = [line for line in blob.data_stream.read().decode('utf-8').splitlines()] |
260 | adict = {} | 262 | adict = {} |
@@ -281,11 +283,14 @@ def file_list_to_dict(lines): | |||
281 | adict[path] = splitv[0:3] | 283 | adict[path] = splitv[0:3] |
282 | return adict | 284 | return adict |
283 | 285 | ||
286 | numeric_removal = str.maketrans('0123456789', 'XXXXXXXXXX') | ||
284 | 287 | ||
285 | def compare_file_lists(alines, blines, compare_ownership=True): | 288 | def compare_file_lists(alines, blines, compare_ownership=True): |
286 | adict = file_list_to_dict(alines) | 289 | adict = file_list_to_dict(alines) |
287 | bdict = file_list_to_dict(blines) | 290 | bdict = file_list_to_dict(blines) |
288 | filechanges = [] | 291 | filechanges = [] |
292 | additions = [] | ||
293 | removals = [] | ||
289 | for path, splitv in adict.items(): | 294 | for path, splitv in adict.items(): |
290 | newsplitv = bdict.pop(path, None) | 295 | newsplitv = bdict.pop(path, None) |
291 | if newsplitv: | 296 | if newsplitv: |
@@ -318,11 +323,65 @@ def compare_file_lists(alines, blines, compare_ownership=True): | |||
318 | if oldvalue != newvalue: | 323 | if oldvalue != newvalue: |
319 | filechanges.append(FileChange(path, FileChange.changetype_link, oldvalue, newvalue)) | 324 | filechanges.append(FileChange(path, FileChange.changetype_link, oldvalue, newvalue)) |
320 | else: | 325 | else: |
321 | filechanges.append(FileChange(path, FileChange.changetype_remove)) | 326 | removals.append(path) |
322 | 327 | ||
323 | # Whatever is left over has been added | 328 | # Whatever is left over has been added |
324 | for path in bdict: | 329 | for path in bdict: |
325 | filechanges.append(FileChange(path, FileChange.changetype_add)) | 330 | additions.append(path) |
331 | |||
332 | # Rather than print additions and removals, its nicer to print file 'moves' | ||
333 | # where names or paths are similar. | ||
334 | revmap_remove = {} | ||
335 | for removal in removals: | ||
336 | translated = removal.translate(numeric_removal) | ||
337 | if translated not in revmap_remove: | ||
338 | revmap_remove[translated] = [] | ||
339 | revmap_remove[translated].append(removal) | ||
340 | |||
341 | # | ||
342 | # We want to detect renames of large trees of files like | ||
343 | # /lib/modules/5.4.40-yocto-standard to /lib/modules/5.4.43-yocto-standard | ||
344 | # | ||
345 | renames = {} | ||
346 | for addition in additions.copy(): | ||
347 | if addition not in additions: | ||
348 | continue | ||
349 | translated = addition.translate(numeric_removal) | ||
350 | if translated in revmap_remove: | ||
351 | if len(revmap_remove[translated]) != 1: | ||
352 | continue | ||
353 | removal = revmap_remove[translated][0] | ||
354 | commondir = addition.split("/") | ||
355 | commondir2 = removal.split("/") | ||
356 | idx = None | ||
357 | for i in range(len(commondir)): | ||
358 | if commondir[i] != commondir2[i]: | ||
359 | idx = i | ||
360 | break | ||
361 | commondir = "/".join(commondir[:i+1]) | ||
362 | commondir2 = "/".join(commondir2[:i+1]) | ||
363 | # If the common parent is in one dict and not the other its likely a rename | ||
364 | # so iterate through those files and process as such | ||
365 | if commondir2 not in bdict and commondir not in adict: | ||
366 | if commondir not in renames: | ||
367 | renames[commondir] = commondir2 | ||
368 | for addition2 in additions.copy(): | ||
369 | if addition2.startswith(commondir): | ||
370 | removal2 = addition2.replace(commondir, commondir2) | ||
371 | if removal2 in removals: | ||
372 | additions.remove(addition2) | ||
373 | removals.remove(removal2) | ||
374 | continue | ||
375 | filechanges.append(FileChange(removal, FileChange.changetype_move, addition)) | ||
376 | additions.remove(addition) | ||
377 | removals.remove(removal) | ||
378 | for rename in renames: | ||
379 | filechanges.append(FileChange(renames[rename], FileChange.changetype_move, rename)) | ||
380 | |||
381 | for addition in additions: | ||
382 | filechanges.append(FileChange(addition, FileChange.changetype_add)) | ||
383 | for removal in removals: | ||
384 | filechanges.append(FileChange(removal, FileChange.changetype_remove)) | ||
326 | 385 | ||
327 | return filechanges | 386 | return filechanges |
328 | 387 | ||