utils/genrandconfig: migrate to asyncio subprocess calls

Since genrandconfig no longer appears to support python2 we can
migrate the subprocess calls to use asyncio variants.

This has the advantage of allowing for runners like autobuild-run to
integrate directly into genrandconfig by calling the asyncio
gen_config using importlib instead of having to run genrandconfig as
a subprocess.

Using asyncio is advantageous here as it eliminates the requirement
for the runner to deal with blocking subprocess calls(by having to
use threading for example).

Also cleanup some unused functions/python2 compatibility shims.

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
Signed-off-by: Arnout Vandecappelle <arnout@mind.be>
This commit is contained in:
James Hilliard
2022-04-13 16:40:04 -06:00
committed by Arnout Vandecappelle
parent 00410ab668
commit 579b65b552

View File

@@ -19,24 +19,19 @@
# This script generates a random configuration for testing Buildroot. # This script generates a random configuration for testing Buildroot.
from binascii import hexlify from binascii import hexlify
import contextlib import asyncio
import csv import csv
import os import os
from random import randint from random import randint
import subprocess
import sys import sys
import traceback import traceback
from distutils.version import StrictVersion from distutils.version import StrictVersion
import platform import platform
if sys.hexversion >= 0x3000000: if sys.version_info < (3, 8):
import urllib.request as _urllib from asyncio import coroutine
else: else:
import urllib2 as _urllib from types import coroutine
def urlopen_closing(uri):
return contextlib.closing(_urllib.urlopen(uri))
class SystemInfo: class SystemInfo:
@@ -65,6 +60,7 @@ class SystemInfo:
# -- # --
return None return None
@coroutine
def has(self, prog): def has(self, prog):
"""Checks whether a program is available. """Checks whether a program is available.
Lazily evaluates missing entries. Lazily evaluates missing entries.
@@ -80,11 +76,13 @@ class SystemInfo:
have_it = self.find_prog(prog) have_it = self.find_prog(prog)
# java[c] needs special care # java[c] needs special care
if have_it and prog in ('java', 'javac'): if have_it and prog in ('java', 'javac'):
with open(os.devnull, "w") as devnull: proc = yield from asyncio.create_subprocess_shell(
if subprocess.call("%s -version | grep gcj" % prog, "%s -version | grep gcj" % prog,
shell=True, stdout=asyncio.subprocess.DEVNULL,
stdout=devnull, stderr=devnull) != 1: stderr=asyncio.subprocess.DEVNULL)
have_it = False ret = yield from proc.wait()
if ret != 1:
have_it = False
# -- # --
self.progs[prog] = have_it self.progs[prog] = have_it
return have_it return have_it
@@ -161,6 +159,7 @@ def get_toolchain_configs(toolchains_csv, buildrootdir):
return configs return configs
@coroutine
def is_toolchain_usable(configfile, config): def is_toolchain_usable(configfile, config):
"""Check if the toolchain is actually usable.""" """Check if the toolchain is actually usable."""
@@ -181,7 +180,11 @@ def is_toolchain_usable(configfile, config):
'BR2_TOOLCHAIN_EXTERNAL_LINARO_AARCH64=y\n' in configlines or \ 'BR2_TOOLCHAIN_EXTERNAL_LINARO_AARCH64=y\n' in configlines or \
'BR2_TOOLCHAIN_EXTERNAL_LINARO_AARCH64_BE=y\n' in configlines or \ 'BR2_TOOLCHAIN_EXTERNAL_LINARO_AARCH64_BE=y\n' in configlines or \
'BR2_TOOLCHAIN_EXTERNAL_LINARO_ARMEB=y\n' in configlines: 'BR2_TOOLCHAIN_EXTERNAL_LINARO_ARMEB=y\n' in configlines:
ldd_version_output = subprocess.check_output(['ldd', '--version']) proc = yield from asyncio.create_subprocess_exec(
'ldd', '--version', stdout=asyncio.subprocess.PIPE)
ldd_version_output, _ = yield from proc.communicate()
if proc.returncode:
return False
glibc_version = ldd_version_output.decode().splitlines()[0].split()[-1] glibc_version = ldd_version_output.decode().splitlines()[0].split()[-1]
if StrictVersion('2.14') > StrictVersion(glibc_version): if StrictVersion('2.14') > StrictVersion(glibc_version):
print("WARN: ignoring the Linaro ARM toolchains because too old host glibc", file=sys.stderr) print("WARN: ignoring the Linaro ARM toolchains because too old host glibc", file=sys.stderr)
@@ -190,6 +193,7 @@ def is_toolchain_usable(configfile, config):
return True return True
@coroutine
def fixup_config(sysinfo, configfile): def fixup_config(sysinfo, configfile):
"""Finalize the configuration and reject any problematic combinations """Finalize the configuration and reject any problematic combinations
@@ -206,7 +210,8 @@ def fixup_config(sysinfo, configfile):
BR2_TOOLCHAIN_EXTERNAL_URL = 'BR2_TOOLCHAIN_EXTERNAL_URL="http://autobuild.buildroot.org/toolchains/tarballs/' BR2_TOOLCHAIN_EXTERNAL_URL = 'BR2_TOOLCHAIN_EXTERNAL_URL="http://autobuild.buildroot.org/toolchains/tarballs/'
if "BR2_NEEDS_HOST_JAVA=y\n" in configlines and not sysinfo.has("java"): has_java = yield from sysinfo.has("java")
if "BR2_NEEDS_HOST_JAVA=y\n" in configlines and not has_java:
return False return False
# The ctng toolchain is affected by PR58854 # The ctng toolchain is affected by PR58854
if 'BR2_PACKAGE_LTTNG_TOOLS=y\n' in configlines and \ if 'BR2_PACKAGE_LTTNG_TOOLS=y\n' in configlines and \
@@ -649,6 +654,7 @@ def fixup_config(sysinfo, configfile):
return True return True
@coroutine
def gen_config(args): def gen_config(args):
"""Generate a new random configuration """Generate a new random configuration
@@ -707,7 +713,8 @@ def gen_config(args):
# Randomly enable BR2_REPRODUCIBLE 10% of times # Randomly enable BR2_REPRODUCIBLE 10% of times
# also enable tar filesystem images for testing # also enable tar filesystem images for testing
if sysinfo.has("diffoscope") and randint(0, 10) == 0: has_diffoscope = yield from sysinfo.has("diffoscope")
if has_diffoscope and randint(0, 10) == 0:
configlines.append("BR2_REPRODUCIBLE=y\n") configlines.append("BR2_REPRODUCIBLE=y\n")
configlines.append("BR2_TARGET_ROOTFS_TAR=y\n") configlines.append("BR2_TARGET_ROOTFS_TAR=y\n")
@@ -721,10 +728,14 @@ def gen_config(args):
with open(configfile, "w+") as configf: with open(configfile, "w+") as configf:
configf.writelines(configlines) configf.writelines(configlines)
subprocess.check_call(["make", "O=%s" % args.outputdir, "-C", args.buildrootdir, proc = yield from asyncio.create_subprocess_exec(
"olddefconfig"]) "make", "O=%s" % args.outputdir, "-C", args.buildrootdir, "olddefconfig")
ret = yield from proc.wait()
if ret:
return ret
if not is_toolchain_usable(configfile, toolchainconfig): toolchain_usable = yield from is_toolchain_usable(configfile, toolchainconfig)
if not toolchain_usable:
return 2 return 2
# Now, generate the random selection of packages, and fixup # Now, generate the random selection of packages, and fixup
@@ -744,19 +755,31 @@ def gen_config(args):
"KCONFIG_PROBABILITY=%d" % randint(1, 20), "KCONFIG_PROBABILITY=%d" % randint(1, 20),
"randpackageconfig" if args.toolchains_csv else "randconfig" "randpackageconfig" if args.toolchains_csv else "randconfig"
] ]
subprocess.check_call(make_rand) proc = yield from asyncio.create_subprocess_exec(*make_rand)
ret = yield from proc.wait()
if ret:
return ret
if fixup_config(sysinfo, configfile): ret = yield from fixup_config(sysinfo, configfile)
if ret:
break break
subprocess.check_call(["make", "O=%s" % args.outputdir, "-C", args.buildrootdir, proc = yield from asyncio.create_subprocess_exec(
"olddefconfig"]) "make", "O=%s" % args.outputdir, "-C", args.buildrootdir, "olddefconfig")
ret = yield from proc.wait()
if ret:
return ret
subprocess.check_call(["make", "O=%s" % args.outputdir, "-C", args.buildrootdir, proc = yield from asyncio.create_subprocess_exec(
"savedefconfig"]) "make", "O=%s" % args.outputdir, "-C", args.buildrootdir, "savedefconfig")
ret = yield from proc.wait()
if ret:
return ret
return subprocess.call(["make", "O=%s" % args.outputdir, "-C", args.buildrootdir, proc = yield from asyncio.create_subprocess_exec(
"dependencies"]) "make", "O=%s" % args.outputdir, "-C", args.buildrootdir, "dependencies")
ret = yield from proc.wait()
return ret
if __name__ == '__main__': if __name__ == '__main__':
@@ -788,7 +811,11 @@ if __name__ == '__main__':
args.outputdir = os.path.abspath(args.outputdir) args.outputdir = os.path.abspath(args.outputdir)
try: try:
ret = gen_config(args) if sys.version_info < (3, 7):
loop = asyncio.get_event_loop()
ret = loop.run_until_complete(gen_config(args))
else:
ret = asyncio.run(gen_config(args))
except Exception: except Exception:
traceback.print_exc() traceback.print_exc()
parser.exit(1) parser.exit(1)