Permalink
Find file
Fetching contributors…
Cannot retrieve contributors at this time
executable file 182 lines (150 sloc) 7.25 KB
#!/usr/bin/env python2.7
#
# This is a simple script which uses a default configuration,
# and adds each available bsp or distro layer to the configuration, one at
# a time, monitoring for changes to the appended recipes with
# bitbake-whatchanged. This helps one to identify bsp layers which affect
# machines other than the ones it provides, and distro layers which affect
# other distros when included.
#
# This script expects the user to be in a functional bitbake environment (that
# is, the setup scripts sourced, with the current directory under the build
# directory).
#
# For layers which cause a delta, a text file holding the verbose
# bitbake-whatchanged output will be written to the current directory.
#
# If the attempt to parse the recipes in the layer, or the run of
# bitbake-whatchanged fails, testing of that layer will be skipped.
import argparse
import bb_test
import errno
import glob
import os
import sh
import shutil
import subprocess
import sys
NULL = open('/dev/null', 'w')
def main(cmdline_args):
parser = argparse.ArgumentParser(description='Run leaky layer tests')
parser.add_argument('--excluded', '-x',
default='poky/meta oe-core/meta poky/meta-yocto oe-core/meta-yocto',
help='layers to exclude from processing (default: %(default)s)')
parser.add_argument('layers', metavar='LAYER', nargs='*',
help='layers to include (default: all available)')
args = parser.parse_args(cmdline_args[1:])
scriptdir = os.path.dirname(cmdline_args[0])
if not args.layers:
lconfs = set(glob.glob('{0}/../*/conf/layer.conf'.format(corebase)))
lconfs |= set(glob.glob('{0}/../*/*/conf/layer.conf'.format(corebase)))
args.layers = (l[:-len('/conf/layer.conf')] for l in lconfs)
with open('conf/auto.conf', 'w') as f:
# We only want bitbake -S printdiff to pay attention to STAMPS_DIR
f.write('SSTATE_DIR_forcevariable = "${TOPDIR}/sstate-leaky-layer-tests"\n')
filter_lines('conf/bblayers.conf', lambda l: not l.startswith('BBLAYERS =+ '))
try:
bitbake_env = bb_test.get_bitbake_env()
except sh.CommandNotFound:
sys.exit("Error: unable to run bitbake, did you source the setup script?")
except sh.ErrorReturnCode as exc:
sys.exit("Error running bitbake -e: {0}".format(exc.message))
layers = set()
corebase = bitbake_env['COREBASE']
tmpdir = bitbake_env['TMPDIR']
excluded = args.excluded.split()
for layerpath in sorted(args.layers):
layerpath = os.path.realpath(layerpath)
layerbase = os.path.relpath(layerpath, os.path.join(corebase, '..'))
if layerbase in excluded or layerpath in excluded:
continue
is_machine_layer = bool(glob.glob('{0}/conf/machine/*.conf'.format(layerpath)))
is_distro_layer = bool(glob.glob('{0}/conf/distro/*.conf'.format(layerpath)))
if not is_machine_layer and not is_distro_layer:
continue
try:
layerinfo = subprocess.check_output([os.path.join(scriptdir, 'bb-print-layer-data'), layerpath], stderr=NULL)
except subprocess.CalledProcessError as exc:
sys.exit("Error running bb-print-layer-data against {0}: {1}".format(layerpath, exc.output))
else:
layername = layerinfo.split(':')[0]
sh.rm('-rf', tmpdir)
filter_lines('conf/bblayers.conf', lambda l: not l.startswith('BBLAYERS =+ '))
try:
layers_to_cfg = subprocess.check_output([os.path.join(scriptdir, 'bb-determine-layers'), '-l', layername, '-g', '{0}/../*:{0}/../*/*'.format(corebase), 'qemux86'], stderr=NULL).splitlines()
except subprocess.CalledProcessError as exc:
sys.exit("Error running bb-determine-layers: {0}".format(exc))
bblayers_add = ' '.join(l.rstrip() for l in layers_to_cfg)
with open('conf/bblayers.conf', 'a') as f:
f.write('BBLAYERS =+ "{0}"\n'.format(bblayers_add))
with bb_test.status('Testing parsing for {0}'.format(layerbase)) as s:
try:
bb_test.bitbake('-p')
except sh.ErrorReturnCode:
s.set_status('error, skipping')
continue
targets = set()
with bb_test.status('Gathering appends for {0}'.format(layerbase)) as s:
try:
output = subprocess.check_output(['bitbake-layers', 'show-appends'], stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as exc:
if exc.returncode == 1 and 'missing append for preferred version' in exc.output:
# show-appends returns 1 both for real errors and this
# case, which is not a failure
output = exc.output
else:
s.set_status('skipping due to error:\n{0}'.format(exc.output))
continue
for line in output.splitlines():
line = line.strip()
if line.startswith(layerpath + '/'):
filename = os.path.basename(line)
base, ext = os.path.splitext(filename)
if ext == '.bbappend':
targets.add(base.split('_')[0])
if targets:
s.set_status(', '.join(targets))
filter_lines('conf/bblayers.conf', lambda l: not l.startswith('BBLAYERS =+ '))
for target in sorted(targets):
with bb_test.status('Generating baseline data for {0}: {1}'.format(layerbase, target)) as s:
try:
bb_test.bitbake('-S', 'none', target)
except sh.ErrorReturnCode:
s.set_status('error, skipped')
targets.remove(target)
if not targets:
continue
with open('conf/bblayers.conf', 'a') as f:
f.write('BBLAYERS =+ "{0}"\n'.format(bblayers_add))
for target in sorted(targets):
with bb_test.status('Testing {0}: {1}'.format(layerbase, target)) as s:
try:
output = bb_test.bitbake('-S', 'printdiff', target)
except sh.ErrorReturnCode as exc:
sys.exit("Error testing {0}: {1}".format(layerbase, exc.message))
else:
output_fn = 'leaky-layer-test.{0}.{1}'.format(layerbase.replace('/', '_'), target)
if 'Variable' in output:
with open(output_fn, 'w') as f:
f.write(output)
s.set_status('modified (see `{0}`)'.format(output_fn))
else:
try:
os.unlink(output_fn)
except OSError as exc:
if exc.errno != errno.ENOENT:
raise
topdir = bitbake_env['TOPDIR']
try:
shutil.rmtree(os.path.join(topdir, 'sstate-leaky-layer-tests'))
except OSError as exc:
if exc.errno != errno.ENOENT:
sys.exit(str(exc))
def filter_lines(filename, keepfunc):
with open(filename, 'rU') as f:
lines = f.readlines()
with open(filename, 'w') as f:
lines = filter(keepfunc, lines)
f.writelines(lines)
if __name__ == '__main__':
bb_test.run_main(main, sys.argv)