poky/scripts/yocto-check-layer
Richard Purdie 7458ad340f yocto-check-layer: Allow OE-Core to be tested
For unknown reasons we've never seemingly run the check layer script
against OE-Core itself. This isn't entirely straightforward as the core
layer is a bit of a special case, we can't for example compare signatures
against ourselve and we can't remove core from bblayers.conf.

Core does have distro, machine and software components too, in the case
of distro, our fallback default settings. Whilst the qemu machines could
be split into a seperate layer directory, core wouldn't then parse at all
standalone due to the lack of any machine so it seems a bit pointless to
do that.

These changes tweak the script to handle core's special cases, specifically
to allow distro and machine directories and to account for the README placed
a directory level higher than other layers.

(From OE-Core rev: ba312ed228507d05f280aeb96819d671b01400b8)

Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2022-12-09 13:18:41 +00:00

257 lines
10 KiB
Python
Executable File

#!/usr/bin/env python3
# Yocto Project layer checking tool
#
# Copyright (C) 2017 Intel Corporation
#
# SPDX-License-Identifier: MIT
#
import os
import sys
import argparse
import logging
import time
import signal
import shutil
import collections
scripts_path = os.path.dirname(os.path.realpath(__file__))
lib_path = scripts_path + '/lib'
sys.path = sys.path + [lib_path]
import scriptutils
import scriptpath
scriptpath.add_oe_lib_path()
scriptpath.add_bitbake_lib_path()
from checklayer import LayerType, detect_layers, add_layers, add_layer_dependencies, get_layer_dependencies, get_signatures, check_bblayers, sanity_check_layers
from oeqa.utils.commands import get_bb_vars
PROGNAME = 'yocto-check-layer'
CASES_PATHS = [os.path.join(os.path.abspath(os.path.dirname(__file__)),
'lib', 'checklayer', 'cases')]
logger = scriptutils.logger_create(PROGNAME, stream=sys.stdout)
def test_layer(td, layer, test_software_layer_signatures):
from checklayer.context import CheckLayerTestContext
logger.info("Starting to analyze: %s" % layer['name'])
logger.info("----------------------------------------------------------------------")
tc = CheckLayerTestContext(td=td, logger=logger, layer=layer, test_software_layer_signatures=test_software_layer_signatures)
tc.loadTests(CASES_PATHS)
return tc.runTests()
def dump_layer_debug(layer):
logger.debug("Found layer %s (%s)" % (layer["name"], layer["path"]))
collections = layer.get("collections", {})
if collections:
logger.debug("%s collections: %s" % (layer["name"], ", ".join(collections)))
def main():
parser = argparse.ArgumentParser(
description="Yocto Project layer checking tool",
add_help=False)
parser.add_argument('layers', metavar='LAYER_DIR', nargs='+',
help='Layer to check')
parser.add_argument('-o', '--output-log',
help='File to output log (optional)', action='store')
parser.add_argument('--dependency', nargs="+",
help='Layers to process for dependencies', action='store')
parser.add_argument('--no-auto-dependency', help='Disable automatic testing of dependencies',
action='store_true')
parser.add_argument('--machines', nargs="+",
help='List of MACHINEs to be used during testing', action='store')
parser.add_argument('--additional-layers', nargs="+",
help='List of additional layers to add during testing', action='store')
group = parser.add_mutually_exclusive_group()
group.add_argument('--with-software-layer-signature-check', action='store_true', dest='test_software_layer_signatures',
default=True,
help='check that software layers do not change signatures (on by default)')
group.add_argument('--without-software-layer-signature-check', action='store_false', dest='test_software_layer_signatures',
help='disable signature checking for software layers')
parser.add_argument('-n', '--no-auto', help='Disable auto layer discovery',
action='store_true')
parser.add_argument('-d', '--debug', help='Enable debug output',
action='store_true')
parser.add_argument('-q', '--quiet', help='Print only errors',
action='store_true')
parser.add_argument('-h', '--help', action='help',
default=argparse.SUPPRESS,
help='show this help message and exit')
args = parser.parse_args()
if args.output_log:
fh = logging.FileHandler(args.output_log)
fh.setFormatter(logging.Formatter("%(levelname)s: %(message)s"))
logger.addHandler(fh)
if args.debug:
logger.setLevel(logging.DEBUG)
elif args.quiet:
logger.setLevel(logging.ERROR)
if not 'BUILDDIR' in os.environ:
logger.error("You must source the environment before running this script.")
logger.error("$ source oe-init-build-env")
return 1
builddir = os.environ['BUILDDIR']
bblayersconf = os.path.join(builddir, 'conf', 'bblayers.conf')
layers = detect_layers(args.layers, args.no_auto)
if not layers:
logger.error("Failed to detect layers")
return 1
if args.additional_layers:
additional_layers = detect_layers(args.additional_layers, args.no_auto)
else:
additional_layers = []
if args.dependency:
dep_layers = detect_layers(args.dependency, args.no_auto)
dep_layers = dep_layers + layers
else:
dep_layers = layers
logger.debug("Found additional layers:")
for l in additional_layers:
dump_layer_debug(l)
logger.debug("Found dependency layers:")
for l in dep_layers:
dump_layer_debug(l)
if not sanity_check_layers(additional_layers + dep_layers, logger):
logger.error("Failed layer validation")
return 1
logger.info("Detected layers:")
for layer in layers:
if layer['type'] == LayerType.ERROR_BSP_DISTRO:
logger.error("%s: Can't be DISTRO and BSP type at the same time."\
" Both conf/distro and conf/machine folders were found."\
% layer['name'])
layers.remove(layer)
elif layer['type'] == LayerType.ERROR_NO_LAYER_CONF:
logger.info("%s: Doesn't have conf/layer.conf file, so ignoring"\
% layer['name'])
layers.remove(layer)
else:
logger.info("%s: %s, %s" % (layer['name'], layer['type'],
layer['path']))
if not layers:
return 1
# Find all dependencies, and get them checked too
if not args.no_auto_dependency:
depends = []
for layer in layers:
layer_depends = get_layer_dependencies(layer, dep_layers, logger)
if layer_depends:
for d in layer_depends:
if d not in depends:
depends.append(d)
for d in depends:
if d not in layers:
logger.info("Adding %s to the list of layers to test, as a dependency", d['name'])
layers.append(d)
shutil.copyfile(bblayersconf, bblayersconf + '.backup')
def cleanup_bblayers(signum, frame):
shutil.copyfile(bblayersconf + '.backup', bblayersconf)
os.unlink(bblayersconf + '.backup')
signal.signal(signal.SIGTERM, cleanup_bblayers)
signal.signal(signal.SIGINT, cleanup_bblayers)
td = {}
results = collections.OrderedDict()
results_status = collections.OrderedDict()
layers_tested = 0
for layer in layers:
if layer['type'] in (LayerType.ERROR_NO_LAYER_CONF, LayerType.ERROR_BSP_DISTRO):
continue
# Reset to a clean backup copy for each run
shutil.copyfile(bblayersconf + '.backup', bblayersconf)
if layer['type'] not in (LayerType.CORE, ) and check_bblayers(bblayersconf, layer['path'], logger):
logger.info("%s already in %s. To capture initial signatures, layer under test should not present "
"in BBLAYERS. Please remove %s from BBLAYERS." % (layer['name'], bblayersconf, layer['name']))
results[layer['name']] = None
results_status[layer['name']] = 'SKIPPED (Layer under test should not present in BBLAYERS)'
continue
logger.info('')
logger.info("Setting up for %s(%s), %s" % (layer['name'], layer['type'],
layer['path']))
missing_dependencies = not add_layer_dependencies(bblayersconf, layer, dep_layers, logger)
if not missing_dependencies:
for additional_layer in additional_layers:
if not add_layer_dependencies(bblayersconf, additional_layer, dep_layers, logger):
missing_dependencies = True
break
if missing_dependencies:
logger.info('Skipping %s due to missing dependencies.' % layer['name'])
results[layer['name']] = None
results_status[layer['name']] = 'SKIPPED (Missing dependencies)'
layers_tested = layers_tested + 1
continue
if any(map(lambda additional_layer: not add_layers(bblayersconf, [additional_layer], logger),
additional_layers)):
logger.info('Skipping %s due to missing additional layers.' % layer['name'])
results[layer['name']] = None
results_status[layer['name']] = 'SKIPPED (Missing additional layers)'
layers_tested = layers_tested + 1
continue
logger.info('Getting initial bitbake variables ...')
td['bbvars'] = get_bb_vars()
logger.info('Getting initial signatures ...')
td['builddir'] = builddir
try:
td['sigs'], td['tunetasks'] = get_signatures(td['builddir'])
except RuntimeError as e:
logger.info(str(e))
results[layer['name']] = None
results_status[layer['name']] = 'FAIL (Generating world signatures)'
layers_tested = layers_tested + 1
continue
td['machines'] = args.machines
if not add_layers(bblayersconf, [layer], logger):
logger.info('Skipping %s ???.' % layer['name'])
results[layer['name']] = None
results_status[layer['name']] = 'SKIPPED (Unknown)'
layers_tested = layers_tested + 1
continue
result = test_layer(td, layer, args.test_software_layer_signatures)
results[layer['name']] = result
results_status[layer['name']] = 'PASS' if results[layer['name']].wasSuccessful() else 'FAIL'
layers_tested = layers_tested + 1
ret = 0
if layers_tested:
logger.info('')
logger.info('Summary of results:')
logger.info('')
for layer_name in results_status:
logger.info('%s ... %s' % (layer_name, results_status[layer_name]))
if not results[layer_name] or not results[layer_name].wasSuccessful():
ret = 2 # ret = 1 used for initialization errors
cleanup_bblayers(None, None)
return ret
if __name__ == '__main__':
try:
ret = main()
except Exception:
ret = 1
import traceback
traceback.print_exc()
sys.exit(ret)