1*4882a593Smuzhiyun#!/usr/bin/env python3 2*4882a593Smuzhiyun 3*4882a593Smuzhiyun# Copyright (C) 2014 by Thomas Petazzoni <thomas.petazzoni@free-electrons.com> 4*4882a593Smuzhiyun# 5*4882a593Smuzhiyun# This program is free software; you can redistribute it and/or modify 6*4882a593Smuzhiyun# it under the terms of the GNU General Public License as published by 7*4882a593Smuzhiyun# the Free Software Foundation; either version 2 of the License, or 8*4882a593Smuzhiyun# (at your option) any later version. 9*4882a593Smuzhiyun# 10*4882a593Smuzhiyun# This program is distributed in the hope that it will be useful, 11*4882a593Smuzhiyun# but WITHOUT ANY WARRANTY; without even the implied warranty of 12*4882a593Smuzhiyun# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13*4882a593Smuzhiyun# General Public License for more details. 14*4882a593Smuzhiyun# 15*4882a593Smuzhiyun# You should have received a copy of the GNU General Public License 16*4882a593Smuzhiyun# along with this program; if not, write to the Free Software 17*4882a593Smuzhiyun# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18*4882a593Smuzhiyun 19*4882a593Smuzhiyun# This script generates a random configuration for testing Buildroot. 20*4882a593Smuzhiyun 21*4882a593Smuzhiyunimport contextlib 22*4882a593Smuzhiyunimport csv 23*4882a593Smuzhiyunimport os 24*4882a593Smuzhiyunfrom random import randint 25*4882a593Smuzhiyunimport subprocess 26*4882a593Smuzhiyunimport sys 27*4882a593Smuzhiyunfrom distutils.version import StrictVersion 28*4882a593Smuzhiyunimport platform 29*4882a593Smuzhiyun 30*4882a593Smuzhiyunif sys.hexversion >= 0x3000000: 31*4882a593Smuzhiyun import urllib.request as _urllib 32*4882a593Smuzhiyunelse: 33*4882a593Smuzhiyun import urllib2 as _urllib 34*4882a593Smuzhiyun 35*4882a593Smuzhiyun 36*4882a593Smuzhiyundef urlopen_closing(uri): 37*4882a593Smuzhiyun return contextlib.closing(_urllib.urlopen(uri)) 38*4882a593Smuzhiyun 39*4882a593Smuzhiyun 40*4882a593Smuzhiyunclass SystemInfo: 41*4882a593Smuzhiyun DEFAULT_NEEDED_PROGS = ["make", "git", "gcc", "timeout"] 42*4882a593Smuzhiyun DEFAULT_OPTIONAL_PROGS = ["bzr", "java", "javac", "jar", "diffoscope"] 43*4882a593Smuzhiyun 44*4882a593Smuzhiyun def __init__(self): 45*4882a593Smuzhiyun self.needed_progs = list(self.__class__.DEFAULT_NEEDED_PROGS) 46*4882a593Smuzhiyun self.optional_progs = list(self.__class__.DEFAULT_OPTIONAL_PROGS) 47*4882a593Smuzhiyun self.progs = {} 48*4882a593Smuzhiyun 49*4882a593Smuzhiyun def find_prog(self, name, flags=os.X_OK, env=os.environ): 50*4882a593Smuzhiyun if not name or name[0] == os.sep: 51*4882a593Smuzhiyun raise ValueError(name) 52*4882a593Smuzhiyun 53*4882a593Smuzhiyun prog_path = env.get("PATH", None) 54*4882a593Smuzhiyun # for windows compatibility, we'd need to take PATHEXT into account 55*4882a593Smuzhiyun 56*4882a593Smuzhiyun if prog_path: 57*4882a593Smuzhiyun for prog_dir in filter(None, prog_path.split(os.pathsep)): 58*4882a593Smuzhiyun # os.join() not necessary: non-empty prog_dir 59*4882a593Smuzhiyun # and name[0] != os.sep 60*4882a593Smuzhiyun prog = prog_dir + os.sep + name 61*4882a593Smuzhiyun if os.access(prog, flags): 62*4882a593Smuzhiyun return prog 63*4882a593Smuzhiyun # -- 64*4882a593Smuzhiyun return None 65*4882a593Smuzhiyun 66*4882a593Smuzhiyun def has(self, prog): 67*4882a593Smuzhiyun """Checks whether a program is available. 68*4882a593Smuzhiyun Lazily evaluates missing entries. 69*4882a593Smuzhiyun 70*4882a593Smuzhiyun Returns: None if prog not found, else path to the program [evaluates 71*4882a593Smuzhiyun to True] 72*4882a593Smuzhiyun """ 73*4882a593Smuzhiyun try: 74*4882a593Smuzhiyun return self.progs[prog] 75*4882a593Smuzhiyun except KeyError: 76*4882a593Smuzhiyun pass 77*4882a593Smuzhiyun 78*4882a593Smuzhiyun have_it = self.find_prog(prog) 79*4882a593Smuzhiyun # java[c] needs special care 80*4882a593Smuzhiyun if have_it and prog in ('java', 'javac'): 81*4882a593Smuzhiyun with open(os.devnull, "w") as devnull: 82*4882a593Smuzhiyun if subprocess.call("%s -version | grep gcj" % prog, 83*4882a593Smuzhiyun shell=True, 84*4882a593Smuzhiyun stdout=devnull, stderr=devnull) != 1: 85*4882a593Smuzhiyun have_it = False 86*4882a593Smuzhiyun # -- 87*4882a593Smuzhiyun self.progs[prog] = have_it 88*4882a593Smuzhiyun return have_it 89*4882a593Smuzhiyun 90*4882a593Smuzhiyun def check_requirements(self): 91*4882a593Smuzhiyun """Checks program dependencies. 92*4882a593Smuzhiyun 93*4882a593Smuzhiyun Returns: True if all mandatory programs are present, else False. 94*4882a593Smuzhiyun """ 95*4882a593Smuzhiyun do_check_has_prog = self.has 96*4882a593Smuzhiyun 97*4882a593Smuzhiyun missing_requirements = False 98*4882a593Smuzhiyun for prog in self.needed_progs: 99*4882a593Smuzhiyun if not do_check_has_prog(prog): 100*4882a593Smuzhiyun print("ERROR: your system lacks the '%s' program" % prog) 101*4882a593Smuzhiyun missing_requirements = True 102*4882a593Smuzhiyun 103*4882a593Smuzhiyun # check optional programs here, 104*4882a593Smuzhiyun # else they'd get checked by each worker instance 105*4882a593Smuzhiyun for prog in self.optional_progs: 106*4882a593Smuzhiyun do_check_has_prog(prog) 107*4882a593Smuzhiyun 108*4882a593Smuzhiyun return not missing_requirements 109*4882a593Smuzhiyun 110*4882a593Smuzhiyun 111*4882a593Smuzhiyundef get_toolchain_configs(toolchains_csv, buildrootdir): 112*4882a593Smuzhiyun """Fetch and return the possible toolchain configurations 113*4882a593Smuzhiyun 114*4882a593Smuzhiyun This function returns an array of toolchain configurations. Each 115*4882a593Smuzhiyun toolchain configuration is itself an array of lines of the defconfig. 116*4882a593Smuzhiyun """ 117*4882a593Smuzhiyun 118*4882a593Smuzhiyun with open(toolchains_csv) as r: 119*4882a593Smuzhiyun # filter empty lines and comments 120*4882a593Smuzhiyun lines = [t for t in r.readlines() if len(t.strip()) > 0 and t[0] != '#'] 121*4882a593Smuzhiyun toolchains = lines 122*4882a593Smuzhiyun configs = [] 123*4882a593Smuzhiyun 124*4882a593Smuzhiyun (_, _, _, _, hostarch) = os.uname() 125*4882a593Smuzhiyun # ~2015 distros report x86 when on a 32bit install 126*4882a593Smuzhiyun if hostarch == 'i686' or hostarch == 'i386' or hostarch == 'x86': 127*4882a593Smuzhiyun hostarch = 'x86' 128*4882a593Smuzhiyun 129*4882a593Smuzhiyun for row in csv.reader(toolchains): 130*4882a593Smuzhiyun config = {} 131*4882a593Smuzhiyun configfile = row[0] 132*4882a593Smuzhiyun config_hostarch = row[1] 133*4882a593Smuzhiyun keep = False 134*4882a593Smuzhiyun 135*4882a593Smuzhiyun # Keep all toolchain configs that work regardless of the host 136*4882a593Smuzhiyun # architecture 137*4882a593Smuzhiyun if config_hostarch == "any": 138*4882a593Smuzhiyun keep = True 139*4882a593Smuzhiyun 140*4882a593Smuzhiyun # Keep all toolchain configs that can work on the current host 141*4882a593Smuzhiyun # architecture 142*4882a593Smuzhiyun if hostarch == config_hostarch: 143*4882a593Smuzhiyun keep = True 144*4882a593Smuzhiyun 145*4882a593Smuzhiyun # Assume that x86 32 bits toolchains work on x86_64 build 146*4882a593Smuzhiyun # machines 147*4882a593Smuzhiyun if hostarch == 'x86_64' and config_hostarch == "x86": 148*4882a593Smuzhiyun keep = True 149*4882a593Smuzhiyun 150*4882a593Smuzhiyun if not keep: 151*4882a593Smuzhiyun continue 152*4882a593Smuzhiyun 153*4882a593Smuzhiyun if not os.path.isabs(configfile): 154*4882a593Smuzhiyun configfile = os.path.join(buildrootdir, configfile) 155*4882a593Smuzhiyun 156*4882a593Smuzhiyun with open(configfile) as r: 157*4882a593Smuzhiyun config = r.readlines() 158*4882a593Smuzhiyun configs.append(config) 159*4882a593Smuzhiyun return configs 160*4882a593Smuzhiyun 161*4882a593Smuzhiyun 162*4882a593Smuzhiyundef is_toolchain_usable(configfile, config): 163*4882a593Smuzhiyun """Check if the toolchain is actually usable.""" 164*4882a593Smuzhiyun 165*4882a593Smuzhiyun with open(configfile) as configf: 166*4882a593Smuzhiyun configlines = configf.readlines() 167*4882a593Smuzhiyun 168*4882a593Smuzhiyun # Check that the toolchain configuration is still present 169*4882a593Smuzhiyun for toolchainline in config: 170*4882a593Smuzhiyun if toolchainline not in configlines: 171*4882a593Smuzhiyun print("WARN: toolchain can't be used", file=sys.stderr) 172*4882a593Smuzhiyun print(" Missing: %s" % toolchainline.strip(), file=sys.stderr) 173*4882a593Smuzhiyun return False 174*4882a593Smuzhiyun 175*4882a593Smuzhiyun # The latest Linaro toolchains on x86-64 hosts requires glibc 176*4882a593Smuzhiyun # 2.14+ on the host. 177*4882a593Smuzhiyun if platform.machine() == 'x86_64': 178*4882a593Smuzhiyun if 'BR2_TOOLCHAIN_EXTERNAL_LINARO_ARM=y\n' in configlines or \ 179*4882a593Smuzhiyun 'BR2_TOOLCHAIN_EXTERNAL_LINARO_AARCH64=y\n' in configlines or \ 180*4882a593Smuzhiyun 'BR2_TOOLCHAIN_EXTERNAL_LINARO_AARCH64_BE=y\n' in configlines or \ 181*4882a593Smuzhiyun 'BR2_TOOLCHAIN_EXTERNAL_LINARO_ARMEB=y\n' in configlines: 182*4882a593Smuzhiyun ldd_version_output = subprocess.check_output(['ldd', '--version']) 183*4882a593Smuzhiyun glibc_version = ldd_version_output.splitlines()[0].split()[-1] 184*4882a593Smuzhiyun if StrictVersion('2.14') > StrictVersion(glibc_version): 185*4882a593Smuzhiyun print("WARN: ignoring the Linaro ARM toolchains because too old host glibc", file=sys.stderr) 186*4882a593Smuzhiyun return False 187*4882a593Smuzhiyun 188*4882a593Smuzhiyun return True 189*4882a593Smuzhiyun 190*4882a593Smuzhiyun 191*4882a593Smuzhiyundef fixup_config(sysinfo, configfile): 192*4882a593Smuzhiyun """Finalize the configuration and reject any problematic combinations 193*4882a593Smuzhiyun 194*4882a593Smuzhiyun This function returns 'True' when the configuration has been 195*4882a593Smuzhiyun accepted, and 'False' when the configuration has not been accepted because 196*4882a593Smuzhiyun it is known to fail (in which case another random configuration will be 197*4882a593Smuzhiyun generated). 198*4882a593Smuzhiyun """ 199*4882a593Smuzhiyun 200*4882a593Smuzhiyun with open(configfile) as configf: 201*4882a593Smuzhiyun configlines = configf.readlines() 202*4882a593Smuzhiyun 203*4882a593Smuzhiyun BR2_TOOLCHAIN_EXTERNAL_URL = 'BR2_TOOLCHAIN_EXTERNAL_URL="http://autobuild.buildroot.org/toolchains/tarballs/' 204*4882a593Smuzhiyun 205*4882a593Smuzhiyun if "BR2_NEEDS_HOST_JAVA=y\n" in configlines and not sysinfo.has("java"): 206*4882a593Smuzhiyun return False 207*4882a593Smuzhiyun # The ctng toolchain is affected by PR58854 208*4882a593Smuzhiyun if 'BR2_PACKAGE_LTTNG_TOOLS=y\n' in configlines and \ 209*4882a593Smuzhiyun BR2_TOOLCHAIN_EXTERNAL_URL + 'armv5-ctng-linux-gnueabi.tar.xz"\n' in configlines: 210*4882a593Smuzhiyun return False 211*4882a593Smuzhiyun # The ctng toolchain tigger an assembler error with guile package when compiled with -Os (same issue as for CS ARM 2014.05-29) 212*4882a593Smuzhiyun if 'BR2_PACKAGE_GUILE=y\n' in configlines and \ 213*4882a593Smuzhiyun 'BR2_OPTIMIZE_S=y\n' in configlines and \ 214*4882a593Smuzhiyun BR2_TOOLCHAIN_EXTERNAL_URL + 'armv5-ctng-linux-gnueabi.tar.xz"\n' in configlines: 215*4882a593Smuzhiyun return False 216*4882a593Smuzhiyun # The ctng toolchain is affected by PR58854 217*4882a593Smuzhiyun if 'BR2_PACKAGE_LTTNG_TOOLS=y\n' in configlines and \ 218*4882a593Smuzhiyun BR2_TOOLCHAIN_EXTERNAL_URL + 'armv6-ctng-linux-uclibcgnueabi.tar.xz"\n' in configlines: 219*4882a593Smuzhiyun return False 220*4882a593Smuzhiyun # The ctng toolchain is affected by PR58854 221*4882a593Smuzhiyun if 'BR2_PACKAGE_LTTNG_TOOLS=y\n' in configlines and \ 222*4882a593Smuzhiyun BR2_TOOLCHAIN_EXTERNAL_URL + 'armv7-ctng-linux-gnueabihf.tar.xz"\n' in configlines: 223*4882a593Smuzhiyun return False 224*4882a593Smuzhiyun # The ctng toolchain is affected by PR60155 225*4882a593Smuzhiyun if 'BR2_PACKAGE_SDL=y\n' in configlines and \ 226*4882a593Smuzhiyun BR2_TOOLCHAIN_EXTERNAL_URL + 'powerpc-ctng-linux-uclibc.tar.xz"\n' in configlines: 227*4882a593Smuzhiyun return False 228*4882a593Smuzhiyun # The ctng toolchain is affected by PR60155 229*4882a593Smuzhiyun if 'BR2_PACKAGE_LIBMPEG2=y\n' in configlines and \ 230*4882a593Smuzhiyun BR2_TOOLCHAIN_EXTERNAL_URL + 'powerpc-ctng-linux-uclibc.tar.xz"\n' in configlines: 231*4882a593Smuzhiyun return False 232*4882a593Smuzhiyun # This MIPS toolchain uses eglibc-2.18 which lacks SYS_getdents64 233*4882a593Smuzhiyun if 'BR2_PACKAGE_STRONGSWAN=y\n' in configlines and \ 234*4882a593Smuzhiyun BR2_TOOLCHAIN_EXTERNAL_URL + 'mips64el-ctng_n64-linux-gnu.tar.xz"\n' in configlines: 235*4882a593Smuzhiyun return False 236*4882a593Smuzhiyun # This MIPS toolchain uses eglibc-2.18 which lacks SYS_getdents64 237*4882a593Smuzhiyun if 'BR2_PACKAGE_PYTHON3=y\n' in configlines and \ 238*4882a593Smuzhiyun BR2_TOOLCHAIN_EXTERNAL_URL + 'mips64el-ctng_n64-linux-gnu.tar.xz"\n' in configlines: 239*4882a593Smuzhiyun return False 240*4882a593Smuzhiyun # libffi not available on sh2a and ARMv7-M, but propagating libffi 241*4882a593Smuzhiyun # arch dependencies in Buildroot is really too much work, so we 242*4882a593Smuzhiyun # handle this here. 243*4882a593Smuzhiyun if 'BR2_sh2a=y\n' in configlines and \ 244*4882a593Smuzhiyun 'BR2_PACKAGE_LIBFFI=y\n' in configlines: 245*4882a593Smuzhiyun return False 246*4882a593Smuzhiyun if 'BR2_ARM_CPU_ARMV7M=y\n' in configlines and \ 247*4882a593Smuzhiyun 'BR2_PACKAGE_LIBFFI=y\n' in configlines: 248*4882a593Smuzhiyun return False 249*4882a593Smuzhiyun if 'BR2_nds32=y\n' in configlines and \ 250*4882a593Smuzhiyun 'BR2_PACKAGE_LIBFFI=y\n' in configlines: 251*4882a593Smuzhiyun return False 252*4882a593Smuzhiyun if 'BR2_PACKAGE_SUNXI_BOARDS=y\n' in configlines: 253*4882a593Smuzhiyun configlines.remove('BR2_PACKAGE_SUNXI_BOARDS_FEX_FILE=""\n') 254*4882a593Smuzhiyun configlines.append('BR2_PACKAGE_SUNXI_BOARDS_FEX_FILE="a10/hackberry.fex"\n') 255*4882a593Smuzhiyun # This MIPS uClibc toolchain fails to build the gdb package 256*4882a593Smuzhiyun if 'BR2_PACKAGE_GDB=y\n' in configlines and \ 257*4882a593Smuzhiyun BR2_TOOLCHAIN_EXTERNAL_URL + 'mipsel-ctng-linux-uclibc.tar.xz"\n' in configlines: 258*4882a593Smuzhiyun return False 259*4882a593Smuzhiyun # This MIPS uClibc toolchain fails to build the rt-tests package 260*4882a593Smuzhiyun if 'BR2_PACKAGE_RT_TESTS=y\n' in configlines and \ 261*4882a593Smuzhiyun BR2_TOOLCHAIN_EXTERNAL_URL + 'mipsel-ctng-linux-uclibc.tar.xz"\n' in configlines: 262*4882a593Smuzhiyun return False 263*4882a593Smuzhiyun # This MIPS uClibc toolchain fails to build the civetweb package 264*4882a593Smuzhiyun if 'BR2_PACKAGE_CIVETWEB=y\n' in configlines and \ 265*4882a593Smuzhiyun BR2_TOOLCHAIN_EXTERNAL_URL + 'mipsel-ctng-linux-uclibc.tar.xz"\n' in configlines: 266*4882a593Smuzhiyun return False 267*4882a593Smuzhiyun # This MIPS ctng toolchain fails to build the python3 package 268*4882a593Smuzhiyun if 'BR2_PACKAGE_PYTHON3=y\n' in configlines and \ 269*4882a593Smuzhiyun BR2_TOOLCHAIN_EXTERNAL_URL + 'mips64el-ctng_n64-linux-gnu.tar.xz"\n' in configlines: 270*4882a593Smuzhiyun return False 271*4882a593Smuzhiyun # This MIPS uClibc toolchain fails to build the strace package 272*4882a593Smuzhiyun if 'BR2_PACKAGE_STRACE=y\n' in configlines and \ 273*4882a593Smuzhiyun BR2_TOOLCHAIN_EXTERNAL_URL + 'mipsel-ctng-linux-uclibc.tar.xz"\n' in configlines: 274*4882a593Smuzhiyun return False 275*4882a593Smuzhiyun # This MIPS uClibc toolchain fails to build the cdrkit package 276*4882a593Smuzhiyun if 'BR2_PACKAGE_CDRKIT=y\n' in configlines and \ 277*4882a593Smuzhiyun 'BR2_STATIC_LIBS=y\n' in configlines and \ 278*4882a593Smuzhiyun BR2_TOOLCHAIN_EXTERNAL_URL + 'mipsel-ctng-linux-uclibc.tar.xz"\n' in configlines: 279*4882a593Smuzhiyun return False 280*4882a593Smuzhiyun # uClibc vfork static linking issue 281*4882a593Smuzhiyun if 'BR2_PACKAGE_ALSA_LIB=y\n' in configlines and \ 282*4882a593Smuzhiyun 'BR2_STATIC_LIBS=y\n' in configlines and \ 283*4882a593Smuzhiyun BR2_TOOLCHAIN_EXTERNAL_URL + 'i486-ctng-linux-uclibc.tar.xz"\n' in configlines: 284*4882a593Smuzhiyun return False 285*4882a593Smuzhiyun # This MIPS uClibc toolchain fails to build the weston package 286*4882a593Smuzhiyun if 'BR2_PACKAGE_WESTON=y\n' in configlines and \ 287*4882a593Smuzhiyun BR2_TOOLCHAIN_EXTERNAL_URL + 'mipsel-ctng-linux-uclibc.tar.xz"\n' in configlines: 288*4882a593Smuzhiyun return False 289*4882a593Smuzhiyun # The cs nios2 2017.02 toolchain is affected by binutils PR19405 290*4882a593Smuzhiyun if 'BR2_TOOLCHAIN_EXTERNAL_CODESOURCERY_NIOSII=y\n' in configlines and \ 291*4882a593Smuzhiyun 'BR2_PACKAGE_BOOST=y\n' in configlines: 292*4882a593Smuzhiyun return False 293*4882a593Smuzhiyun # The cs nios2 2017.02 toolchain is affected by binutils PR19405 294*4882a593Smuzhiyun if 'BR2_TOOLCHAIN_EXTERNAL_CODESOURCERY_NIOSII=y\n' in configlines and \ 295*4882a593Smuzhiyun 'BR2_PACKAGE_QT5BASE_GUI=y\n' in configlines: 296*4882a593Smuzhiyun return False 297*4882a593Smuzhiyun # The cs nios2 2017.02 toolchain is affected by binutils PR19405 298*4882a593Smuzhiyun if 'BR2_TOOLCHAIN_EXTERNAL_CODESOURCERY_NIOSII=y\n' in configlines and \ 299*4882a593Smuzhiyun 'BR2_PACKAGE_FLANN=y\n' in configlines: 300*4882a593Smuzhiyun return False 301*4882a593Smuzhiyun 302*4882a593Smuzhiyun if 'BR2_PACKAGE_HOST_UBOOT_TOOLS_ENVIMAGE=y\n' in configlines: 303*4882a593Smuzhiyun bootenv = os.path.join(args.outputdir, "boot_env.txt") 304*4882a593Smuzhiyun with open(bootenv, "w+") as bootenvf: 305*4882a593Smuzhiyun bootenvf.write("prop=value") 306*4882a593Smuzhiyun configlines.remove('BR2_PACKAGE_HOST_UBOOT_TOOLS_ENVIMAGE_SOURCE=""\n') 307*4882a593Smuzhiyun configlines.append('BR2_PACKAGE_HOST_UBOOT_TOOLS_ENVIMAGE_SOURCE="%s"\n' % bootenv) 308*4882a593Smuzhiyun configlines.remove('BR2_PACKAGE_HOST_UBOOT_TOOLS_ENVIMAGE_SIZE=""\n') 309*4882a593Smuzhiyun configlines.append('BR2_PACKAGE_HOST_UBOOT_TOOLS_ENVIMAGE_SIZE="0x1000"\n') 310*4882a593Smuzhiyun 311*4882a593Smuzhiyun if 'BR2_PACKAGE_HOST_UBOOT_TOOLS_BOOT_SCRIPT=y\n' in configlines: 312*4882a593Smuzhiyun bootscr = os.path.join(args.outputdir, "boot_script.txt") 313*4882a593Smuzhiyun with open(bootscr, "w+") as bootscrf: 314*4882a593Smuzhiyun bootscrf.write("prop=value") 315*4882a593Smuzhiyun configlines.remove('BR2_PACKAGE_HOST_UBOOT_TOOLS_BOOT_SCRIPT_SOURCE=""\n') 316*4882a593Smuzhiyun configlines.append('BR2_PACKAGE_HOST_UBOOT_TOOLS_BOOT_SCRIPT_SOURCE="%s"\n' % bootscr) 317*4882a593Smuzhiyun 318*4882a593Smuzhiyun with open(configfile, "w+") as configf: 319*4882a593Smuzhiyun configf.writelines(configlines) 320*4882a593Smuzhiyun 321*4882a593Smuzhiyun return True 322*4882a593Smuzhiyun 323*4882a593Smuzhiyun 324*4882a593Smuzhiyundef gen_config(args): 325*4882a593Smuzhiyun """Generate a new random configuration 326*4882a593Smuzhiyun 327*4882a593Smuzhiyun This function generates the configuration, by choosing a random 328*4882a593Smuzhiyun toolchain configuration and then generating a random selection of 329*4882a593Smuzhiyun packages. 330*4882a593Smuzhiyun """ 331*4882a593Smuzhiyun 332*4882a593Smuzhiyun sysinfo = SystemInfo() 333*4882a593Smuzhiyun 334*4882a593Smuzhiyun # Select a random toolchain configuration 335*4882a593Smuzhiyun configs = get_toolchain_configs(args.toolchains_csv, args.buildrootdir) 336*4882a593Smuzhiyun 337*4882a593Smuzhiyun i = randint(0, len(configs) - 1) 338*4882a593Smuzhiyun toolchainconfig = configs[i] 339*4882a593Smuzhiyun 340*4882a593Smuzhiyun configlines = list(toolchainconfig) 341*4882a593Smuzhiyun 342*4882a593Smuzhiyun # Combine with the minimal configuration 343*4882a593Smuzhiyun minimalconfigfile = os.path.join(args.buildrootdir, 344*4882a593Smuzhiyun 'support/config-fragments/minimal.config') 345*4882a593Smuzhiyun with open(minimalconfigfile) as minimalf: 346*4882a593Smuzhiyun configlines += minimalf.readlines() 347*4882a593Smuzhiyun 348*4882a593Smuzhiyun # Allow hosts with old certificates to download over https 349*4882a593Smuzhiyun configlines.append("BR2_WGET=\"wget --passive-ftp -nd -t 3 --no-check-certificate\"\n") 350*4882a593Smuzhiyun 351*4882a593Smuzhiyun # Per-package folder 352*4882a593Smuzhiyun if randint(0, 15) == 0: 353*4882a593Smuzhiyun configlines.append("BR2_PER_PACKAGE_DIRECTORIES=y\n") 354*4882a593Smuzhiyun 355*4882a593Smuzhiyun # Amend the configuration with a few things. 356*4882a593Smuzhiyun if randint(0, 20) == 0: 357*4882a593Smuzhiyun configlines.append("BR2_ENABLE_DEBUG=y\n") 358*4882a593Smuzhiyun if randint(0, 20) == 0: 359*4882a593Smuzhiyun configlines.append("BR2_ENABLE_RUNTIME_DEBUG=y\n") 360*4882a593Smuzhiyun if randint(0, 1) == 0: 361*4882a593Smuzhiyun configlines.append("BR2_INIT_BUSYBOX=y\n") 362*4882a593Smuzhiyun elif randint(0, 15) == 0: 363*4882a593Smuzhiyun configlines.append("BR2_INIT_SYSTEMD=y\n") 364*4882a593Smuzhiyun elif randint(0, 10) == 0: 365*4882a593Smuzhiyun configlines.append("BR2_ROOTFS_DEVICE_CREATION_DYNAMIC_EUDEV=y\n") 366*4882a593Smuzhiyun if randint(0, 20) == 0: 367*4882a593Smuzhiyun configlines.append("BR2_STATIC_LIBS=y\n") 368*4882a593Smuzhiyun if randint(0, 20) == 0: 369*4882a593Smuzhiyun configlines.append("BR2_PACKAGE_PYTHON_PY_ONLY=y\n") 370*4882a593Smuzhiyun if randint(0, 20) == 0: 371*4882a593Smuzhiyun configlines.append("BR2_PACKAGE_PYTHON3_PY_ONLY=y\n") 372*4882a593Smuzhiyun if randint(0, 5) == 0: 373*4882a593Smuzhiyun configlines.append("BR2_OPTIMIZE_2=y\n") 374*4882a593Smuzhiyun if randint(0, 4) == 0: 375*4882a593Smuzhiyun configlines.append("BR2_SYSTEM_ENABLE_NLS=y\n") 376*4882a593Smuzhiyun if randint(0, 4) == 0: 377*4882a593Smuzhiyun configlines.append("BR2_FORTIFY_SOURCE_2=y\n") 378*4882a593Smuzhiyun 379*4882a593Smuzhiyun # Randomly enable BR2_REPRODUCIBLE 10% of times 380*4882a593Smuzhiyun # also enable tar filesystem images for testing 381*4882a593Smuzhiyun if sysinfo.has("diffoscope") and randint(0, 10) == 0: 382*4882a593Smuzhiyun configlines.append("BR2_REPRODUCIBLE=y\n") 383*4882a593Smuzhiyun configlines.append("BR2_TARGET_ROOTFS_TAR=y\n") 384*4882a593Smuzhiyun 385*4882a593Smuzhiyun # Write out the configuration file 386*4882a593Smuzhiyun if not os.path.exists(args.outputdir): 387*4882a593Smuzhiyun os.makedirs(args.outputdir) 388*4882a593Smuzhiyun if args.outputdir == os.path.abspath(os.path.join(args.buildrootdir, "output")): 389*4882a593Smuzhiyun configfile = os.path.join(args.buildrootdir, ".config") 390*4882a593Smuzhiyun else: 391*4882a593Smuzhiyun configfile = os.path.join(args.outputdir, ".config") 392*4882a593Smuzhiyun with open(configfile, "w+") as configf: 393*4882a593Smuzhiyun configf.writelines(configlines) 394*4882a593Smuzhiyun 395*4882a593Smuzhiyun subprocess.check_call(["make", "O=%s" % args.outputdir, "-C", args.buildrootdir, 396*4882a593Smuzhiyun "olddefconfig"]) 397*4882a593Smuzhiyun 398*4882a593Smuzhiyun if not is_toolchain_usable(configfile, toolchainconfig): 399*4882a593Smuzhiyun return 2 400*4882a593Smuzhiyun 401*4882a593Smuzhiyun # Now, generate the random selection of packages, and fixup 402*4882a593Smuzhiyun # things if needed. 403*4882a593Smuzhiyun # Safe-guard, in case we can not quickly come to a valid 404*4882a593Smuzhiyun # configuration: allow at most 100 (arbitrary) iterations. 405*4882a593Smuzhiyun bounded_loop = 100 406*4882a593Smuzhiyun while True: 407*4882a593Smuzhiyun if bounded_loop == 0: 408*4882a593Smuzhiyun print("ERROR: cannot generate random configuration after 100 iterations", 409*4882a593Smuzhiyun file=sys.stderr) 410*4882a593Smuzhiyun return 1 411*4882a593Smuzhiyun bounded_loop -= 1 412*4882a593Smuzhiyun subprocess.check_call(["make", "O=%s" % args.outputdir, "-C", args.buildrootdir, 413*4882a593Smuzhiyun "KCONFIG_PROBABILITY=%d" % randint(1, 20), 414*4882a593Smuzhiyun "randpackageconfig"]) 415*4882a593Smuzhiyun 416*4882a593Smuzhiyun if fixup_config(sysinfo, configfile): 417*4882a593Smuzhiyun break 418*4882a593Smuzhiyun 419*4882a593Smuzhiyun subprocess.check_call(["make", "O=%s" % args.outputdir, "-C", args.buildrootdir, 420*4882a593Smuzhiyun "olddefconfig"]) 421*4882a593Smuzhiyun 422*4882a593Smuzhiyun subprocess.check_call(["make", "O=%s" % args.outputdir, "-C", args.buildrootdir, 423*4882a593Smuzhiyun "savedefconfig"]) 424*4882a593Smuzhiyun 425*4882a593Smuzhiyun return subprocess.call(["make", "O=%s" % args.outputdir, "-C", args.buildrootdir, 426*4882a593Smuzhiyun "dependencies"]) 427*4882a593Smuzhiyun 428*4882a593Smuzhiyun 429*4882a593Smuzhiyunif __name__ == '__main__': 430*4882a593Smuzhiyun import argparse 431*4882a593Smuzhiyun parser = argparse.ArgumentParser(description="Generate a random configuration") 432*4882a593Smuzhiyun parser.add_argument("--outputdir", "-o", 433*4882a593Smuzhiyun help="Output directory (relative to current directory)", 434*4882a593Smuzhiyun type=str, default='output') 435*4882a593Smuzhiyun parser.add_argument("--buildrootdir", "-b", 436*4882a593Smuzhiyun help="Buildroot directory (relative to current directory)", 437*4882a593Smuzhiyun type=str, default='.') 438*4882a593Smuzhiyun parser.add_argument("--toolchains-csv", 439*4882a593Smuzhiyun help="Path of the toolchain configuration file", 440*4882a593Smuzhiyun type=str, 441*4882a593Smuzhiyun default="support/config-fragments/autobuild/toolchain-configs.csv") 442*4882a593Smuzhiyun args = parser.parse_args() 443*4882a593Smuzhiyun 444*4882a593Smuzhiyun # We need the absolute path to use with O=, because the relative 445*4882a593Smuzhiyun # path to the output directory here is not relative to the 446*4882a593Smuzhiyun # Buildroot sources, but to the current directory. 447*4882a593Smuzhiyun args.outputdir = os.path.abspath(args.outputdir) 448*4882a593Smuzhiyun 449*4882a593Smuzhiyun try: 450*4882a593Smuzhiyun ret = gen_config(args) 451*4882a593Smuzhiyun except Exception as e: 452*4882a593Smuzhiyun print(str(e), file=sys.stderr) 453*4882a593Smuzhiyun parser.exit(1) 454*4882a593Smuzhiyun parser.exit(ret) 455