mirror of
git://git.yoctoproject.org/poky.git
synced 2025-07-19 21:09:03 +02:00

This adds SPDX license headers in place of the wide assortment of things currently in our script headers. We default to GPL-2.0-only except for the oeqa code where it was clearly submitted and marked as MIT on the most part or some scripts which had the "or later" GPL versioning. The patch also drops other obsolete bits of file headers where they were encoountered such as editor modelines, obsolete maintainer information or the phrase "All rights reserved" which is now obsolete and not required in copyright headers (in this case its actually confusing for licensing as all rights were not reserved). More work is needed for OE-Core but this takes care of the bulk of the scripts and meta/lib directories. The top level LICENSE files are tweaked to match the new structure and the SPDX naming. (From OE-Core rev: f8c9c511b5f1b7dbd45b77f345cb6c048ae6763e) Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
207 lines
9.9 KiB
Python
207 lines
9.9 KiB
Python
# Copyright (C) 2017 Intel Corporation
|
|
#
|
|
# SPDX-License-Identifier: MIT
|
|
#
|
|
|
|
import unittest
|
|
|
|
from checklayer import LayerType, get_signatures, check_command, get_depgraph
|
|
from checklayer.case import OECheckLayerTestCase
|
|
|
|
class BSPCheckLayer(OECheckLayerTestCase):
|
|
@classmethod
|
|
def setUpClass(self):
|
|
if self.tc.layer['type'] != LayerType.BSP:
|
|
raise unittest.SkipTest("BSPCheckLayer: Layer %s isn't BSP one." %\
|
|
self.tc.layer['name'])
|
|
|
|
def test_bsp_defines_machines(self):
|
|
self.assertTrue(self.tc.layer['conf']['machines'],
|
|
"Layer is BSP but doesn't defines machines.")
|
|
|
|
def test_bsp_no_set_machine(self):
|
|
from oeqa.utils.commands import get_bb_var
|
|
|
|
machine = get_bb_var('MACHINE')
|
|
self.assertEqual(self.td['bbvars']['MACHINE'], machine,
|
|
msg="Layer %s modified machine %s -> %s" % \
|
|
(self.tc.layer['name'], self.td['bbvars']['MACHINE'], machine))
|
|
|
|
|
|
def test_machine_world(self):
|
|
'''
|
|
"bitbake world" is expected to work regardless which machine is selected.
|
|
BSP layers sometimes break that by enabling a recipe for a certain machine
|
|
without checking whether that recipe actually can be built in the current
|
|
distro configuration (for example, OpenGL might not enabled).
|
|
|
|
This test iterates over all machines. It would be nicer to instantiate
|
|
it once per machine. It merely checks for errors during parse
|
|
time. It does not actually attempt to build anything.
|
|
'''
|
|
|
|
if not self.td['machines']:
|
|
self.skipTest('No machines set with --machines.')
|
|
msg = []
|
|
for machine in self.td['machines']:
|
|
# In contrast to test_machine_signatures() below, errors are fatal here.
|
|
try:
|
|
get_signatures(self.td['builddir'], failsafe=False, machine=machine)
|
|
except RuntimeError as ex:
|
|
msg.append(str(ex))
|
|
if msg:
|
|
msg.insert(0, 'The following machines broke a world build:')
|
|
self.fail('\n'.join(msg))
|
|
|
|
def test_machine_signatures(self):
|
|
'''
|
|
Selecting a machine may only affect the signature of tasks that are specific
|
|
to that machine. In other words, when MACHINE=A and MACHINE=B share a recipe
|
|
foo and the output of foo, then both machine configurations must build foo
|
|
in exactly the same way. Otherwise it is not possible to use both machines
|
|
in the same distribution.
|
|
|
|
This criteria can only be tested by testing different machines in combination,
|
|
i.e. one main layer, potentially several additional BSP layers and an explicit
|
|
choice of machines:
|
|
yocto-check-layer --additional-layers .../meta-intel --machines intel-corei7-64 imx6slevk -- .../meta-freescale
|
|
'''
|
|
|
|
if not self.td['machines']:
|
|
self.skipTest('No machines set with --machines.')
|
|
|
|
# Collect signatures for all machines that we are testing
|
|
# and merge that into a hash:
|
|
# tune -> task -> signature -> list of machines with that combination
|
|
#
|
|
# It is an error if any tune/task pair has more than one signature,
|
|
# because that implies that the machines that caused those different
|
|
# signatures do not agree on how to execute the task.
|
|
tunes = {}
|
|
# Preserve ordering of machines as chosen by the user.
|
|
for machine in self.td['machines']:
|
|
curr_sigs, tune2tasks = get_signatures(self.td['builddir'], failsafe=True, machine=machine)
|
|
# Invert the tune -> [tasks] mapping.
|
|
tasks2tune = {}
|
|
for tune, tasks in tune2tasks.items():
|
|
for task in tasks:
|
|
tasks2tune[task] = tune
|
|
for task, sighash in curr_sigs.items():
|
|
tunes.setdefault(tasks2tune[task], {}).setdefault(task, {}).setdefault(sighash, []).append(machine)
|
|
|
|
msg = []
|
|
pruned = 0
|
|
last_line_key = None
|
|
# do_fetch, do_unpack, ..., do_build
|
|
taskname_list = []
|
|
if tunes:
|
|
# The output below is most useful when we start with tasks that are at
|
|
# the bottom of the dependency chain, i.e. those that run first. If
|
|
# those tasks differ, the rest also does.
|
|
#
|
|
# To get an ordering of tasks, we do a topological sort of the entire
|
|
# depgraph for the base configuration, then on-the-fly flatten that list by stripping
|
|
# out the recipe names and removing duplicates. The base configuration
|
|
# is not necessarily representative, but should be close enough. Tasks
|
|
# that were not encountered get a default priority.
|
|
depgraph = get_depgraph()
|
|
depends = depgraph['tdepends']
|
|
WHITE = 1
|
|
GRAY = 2
|
|
BLACK = 3
|
|
color = {}
|
|
found = set()
|
|
def visit(task):
|
|
color[task] = GRAY
|
|
for dep in depends.get(task, ()):
|
|
if color.setdefault(dep, WHITE) == WHITE:
|
|
visit(dep)
|
|
color[task] = BLACK
|
|
pn, taskname = task.rsplit('.', 1)
|
|
if taskname not in found:
|
|
taskname_list.append(taskname)
|
|
found.add(taskname)
|
|
for task in depends.keys():
|
|
if color.setdefault(task, WHITE) == WHITE:
|
|
visit(task)
|
|
|
|
taskname_order = dict([(task, index) for index, task in enumerate(taskname_list) ])
|
|
def task_key(task):
|
|
pn, taskname = task.rsplit(':', 1)
|
|
return (pn, taskname_order.get(taskname, len(taskname_list)), taskname)
|
|
|
|
for tune in sorted(tunes.keys()):
|
|
tasks = tunes[tune]
|
|
# As for test_signatures it would be nicer to sort tasks
|
|
# by dependencies here, but that is harder because we have
|
|
# to report on tasks from different machines, which might
|
|
# have different dependencies. We resort to pruning the
|
|
# output by reporting only one task per recipe if the set
|
|
# of machines matches.
|
|
#
|
|
# "bitbake-diffsigs -t -s" is intelligent enough to print
|
|
# diffs recursively, so often it does not matter that much
|
|
# if we don't pick the underlying difference
|
|
# here. However, sometimes recursion fails
|
|
# (https://bugzilla.yoctoproject.org/show_bug.cgi?id=6428).
|
|
#
|
|
# To mitigate that a bit, we use a hard-coded ordering of
|
|
# tasks that represents how they normally run and prefer
|
|
# to print the ones that run first.
|
|
for task in sorted(tasks.keys(), key=task_key):
|
|
signatures = tasks[task]
|
|
# do_build can be ignored: it is know to have
|
|
# different signatures in some cases, for example in
|
|
# the allarch ca-certificates due to RDEPENDS=openssl.
|
|
# That particular dependency is whitelisted via
|
|
# SIGGEN_EXCLUDE_SAFE_RECIPE_DEPS, but still shows up
|
|
# in the sstate signature hash because filtering it
|
|
# out would be hard and running do_build multiple
|
|
# times doesn't really matter.
|
|
if len(signatures.keys()) > 1 and \
|
|
not task.endswith(':do_build'):
|
|
# Error!
|
|
#
|
|
# Sort signatures by machines, because the hex values don't mean anything.
|
|
# => all-arch adwaita-icon-theme:do_build: 1234... (beaglebone, qemux86) != abcdf... (qemux86-64)
|
|
#
|
|
# Skip the line if it is covered already by the predecessor (same pn, same sets of machines).
|
|
pn, taskname = task.rsplit(':', 1)
|
|
next_line_key = (pn, sorted(signatures.values()))
|
|
if next_line_key != last_line_key:
|
|
line = ' %s %s: ' % (tune, task)
|
|
line += ' != '.join(['%s (%s)' % (signature, ', '.join([m for m in signatures[signature]])) for
|
|
signature in sorted(signatures.keys(), key=lambda s: signatures[s])])
|
|
last_line_key = next_line_key
|
|
msg.append(line)
|
|
# Randomly pick two mismatched signatures and remember how to invoke
|
|
# bitbake-diffsigs for them.
|
|
iterator = iter(signatures.items())
|
|
a = next(iterator)
|
|
b = next(iterator)
|
|
diffsig_machines = '(%s) != (%s)' % (', '.join(a[1]), ', '.join(b[1]))
|
|
diffsig_params = '-t %s %s -s %s %s' % (pn, taskname, a[0], b[0])
|
|
else:
|
|
pruned += 1
|
|
|
|
if msg:
|
|
msg.insert(0, 'The machines have conflicting signatures for some shared tasks:')
|
|
if pruned > 0:
|
|
msg.append('')
|
|
msg.append('%d tasks where not listed because some other task of the recipe already differed.' % pruned)
|
|
msg.append('It is likely that differences from different recipes also have the same root cause.')
|
|
msg.append('')
|
|
# Explain how to investigate...
|
|
msg.append('To investigate, run bitbake-diffsigs -t recipename taskname -s fromsig tosig.')
|
|
cmd = 'bitbake-diffsigs %s' % diffsig_params
|
|
msg.append('Example: %s in the last line' % diffsig_machines)
|
|
msg.append('Command: %s' % cmd)
|
|
# ... and actually do it automatically for that example, but without aborting
|
|
# when that fails.
|
|
try:
|
|
output = check_command('Comparing signatures failed.', cmd).decode('utf-8')
|
|
except RuntimeError as ex:
|
|
output = str(ex)
|
|
msg.extend([' ' + line for line in output.splitlines()])
|
|
self.fail('\n'.join(msg))
|