xref: /OK3568_Linux_fs/buildroot/utils/genrandconfig (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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