summaryrefslogtreecommitdiffstats
path: root/bitbake
diff options
context:
space:
mode:
authorPaul Eggleton <paul.eggleton@linux.intel.com>2017-04-07 09:52:10 +1200
committerRichard Purdie <richard.purdie@linuxfoundation.org>2017-04-10 23:00:32 +0100
commit5f7bf1f66d21155dfa5328aa57b4302cc64c132b (patch)
treece0fd73f0c03de93a367ab124b0e6a42247f7850 /bitbake
parent5d8b89fc0b9a703243dcd4cce483c335effee78d (diff)
downloadpoky-5f7bf1f66d21155dfa5328aa57b4302cc64c132b.tar.gz
bitbake: lib/bb/siggen: show word-diff for single-line values containing spaces
If a variable value has changed and either the new or old value contains spaces, a word diff should be appropriate and may be a bit more readable. Import the "simplediff" module and use it to show a word diff (in the style of GNU wdiff and git diff --word-diff). Also use a similar style diff to show changes in the runtaskhashes list. I didn't use an actual word-diff here since it's a little different - we can be sure that the list is a list and not simply a free-format string. (Bitbake rev: 20db6b6553c80e18afc4f43dc2495435f7477822) Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'bitbake')
-rw-r--r--bitbake/LICENSE2
-rw-r--r--bitbake/lib/bb/siggen.py38
-rw-r--r--bitbake/lib/simplediff/LICENSE22
-rw-r--r--bitbake/lib/simplediff/__init__.py198
4 files changed, 259 insertions, 1 deletions
diff --git a/bitbake/LICENSE b/bitbake/LICENSE
index 5d4a4c2a8a..7d4e5f44b5 100644
--- a/bitbake/LICENSE
+++ b/bitbake/LICENSE
@@ -15,3 +15,5 @@ Foundation and individual contributors.
15* QUnit is redistributed under the MIT license. 15* QUnit is redistributed under the MIT license.
16 16
17* Font Awesome fonts redistributed under the SIL Open Font License 1.1 17* Font Awesome fonts redistributed under the SIL Open Font License 1.1
18
19* simplediff is distributed under the zlib license.
diff --git a/bitbake/lib/bb/siggen.py b/bitbake/lib/bb/siggen.py
index 3c5d86247c..d40c721fbf 100644
--- a/bitbake/lib/bb/siggen.py
+++ b/bitbake/lib/bb/siggen.py
@@ -6,6 +6,7 @@ import tempfile
6import pickle 6import pickle
7import bb.data 7import bb.data
8import difflib 8import difflib
9import simplediff
9from bb.checksum import FileChecksumCache 10from bb.checksum import FileChecksumCache
10 11
11logger = logging.getLogger('BitBake.SigGen') 12logger = logging.getLogger('BitBake.SigGen')
@@ -352,6 +353,39 @@ def dump_this_task(outfile, d):
352 referencestamp = bb.build.stamp_internal(task, d, None, True) 353 referencestamp = bb.build.stamp_internal(task, d, None, True)
353 bb.parse.siggen.dump_sigtask(fn, task, outfile, "customfile:" + referencestamp) 354 bb.parse.siggen.dump_sigtask(fn, task, outfile, "customfile:" + referencestamp)
354 355
356def worddiff_str(oldstr, newstr):
357 diff = simplediff.diff(oldstr.split(' '), newstr.split(' '))
358 ret = []
359 for change, value in diff:
360 value = ' '.join(value)
361 if change == '=':
362 ret.append(value)
363 elif change == '+':
364 item = '{+%s+}' % value
365 ret.append(item)
366 elif change == '-':
367 item = '[-%s-]' % value
368 ret.append(item)
369 whitespace_note = ''
370 if oldstr != newstr and ' '.join(oldstr.split()) == ' '.join(newstr.split()):
371 whitespace_note = ' (whitespace changed)'
372 return '"%s"%s' % (' '.join(ret), whitespace_note)
373
374def list_inline_diff(oldlist, newlist):
375 diff = simplediff.diff(oldlist, newlist)
376 ret = []
377 for change, value in diff:
378 value = ' '.join(value)
379 if change == '=':
380 ret.append("'%s'" % value)
381 elif change == '+':
382 item = "+'%s'" % value
383 ret.append(item)
384 elif change == '-':
385 item = "-'%s'" % value
386 ret.append(item)
387 return '[%s]' % (', '.join(ret))
388
355def clean_basepath(a): 389def clean_basepath(a):
356 mc = None 390 mc = None
357 if a.startswith("multiconfig:"): 391 if a.startswith("multiconfig:"):
@@ -471,6 +505,8 @@ def compare_sigfiles(a, b, recursecb=None, collapsed=False):
471 # the old/new filename (they are blank anyway in this case) 505 # the old/new filename (they are blank anyway in this case)
472 difflines = list(diff)[2:] 506 difflines = list(diff)[2:]
473 output.append("Variable %s value changed:\n%s" % (dep, '\n'.join(difflines))) 507 output.append("Variable %s value changed:\n%s" % (dep, '\n'.join(difflines)))
508 elif newval and oldval and (' ' in oldval or ' ' in newval):
509 output.append("Variable %s value changed:\n%s" % (dep, worddiff_str(oldval, newval)))
474 else: 510 else:
475 output.append("Variable %s value changed from '%s' to '%s'" % (dep, oldval, newval)) 511 output.append("Variable %s value changed from '%s' to '%s'" % (dep, oldval, newval))
476 512
@@ -510,7 +546,7 @@ def compare_sigfiles(a, b, recursecb=None, collapsed=False):
510 clean_a = clean_basepaths_list(a_data['runtaskdeps']) 546 clean_a = clean_basepaths_list(a_data['runtaskdeps'])
511 clean_b = clean_basepaths_list(b_data['runtaskdeps']) 547 clean_b = clean_basepaths_list(b_data['runtaskdeps'])
512 if clean_a != clean_b: 548 if clean_a != clean_b:
513 output.append("runtaskdeps changed from %s to %s" % (clean_a, clean_b)) 549 output.append("runtaskdeps changed:\n%s" % list_inline_diff(clean_a, clean_b))
514 else: 550 else:
515 output.append("runtaskdeps changed:") 551 output.append("runtaskdeps changed:")
516 output.append("\n".join(changed)) 552 output.append("\n".join(changed))
diff --git a/bitbake/lib/simplediff/LICENSE b/bitbake/lib/simplediff/LICENSE
new file mode 100644
index 0000000000..8242dde97c
--- /dev/null
+++ b/bitbake/lib/simplediff/LICENSE
@@ -0,0 +1,22 @@
1Copyright (c) 2008 - 2013 Paul Butler and contributors
2
3This sofware may be used under a zlib/libpng-style license:
4
5This software is provided 'as-is', without any express or implied warranty. In
6no event will the authors be held liable for any damages arising from the use
7of this software.
8
9Permission is granted to anyone to use this software for any purpose, including
10commercial applications, and to alter it and redistribute it freely, subject to
11the following restrictions:
12
131. The origin of this software must not be misrepresented; you must not claim
14that you wrote the original software. If you use this software in a product, an
15acknowledgment in the product documentation would be appreciated but is not
16required.
17
182. Altered source versions must be plainly marked as such, and must not be
19misrepresented as being the original software.
20
213. This notice may not be removed or altered from any source distribution.
22
diff --git a/bitbake/lib/simplediff/__init__.py b/bitbake/lib/simplediff/__init__.py
new file mode 100644
index 0000000000..57ee3c5c40
--- /dev/null
+++ b/bitbake/lib/simplediff/__init__.py
@@ -0,0 +1,198 @@
1'''
2Simple Diff for Python version 1.0
3
4Annotate two versions of a list with the values that have been
5changed between the versions, similar to unix's `diff` but with
6a dead-simple Python interface.
7
8(C) Paul Butler 2008-2012 <http://www.paulbutler.org/>
9May be used and distributed under the zlib/libpng license
10<http://www.opensource.org/licenses/zlib-license.php>
11'''
12
13__all__ = ['diff', 'string_diff', 'html_diff']
14__version__ = '1.0'
15
16
17def diff(old, new):
18 '''
19 Find the differences between two lists. Returns a list of pairs, where the
20 first value is in ['+','-','='] and represents an insertion, deletion, or
21 no change for that list. The second value of the pair is the list
22 of elements.
23
24 Params:
25 old the old list of immutable, comparable values (ie. a list
26 of strings)
27 new the new list of immutable, comparable values
28
29 Returns:
30 A list of pairs, with the first part of the pair being one of three
31 strings ('-', '+', '=') and the second part being a list of values from
32 the original old and/or new lists. The first part of the pair
33 corresponds to whether the list of values is a deletion, insertion, or
34 unchanged, respectively.
35
36 Examples:
37 >>> diff([1,2,3,4],[1,3,4])
38 [('=', [1]), ('-', [2]), ('=', [3, 4])]
39
40 >>> diff([1,2,3,4],[2,3,4,1])
41 [('-', [1]), ('=', [2, 3, 4]), ('+', [1])]
42
43 >>> diff('The quick brown fox jumps over the lazy dog'.split(),
44 ... 'The slow blue cheese drips over the lazy carrot'.split())
45 ... # doctest: +NORMALIZE_WHITESPACE
46 [('=', ['The']),
47 ('-', ['quick', 'brown', 'fox', 'jumps']),
48 ('+', ['slow', 'blue', 'cheese', 'drips']),
49 ('=', ['over', 'the', 'lazy']),
50 ('-', ['dog']),
51 ('+', ['carrot'])]
52
53 '''
54
55 # Create a map from old values to their indices
56 old_index_map = dict()
57 for i, val in enumerate(old):
58 old_index_map.setdefault(val,list()).append(i)
59
60 # Find the largest substring common to old and new.
61 # We use a dynamic programming approach here.
62 #
63 # We iterate over each value in the `new` list, calling the
64 # index `inew`. At each iteration, `overlap[i]` is the
65 # length of the largest suffix of `old[:i]` equal to a suffix
66 # of `new[:inew]` (or unset when `old[i]` != `new[inew]`).
67 #
68 # At each stage of iteration, the new `overlap` (called
69 # `_overlap` until the original `overlap` is no longer needed)
70 # is built from the old one.
71 #
72 # If the length of overlap exceeds the largest substring
73 # seen so far (`sub_length`), we update the largest substring
74 # to the overlapping strings.
75
76 overlap = dict()
77 # `sub_start_old` is the index of the beginning of the largest overlapping
78 # substring in the old list. `sub_start_new` is the index of the beginning
79 # of the same substring in the new list. `sub_length` is the length that
80 # overlaps in both.
81 # These track the largest overlapping substring seen so far, so naturally
82 # we start with a 0-length substring.
83 sub_start_old = 0
84 sub_start_new = 0
85 sub_length = 0
86
87 for inew, val in enumerate(new):
88 _overlap = dict()
89 for iold in old_index_map.get(val,list()):
90 # now we are considering all values of iold such that
91 # `old[iold] == new[inew]`.
92 _overlap[iold] = (iold and overlap.get(iold - 1, 0)) + 1
93 if(_overlap[iold] > sub_length):
94 # this is the largest substring seen so far, so store its
95 # indices
96 sub_length = _overlap[iold]
97 sub_start_old = iold - sub_length + 1
98 sub_start_new = inew - sub_length + 1
99 overlap = _overlap
100
101 if sub_length == 0:
102 # If no common substring is found, we return an insert and delete...
103 return (old and [('-', old)] or []) + (new and [('+', new)] or [])
104 else:
105 # ...otherwise, the common substring is unchanged and we recursively
106 # diff the text before and after that substring
107 return diff(old[ : sub_start_old], new[ : sub_start_new]) + \
108 [('=', new[sub_start_new : sub_start_new + sub_length])] + \
109 diff(old[sub_start_old + sub_length : ],
110 new[sub_start_new + sub_length : ])
111
112
113def string_diff(old, new):
114 '''
115 Returns the difference between the old and new strings when split on
116 whitespace. Considers punctuation a part of the word
117
118 This function is intended as an example; you'll probably want
119 a more sophisticated wrapper in practice.
120
121 Params:
122 old the old string
123 new the new string
124
125 Returns:
126 the output of `diff` on the two strings after splitting them
127 on whitespace (a list of change instructions; see the docstring
128 of `diff`)
129
130 Examples:
131 >>> string_diff('The quick brown fox', 'The fast blue fox')
132 ... # doctest: +NORMALIZE_WHITESPACE
133 [('=', ['The']),
134 ('-', ['quick', 'brown']),
135 ('+', ['fast', 'blue']),
136 ('=', ['fox'])]
137
138 '''
139 return diff(old.split(), new.split())
140
141
142def html_diff(old, new):
143 '''
144 Returns the difference between two strings (as in stringDiff) in
145 HTML format. HTML code in the strings is NOT escaped, so you
146 will get weird results if the strings contain HTML.
147
148 This function is intended as an example; you'll probably want
149 a more sophisticated wrapper in practice.
150
151 Params:
152 old the old string
153 new the new string
154
155 Returns:
156 the output of the diff expressed with HTML <ins> and <del>
157 tags.
158
159 Examples:
160 >>> html_diff('The quick brown fox', 'The fast blue fox')
161 'The <del>quick brown</del> <ins>fast blue</ins> fox'
162 '''
163 con = {'=': (lambda x: x),
164 '+': (lambda x: "<ins>" + x + "</ins>"),
165 '-': (lambda x: "<del>" + x + "</del>")}
166 return " ".join([(con[a])(" ".join(b)) for a, b in string_diff(old, new)])
167
168
169def check_diff(old, new):
170 '''
171 This tests that diffs returned by `diff` are valid. You probably won't
172 want to use this function, but it's provided for documentation and
173 testing.
174
175 A diff should satisfy the property that the old input is equal to the
176 elements of the result annotated with '-' or '=' concatenated together.
177 Likewise, the new input is equal to the elements of the result annotated
178 with '+' or '=' concatenated together. This function compares `old`,
179 `new`, and the results of `diff(old, new)` to ensure this is true.
180
181 Tests:
182 >>> check_diff('ABCBA', 'CBABA')
183 >>> check_diff('Foobarbaz', 'Foobarbaz')
184 >>> check_diff('Foobarbaz', 'Boobazbam')
185 >>> check_diff('The quick brown fox', 'Some quick brown car')
186 >>> check_diff('A thick red book', 'A quick blue book')
187 >>> check_diff('dafhjkdashfkhasfjsdafdasfsda', 'asdfaskjfhksahkfjsdha')
188 >>> check_diff('88288822828828288282828', '88288882882828282882828')
189 >>> check_diff('1234567890', '24689')
190 '''
191 old = list(old)
192 new = list(new)
193 result = diff(old, new)
194 _old = [val for (a, vals) in result if (a in '=-') for val in vals]
195 assert old == _old, 'Expected %s, got %s' % (old, _old)
196 _new = [val for (a, vals) in result if (a in '=+') for val in vals]
197 assert new == _new, 'Expected %s, got %s' % (new, _new)
198