diff options
| author | Patrick Ohly <patrick.ohly@intel.com> | 2017-04-05 15:36:05 +0200 |
|---|---|---|
| committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2017-04-10 23:00:42 +0100 |
| commit | 7d4fa58c5c612915a11e9faaa88d24d32540f1b6 (patch) | |
| tree | aa11dedd88fc66bb5a6b7f18b3252ef1c0d16e2c /scripts/lib | |
| parent | efd3b0ee305e7372e736262fd2ab2bcc983b1b15 (diff) | |
| download | poky-7d4fa58c5c612915a11e9faaa88d24d32540f1b6.tar.gz | |
yocto-compat-layer: limit report of signature changes
Typically a single change cascades through the entire task dependency
chain. Developers had to figure that out themselves, based on hard to
read and interpret output (not sorted, no indention, no explanations):
$ yocto-compat-layer.py -n meta-xxxx
...
AssertionError: True is not false : Layer meta-xxxx changed signatures.
webkitgtk:do_install changed fe2edc9082bc0da98f9cb1391c52f565 -> b3a44684c5cd9aacd3f7c6ed88eefab5
gstreamer1.0-plugins-good:do_configure changed 3b2f8211be3fe08422bf6087f3af16d1 -> 7d80e42fa1f4f01ff4dfe2ea4477d382
pulseaudio:do_package_qa changed 5d0a58ada66ff17f5576555302ac319a -> 0e13bcb96143d1ae54c451bc3de0aa30
epiphany:do_prepare_recipe_sysroot changed 29e1b277dbcb005bd54950594c50d91b -> d3c45527b37677a0668ce483c6db3052
...
gst-player:do_packagedata changed 9ce6efdd357dd74919bc4957458b1e95 -> d0c083ce629f37adfc9c4ba9eff81f83
gstreamer1.0-plugins-base:do_install changed 1161cd867d15bea63e5dd5d9abf0519c -> 5bf2b652a2d77fee3eedb35af2f201a0
gstreamer1.0-rtsp-server:do_packagedata changed 6781dc3070f80b843ed1970d74dd323e -> 454620c2e3b9fea87e525d14b6ed0344
alsa-plugins:do_packagedata changed 1808c3f737cb805b169d004e948ea19c -> 480124b7fa5eab1f73bf96440d725231
Now the tool automates the problem analysis: it retrieves the depgraph
using the tinfoil API and only reports those tasks with modified
signatures whose dependencies have not changed, i.e. those tasks which
definitely introduce a change.
>From the previous example, that just leaves two tasks that need to be
checked:
AssertionError: False is not true : Layer meta-xxxx changed 120 signatures, initial differences (first hash without, second with layer):
gstreamer1.0-plugins-base:do_fetch: 76973f19f2e30d282152bdd7e4efe5bb -> e6e7c6fa9f2bd59d7d8d107f7c6ca1ac
pulseaudio:do_install: 668eb1e30af129df9806b0aa0d7c10cd -> 1196bdb88eef56eeee4613bb06b9387e
This pruning might be a bit too aggressive in the sense that tasks
which inherit a change and then add more changes themselves won't be
reported initially. They will be found when fixing the reported tasks
and re-running the check.
For a developer it seems better to have something listed which
definitely is a problem and needs fixing instead of everything,
including the tasks which don't need fixes.
(From OE-Core rev: 7ab0e09de75bfd7e7498bfa72d1f2f5d02a96747)
Signed-off-by: Patrick Ohly <patrick.ohly@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'scripts/lib')
| -rw-r--r-- | scripts/lib/compatlayer/__init__.py | 32 | ||||
| -rw-r--r-- | scripts/lib/compatlayer/cases/common.py | 54 |
2 files changed, 73 insertions, 13 deletions
diff --git a/scripts/lib/compatlayer/__init__.py b/scripts/lib/compatlayer/__init__.py index 9eb862dc6b..b46527a185 100644 --- a/scripts/lib/compatlayer/__init__.py +++ b/scripts/lib/compatlayer/__init__.py | |||
| @@ -7,6 +7,8 @@ import os | |||
| 7 | import subprocess | 7 | import subprocess |
| 8 | from enum import Enum | 8 | from enum import Enum |
| 9 | 9 | ||
| 10 | import bb.tinfoil | ||
| 11 | |||
| 10 | class LayerType(Enum): | 12 | class LayerType(Enum): |
| 11 | BSP = 0 | 13 | BSP = 0 |
| 12 | DISTRO = 1 | 14 | DISTRO = 1 |
| @@ -252,3 +254,33 @@ def get_signatures(builddir, failsafe=False): | |||
| 252 | raise RuntimeError('Can\'t load signatures from %s' % sigs_file) | 254 | raise RuntimeError('Can\'t load signatures from %s' % sigs_file) |
| 253 | 255 | ||
| 254 | return sigs | 256 | return sigs |
| 257 | |||
| 258 | def get_depgraph(targets=['world']): | ||
| 259 | ''' | ||
| 260 | Returns the dependency graph for the given target(s). | ||
| 261 | The dependency graph is taken directly from DepTreeEvent. | ||
| 262 | ''' | ||
| 263 | depgraph = None | ||
| 264 | with bb.tinfoil.Tinfoil() as tinfoil: | ||
| 265 | tinfoil.prepare(config_only=False) | ||
| 266 | tinfoil.set_event_mask(['bb.event.NoProvider', 'bb.event.DepTreeGenerated', 'bb.command.CommandCompleted']) | ||
| 267 | if not tinfoil.run_command('generateDepTreeEvent', targets, 'do_build'): | ||
| 268 | raise RuntimeError('starting generateDepTreeEvent failed') | ||
| 269 | while True: | ||
| 270 | event = tinfoil.wait_event(timeout=1000) | ||
| 271 | if event: | ||
| 272 | if isinstance(event, bb.command.CommandFailed): | ||
| 273 | raise RuntimeError('Generating dependency information failed: %s' % event.error) | ||
| 274 | elif isinstance(event, bb.command.CommandCompleted): | ||
| 275 | break | ||
| 276 | elif isinstance(event, bb.event.NoProvider): | ||
| 277 | if event._reasons: | ||
| 278 | raise RuntimeError('Nothing provides %s: %s' % (event._item, event._reasons)) | ||
| 279 | else: | ||
| 280 | raise RuntimeError('Nothing provides %s.' % (event._item)) | ||
| 281 | elif isinstance(event, bb.event.DepTreeGenerated): | ||
| 282 | depgraph = event._depgraph | ||
| 283 | |||
| 284 | if depgraph is None: | ||
| 285 | raise RuntimeError('Could not retrieve the depgraph.') | ||
| 286 | return depgraph | ||
diff --git a/scripts/lib/compatlayer/cases/common.py b/scripts/lib/compatlayer/cases/common.py index 9cc682e2c1..b91da9bb14 100644 --- a/scripts/lib/compatlayer/cases/common.py +++ b/scripts/lib/compatlayer/cases/common.py | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | import os | 4 | import os |
| 5 | import unittest | 5 | import unittest |
| 6 | from compatlayer import get_signatures, LayerType, check_command | 6 | from compatlayer import get_signatures, LayerType, check_command, get_depgraph |
| 7 | from compatlayer.case import OECompatLayerTestCase | 7 | from compatlayer.case import OECompatLayerTestCase |
| 8 | 8 | ||
| 9 | class CommonCompatLayer(OECompatLayerTestCase): | 9 | class CommonCompatLayer(OECompatLayerTestCase): |
| @@ -31,21 +31,49 @@ class CommonCompatLayer(OECompatLayerTestCase): | |||
| 31 | raise unittest.SkipTest("Layer %s isn't BSP or DISTRO one." \ | 31 | raise unittest.SkipTest("Layer %s isn't BSP or DISTRO one." \ |
| 32 | % self.tc.layer['name']) | 32 | % self.tc.layer['name']) |
| 33 | 33 | ||
| 34 | # task -> (old signature, new signature) | ||
| 34 | sig_diff = {} | 35 | sig_diff = {} |
| 35 | |||
| 36 | curr_sigs = get_signatures(self.td['builddir'], failsafe=True) | 36 | curr_sigs = get_signatures(self.td['builddir'], failsafe=True) |
| 37 | for task in self.td['sigs']: | 37 | for task in self.td['sigs']: |
| 38 | if task not in curr_sigs: | 38 | if task in curr_sigs and \ |
| 39 | continue | 39 | self.td['sigs'][task] != curr_sigs[task]: |
| 40 | 40 | sig_diff[task] = (self.td['sigs'][task], curr_sigs[task]) | |
| 41 | if self.td['sigs'][task] != curr_sigs[task]: | ||
| 42 | sig_diff[task] = '%s -> %s' % \ | ||
| 43 | (self.td['sigs'][task], curr_sigs[task]) | ||
| 44 | 41 | ||
| 45 | detail = '' | ||
| 46 | if sig_diff: | 42 | if sig_diff: |
| 47 | for task in sig_diff: | 43 | # Beware, depgraph uses task=<pn>.<taskname> whereas get_signatures() |
| 48 | detail += "%s changed %s\n" % (task, sig_diff[task]) | 44 | # uses <pn>:<taskname>. Need to convert sometimes. The output follows |
| 49 | self.assertFalse(bool(sig_diff), "Layer %s changed signatures.\n%s" % \ | 45 | # the convention from get_signatures() because that seems closer to |
| 50 | (self.tc.layer['name'], detail)) | 46 | # normal bitbake output. |
| 47 | def sig2graph(task): | ||
| 48 | pn, taskname = task.rsplit(':', 1) | ||
| 49 | return pn + '.' + taskname | ||
| 50 | def graph2sig(task): | ||
| 51 | pn, taskname = task.rsplit('.', 1) | ||
| 52 | return pn + ':' + taskname | ||
| 53 | depgraph = get_depgraph() | ||
| 54 | depends = depgraph['tdepends'] | ||
| 55 | |||
| 56 | # If a task A has a changed signature, but none of its | ||
| 57 | # dependencies, then we need to report it because it is | ||
| 58 | # the one which introduces a change. Any task depending on | ||
| 59 | # A (directly or indirectly) will also have a changed | ||
| 60 | # signature, but we don't need to report it. It might have | ||
| 61 | # its own changes, which will become apparent once the | ||
| 62 | # issues that we do report are fixed and the test gets run | ||
| 63 | # again. | ||
| 64 | sig_diff_filtered = [] | ||
| 65 | for task, (old_sig, new_sig) in sig_diff.items(): | ||
| 66 | deps_tainted = False | ||
| 67 | for dep in depends.get(sig2graph(task), ()): | ||
| 68 | if graph2sig(dep) in sig_diff: | ||
| 69 | deps_tainted = True | ||
| 70 | break | ||
| 71 | if not deps_tainted: | ||
| 72 | sig_diff_filtered.append((task, old_sig, new_sig)) | ||
| 51 | 73 | ||
| 74 | msg = [] | ||
| 75 | msg.append('Layer %s changed %d signatures, initial differences (first hash without, second with layer):' % | ||
| 76 | (self.tc.layer['name'], len(sig_diff))) | ||
| 77 | for diff in sorted(sig_diff_filtered): | ||
| 78 | msg.append(' %s: %s -> %s' % diff) | ||
| 79 | self.assertTrue(False, '\n'.join(msg)) | ||
