1#!/usr/bin/env python3
2
3# Copyright (C) 2014 by Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19# This script generates a random configuration for testing Buildroot.
20
21from binascii import hexlify
22import asyncio
23import csv
24import os
25from random import randint
26import sys
27import traceback
28
29
30class SystemInfo:
31    DEFAULT_NEEDED_PROGS = ["make", "git", "gcc", "timeout"]
32    DEFAULT_OPTIONAL_PROGS = ["bzr", "java", "javac", "jar", "diffoscope"]
33
34    def __init__(self):
35        self.needed_progs = list(self.__class__.DEFAULT_NEEDED_PROGS)
36        self.optional_progs = list(self.__class__.DEFAULT_OPTIONAL_PROGS)
37        self.progs = {}
38
39    def find_prog(self, name, flags=os.X_OK, env=os.environ):
40        if not name or name[0] == os.sep:
41            raise ValueError(name)
42
43        prog_path = env.get("PATH", None)
44        # for windows compatibility, we'd need to take PATHEXT into account
45
46        if prog_path:
47            for prog_dir in filter(None, prog_path.split(os.pathsep)):
48                # os.join() not necessary: non-empty prog_dir
49                # and name[0] != os.sep
50                prog = prog_dir + os.sep + name
51                if os.access(prog, flags):
52                    return prog
53        # --
54        return None
55
56    async def has(self, prog):
57        """Checks whether a program is available.
58        Lazily evaluates missing entries.
59
60        Returns: None if prog not found, else path to the program [evaluates
61        to True]
62        """
63        try:
64            return self.progs[prog]
65        except KeyError:
66            pass
67
68        have_it = self.find_prog(prog)
69        # java[c] needs special care
70        if have_it and prog in ('java', 'javac'):
71            proc = await asyncio.create_subprocess_shell(
72                "%s -version | grep gcj" % prog,
73                stdout=asyncio.subprocess.DEVNULL,
74                stderr=asyncio.subprocess.DEVNULL)
75            ret = await proc.wait()
76            if ret != 1:
77                have_it = False
78        # --
79        self.progs[prog] = have_it
80        return have_it
81
82    def check_requirements(self):
83        """Checks program dependencies.
84
85        Returns: True if all mandatory programs are present, else False.
86        """
87        do_check_has_prog = self.has
88
89        missing_requirements = False
90        for prog in self.needed_progs:
91            if not do_check_has_prog(prog):
92                print("ERROR: your system lacks the '%s' program" % prog)
93                missing_requirements = True
94
95        # check optional programs here,
96        # else they'd get checked by each worker instance
97        for prog in self.optional_progs:
98            do_check_has_prog(prog)
99
100        return not missing_requirements
101
102
103def get_toolchain_configs(toolchains_csv, buildrootdir):
104    """Fetch and return the possible toolchain configurations
105
106    This function returns an array of toolchain configurations. Each
107    toolchain configuration is itself an array of lines of the defconfig.
108    """
109
110    with open(toolchains_csv) as r:
111        # filter empty lines and comments
112        lines = [t for t in r.readlines() if len(t.strip()) > 0 and t[0] != '#']
113        toolchains = lines
114    configs = []
115
116    (_, _, _, _, hostarch) = os.uname()
117    # ~2015 distros report x86 when on a 32bit install
118    if hostarch == 'i686' or hostarch == 'i386' or hostarch == 'x86':
119        hostarch = 'x86'
120
121    for row in csv.reader(toolchains):
122        config = {}
123        configfile = row[0]
124        config_hostarch = row[1]
125        keep = False
126
127        # Keep all toolchain configs that work regardless of the host
128        # architecture
129        if config_hostarch == "any":
130            keep = True
131
132        # Keep all toolchain configs that can work on the current host
133        # architecture
134        if hostarch == config_hostarch:
135            keep = True
136
137        # Assume that x86 32 bits toolchains work on x86_64 build
138        # machines
139        if hostarch == 'x86_64' and config_hostarch == "x86":
140            keep = True
141
142        if not keep:
143            continue
144
145        if not os.path.isabs(configfile):
146            configfile = os.path.join(buildrootdir, configfile)
147
148        with open(configfile) as r:
149            config = r.readlines()
150        configs.append(config)
151    return configs
152
153
154async def is_toolchain_usable(configfile, config):
155    """Check if the toolchain is actually usable."""
156
157    with open(configfile) as configf:
158        configlines = configf.readlines()
159
160    # Check that the toolchain configuration is still present
161    for toolchainline in config:
162        if toolchainline not in configlines:
163            print("WARN: toolchain can't be used", file=sys.stderr)
164            print("      Missing: %s" % toolchainline.strip(), file=sys.stderr)
165            return False
166
167    return True
168
169
170async def fixup_config(sysinfo, configfile):
171    """Finalize the configuration and reject any problematic combinations
172
173    This function returns 'True' when the configuration has been
174    accepted, and 'False' when the configuration has not been accepted because
175    it is known to fail (in which case another random configuration will be
176    generated).
177    """
178
179    with open(configfile) as configf:
180        configlines = configf.readlines()
181
182    ROOTFS_SIZE = '5G'
183
184    BR2_TOOLCHAIN_EXTERNAL_URL = 'BR2_TOOLCHAIN_EXTERNAL_URL="http://autobuild.buildroot.org/toolchains/tarballs/'
185
186    if "BR2_NEEDS_HOST_JAVA=y\n" in configlines and not await sysinfo.has("java"):
187        return False
188    # The ctng toolchain is affected by PR58854
189    if 'BR2_PACKAGE_LTTNG_TOOLS=y\n' in configlines and \
190       BR2_TOOLCHAIN_EXTERNAL_URL + 'armv5-ctng-linux-gnueabi.tar.xz"\n' in configlines:
191        return False
192    # The ctng toolchain tigger an assembler error with guile package when compiled with -Os (same issue as for CS ARM 2014.05-29)
193    if 'BR2_PACKAGE_GUILE=y\n' in configlines and \
194       'BR2_OPTIMIZE_S=y\n' in configlines and \
195       BR2_TOOLCHAIN_EXTERNAL_URL + 'armv5-ctng-linux-gnueabi.tar.xz"\n' in configlines:
196        return False
197    # The ctng toolchain is affected by PR58854
198    if 'BR2_PACKAGE_LTTNG_TOOLS=y\n' in configlines and \
199       BR2_TOOLCHAIN_EXTERNAL_URL + 'armv6-ctng-linux-uclibcgnueabi.tar.xz"\n' in configlines:
200        return False
201    # The ctng toolchain is affected by PR58854
202    if 'BR2_PACKAGE_LTTNG_TOOLS=y\n' in configlines and \
203       BR2_TOOLCHAIN_EXTERNAL_URL + 'armv7-ctng-linux-gnueabihf.tar.xz"\n' in configlines:
204        return False
205    # The ctng toolchain is affected by PR60155
206    if 'BR2_PACKAGE_SDL=y\n' in configlines and \
207       BR2_TOOLCHAIN_EXTERNAL_URL + 'powerpc-ctng-linux-uclibc.tar.xz"\n' in configlines:
208        return False
209    # The ctng toolchain is affected by PR60155
210    if 'BR2_PACKAGE_LIBMPEG2=y\n' in configlines and \
211       BR2_TOOLCHAIN_EXTERNAL_URL + 'powerpc-ctng-linux-uclibc.tar.xz"\n' in configlines:
212        return False
213    # This MIPS toolchain uses eglibc-2.18 which lacks SYS_getdents64
214    if 'BR2_PACKAGE_STRONGSWAN=y\n' in configlines and \
215       BR2_TOOLCHAIN_EXTERNAL_URL + 'mips64el-ctng_n64-linux-gnu.tar.xz"\n' in configlines:
216        return False
217    # This MIPS toolchain uses eglibc-2.18 which lacks SYS_getdents64
218    if 'BR2_PACKAGE_PYTHON3=y\n' in configlines and \
219       BR2_TOOLCHAIN_EXTERNAL_URL + 'mips64el-ctng_n64-linux-gnu.tar.xz"\n' in configlines:
220        return False
221    # libffi not available on ARMv7-M, but propagating libffi arch
222    # dependencies in Buildroot is really too much work, so we handle
223    # this here.
224    if 'BR2_ARM_CPU_ARMV7M=y\n' in configlines and \
225       'BR2_PACKAGE_LIBFFI=y\n' in configlines:
226        return False
227    # libopenssl needs atomic, but propagating this dependency in
228    # Buildroot is really too much work, so we handle this here.
229    if 'BR2_PACKAGE_LIBOPENSSL=y\n' in configlines and \
230       'BR2_TOOLCHAIN_HAS_ATOMIC=y\n' not in configlines:
231        return False
232    if 'BR2_PACKAGE_SUNXI_BOARDS=y\n' in configlines:
233        configlines.remove('BR2_PACKAGE_SUNXI_BOARDS_FEX_FILE=""\n')
234        configlines.append('BR2_PACKAGE_SUNXI_BOARDS_FEX_FILE="a10/hackberry.fex"\n')
235    # This MIPS uClibc toolchain fails to build the gdb package
236    if 'BR2_PACKAGE_GDB=y\n' in configlines and \
237       BR2_TOOLCHAIN_EXTERNAL_URL + 'mipsel-ctng-linux-uclibc.tar.xz"\n' in configlines:
238        return False
239    # This MIPS uClibc toolchain fails to build the rt-tests package
240    if 'BR2_PACKAGE_RT_TESTS=y\n' in configlines and \
241       BR2_TOOLCHAIN_EXTERNAL_URL + 'mipsel-ctng-linux-uclibc.tar.xz"\n' in configlines:
242        return False
243    # This MIPS uClibc toolchain fails to build the civetweb package
244    if 'BR2_PACKAGE_CIVETWEB=y\n' in configlines and \
245       BR2_TOOLCHAIN_EXTERNAL_URL + 'mipsel-ctng-linux-uclibc.tar.xz"\n' in configlines:
246        return False
247    # This MIPS ctng toolchain fails to build the python3 package
248    if 'BR2_PACKAGE_PYTHON3=y\n' in configlines and \
249       BR2_TOOLCHAIN_EXTERNAL_URL + 'mips64el-ctng_n64-linux-gnu.tar.xz"\n' in configlines:
250        return False
251    # This MIPS uClibc toolchain fails to build the strace package
252    if 'BR2_PACKAGE_STRACE=y\n' in configlines and \
253       BR2_TOOLCHAIN_EXTERNAL_URL + 'mipsel-ctng-linux-uclibc.tar.xz"\n' in configlines:
254        return False
255    # This MIPS uClibc toolchain fails to build the cdrkit package
256    if 'BR2_PACKAGE_CDRKIT=y\n' in configlines and \
257       'BR2_STATIC_LIBS=y\n' in configlines and \
258       BR2_TOOLCHAIN_EXTERNAL_URL + 'mipsel-ctng-linux-uclibc.tar.xz"\n' in configlines:
259        return False
260    # uClibc vfork static linking issue
261    if 'BR2_PACKAGE_ALSA_LIB=y\n' in configlines and \
262       'BR2_STATIC_LIBS=y\n' in configlines and \
263       BR2_TOOLCHAIN_EXTERNAL_URL + 'i486-ctng-linux-uclibc.tar.xz"\n' in configlines:
264        return False
265    # This MIPS uClibc toolchain fails to build the weston package
266    if 'BR2_PACKAGE_WESTON=y\n' in configlines and \
267       BR2_TOOLCHAIN_EXTERNAL_URL + 'mipsel-ctng-linux-uclibc.tar.xz"\n' in configlines:
268        return False
269    # No C library for internal toolchain
270    if 'BR2_TOOLCHAIN_BUILDROOT_NONE=y\n' in configlines:
271        return False
272    # Xtensa custom cores require an overlay file with internal
273    # toolchains
274    if 'BR2_XTENSA_CUSTOM=y\n' in configlines and \
275       'BR2_TOOLCHAIN_BUILDROOT=y\n' in configlines:
276        return False
277
278    if 'BR2_TOOLCHAIN_BARE_METAL_BUILDROOT=y\n' in configlines:
279        configlines.remove('BR2_TOOLCHAIN_BARE_METAL_BUILDROOT_ARCH=""\n')
280        configlines.append('BR2_TOOLCHAIN_BARE_METAL_BUILDROOT_ARCH="microblazeel-xilinx-elf"\n')
281
282    if 'BR2_PACKAGE_AUFS_UTIL=y\n' in configlines and \
283       'BR2_PACKAGE_AUFS_UTIL_VERSION=""\n' in configlines:
284        return False
285    if 'BR2_PACKAGE_A10DISP=y\n' in configlines:
286        return False
287
288    if 'BR2_PACKAGE_HOST_UBOOT_TOOLS_ENVIMAGE=y\n' in configlines and \
289       'BR2_PACKAGE_HOST_UBOOT_TOOLS_ENVIMAGE_SOURCE=""\n' in configlines and \
290       'BR2_PACKAGE_HOST_UBOOT_TOOLS_ENVIMAGE_SIZE=""\n' in configlines:
291        bootenv = os.path.join(args.outputdir, "boot_env.txt")
292        with open(bootenv, "w+") as bootenvf:
293            bootenvf.write("prop=value")
294        configlines.remove('BR2_PACKAGE_HOST_UBOOT_TOOLS_ENVIMAGE_SOURCE=""\n')
295        configlines.append('BR2_PACKAGE_HOST_UBOOT_TOOLS_ENVIMAGE_SOURCE="%s"\n' % bootenv)
296        configlines.remove('BR2_PACKAGE_HOST_UBOOT_TOOLS_ENVIMAGE_SIZE=""\n')
297        configlines.append('BR2_PACKAGE_HOST_UBOOT_TOOLS_ENVIMAGE_SIZE="0x1000"\n')
298
299    if 'BR2_PACKAGE_HOST_UBOOT_TOOLS_BOOT_SCRIPT=y\n' in configlines and \
300       'BR2_PACKAGE_HOST_UBOOT_TOOLS_BOOT_SCRIPT_SOURCE=""\n' in configlines:
301        bootscr = os.path.join(args.outputdir, "boot_script.txt")
302        with open(bootscr, "w+") as bootscrf:
303            bootscrf.write("prop=value")
304        configlines.remove('BR2_PACKAGE_HOST_UBOOT_TOOLS_BOOT_SCRIPT_SOURCE=""\n')
305        configlines.append('BR2_PACKAGE_HOST_UBOOT_TOOLS_BOOT_SCRIPT_SOURCE="%s"\n' % bootscr)
306
307    if 'BR2_ROOTFS_SKELETON_CUSTOM=y\n' in configlines and \
308       'BR2_ROOTFS_SKELETON_CUSTOM_PATH=""\n' in configlines:
309        configlines.remove('BR2_ROOTFS_SKELETON_CUSTOM=y\n')
310        configlines.remove('BR2_ROOTFS_SKELETON_CUSTOM_PATH=""\n')
311
312    if 'BR2_LINUX_KERNEL=y\n' in configlines and \
313       'BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y\n' in configlines and \
314       'BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE=""\n' in configlines:
315        configlines.remove('BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y\n')
316        configlines.append('BR2_LINUX_KERNEL_USE_ARCH_DEFAULT_CONFIG=y\n')
317        configlines.remove('BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE=""\n')
318
319    if 'BR2_LINUX_KERNEL=y\n' in configlines and \
320       'BR2_LINUX_KERNEL_USE_DEFCONFIG=y\n' in configlines and \
321       'BR2_LINUX_KERNEL_DEFCONFIG=""\n' in configlines:
322        configlines.remove('BR2_LINUX_KERNEL_USE_DEFCONFIG=y\n')
323        configlines.append('BR2_LINUX_KERNEL_USE_ARCH_DEFAULT_CONFIG=y\n')
324        configlines.remove('BR2_LINUX_KERNEL_DEFCONFIG=""\n')
325
326    if 'BR2_LINUX_KERNEL=y\n' in configlines and \
327       'BR2_LINUX_KERNEL_CUSTOM_GIT=y\n' in configlines and \
328       'BR2_LINUX_KERNEL_CUSTOM_REPO_URL=""\n' in configlines:
329        configlines.remove('BR2_LINUX_KERNEL_CUSTOM_GIT=y\n')
330        configlines.append('BR2_LINUX_KERNEL_LATEST_VERSION=y\n')
331        configlines.remove('BR2_LINUX_KERNEL_CUSTOM_REPO_URL=""\n')
332
333    if 'BR2_LINUX_KERNEL=y\n' in configlines and \
334       'BR2_LINUX_KERNEL_CUSTOM_HG=y\n' in configlines and \
335       'BR2_LINUX_KERNEL_CUSTOM_REPO_URL=""\n' in configlines:
336        configlines.remove('BR2_LINUX_KERNEL_CUSTOM_HG=y\n')
337        configlines.append('BR2_LINUX_KERNEL_LATEST_VERSION=y\n')
338        configlines.remove('BR2_LINUX_KERNEL_CUSTOM_REPO_URL=""\n')
339
340    if 'BR2_LINUX_KERNEL=y\n' in configlines and \
341       'BR2_LINUX_KERNEL_CUSTOM_SVN=y\n' in configlines and \
342       'BR2_LINUX_KERNEL_CUSTOM_REPO_URL=""\n' in configlines:
343        configlines.remove('BR2_LINUX_KERNEL_CUSTOM_SVN=y\n')
344        configlines.append('BR2_LINUX_KERNEL_LATEST_VERSION=y\n')
345        configlines.remove('BR2_LINUX_KERNEL_CUSTOM_REPO_URL=""\n')
346
347    if 'BR2_LINUX_KERNEL=y\n' in configlines and \
348       'BR2_LINUX_KERNEL_CUSTOM_TARBALL=y\n' in configlines and \
349       'BR2_LINUX_KERNEL_CUSTOM_TARBALL_LOCATION=""\n' in configlines:
350        configlines.remove('BR2_LINUX_KERNEL_CUSTOM_TARBALL=y\n')
351        configlines.append('BR2_LINUX_KERNEL_LATEST_VERSION=y\n')
352        configlines.remove('BR2_LINUX_KERNEL_CUSTOM_TARBALL_LOCATION=""\n')
353
354    if 'BR2_LINUX_KERNEL=y\n' in configlines and \
355       'BR2_LINUX_KERNEL_CUSTOM_VERSION=y\n' in configlines and \
356       'BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE=""\n' in configlines:
357        configlines.remove('BR2_LINUX_KERNEL_CUSTOM_VERSION=y\n')
358        configlines.append('BR2_LINUX_KERNEL_LATEST_VERSION=y\n')
359        configlines.remove('BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE=""\n')
360
361    if 'BR2_LINUX_KERNEL=y\n' in configlines and \
362       'BR2_LINUX_KERNEL_DTS_SUPPORT=y\n' in configlines and \
363       'BR2_LINUX_KERNEL_INTREE_DTS_NAME=""\n' in configlines and \
364       'BR2_LINUX_KERNEL_CUSTOM_DTS_PATH=""\n' in configlines:
365        configlines.remove('BR2_LINUX_KERNEL_DTS_SUPPORT=y\n')
366        configlines.remove('BR2_LINUX_KERNEL_INTREE_DTS_NAME=""\n')
367        configlines.remove('BR2_LINUX_KERNEL_CUSTOM_DTS_PATH=""\n')
368        if 'BR2_LINUX_KERNEL_APPENDED_UIMAGE=y\n' in configlines:
369            configlines.remove('BR2_LINUX_KERNEL_APPENDED_UIMAGE=y\n')
370            configlines.append('BR2_LINUX_KERNEL_UIMAGE=y\n')
371        if 'BR2_LINUX_KERNEL_APPENDED_ZIMAGE=y\n' in configlines:
372            configlines.remove('BR2_LINUX_KERNEL_APPENDED_ZIMAGE=y\n')
373            configlines.append('BR2_LINUX_KERNEL_ZIMAGE=y\n')
374        if 'BR2_LINUX_KERNEL_CUIMAGE=y\n' in configlines:
375            configlines.remove('BR2_LINUX_KERNEL_CUIMAGE=y\n')
376            configlines.append('BR2_LINUX_KERNEL_UIMAGE=y\n')
377        if 'BR2_LINUX_KERNEL_SIMPLEIMAGE=y\n' in configlines:
378            configlines.remove('BR2_LINUX_KERNEL_SIMPLEIMAGE=y\n')
379            configlines.append('BR2_LINUX_KERNEL_VMLINUX=y\n')
380
381    if 'BR2_LINUX_KERNEL_EXT_AUFS=y\n' in configlines and \
382       'BR2_LINUX_KERNEL_EXT_AUFS_VERSION=""\n' in configlines:
383        configlines.remove('BR2_LINUX_KERNEL_EXT_AUFS=y\n')
384        configlines.remove('BR2_LINUX_KERNEL_EXT_AUFS_VERSION=""\n')
385
386    if 'BR2_PACKAGE_LINUX_BACKPORTS=y\n' in configlines and \
387       'BR2_PACKAGE_LINUX_BACKPORTS_USE_CUSTOM_CONFIG=y\n' in configlines and \
388       'BR2_PACKAGE_LINUX_BACKPORTS_CUSTOM_CONFIG_FILE=""\n' in configlines:
389        configlines.remove('BR2_PACKAGE_LINUX_BACKPORTS=y\n')
390        configlines.remove('BR2_PACKAGE_LINUX_BACKPORTS_USE_CUSTOM_CONFIG=y\n')
391        configlines.remove('BR2_PACKAGE_LINUX_BACKPORTS_CUSTOM_CONFIG_FILE=""\n')
392
393    if 'BR2_PACKAGE_LINUX_BACKPORTS=y\n' in configlines and \
394       'BR2_PACKAGE_LINUX_BACKPORTS_USE_DEFCONFIG=y\n' in configlines and \
395       'BR2_PACKAGE_LINUX_BACKPORTS_DEFCONFIG=""\n' in configlines:
396        configlines.remove('BR2_PACKAGE_LINUX_BACKPORTS=y\n')
397        configlines.remove('BR2_PACKAGE_LINUX_BACKPORTS_USE_DEFCONFIG=y\n')
398        configlines.remove('BR2_PACKAGE_LINUX_BACKPORTS_DEFCONFIG=""\n')
399
400    if 'BR2_KERNEL_HEADERS_VERSION=y\n' in configlines and \
401       'BR2_DEFAULT_KERNEL_VERSION=""\n' in configlines:
402        configlines.remove('BR2_KERNEL_HEADERS_VERSION=y\n')
403        configlines.remove('BR2_DEFAULT_KERNEL_VERSION=""\n')
404
405    if 'BR2_KERNEL_HEADERS_CUSTOM_GIT=y\n' in configlines and \
406       'BR2_KERNEL_HEADERS_CUSTOM_REPO_URL=""\n':
407        configlines.remove('BR2_KERNEL_HEADERS_CUSTOM_GIT=y\n')
408        configlines.remove('BR2_KERNEL_HEADERS_CUSTOM_REPO_URL=""\n')
409
410    if 'BR2_KERNEL_HEADERS_CUSTOM_TARBALL=y\n' in configlines and \
411       'BR2_KERNEL_HEADERS_CUSTOM_TARBALL_LOCATION=""\n' in configlines:
412        configlines.remove('BR2_KERNEL_HEADERS_CUSTOM_TARBALL=y\n')
413        configlines.remove('BR2_KERNEL_HEADERS_CUSTOM_TARBALL_LOCATION=""\n')
414
415    if 'BR2_TARGET_ARM_TRUSTED_FIRMWARE=y\n' in configlines and \
416       'BR2_TARGET_ARM_TRUSTED_FIRMWARE_PLATFORM=""\n' in configlines:
417        return False
418
419    if 'BR2_TARGET_ARM_TRUSTED_FIRMWARE=y\n' in configlines and \
420       'BR2_TARGET_ARM_TRUSTED_FIRMWARE_CUSTOM_VERSION=y\n' in configlines and \
421       'BR2_TARGET_ARM_TRUSTED_FIRMWARE_CUSTOM_VERSION_VALUE=""\n' in configlines:
422        configlines.remove('BR2_TARGET_ARM_TRUSTED_FIRMWARE_CUSTOM_VERSION=y\n')
423        configlines.append('BR2_TARGET_ARM_TRUSTED_FIRMWARE_LATEST_VERSION=y\n')
424        configlines.remove('BR2_TARGET_ARM_TRUSTED_FIRMWARE_CUSTOM_VERSION_VALUE=""\n')
425
426    if 'BR2_TARGET_ARM_TRUSTED_FIRMWARE=y\n' in configlines and \
427       'BR2_TARGET_ARM_TRUSTED_FIRMWARE_CUSTOM_TARBALL=y\n' in configlines and \
428       'BR2_TARGET_ARM_TRUSTED_FIRMWARE_CUSTOM_TARBALL_LOCATION=""\n' in configlines:
429        configlines.remove('BR2_TARGET_ARM_TRUSTED_FIRMWARE_CUSTOM_TARBALL=y\n')
430        configlines.append('BR2_TARGET_ARM_TRUSTED_FIRMWARE_LATEST_VERSION=y\n')
431        configlines.remove('BR2_TARGET_ARM_TRUSTED_FIRMWARE_CUSTOM_TARBALL_LOCATION=""\n')
432
433    if 'BR2_TARGET_ARM_TRUSTED_FIRMWARE=y\n' in configlines and \
434       'BR2_TARGET_ARM_TRUSTED_FIRMWARE_CUSTOM_GIT=y\n' in configlines and \
435       'BR2_TARGET_ARM_TRUSTED_FIRMWARE_CUSTOM_REPO_URL=""\n' in configlines:
436        configlines.remove('BR2_TARGET_ARM_TRUSTED_FIRMWARE_CUSTOM_GIT=y\n')
437        configlines.append('BR2_TARGET_ARM_TRUSTED_FIRMWARE_LATEST_VERSION=y\n')
438        configlines.remove('BR2_TARGET_ARM_TRUSTED_FIRMWARE_CUSTOM_REPO_URL=""\n')
439
440    if 'BR2_TARGET_AT91BOOTSTRAP3=y\n' in configlines and \
441       'BR2_TARGET_AT91BOOTSTRAP3_DEFCONFIG=""\n' in configlines:
442        configlines.remove('BR2_TARGET_AT91BOOTSTRAP3=y\n')
443        configlines.remove('BR2_TARGET_AT91BOOTSTRAP3_DEFCONFIG=""\n')
444
445    if 'BR2_TARGET_BAREBOX=y\n' in configlines and \
446       'BR2_TARGET_BAREBOX_USE_CUSTOM_CONFIG=y\n' in configlines and \
447       'BR2_TARGET_BAREBOX_CUSTOM_CONFIG_FILE=""\n' in configlines:
448        configlines.remove('BR2_TARGET_BAREBOX=y\n')
449        configlines.remove('BR2_TARGET_BAREBOX_USE_CUSTOM_CONFIG=y\n')
450        configlines.remove('BR2_TARGET_BAREBOX_CUSTOM_CONFIG_FILE=""\n')
451
452    if 'BR2_TARGET_BAREBOX=y\n' in configlines and \
453       'BR2_TARGET_BAREBOX_USE_DEFCONFIG=y\n' in configlines and \
454       'BR2_TARGET_BAREBOX_BOARD_DEFCONFIG=""\n' in configlines:
455        configlines.remove('BR2_TARGET_BAREBOX=y\n')
456        configlines.remove('BR2_TARGET_BAREBOX_USE_DEFCONFIG=y\n')
457        configlines.remove('BR2_TARGET_BAREBOX_BOARD_DEFCONFIG=""\n')
458
459    if 'BR2_TARGET_BOOT_WRAPPER_AARCH64=y\n' in configlines and \
460       'BR2_TARGET_BOOT_WRAPPER_AARCH64_DTS=""\n' in configlines:
461        return False
462
463    if 'BR2_TARGET_OPTEE_OS=y\n' in configlines and \
464       'BR2_TARGET_OPTEE_OS_CUSTOM_TARBALL=y\n' in configlines and \
465       'BR2_TARGET_OPTEE_OS_CUSTOM_TARBALL_LOCATION=""\n' in configlines:
466        configlines.remove('BR2_TARGET_OPTEE_OS_CUSTOM_TARBALL=y\n')
467        configlines.append('BR2_TARGET_OPTEE_OS_LATEST=y\n')
468        configlines.remove('BR2_TARGET_OPTEE_OS_CUSTOM_TARBALL_LOCATION=""\n')
469
470    if 'BR2_TARGET_OPTEE_OS=y\n' in configlines and \
471       'BR2_TARGET_OPTEE_OS_PLATFORM=""\n' in configlines:
472        configlines.remove('BR2_TARGET_OPTEE_OS=y\n')
473        configlines.remove('BR2_TARGET_OPTEE_OS_PLATFORM=""\n')
474
475    if 'BR2_TARGET_ROOTFS_CRAMFS=y\n' in configlines:
476        configlines.remove('BR2_TARGET_ROOTFS_CRAMFS=y\n')
477
478    if 'BR2_TARGET_ROOTFS_EXT2=y\n' in configlines and \
479       'BR2_TARGET_ROOTFS_EXT2_SIZE="60M"\n' in configlines:
480        configlines.remove('BR2_TARGET_ROOTFS_EXT2_SIZE="60M"\n')
481        configlines.append('BR2_TARGET_ROOTFS_EXT2_SIZE="%s"\n' % ROOTFS_SIZE)
482
483    if 'BR2_TARGET_ROOTFS_F2FS=y\n' in configlines and \
484       'BR2_TARGET_ROOTFS_F2FS_SIZE="100M"\n' in configlines:
485        configlines.remove('BR2_TARGET_ROOTFS_F2FS_SIZE="100M"\n')
486        configlines.append('BR2_TARGET_ROOTFS_F2FS_SIZE="%s"\n' % ROOTFS_SIZE)
487
488    if 'BR2_TARGET_ROOTFS_UBIFS=y\n' in configlines and \
489       'BR2_TARGET_ROOTFS_UBIFS_MAXLEBCNT=2048\n' in configlines:
490        configlines.remove('BR2_TARGET_ROOTFS_UBIFS_MAXLEBCNT=2048\n')
491        configlines.append('BR2_TARGET_ROOTFS_UBIFS_MAXLEBCNT=41610\n')
492
493    if 'BR2_TARGET_ROOTFS_UBI=y\n' in configlines and \
494       'BR2_TARGET_ROOTFS_UBI_USE_CUSTOM_CONFIG=y\n' in configlines and \
495       'BR2_TARGET_ROOTFS_UBI_CUSTOM_CONFIG_FILE=""\n' in configlines:
496        configlines.remove('BR2_TARGET_ROOTFS_UBI_USE_CUSTOM_CONFIG=y\n')
497        configlines.remove('BR2_TARGET_ROOTFS_UBI_CUSTOM_CONFIG_FILE=""\n')
498
499    if 'BR2_TARGET_S500_BOOTLOADER=y\n' in configlines and \
500       'BR2_TARGET_S500_BOOTLOADER_BOARD=""\n' in configlines:
501        configlines.remove('BR2_TARGET_S500_BOOTLOADER=y\n')
502        configlines.remove('BR2_TARGET_S500_BOOTLOADER_BOARD=""\n')
503
504    if 'BR2_TARGET_TI_K3_R5_LOADER=y\n' in configlines and \
505       'BR2_TARGET_TI_K3_R5_LOADER_USE_DEFCONFIG=y\n' in configlines and \
506       'BR2_TARGET_TI_K3_R5_LOADER_BOARD_DEFCONFIG=""\n' in configlines:
507        return False
508
509    if 'BR2_TARGET_UBOOT=y\n' in configlines and \
510       'BR2_TARGET_UBOOT_BUILD_SYSTEM_KCONFIG=y\n' in configlines and \
511       'BR2_TARGET_UBOOT_USE_CUSTOM_CONFIG=y\n' in configlines and \
512       'BR2_TARGET_UBOOT_CUSTOM_CONFIG_FILE=""\n' in configlines:
513        configlines.remove('BR2_TARGET_UBOOT=y\n')
514        configlines.remove('BR2_TARGET_UBOOT_BUILD_SYSTEM_KCONFIG=y\n')
515        configlines.remove('BR2_TARGET_UBOOT_USE_CUSTOM_CONFIG=y\n')
516        configlines.remove('BR2_TARGET_UBOOT_CUSTOM_CONFIG_FILE=""\n')
517
518    if 'BR2_TARGET_UBOOT=y\n' in configlines and \
519       'BR2_TARGET_UBOOT_BUILD_SYSTEM_KCONFIG=y\n' in configlines and \
520       'BR2_TARGET_UBOOT_USE_DEFCONFIG=y\n' in configlines and \
521       'BR2_TARGET_UBOOT_BOARD_DEFCONFIG=""\n' in configlines:
522        configlines.remove('BR2_TARGET_UBOOT=y\n')
523        configlines.remove('BR2_TARGET_UBOOT_BUILD_SYSTEM_KCONFIG=y\n')
524        configlines.remove('BR2_TARGET_UBOOT_USE_DEFCONFIG=y\n')
525        configlines.remove('BR2_TARGET_UBOOT_BOARD_DEFCONFIG=""\n')
526
527    if 'BR2_TARGET_UBOOT=y\n' in configlines and \
528       'BR2_TARGET_UBOOT_BUILD_SYSTEM_LEGACY=y\n' in configlines and \
529       'BR2_TARGET_UBOOT_BOARDNAME=""\n' in configlines:
530        configlines.remove('BR2_TARGET_UBOOT=y\n')
531        configlines.remove('BR2_TARGET_UBOOT_BUILD_SYSTEM_LEGACY=y\n')
532        configlines.remove('BR2_TARGET_UBOOT_BOARDNAME=""\n')
533
534    if 'BR2_TOOLCHAIN_EXTERNAL=y\n' in configlines and \
535       'BR2_TOOLCHAIN_EXTERNAL_PREINSTALLED=y\n' in configlines and \
536       'BR2_TOOLCHAIN_EXTERNAL_PATH=""\n' in configlines:
537        configlines.remove('BR2_TOOLCHAIN_EXTERNAL=y\n')
538        configlines.remove('BR2_TOOLCHAIN_EXTERNAL_PREINSTALLED=y\n')
539        configlines.remove('BR2_TOOLCHAIN_EXTERNAL_PATH=""\n')
540        if 'BR2_ARCH_HAS_NO_TOOLCHAIN_BUILDROOT=y\n' in configlines:
541            return False
542
543    if 'BR2_TOOLCHAIN_EXTERNAL=y\n' in configlines and \
544       'BR2_TOOLCHAIN_EXTERNAL_CUSTOM=y\n' in configlines and \
545       'BR2_TOOLCHAIN_EXTERNAL_DOWNLOAD=y\n' in configlines and \
546       'BR2_TOOLCHAIN_EXTERNAL_URL=""\n' in configlines:
547        configlines.remove('BR2_TOOLCHAIN_EXTERNAL=y\n')
548        configlines.remove('BR2_TOOLCHAIN_EXTERNAL_CUSTOM=y\n')
549        configlines.remove('BR2_TOOLCHAIN_EXTERNAL_DOWNLOAD=y\n')
550        configlines.remove('BR2_TOOLCHAIN_EXTERNAL_URL=""\n')
551        if 'BR2_ARCH_HAS_NO_TOOLCHAIN_BUILDROOT=y\n' in configlines:
552            return False
553
554    if 'BR2_TARGET_MXS_BOOTLETS=y\n' in configlines and \
555       'BR2_TARGET_MXS_BOOTLETS_CUSTOM_BOARD=y\n' in configlines and \
556       'BR2_TARGET_MXS_BOOTLETS_CUSTOM_BOARD_NAME=""\n' in configlines:
557        configlines.remove('BR2_TARGET_MXS_BOOTLETS_CUSTOM_BOARD=y\n')
558        configlines.append('BR2_TARGET_MXS_BOOTLETS_STMP37xx=y\n')
559        configlines.remove('BR2_TARGET_MXS_BOOTLETS_CUSTOM_BOARD_NAME=""\n')
560
561    if 'BR2_TARGET_MXS_BOOTLETS=y\n' in configlines and \
562       'BR2_TARGET_MXS_BOOTLETS_CUSTOM_GIT=y\n' in configlines and \
563       'BR2_TARGET_MXS_BOOTLETS_CUSTOM_GIT_URL=""\n' in configlines:
564        configlines.remove('BR2_TARGET_MXS_BOOTLETS_CUSTOM_GIT=y\n')
565        configlines.append('BR2_TARGET_MXS_BOOTLETS_FREESCALE=y\n')
566        configlines.remove('BR2_TARGET_MXS_BOOTLETS_CUSTOM_GIT_URL=""\n')
567
568    if 'BR2_TARGET_MXS_BOOTLETS=y\n' in configlines and \
569       'BR2_TARGET_MXS_BOOTLETS_CUSTOM_TARBALL=y\n' in configlines and \
570       'BR2_TARGET_MXS_BOOTLETS_CUSTOM_TARBALL_URL=""\n' in configlines:
571        configlines.remove('BR2_TARGET_MXS_BOOTLETS_CUSTOM_TARBALL=y\n')
572        configlines.append('BR2_TARGET_MXS_BOOTLETS_FREESCALE=y\n')
573        configlines.remove('BR2_TARGET_MXS_BOOTLETS_CUSTOM_TARBALL_URL=""\n')
574
575    if 'BR2_TARGET_OPENSBI=y\n' in configlines and \
576       'BR2_TARGET_OPENSBI_CUSTOM_GIT=y\n' in configlines and \
577       'BR2_TARGET_OPENSBI_CUSTOM_REPO_URL=""\n' in configlines:
578        configlines.remove('BR2_TARGET_OPENSBI_CUSTOM_GIT=y\n')
579        configlines.append('BR2_TARGET_OPENSBI_LATEST_VERSION=y\n')
580        configlines.remove('BR2_TARGET_OPENSBI_CUSTOM_REPO_URL=""\n')
581
582    if 'BR2_TARGET_OPENSBI=y\n' in configlines and \
583       'BR2_TARGET_OPENSBI_CUSTOM_TARBALL=y\n' in configlines and \
584       'BR2_TARGET_OPENSBI_CUSTOM_TARBALL_LOCATION=""\n' in configlines:
585        configlines.remove('BR2_TARGET_OPENSBI_CUSTOM_TARBALL=y\n')
586        configlines.append('BR2_TARGET_OPENSBI_LATEST_VERSION=y\n')
587        configlines.remove('BR2_TARGET_OPENSBI_CUSTOM_TARBALL_LOCATION=""\n')
588
589    if 'BR2_TARGET_OPENSBI=y\n' in configlines and \
590       'BR2_TARGET_OPENSBI_CUSTOM_VERSION=y\n' in configlines and \
591       'BR2_TARGET_OPENSBI_CUSTOM_VERSION_VALUE=""\n' in configlines:
592        configlines.remove('BR2_TARGET_OPENSBI_CUSTOM_VERSION=y\n')
593        configlines.append('BR2_TARGET_OPENSBI_LATEST_VERSION=y\n')
594        configlines.remove('BR2_TARGET_OPENSBI_CUSTOM_VERSION_VALUE=""\n')
595
596    if 'BR2_PACKAGE_REFPOLICY=y\n' in configlines and \
597       'BR2_PACKAGE_REFPOLICY_CUSTOM_GIT=y\n' in configlines and \
598       'BR2_PACKAGE_REFPOLICY_CUSTOM_REPO_URL=""\n' in configlines:
599        configlines.remove('BR2_PACKAGE_REFPOLICY_CUSTOM_GIT=y\n')
600        configlines.append('BR2_PACKAGE_REFPOLICY_UPSTREAM_VERSION=y\n')
601        configlines.remove('BR2_PACKAGE_REFPOLICY_CUSTOM_REPO_URL=""\n')
602
603    if 'BR2_PACKAGE_XENOMAI=y\n' in configlines and \
604       'BR2_PACKAGE_XENOMAI_CUSTOM_GIT=y\n' in configlines and \
605       'BR2_PACKAGE_XENOMAI_REPOSITORY=""\n' in configlines:
606        configlines.remove('BR2_PACKAGE_XENOMAI_CUSTOM_GIT=y\n')
607        configlines.append('BR2_PACKAGE_XENOMAI_LATEST_VERSION=y\n')
608        configlines.remove('BR2_PACKAGE_XENOMAI_REPOSITORY=""\n')
609
610    if 'BR2_PACKAGE_XENOMAI=y\n' in configlines and \
611       'BR2_PACKAGE_XENOMAI_CUSTOM_TARBALL=y\n' in configlines and \
612       'BR2_PACKAGE_XENOMAI_CUSTOM_TARBALL_URL=""\n' in configlines:
613        configlines.remove('BR2_PACKAGE_XENOMAI_CUSTOM_TARBALL=y\n')
614        configlines.append('BR2_PACKAGE_XENOMAI_LATEST_VERSION=y\n')
615        configlines.remove('BR2_PACKAGE_XENOMAI_CUSTOM_TARBALL_URL=""\n')
616
617    if 'BR2_PACKAGE_XENOMAI=y\n' in configlines and \
618       'BR2_PACKAGE_XENOMAI_CUSTOM_VERSION=y\n' in configlines and \
619       'BR2_PACKAGE_XENOMAI_CUSTOM_VERSION_VALUE=""\n' in configlines:
620        configlines.remove('BR2_PACKAGE_XENOMAI_CUSTOM_VERSION=y\n')
621        configlines.append('BR2_PACKAGE_XENOMAI_LATEST_VERSION=y\n')
622        configlines.remove('BR2_PACKAGE_XENOMAI_CUSTOM_VERSION_VALUE=""\n')
623
624    if 'BR2_PACKAGE_XVISOR=y\n' in configlines and \
625       'BR2_PACKAGE_XVISOR_USE_CUSTOM_CONFIG=y\n' in configlines and \
626       'BR2_PACKAGE_XVISOR_CUSTOM_CONFIG_FILE=""\n' in configlines:
627        configlines.remove('BR2_PACKAGE_XVISOR_USE_CUSTOM_CONFIG=y\n')
628        configlines.append('BR2_PACKAGE_XVISOR_USE_DEFCONFIG=y\n')
629        configlines.remove('BR2_PACKAGE_XVISOR_CUSTOM_CONFIG_FILE=""\n')
630
631    # Don't build igh-ethercat driver as they are highly
632    # kernel-version specific
633    for opt in ['8139TOO', 'E100', 'E1000', 'E1000E', 'R8169']:
634        optstr = 'BR2_PACKAGE_IGH_ETHERCAT_%s=y\n' % opt
635        if optstr in configlines:
636            configlines.remove(optstr)
637
638    with open(configfile, "w+") as configf:
639        configf.writelines(configlines)
640
641    return True
642
643
644async def gen_config(args):
645    """Generate a new random configuration
646
647    This function generates the configuration, by choosing a random
648    toolchain configuration and then generating a random selection of
649    packages.
650    """
651
652    sysinfo = SystemInfo()
653
654    if args.toolchains_csv:
655        # Select a random toolchain configuration
656        configs = get_toolchain_configs(args.toolchains_csv, args.buildrootdir)
657
658        i = randint(0, len(configs) - 1)
659        toolchainconfig = configs[i]
660    else:
661        toolchainconfig = []
662
663    configlines = list(toolchainconfig)
664
665    # Combine with the minimal configuration
666    minimalconfigfile = os.path.join(args.buildrootdir,
667                                     'support/config-fragments/minimal.config')
668    with open(minimalconfigfile) as minimalf:
669        configlines += minimalf.readlines()
670
671    # Allow hosts with old certificates to download over https
672    configlines.append("BR2_WGET=\"wget --passive-ftp -nd -t 3 --no-check-certificate\"\n")
673
674    # Per-package folder
675    if randint(0, 15) == 0:
676        configlines.append("BR2_PER_PACKAGE_DIRECTORIES=y\n")
677
678    # Amend the configuration with a few things.
679    if randint(0, 20) == 0:
680        configlines.append("BR2_ENABLE_DEBUG=y\n")
681    if randint(0, 20) == 0:
682        configlines.append("BR2_ENABLE_RUNTIME_DEBUG=y\n")
683    if randint(0, 1) == 0:
684        configlines.append("BR2_INIT_BUSYBOX=y\n")
685    elif randint(0, 15) == 0:
686        configlines.append("BR2_INIT_SYSTEMD=y\n")
687    elif randint(0, 10) == 0:
688        configlines.append("BR2_ROOTFS_DEVICE_CREATION_DYNAMIC_EUDEV=y\n")
689    if randint(0, 20) == 0:
690        configlines.append("BR2_STATIC_LIBS=y\n")
691    if randint(0, 20) == 0:
692        configlines.append("BR2_PACKAGE_PYTHON3_PY_ONLY=y\n")
693    if randint(0, 5) == 0:
694        configlines.append("BR2_OPTIMIZE_2=y\n")
695    if randint(0, 4) == 0:
696        configlines.append("BR2_SYSTEM_ENABLE_NLS=y\n")
697    if randint(0, 4) == 0:
698        configlines.append("BR2_FORTIFY_SOURCE_2=y\n")
699
700    # Randomly enable BR2_REPRODUCIBLE 10% of times
701    # also enable tar filesystem images for testing
702    if await sysinfo.has("diffoscope") and randint(0, 10) == 0:
703        configlines.append("BR2_REPRODUCIBLE=y\n")
704        configlines.append("BR2_TARGET_ROOTFS_TAR=y\n")
705
706    # Write out the configuration file
707    if not os.path.exists(args.outputdir):
708        os.makedirs(args.outputdir)
709    if args.outputdir == os.path.abspath(os.path.join(args.buildrootdir, "output")):
710        configfile = os.path.join(args.buildrootdir, ".config")
711    else:
712        configfile = os.path.join(args.outputdir, ".config")
713    with open(configfile, "w+") as configf:
714        configf.writelines(configlines)
715
716    proc = await asyncio.create_subprocess_exec(
717        "make", "O=%s" % args.outputdir, "-C", args.buildrootdir, "olddefconfig")
718    ret = await proc.wait()
719    if ret:
720        return ret
721
722    if not await is_toolchain_usable(configfile, toolchainconfig):
723        return 2
724
725    # Now, generate the random selection of packages, and fixup
726    # things if needed.
727    # Safe-guard, in case we can not quickly come to a valid
728    # configuration: allow at most 100 (arbitrary) iterations.
729    bounded_loop = 100
730    while True:
731        if bounded_loop == 0:
732            print("ERROR: cannot generate random configuration after 100 iterations",
733                  file=sys.stderr)
734            return 1
735        bounded_loop -= 1
736        proc = await asyncio.create_subprocess_exec(
737            "make", "O=%s" % args.outputdir, "-C", args.buildrootdir,
738            "KCONFIG_SEED=0x%s" % hexlify(os.urandom(4)).decode("ascii").upper(),
739            "KCONFIG_PROBABILITY=%d" % randint(1, 20),
740            "randpackageconfig" if args.toolchains_csv else "randconfig")
741        ret = await proc.wait()
742        if ret:
743            return ret
744
745        if await fixup_config(sysinfo, configfile):
746            break
747
748    proc = await asyncio.create_subprocess_exec(
749        "make", "O=%s" % args.outputdir, "-C", args.buildrootdir, "olddefconfig")
750    ret = await proc.wait()
751    if ret:
752        return ret
753
754    proc = await asyncio.create_subprocess_exec(
755        "make", "O=%s" % args.outputdir, "-C", args.buildrootdir, "savedefconfig")
756    ret = await proc.wait()
757    if ret:
758        return ret
759
760    proc = await asyncio.create_subprocess_exec(
761        "make", "O=%s" % args.outputdir, "-C", args.buildrootdir, "dependencies")
762    return await proc.wait()
763
764
765if __name__ == '__main__':
766    import argparse
767    parser = argparse.ArgumentParser(description="Generate a random configuration")
768    parser.add_argument("--outputdir", "-o",
769                        help="Output directory (relative to current directory)",
770                        type=str, default='output')
771    parser.add_argument("--buildrootdir", "-b",
772                        help="Buildroot directory (relative to current directory)",
773                        type=str, default='.')
774
775    toolchains_csv = parser.add_mutually_exclusive_group(required=False)
776    toolchains_csv.add_argument("--toolchains-csv",
777                                dest="toolchains_csv",
778                                help="Path of the toolchain configuration file",
779                                type=str)
780    toolchains_csv.add_argument("--no-toolchains-csv",
781                                dest="toolchains_csv",
782                                help="Generate random toolchain configuration",
783                                action='store_false')
784    parser.set_defaults(toolchains_csv="support/config-fragments/autobuild/toolchain-configs.csv")
785
786    args = parser.parse_args()
787
788    # We need the absolute path to use with O=, because the relative
789    # path to the output directory here is not relative to the
790    # Buildroot sources, but to the current directory.
791    args.outputdir = os.path.abspath(args.outputdir)
792
793    try:
794        if sys.version_info < (3, 7):
795            loop = asyncio.get_event_loop()
796            ret = loop.run_until_complete(gen_config(args))
797        else:
798            ret = asyncio.run(gen_config(args))
799    except Exception:
800        traceback.print_exc()
801        parser.exit(1)
802    parser.exit(ret)
803