Blame scripts/build-many-glibcs.py

Packit 6c4009
#!/usr/bin/python3
Packit 6c4009
# Build many configurations of glibc.
Packit 6c4009
# Copyright (C) 2016-2018 Free Software Foundation, Inc.
Packit 6c4009
# This file is part of the GNU C Library.
Packit 6c4009
#
Packit 6c4009
# The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
# modify it under the terms of the GNU Lesser General Public
Packit 6c4009
# License as published by the Free Software Foundation; either
Packit 6c4009
# version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
#
Packit 6c4009
# The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
# but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
# Lesser General Public License for more details.
Packit 6c4009
#
Packit 6c4009
# You should have received a copy of the GNU Lesser General Public
Packit 6c4009
# License along with the GNU C Library; if not, see
Packit 6c4009
# <http://www.gnu.org/licenses/>.
Packit 6c4009
Packit 6c4009
"""Build many configurations of glibc.
Packit 6c4009
Packit 6c4009
This script takes as arguments a directory name (containing a src
Packit 6c4009
subdirectory with sources of the relevant toolchain components) and a
Packit 6c4009
description of what to do: 'checkout', to check out sources into that
Packit 6c4009
directory, 'bot-cycle', to run a series of checkout and build steps,
Packit 6c4009
'bot', to run 'bot-cycle' repeatedly, 'host-libraries', to build
Packit 6c4009
libraries required by the toolchain, 'compilers', to build
Packit 6c4009
cross-compilers for various configurations, or 'glibcs', to build
Packit 6c4009
glibc for various configurations and run the compilation parts of the
Packit 6c4009
testsuite.  Subsequent arguments name the versions of components to
Packit 6c4009
check out (<component>-
Packit 6c4009
other than 'checkout' and 'bot-cycle', name configurations for which
Packit 6c4009
compilers or glibc are to be built.
Packit 6c4009
Packit 6c4009
"""
Packit 6c4009
Packit 6c4009
import argparse
Packit 6c4009
import datetime
Packit 6c4009
import email.mime.text
Packit 6c4009
import email.utils
Packit 6c4009
import json
Packit 6c4009
import os
Packit 6c4009
import re
Packit 6c4009
import shutil
Packit 6c4009
import smtplib
Packit 6c4009
import stat
Packit 6c4009
import subprocess
Packit 6c4009
import sys
Packit 6c4009
import time
Packit 6c4009
import urllib.request
Packit 6c4009
Packit 6c4009
try:
Packit 6c4009
    os.cpu_count
Packit 6c4009
except:
Packit 6c4009
    import multiprocessing
Packit 6c4009
    os.cpu_count = lambda: multiprocessing.cpu_count()
Packit 6c4009
Packit 6c4009
try:
Packit 6c4009
    re.fullmatch
Packit 6c4009
except:
Packit 6c4009
    re.fullmatch = lambda p,s,f=0: re.match(p+"\\Z",s,f)
Packit 6c4009
Packit 6c4009
try:
Packit 6c4009
    subprocess.run
Packit 6c4009
except:
Packit 6c4009
    class _CompletedProcess:
Packit 6c4009
        def __init__(self, args, returncode, stdout=None, stderr=None):
Packit 6c4009
            self.args = args
Packit 6c4009
            self.returncode = returncode
Packit 6c4009
            self.stdout = stdout
Packit 6c4009
            self.stderr = stderr
Packit 6c4009
Packit 6c4009
    def _run(*popenargs, input=None, timeout=None, check=False, **kwargs):
Packit 6c4009
        assert(timeout is None)
Packit 6c4009
        with subprocess.Popen(*popenargs, **kwargs) as process:
Packit 6c4009
            try:
Packit 6c4009
                stdout, stderr = process.communicate(input)
Packit 6c4009
            except:
Packit 6c4009
                process.kill()
Packit 6c4009
                process.wait()
Packit 6c4009
                raise
Packit 6c4009
            returncode = process.poll()
Packit 6c4009
            if check and returncode:
Packit 6c4009
                raise subprocess.CalledProcessError(returncode, popenargs)
Packit 6c4009
        return _CompletedProcess(popenargs, returncode, stdout, stderr)
Packit 6c4009
Packit 6c4009
    subprocess.run = _run
Packit 6c4009
Packit 6c4009
Packit 6c4009
class Context(object):
Packit 6c4009
    """The global state associated with builds in a given directory."""
Packit 6c4009
Packit 6c4009
    def __init__(self, topdir, parallelism, keep, replace_sources, strip,
Packit 6c4009
                 action):
Packit 6c4009
        """Initialize the context."""
Packit 6c4009
        self.topdir = topdir
Packit 6c4009
        self.parallelism = parallelism
Packit 6c4009
        self.keep = keep
Packit 6c4009
        self.replace_sources = replace_sources
Packit 6c4009
        self.strip = strip
Packit 6c4009
        self.srcdir = os.path.join(topdir, 'src')
Packit 6c4009
        self.versions_json = os.path.join(self.srcdir, 'versions.json')
Packit 6c4009
        self.build_state_json = os.path.join(topdir, 'build-state.json')
Packit 6c4009
        self.bot_config_json = os.path.join(topdir, 'bot-config.json')
Packit 6c4009
        self.installdir = os.path.join(topdir, 'install')
Packit 6c4009
        self.host_libraries_installdir = os.path.join(self.installdir,
Packit 6c4009
                                                      'host-libraries')
Packit 6c4009
        self.builddir = os.path.join(topdir, 'build')
Packit 6c4009
        self.logsdir = os.path.join(topdir, 'logs')
Packit 6c4009
        self.logsdir_old = os.path.join(topdir, 'logs-old')
Packit 6c4009
        self.makefile = os.path.join(self.builddir, 'Makefile')
Packit 6c4009
        self.wrapper = os.path.join(self.builddir, 'wrapper')
Packit 6c4009
        self.save_logs = os.path.join(self.builddir, 'save-logs')
Packit 6c4009
        self.script_text = self.get_script_text()
Packit 6c4009
        if action != 'checkout':
Packit 6c4009
            self.build_triplet = self.get_build_triplet()
Packit 6c4009
            self.glibc_version = self.get_glibc_version()
Packit 6c4009
        self.configs = {}
Packit 6c4009
        self.glibc_configs = {}
Packit 6c4009
        self.makefile_pieces = ['.PHONY: all\n']
Packit 6c4009
        self.add_all_configs()
Packit 6c4009
        self.load_versions_json()
Packit 6c4009
        self.load_build_state_json()
Packit 6c4009
        self.status_log_list = []
Packit 6c4009
        self.email_warning = False
Packit 6c4009
Packit 6c4009
    def get_script_text(self):
Packit 6c4009
        """Return the text of this script."""
Packit 6c4009
        with open(sys.argv[0], 'r') as f:
Packit 6c4009
            return f.read()
Packit 6c4009
Packit 6c4009
    def exec_self(self):
Packit 6c4009
        """Re-execute this script with the same arguments."""
Packit 6c4009
        sys.stdout.flush()
Packit 6c4009
        os.execv(sys.executable, [sys.executable] + sys.argv)
Packit 6c4009
Packit 6c4009
    def get_build_triplet(self):
Packit 6c4009
        """Determine the build triplet with config.guess."""
Packit 6c4009
        config_guess = os.path.join(self.component_srcdir('gcc'),
Packit 6c4009
                                    'config.guess')
Packit 6c4009
        cg_out = subprocess.run([config_guess], stdout=subprocess.PIPE,
Packit 6c4009
                                check=True, universal_newlines=True).stdout
Packit 6c4009
        return cg_out.rstrip()
Packit 6c4009
Packit 6c4009
    def get_glibc_version(self):
Packit 6c4009
        """Determine the glibc version number (major.minor)."""
Packit 6c4009
        version_h = os.path.join(self.component_srcdir('glibc'), 'version.h')
Packit 6c4009
        with open(version_h, 'r') as f:
Packit 6c4009
            lines = f.readlines()
Packit 6c4009
        starttext = '#define VERSION "'
Packit 6c4009
        for l in lines:
Packit 6c4009
            if l.startswith(starttext):
Packit 6c4009
                l = l[len(starttext):]
Packit 6c4009
                l = l.rstrip('"\n')
Packit 6c4009
                m = re.fullmatch('([0-9]+)\.([0-9]+)[.0-9]*', l)
Packit 6c4009
                return '%s.%s' % m.group(1, 2)
Packit 6c4009
        print('error: could not determine glibc version')
Packit 6c4009
        exit(1)
Packit 6c4009
Packit 6c4009
    def add_all_configs(self):
Packit 6c4009
        """Add all known glibc build configurations."""
Packit 6c4009
        self.add_config(arch='aarch64',
Packit 6c4009
                        os_name='linux-gnu',
Packit 6c4009
                        extra_glibcs=[{'variant': 'disable-multi-arch',
Packit 6c4009
                                       'cfg': ['--disable-multi-arch']}])
Packit 6c4009
        self.add_config(arch='aarch64_be',
Packit 6c4009
                        os_name='linux-gnu')
Packit 6c4009
        self.add_config(arch='alpha',
Packit 6c4009
                        os_name='linux-gnu')
Packit 6c4009
        self.add_config(arch='arm',
Packit 6c4009
                        os_name='linux-gnueabi')
Packit 6c4009
        self.add_config(arch='armeb',
Packit 6c4009
                        os_name='linux-gnueabi')
Packit 6c4009
        self.add_config(arch='armeb',
Packit 6c4009
                        os_name='linux-gnueabi',
Packit 6c4009
                        variant='be8',
Packit 6c4009
                        gcc_cfg=['--with-arch=armv7-a'])
Packit 6c4009
        self.add_config(arch='arm',
Packit 6c4009
                        os_name='linux-gnueabihf',
Packit 6c4009
                        gcc_cfg=['--with-float=hard', '--with-cpu=arm926ej-s'],
Packit 6c4009
                        extra_glibcs=[{'variant': 'v7a',
Packit 6c4009
                                       'ccopts': '-march=armv7-a -mfpu=vfpv3'},
Packit 6c4009
                                      {'variant': 'v7a-disable-multi-arch',
Packit 6c4009
                                       'ccopts': '-march=armv7-a -mfpu=vfpv3',
Packit 6c4009
                                       'cfg': ['--disable-multi-arch']}])
Packit 6c4009
        self.add_config(arch='armeb',
Packit 6c4009
                        os_name='linux-gnueabihf',
Packit 6c4009
                        gcc_cfg=['--with-float=hard', '--with-cpu=arm926ej-s'])
Packit 6c4009
        self.add_config(arch='armeb',
Packit 6c4009
                        os_name='linux-gnueabihf',
Packit 6c4009
                        variant='be8',
Packit 6c4009
                        gcc_cfg=['--with-float=hard', '--with-arch=armv7-a',
Packit 6c4009
                                 '--with-fpu=vfpv3'])
Packit 6c4009
        self.add_config(arch='hppa',
Packit 6c4009
                        os_name='linux-gnu')
Packit 6c4009
        self.add_config(arch='i686',
Packit 6c4009
                        os_name='gnu')
Packit 6c4009
        self.add_config(arch='ia64',
Packit 6c4009
                        os_name='linux-gnu',
Packit 6c4009
                        first_gcc_cfg=['--with-system-libunwind'])
Packit 6c4009
        self.add_config(arch='m68k',
Packit 6c4009
                        os_name='linux-gnu',
Packit 6c4009
                        gcc_cfg=['--disable-multilib'])
Packit 6c4009
        self.add_config(arch='m68k',
Packit 6c4009
                        os_name='linux-gnu',
Packit 6c4009
                        variant='coldfire',
Packit 6c4009
                        gcc_cfg=['--with-arch=cf', '--disable-multilib'])
Packit 6c4009
        self.add_config(arch='m68k',
Packit 6c4009
                        os_name='linux-gnu',
Packit 6c4009
                        variant='coldfire-soft',
Packit 6c4009
                        gcc_cfg=['--with-arch=cf', '--with-cpu=54455',
Packit 6c4009
                                 '--disable-multilib'])
Packit 6c4009
        self.add_config(arch='microblaze',
Packit 6c4009
                        os_name='linux-gnu',
Packit 6c4009
                        gcc_cfg=['--disable-multilib'])
Packit 6c4009
        self.add_config(arch='microblazeel',
Packit 6c4009
                        os_name='linux-gnu',
Packit 6c4009
                        gcc_cfg=['--disable-multilib'])
Packit 6c4009
        self.add_config(arch='mips64',
Packit 6c4009
                        os_name='linux-gnu',
Packit 6c4009
                        gcc_cfg=['--with-mips-plt'],
Packit 6c4009
                        glibcs=[{'variant': 'n32'},
Packit 6c4009
                                {'arch': 'mips',
Packit 6c4009
                                 'ccopts': '-mabi=32'},
Packit 6c4009
                                {'variant': 'n64',
Packit 6c4009
                                 'ccopts': '-mabi=64'}])
Packit 6c4009
        self.add_config(arch='mips64',
Packit 6c4009
                        os_name='linux-gnu',
Packit 6c4009
                        variant='soft',
Packit 6c4009
                        gcc_cfg=['--with-mips-plt', '--with-float=soft'],
Packit 6c4009
                        glibcs=[{'variant': 'n32-soft'},
Packit 6c4009
                                {'variant': 'soft',
Packit 6c4009
                                 'arch': 'mips',
Packit 6c4009
                                 'ccopts': '-mabi=32'},
Packit 6c4009
                                {'variant': 'n64-soft',
Packit 6c4009
                                 'ccopts': '-mabi=64'}])
Packit 6c4009
        self.add_config(arch='mips64',
Packit 6c4009
                        os_name='linux-gnu',
Packit 6c4009
                        variant='nan2008',
Packit 6c4009
                        gcc_cfg=['--with-mips-plt', '--with-nan=2008',
Packit 6c4009
                                 '--with-arch-64=mips64r2',
Packit 6c4009
                                 '--with-arch-32=mips32r2'],
Packit 6c4009
                        glibcs=[{'variant': 'n32-nan2008'},
Packit 6c4009
                                {'variant': 'nan2008',
Packit 6c4009
                                 'arch': 'mips',
Packit 6c4009
                                 'ccopts': '-mabi=32'},
Packit 6c4009
                                {'variant': 'n64-nan2008',
Packit 6c4009
                                 'ccopts': '-mabi=64'}])
Packit 6c4009
        self.add_config(arch='mips64',
Packit 6c4009
                        os_name='linux-gnu',
Packit 6c4009
                        variant='nan2008-soft',
Packit 6c4009
                        gcc_cfg=['--with-mips-plt', '--with-nan=2008',
Packit 6c4009
                                 '--with-arch-64=mips64r2',
Packit 6c4009
                                 '--with-arch-32=mips32r2',
Packit 6c4009
                                 '--with-float=soft'],
Packit 6c4009
                        glibcs=[{'variant': 'n32-nan2008-soft'},
Packit 6c4009
                                {'variant': 'nan2008-soft',
Packit 6c4009
                                 'arch': 'mips',
Packit 6c4009
                                 'ccopts': '-mabi=32'},
Packit 6c4009
                                {'variant': 'n64-nan2008-soft',
Packit 6c4009
                                 'ccopts': '-mabi=64'}])
Packit 6c4009
        self.add_config(arch='mips64el',
Packit 6c4009
                        os_name='linux-gnu',
Packit 6c4009
                        gcc_cfg=['--with-mips-plt'],
Packit 6c4009
                        glibcs=[{'variant': 'n32'},
Packit 6c4009
                                {'arch': 'mipsel',
Packit 6c4009
                                 'ccopts': '-mabi=32'},
Packit 6c4009
                                {'variant': 'n64',
Packit 6c4009
                                 'ccopts': '-mabi=64'}])
Packit 6c4009
        self.add_config(arch='mips64el',
Packit 6c4009
                        os_name='linux-gnu',
Packit 6c4009
                        variant='soft',
Packit 6c4009
                        gcc_cfg=['--with-mips-plt', '--with-float=soft'],
Packit 6c4009
                        glibcs=[{'variant': 'n32-soft'},
Packit 6c4009
                                {'variant': 'soft',
Packit 6c4009
                                 'arch': 'mipsel',
Packit 6c4009
                                 'ccopts': '-mabi=32'},
Packit 6c4009
                                {'variant': 'n64-soft',
Packit 6c4009
                                 'ccopts': '-mabi=64'}])
Packit 6c4009
        self.add_config(arch='mips64el',
Packit 6c4009
                        os_name='linux-gnu',
Packit 6c4009
                        variant='nan2008',
Packit 6c4009
                        gcc_cfg=['--with-mips-plt', '--with-nan=2008',
Packit 6c4009
                                 '--with-arch-64=mips64r2',
Packit 6c4009
                                 '--with-arch-32=mips32r2'],
Packit 6c4009
                        glibcs=[{'variant': 'n32-nan2008'},
Packit 6c4009
                                {'variant': 'nan2008',
Packit 6c4009
                                 'arch': 'mipsel',
Packit 6c4009
                                 'ccopts': '-mabi=32'},
Packit 6c4009
                                {'variant': 'n64-nan2008',
Packit 6c4009
                                 'ccopts': '-mabi=64'}])
Packit 6c4009
        self.add_config(arch='mips64el',
Packit 6c4009
                        os_name='linux-gnu',
Packit 6c4009
                        variant='nan2008-soft',
Packit 6c4009
                        gcc_cfg=['--with-mips-plt', '--with-nan=2008',
Packit 6c4009
                                 '--with-arch-64=mips64r2',
Packit 6c4009
                                 '--with-arch-32=mips32r2',
Packit 6c4009
                                 '--with-float=soft'],
Packit 6c4009
                        glibcs=[{'variant': 'n32-nan2008-soft'},
Packit 6c4009
                                {'variant': 'nan2008-soft',
Packit 6c4009
                                 'arch': 'mipsel',
Packit 6c4009
                                 'ccopts': '-mabi=32'},
Packit 6c4009
                                {'variant': 'n64-nan2008-soft',
Packit 6c4009
                                 'ccopts': '-mabi=64'}])
Packit 6c4009
        self.add_config(arch='nios2',
Packit 6c4009
                        os_name='linux-gnu')
Packit 6c4009
        self.add_config(arch='powerpc',
Packit 6c4009
                        os_name='linux-gnu',
Packit 6c4009
                        gcc_cfg=['--disable-multilib', '--enable-secureplt'],
Packit 6c4009
                        extra_glibcs=[{'variant': 'power4',
Packit 6c4009
                                       'ccopts': '-mcpu=power4',
Packit 6c4009
                                       'cfg': ['--with-cpu=power4']}])
Packit 6c4009
        self.add_config(arch='powerpc',
Packit 6c4009
                        os_name='linux-gnu',
Packit 6c4009
                        variant='soft',
Packit 6c4009
                        gcc_cfg=['--disable-multilib', '--with-float=soft',
Packit 6c4009
                                 '--enable-secureplt'])
Packit 6c4009
        self.add_config(arch='powerpc64',
Packit 6c4009
                        os_name='linux-gnu',
Packit 6c4009
                        gcc_cfg=['--disable-multilib', '--enable-secureplt'])
Packit 6c4009
        self.add_config(arch='powerpc64le',
Packit 6c4009
                        os_name='linux-gnu',
Packit 6c4009
                        gcc_cfg=['--disable-multilib', '--enable-secureplt'])
Packit 6c4009
        self.add_config(arch='powerpc',
Packit 6c4009
                        os_name='linux-gnuspe',
Packit 6c4009
                        gcc_cfg=['--disable-multilib', '--enable-secureplt',
Packit 6c4009
                                 '--enable-e500-double', '--enable-obsolete'])
Packit 6c4009
        self.add_config(arch='powerpc',
Packit 6c4009
                        os_name='linux-gnuspe',
Packit 6c4009
                        variant='e500v1',
Packit 6c4009
                        gcc_cfg=['--disable-multilib', '--enable-secureplt',
Packit 6c4009
                                 '--enable-obsolete'])
Packit 6c4009
        self.add_config(arch='riscv64',
Packit 6c4009
                        os_name='linux-gnu',
Packit 6c4009
                        variant='rv64imac-lp64',
Packit 6c4009
                        gcc_cfg=['--with-arch=rv64imac', '--with-abi=lp64',
Packit 6c4009
                                 '--disable-multilib'])
Packit 6c4009
        self.add_config(arch='riscv64',
Packit 6c4009
                        os_name='linux-gnu',
Packit 6c4009
                        variant='rv64imafdc-lp64',
Packit 6c4009
                        gcc_cfg=['--with-arch=rv64imafdc', '--with-abi=lp64',
Packit 6c4009
                                 '--disable-multilib'])
Packit 6c4009
        self.add_config(arch='riscv64',
Packit 6c4009
                        os_name='linux-gnu',
Packit 6c4009
                        variant='rv64imafdc-lp64d',
Packit 6c4009
                        gcc_cfg=['--with-arch=rv64imafdc', '--with-abi=lp64d',
Packit 6c4009
                                 '--disable-multilib'])
Packit 6c4009
        self.add_config(arch='s390x',
Packit 6c4009
                        os_name='linux-gnu',
Packit 6c4009
                        glibcs=[{},
Packit 6c4009
                                {'arch': 's390', 'ccopts': '-m31'}])
Packit 6c4009
        self.add_config(arch='sh3',
Packit 6c4009
                        os_name='linux-gnu')
Packit 6c4009
        self.add_config(arch='sh3eb',
Packit 6c4009
                        os_name='linux-gnu')
Packit 6c4009
        self.add_config(arch='sh4',
Packit 6c4009
                        os_name='linux-gnu')
Packit 6c4009
        self.add_config(arch='sh4eb',
Packit 6c4009
                        os_name='linux-gnu')
Packit 6c4009
        self.add_config(arch='sh4',
Packit 6c4009
                        os_name='linux-gnu',
Packit 6c4009
                        variant='soft',
Packit 6c4009
                        gcc_cfg=['--without-fp'])
Packit 6c4009
        self.add_config(arch='sh4eb',
Packit 6c4009
                        os_name='linux-gnu',
Packit 6c4009
                        variant='soft',
Packit 6c4009
                        gcc_cfg=['--without-fp'])
Packit 6c4009
        self.add_config(arch='sparc64',
Packit 6c4009
                        os_name='linux-gnu',
Packit 6c4009
                        glibcs=[{},
Packit 6c4009
                                {'arch': 'sparcv9',
Packit 6c4009
                                 'ccopts': '-m32 -mlong-double-128'}],
Packit 6c4009
                        extra_glibcs=[{'variant': 'disable-multi-arch',
Packit 6c4009
                                       'cfg': ['--disable-multi-arch']},
Packit 6c4009
                                      {'variant': 'disable-multi-arch',
Packit 6c4009
                                       'arch': 'sparcv9',
Packit 6c4009
                                       'ccopts': '-m32 -mlong-double-128',
Packit 6c4009
                                       'cfg': ['--disable-multi-arch']}])
Packit 6c4009
        self.add_config(arch='x86_64',
Packit 6c4009
                        os_name='linux-gnu',
Packit 6c4009
                        gcc_cfg=['--with-multilib-list=m64,m32,mx32'],
Packit 6c4009
                        glibcs=[{},
Packit 6c4009
                                {'variant': 'x32', 'ccopts': '-mx32'},
Packit 6c4009
                                {'arch': 'i686', 'ccopts': '-m32 -march=i686'}],
Packit 6c4009
                        extra_glibcs=[{'variant': 'disable-multi-arch',
Packit 6c4009
                                       'cfg': ['--disable-multi-arch']},
Packit 6c4009
                                      {'variant': 'static-pie',
Packit 6c4009
                                       'cfg': ['--enable-static-pie']},
Packit 6c4009
                                      {'variant': 'x32-static-pie',
Packit 6c4009
                                       'ccopts': '-mx32',
Packit 6c4009
                                       'cfg': ['--enable-static-pie']},
Packit 6c4009
                                      {'variant': 'static-pie',
Packit 6c4009
                                       'arch': 'i686',
Packit 6c4009
                                       'ccopts': '-m32 -march=i686',
Packit 6c4009
                                       'cfg': ['--enable-static-pie']},
Packit 6c4009
                                      {'variant': 'disable-multi-arch',
Packit 6c4009
                                       'arch': 'i686',
Packit 6c4009
                                       'ccopts': '-m32 -march=i686',
Packit 6c4009
                                       'cfg': ['--disable-multi-arch']},
Packit 6c4009
                                      {'arch': 'i486',
Packit 6c4009
                                       'ccopts': '-m32 -march=i486'},
Packit 6c4009
                                      {'arch': 'i586',
Packit 6c4009
                                       'ccopts': '-m32 -march=i586'}])
Packit 6c4009
Packit 6c4009
    def add_config(self, **args):
Packit 6c4009
        """Add an individual build configuration."""
Packit 6c4009
        cfg = Config(self, **args)
Packit 6c4009
        if cfg.name in self.configs:
Packit 6c4009
            print('error: duplicate config %s' % cfg.name)
Packit 6c4009
            exit(1)
Packit 6c4009
        self.configs[cfg.name] = cfg
Packit 6c4009
        for c in cfg.all_glibcs:
Packit 6c4009
            if c.name in self.glibc_configs:
Packit 6c4009
                print('error: duplicate glibc config %s' % c.name)
Packit 6c4009
                exit(1)
Packit 6c4009
            self.glibc_configs[c.name] = c
Packit 6c4009
Packit 6c4009
    def component_srcdir(self, component):
Packit 6c4009
        """Return the source directory for a given component, e.g. gcc."""
Packit 6c4009
        return os.path.join(self.srcdir, component)
Packit 6c4009
Packit 6c4009
    def component_builddir(self, action, config, component, subconfig=None):
Packit 6c4009
        """Return the directory to use for a build."""
Packit 6c4009
        if config is None:
Packit 6c4009
            # Host libraries.
Packit 6c4009
            assert subconfig is None
Packit 6c4009
            return os.path.join(self.builddir, action, component)
Packit 6c4009
        if subconfig is None:
Packit 6c4009
            return os.path.join(self.builddir, action, config, component)
Packit 6c4009
        else:
Packit 6c4009
            # glibc build as part of compiler build.
Packit 6c4009
            return os.path.join(self.builddir, action, config, component,
Packit 6c4009
                                subconfig)
Packit 6c4009
Packit 6c4009
    def compiler_installdir(self, config):
Packit 6c4009
        """Return the directory in which to install a compiler."""
Packit 6c4009
        return os.path.join(self.installdir, 'compilers', config)
Packit 6c4009
Packit 6c4009
    def compiler_bindir(self, config):
Packit 6c4009
        """Return the directory in which to find compiler binaries."""
Packit 6c4009
        return os.path.join(self.compiler_installdir(config), 'bin')
Packit 6c4009
Packit 6c4009
    def compiler_sysroot(self, config):
Packit 6c4009
        """Return the sysroot directory for a compiler."""
Packit 6c4009
        return os.path.join(self.compiler_installdir(config), 'sysroot')
Packit 6c4009
Packit 6c4009
    def glibc_installdir(self, config):
Packit 6c4009
        """Return the directory in which to install glibc."""
Packit 6c4009
        return os.path.join(self.installdir, 'glibcs', config)
Packit 6c4009
Packit 6c4009
    def run_builds(self, action, configs):
Packit 6c4009
        """Run the requested builds."""
Packit 6c4009
        if action == 'checkout':
Packit 6c4009
            self.checkout(configs)
Packit 6c4009
            return
Packit 6c4009
        if action == 'bot-cycle':
Packit 6c4009
            if configs:
Packit 6c4009
                print('error: configurations specified for bot-cycle')
Packit 6c4009
                exit(1)
Packit 6c4009
            self.bot_cycle()
Packit 6c4009
            return
Packit 6c4009
        if action == 'bot':
Packit 6c4009
            if configs:
Packit 6c4009
                print('error: configurations specified for bot')
Packit 6c4009
                exit(1)
Packit 6c4009
            self.bot()
Packit 6c4009
            return
Packit 6c4009
        if action == 'host-libraries' and configs:
Packit 6c4009
            print('error: configurations specified for host-libraries')
Packit 6c4009
            exit(1)
Packit 6c4009
        self.clear_last_build_state(action)
Packit 6c4009
        build_time = datetime.datetime.utcnow()
Packit 6c4009
        if action == 'host-libraries':
Packit 6c4009
            build_components = ('gmp', 'mpfr', 'mpc')
Packit 6c4009
            old_components = ()
Packit 6c4009
            old_versions = {}
Packit 6c4009
            self.build_host_libraries()
Packit 6c4009
        elif action == 'compilers':
Packit 6c4009
            build_components = ('binutils', 'gcc', 'glibc', 'linux', 'mig',
Packit 6c4009
                                'gnumach', 'hurd')
Packit 6c4009
            old_components = ('gmp', 'mpfr', 'mpc')
Packit 6c4009
            old_versions = self.build_state['host-libraries']['build-versions']
Packit 6c4009
            self.build_compilers(configs)
Packit 6c4009
        else:
Packit 6c4009
            build_components = ('glibc',)
Packit 6c4009
            old_components = ('gmp', 'mpfr', 'mpc', 'binutils', 'gcc', 'linux',
Packit 6c4009
                              'mig', 'gnumach', 'hurd')
Packit 6c4009
            old_versions = self.build_state['compilers']['build-versions']
Packit 6c4009
            self.build_glibcs(configs)
Packit 6c4009
        self.write_files()
Packit 6c4009
        self.do_build()
Packit 6c4009
        if configs:
Packit 6c4009
            # Partial build, do not update stored state.
Packit 6c4009
            return
Packit 6c4009
        build_versions = {}
Packit 6c4009
        for k in build_components:
Packit 6c4009
            if k in self.versions:
Packit 6c4009
                build_versions[k] = {'version': self.versions[k]['version'],
Packit 6c4009
                                     'revision': self.versions[k]['revision']}
Packit 6c4009
        for k in old_components:
Packit 6c4009
            if k in old_versions:
Packit 6c4009
                build_versions[k] = {'version': old_versions[k]['version'],
Packit 6c4009
                                     'revision': old_versions[k]['revision']}
Packit 6c4009
        self.update_build_state(action, build_time, build_versions)
Packit 6c4009
Packit 6c4009
    @staticmethod
Packit 6c4009
    def remove_dirs(*args):
Packit 6c4009
        """Remove directories and their contents if they exist."""
Packit 6c4009
        for dir in args:
Packit 6c4009
            shutil.rmtree(dir, ignore_errors=True)
Packit 6c4009
Packit 6c4009
    @staticmethod
Packit 6c4009
    def remove_recreate_dirs(*args):
Packit 6c4009
        """Remove directories if they exist, and create them as empty."""
Packit 6c4009
        Context.remove_dirs(*args)
Packit 6c4009
        for dir in args:
Packit 6c4009
            os.makedirs(dir, exist_ok=True)
Packit 6c4009
Packit 6c4009
    def add_makefile_cmdlist(self, target, cmdlist, logsdir):
Packit 6c4009
        """Add makefile text for a list of commands."""
Packit 6c4009
        commands = cmdlist.makefile_commands(self.wrapper, logsdir)
Packit 6c4009
        self.makefile_pieces.append('all: %s\n.PHONY: %s\n%s:\n%s\n' %
Packit 6c4009
                                    (target, target, target, commands))
Packit 6c4009
        self.status_log_list.extend(cmdlist.status_logs(logsdir))
Packit 6c4009
Packit 6c4009
    def write_files(self):
Packit 6c4009
        """Write out the Makefile and wrapper script."""
Packit 6c4009
        mftext = ''.join(self.makefile_pieces)
Packit 6c4009
        with open(self.makefile, 'w') as f:
Packit 6c4009
            f.write(mftext)
Packit 6c4009
        wrapper_text = (
Packit 6c4009
            '#!/bin/sh\n'
Packit 6c4009
            'prev_base=$1\n'
Packit 6c4009
            'this_base=$2\n'
Packit 6c4009
            'desc=$3\n'
Packit 6c4009
            'dir=$4\n'
Packit 6c4009
            'path=$5\n'
Packit 6c4009
            'shift 5\n'
Packit 6c4009
            'prev_status=$prev_base-status.txt\n'
Packit 6c4009
            'this_status=$this_base-status.txt\n'
Packit 6c4009
            'this_log=$this_base-log.txt\n'
Packit 6c4009
            'date > "$this_log"\n'
Packit 6c4009
            'echo >> "$this_log"\n'
Packit 6c4009
            'echo "Description: $desc" >> "$this_log"\n'
Packit 6c4009
            'printf "%s" "Command:" >> "$this_log"\n'
Packit 6c4009
            'for word in "$@"; do\n'
Packit 6c4009
            '  if expr "$word" : "[]+,./0-9@A-Z_a-z-]\\\\{1,\\\\}\\$" > /dev/null; then\n'
Packit 6c4009
            '    printf " %s" "$word"\n'
Packit 6c4009
            '  else\n'
Packit 6c4009
            '    printf " \'"\n'
Packit 6c4009
            '    printf "%s" "$word" | sed -e "s/\'/\'\\\\\\\\\'\'/"\n'
Packit 6c4009
            '    printf "\'"\n'
Packit 6c4009
            '  fi\n'
Packit 6c4009
            'done >> "$this_log"\n'
Packit 6c4009
            'echo >> "$this_log"\n'
Packit 6c4009
            'echo "Directory: $dir" >> "$this_log"\n'
Packit 6c4009
            'echo "Path addition: $path" >> "$this_log"\n'
Packit 6c4009
            'echo >> "$this_log"\n'
Packit 6c4009
            'record_status ()\n'
Packit 6c4009
            '{\n'
Packit 6c4009
            '  echo >> "$this_log"\n'
Packit 6c4009
            '  echo "$1: $desc" > "$this_status"\n'
Packit 6c4009
            '  echo "$1: $desc" >> "$this_log"\n'
Packit 6c4009
            '  echo >> "$this_log"\n'
Packit 6c4009
            '  date >> "$this_log"\n'
Packit 6c4009
            '  echo "$1: $desc"\n'
Packit 6c4009
            '  exit 0\n'
Packit 6c4009
            '}\n'
Packit 6c4009
            'check_error ()\n'
Packit 6c4009
            '{\n'
Packit 6c4009
            '  if [ "$1" != "0" ]; then\n'
Packit 6c4009
            '    record_status FAIL\n'
Packit 6c4009
            '  fi\n'
Packit 6c4009
            '}\n'
Packit 6c4009
            'if [ "$prev_base" ] && ! grep -q "^PASS" "$prev_status"; then\n'
Packit 6c4009
            '    record_status UNRESOLVED\n'
Packit 6c4009
            'fi\n'
Packit 6c4009
            'if [ "$dir" ]; then\n'
Packit 6c4009
            '  cd "$dir"\n'
Packit 6c4009
            '  check_error "$?"\n'
Packit 6c4009
            'fi\n'
Packit 6c4009
            'if [ "$path" ]; then\n'
Packit 6c4009
            '  PATH=$path:$PATH\n'
Packit 6c4009
            'fi\n'
Packit 6c4009
            '"$@" < /dev/null >> "$this_log" 2>&1\n'
Packit 6c4009
            'check_error "$?"\n'
Packit 6c4009
            'record_status PASS\n')
Packit 6c4009
        with open(self.wrapper, 'w') as f:
Packit 6c4009
            f.write(wrapper_text)
Packit 6c4009
        # Mode 0o755.
Packit 6c4009
        mode_exec = (stat.S_IRWXU|stat.S_IRGRP|stat.S_IXGRP|
Packit 6c4009
                     stat.S_IROTH|stat.S_IXOTH)
Packit 6c4009
        os.chmod(self.wrapper, mode_exec)
Packit 6c4009
        save_logs_text = (
Packit 6c4009
            '#!/bin/sh\n'
Packit 6c4009
            'if ! [ -f tests.sum ]; then\n'
Packit 6c4009
            '  echo "No test summary available."\n'
Packit 6c4009
            '  exit 0\n'
Packit 6c4009
            'fi\n'
Packit 6c4009
            'save_file ()\n'
Packit 6c4009
            '{\n'
Packit 6c4009
            '  echo "Contents of $1:"\n'
Packit 6c4009
            '  echo\n'
Packit 6c4009
            '  cat "$1"\n'
Packit 6c4009
            '  echo\n'
Packit 6c4009
            '  echo "End of contents of $1."\n'
Packit 6c4009
            '  echo\n'
Packit 6c4009
            '}\n'
Packit 6c4009
            'save_file tests.sum\n'
Packit 6c4009
            'non_pass_tests=$(grep -v "^PASS: " tests.sum | sed -e "s/^PASS: //")\n'
Packit 6c4009
            'for t in $non_pass_tests; do\n'
Packit 6c4009
            '  if [ -f "$t.out" ]; then\n'
Packit 6c4009
            '    save_file "$t.out"\n'
Packit 6c4009
            '  fi\n'
Packit 6c4009
            'done\n')
Packit 6c4009
        with open(self.save_logs, 'w') as f:
Packit 6c4009
            f.write(save_logs_text)
Packit 6c4009
        os.chmod(self.save_logs, mode_exec)
Packit 6c4009
Packit 6c4009
    def do_build(self):
Packit 6c4009
        """Do the actual build."""
Packit 6c4009
        cmd = ['make', '-j%d' % self.parallelism]
Packit 6c4009
        subprocess.run(cmd, cwd=self.builddir, check=True)
Packit 6c4009
Packit 6c4009
    def build_host_libraries(self):
Packit 6c4009
        """Build the host libraries."""
Packit 6c4009
        installdir = self.host_libraries_installdir
Packit 6c4009
        builddir = os.path.join(self.builddir, 'host-libraries')
Packit 6c4009
        logsdir = os.path.join(self.logsdir, 'host-libraries')
Packit 6c4009
        self.remove_recreate_dirs(installdir, builddir, logsdir)
Packit 6c4009
        cmdlist = CommandList('host-libraries', self.keep)
Packit 6c4009
        self.build_host_library(cmdlist, 'gmp')
Packit 6c4009
        self.build_host_library(cmdlist, 'mpfr',
Packit 6c4009
                                ['--with-gmp=%s' % installdir])
Packit 6c4009
        self.build_host_library(cmdlist, 'mpc',
Packit 6c4009
                                ['--with-gmp=%s' % installdir,
Packit 6c4009
                                '--with-mpfr=%s' % installdir])
Packit 6c4009
        cmdlist.add_command('done', ['touch', os.path.join(installdir, 'ok')])
Packit 6c4009
        self.add_makefile_cmdlist('host-libraries', cmdlist, logsdir)
Packit 6c4009
Packit 6c4009
    def build_host_library(self, cmdlist, lib, extra_opts=None):
Packit 6c4009
        """Build one host library."""
Packit 6c4009
        srcdir = self.component_srcdir(lib)
Packit 6c4009
        builddir = self.component_builddir('host-libraries', None, lib)
Packit 6c4009
        installdir = self.host_libraries_installdir
Packit 6c4009
        cmdlist.push_subdesc(lib)
Packit 6c4009
        cmdlist.create_use_dir(builddir)
Packit 6c4009
        cfg_cmd = [os.path.join(srcdir, 'configure'),
Packit 6c4009
                   '--prefix=%s' % installdir,
Packit 6c4009
                   '--disable-shared']
Packit 6c4009
        if extra_opts:
Packit 6c4009
            cfg_cmd.extend (extra_opts)
Packit 6c4009
        cmdlist.add_command('configure', cfg_cmd)
Packit 6c4009
        cmdlist.add_command('build', ['make'])
Packit 6c4009
        cmdlist.add_command('check', ['make', 'check'])
Packit 6c4009
        cmdlist.add_command('install', ['make', 'install'])
Packit 6c4009
        cmdlist.cleanup_dir()
Packit 6c4009
        cmdlist.pop_subdesc()
Packit 6c4009
Packit 6c4009
    def build_compilers(self, configs):
Packit 6c4009
        """Build the compilers."""
Packit 6c4009
        if not configs:
Packit 6c4009
            self.remove_dirs(os.path.join(self.builddir, 'compilers'))
Packit 6c4009
            self.remove_dirs(os.path.join(self.installdir, 'compilers'))
Packit 6c4009
            self.remove_dirs(os.path.join(self.logsdir, 'compilers'))
Packit 6c4009
            configs = sorted(self.configs.keys())
Packit 6c4009
        for c in configs:
Packit 6c4009
            self.configs[c].build()
Packit 6c4009
Packit 6c4009
    def build_glibcs(self, configs):
Packit 6c4009
        """Build the glibcs."""
Packit 6c4009
        if not configs:
Packit 6c4009
            self.remove_dirs(os.path.join(self.builddir, 'glibcs'))
Packit 6c4009
            self.remove_dirs(os.path.join(self.installdir, 'glibcs'))
Packit 6c4009
            self.remove_dirs(os.path.join(self.logsdir, 'glibcs'))
Packit 6c4009
            configs = sorted(self.glibc_configs.keys())
Packit 6c4009
        for c in configs:
Packit 6c4009
            self.glibc_configs[c].build()
Packit 6c4009
Packit 6c4009
    def load_versions_json(self):
Packit 6c4009
        """Load information about source directory versions."""
Packit 6c4009
        if not os.access(self.versions_json, os.F_OK):
Packit 6c4009
            self.versions = {}
Packit 6c4009
            return
Packit 6c4009
        with open(self.versions_json, 'r') as f:
Packit 6c4009
            self.versions = json.load(f)
Packit 6c4009
Packit 6c4009
    def store_json(self, data, filename):
Packit 6c4009
        """Store information in a JSON file."""
Packit 6c4009
        filename_tmp = filename + '.tmp'
Packit 6c4009
        with open(filename_tmp, 'w') as f:
Packit 6c4009
            json.dump(data, f, indent=2, sort_keys=True)
Packit 6c4009
        os.rename(filename_tmp, filename)
Packit 6c4009
Packit 6c4009
    def store_versions_json(self):
Packit 6c4009
        """Store information about source directory versions."""
Packit 6c4009
        self.store_json(self.versions, self.versions_json)
Packit 6c4009
Packit 6c4009
    def set_component_version(self, component, version, explicit, revision):
Packit 6c4009
        """Set the version information for a component."""
Packit 6c4009
        self.versions[component] = {'version': version,
Packit 6c4009
                                    'explicit': explicit,
Packit 6c4009
                                    'revision': revision}
Packit 6c4009
        self.store_versions_json()
Packit 6c4009
Packit 6c4009
    def checkout(self, versions):
Packit 6c4009
        """Check out the desired component versions."""
Packit 6c4009
        default_versions = {'binutils': 'vcs-2.31',
Packit 6c4009
                            'gcc': 'vcs-8',
Packit 6c4009
                            'glibc': 'vcs-mainline',
Packit 6c4009
                            'gmp': '6.1.2',
Packit 6c4009
                            'linux': '4.17',
Packit 6c4009
                            'mpc': '1.1.0',
Packit 6c4009
                            'mpfr': '4.0.1',
Packit 6c4009
                            'mig': 'vcs-mainline',
Packit 6c4009
                            'gnumach': 'vcs-mainline',
Packit 6c4009
                            'hurd': 'vcs-mainline'}
Packit 6c4009
        use_versions = {}
Packit 6c4009
        explicit_versions = {}
Packit 6c4009
        for v in versions:
Packit 6c4009
            found_v = False
Packit 6c4009
            for k in default_versions.keys():
Packit 6c4009
                kx = k + '-'
Packit 6c4009
                if v.startswith(kx):
Packit 6c4009
                    vx = v[len(kx):]
Packit 6c4009
                    if k in use_versions:
Packit 6c4009
                        print('error: multiple versions for %s' % k)
Packit 6c4009
                        exit(1)
Packit 6c4009
                    use_versions[k] = vx
Packit 6c4009
                    explicit_versions[k] = True
Packit 6c4009
                    found_v = True
Packit 6c4009
                    break
Packit 6c4009
            if not found_v:
Packit 6c4009
                print('error: unknown component in %s' % v)
Packit 6c4009
                exit(1)
Packit 6c4009
        for k in default_versions.keys():
Packit 6c4009
            if k not in use_versions:
Packit 6c4009
                if k in self.versions and self.versions[k]['explicit']:
Packit 6c4009
                    use_versions[k] = self.versions[k]['version']
Packit 6c4009
                    explicit_versions[k] = True
Packit 6c4009
                else:
Packit 6c4009
                    use_versions[k] = default_versions[k]
Packit 6c4009
                    explicit_versions[k] = False
Packit 6c4009
        os.makedirs(self.srcdir, exist_ok=True)
Packit 6c4009
        for k in sorted(default_versions.keys()):
Packit 6c4009
            update = os.access(self.component_srcdir(k), os.F_OK)
Packit 6c4009
            v = use_versions[k]
Packit 6c4009
            if (update and
Packit 6c4009
                k in self.versions and
Packit 6c4009
                v != self.versions[k]['version']):
Packit 6c4009
                if not self.replace_sources:
Packit 6c4009
                    print('error: version of %s has changed from %s to %s, '
Packit 6c4009
                          'use --replace-sources to check out again' %
Packit 6c4009
                          (k, self.versions[k]['version'], v))
Packit 6c4009
                    exit(1)
Packit 6c4009
                shutil.rmtree(self.component_srcdir(k))
Packit 6c4009
                update = False
Packit 6c4009
            if v.startswith('vcs-'):
Packit 6c4009
                revision = self.checkout_vcs(k, v[4:], update)
Packit 6c4009
            else:
Packit 6c4009
                self.checkout_tar(k, v, update)
Packit 6c4009
                revision = v
Packit 6c4009
            self.set_component_version(k, v, explicit_versions[k], revision)
Packit 6c4009
        if self.get_script_text() != self.script_text:
Packit 6c4009
            # Rerun the checkout process in case the updated script
Packit 6c4009
            # uses different default versions or new components.
Packit 6c4009
            self.exec_self()
Packit 6c4009
Packit 6c4009
    def checkout_vcs(self, component, version, update):
Packit 6c4009
        """Check out the given version of the given component from version
Packit 6c4009
        control.  Return a revision identifier."""
Packit 6c4009
        if component == 'binutils':
Packit 6c4009
            git_url = 'git://sourceware.org/git/binutils-gdb.git'
Packit 6c4009
            if version == 'mainline':
Packit 6c4009
                git_branch = 'master'
Packit 6c4009
            else:
Packit 6c4009
                trans = str.maketrans({'.': '_'})
Packit 6c4009
                git_branch = 'binutils-%s-branch' % version.translate(trans)
Packit 6c4009
            return self.git_checkout(component, git_url, git_branch, update)
Packit 6c4009
        elif component == 'gcc':
Packit 6c4009
            if version == 'mainline':
Packit 6c4009
                branch = 'trunk'
Packit 6c4009
            else:
Packit 6c4009
                trans = str.maketrans({'.': '_'})
Packit 6c4009
                branch = 'branches/gcc-%s-branch' % version.translate(trans)
Packit 6c4009
            svn_url = 'svn://gcc.gnu.org/svn/gcc/%s' % branch
Packit 6c4009
            return self.gcc_checkout(svn_url, update)
Packit 6c4009
        elif component == 'glibc':
Packit 6c4009
            git_url = 'git://sourceware.org/git/glibc.git'
Packit 6c4009
            if version == 'mainline':
Packit 6c4009
                git_branch = 'master'
Packit 6c4009
            else:
Packit 6c4009
                git_branch = 'release/%s/master' % version
Packit 6c4009
            r = self.git_checkout(component, git_url, git_branch, update)
Packit 6c4009
            self.fix_glibc_timestamps()
Packit 6c4009
            return r
Packit 6c4009
        elif component == 'gnumach':
Packit 6c4009
            git_url = 'git://git.savannah.gnu.org/hurd/gnumach.git'
Packit 6c4009
            git_branch = 'master'
Packit 6c4009
            r = self.git_checkout(component, git_url, git_branch, update)
Packit 6c4009
            subprocess.run(['autoreconf', '-i'],
Packit 6c4009
                           cwd=self.component_srcdir(component), check=True)
Packit 6c4009
            return r
Packit 6c4009
        elif component == 'mig':
Packit 6c4009
            git_url = 'git://git.savannah.gnu.org/hurd/mig.git'
Packit 6c4009
            git_branch = 'master'
Packit 6c4009
            r = self.git_checkout(component, git_url, git_branch, update)
Packit 6c4009
            subprocess.run(['autoreconf', '-i'],
Packit 6c4009
                           cwd=self.component_srcdir(component), check=True)
Packit 6c4009
            return r
Packit 6c4009
        elif component == 'hurd':
Packit 6c4009
            git_url = 'git://git.savannah.gnu.org/hurd/hurd.git'
Packit 6c4009
            git_branch = 'master'
Packit 6c4009
            r = self.git_checkout(component, git_url, git_branch, update)
Packit 6c4009
            subprocess.run(['autoconf'],
Packit 6c4009
                           cwd=self.component_srcdir(component), check=True)
Packit 6c4009
            return r
Packit 6c4009
        else:
Packit 6c4009
            print('error: component %s coming from VCS' % component)
Packit 6c4009
            exit(1)
Packit 6c4009
Packit 6c4009
    def git_checkout(self, component, git_url, git_branch, update):
Packit 6c4009
        """Check out a component from git.  Return a commit identifier."""
Packit 6c4009
        if update:
Packit 6c4009
            subprocess.run(['git', 'remote', 'prune', 'origin'],
Packit 6c4009
                           cwd=self.component_srcdir(component), check=True)
Packit 6c4009
            if self.replace_sources:
Packit 6c4009
                subprocess.run(['git', 'clean', '-dxfq'],
Packit 6c4009
                               cwd=self.component_srcdir(component), check=True)
Packit 6c4009
            subprocess.run(['git', 'pull', '-q'],
Packit 6c4009
                           cwd=self.component_srcdir(component), check=True)
Packit 6c4009
        else:
Packit 6c4009
            subprocess.run(['git', 'clone', '-q', '-b', git_branch, git_url,
Packit 6c4009
                            self.component_srcdir(component)], check=True)
Packit 6c4009
        r = subprocess.run(['git', 'rev-parse', 'HEAD'],
Packit 6c4009
                           cwd=self.component_srcdir(component),
Packit 6c4009
                           stdout=subprocess.PIPE,
Packit 6c4009
                           check=True, universal_newlines=True).stdout
Packit 6c4009
        return r.rstrip()
Packit 6c4009
Packit 6c4009
    def fix_glibc_timestamps(self):
Packit 6c4009
        """Fix timestamps in a glibc checkout."""
Packit 6c4009
        # Ensure that builds do not try to regenerate generated files
Packit 6c4009
        # in the source tree.
Packit 6c4009
        srcdir = self.component_srcdir('glibc')
Packit 6c4009
        for dirpath, dirnames, filenames in os.walk(srcdir):
Packit 6c4009
            for f in filenames:
Packit 6c4009
                if (f == 'configure' or
Packit 6c4009
                    f == 'preconfigure' or
Packit 6c4009
                    f.endswith('-kw.h')):
Packit 6c4009
                    to_touch = os.path.join(dirpath, f)
Packit 6c4009
                    subprocess.run(['touch', to_touch], check=True)
Packit 6c4009
Packit 6c4009
    def gcc_checkout(self, svn_url, update):
Packit 6c4009
        """Check out GCC from SVN.  Return the revision number."""
Packit 6c4009
        if not update:
Packit 6c4009
            subprocess.run(['svn', 'co', '-q', svn_url,
Packit 6c4009
                            self.component_srcdir('gcc')], check=True)
Packit 6c4009
        subprocess.run(['contrib/gcc_update', '--silent'],
Packit 6c4009
                       cwd=self.component_srcdir('gcc'), check=True)
Packit 6c4009
        r = subprocess.run(['svnversion', self.component_srcdir('gcc')],
Packit 6c4009
                           stdout=subprocess.PIPE,
Packit 6c4009
                           check=True, universal_newlines=True).stdout
Packit 6c4009
        return r.rstrip()
Packit 6c4009
Packit 6c4009
    def checkout_tar(self, component, version, update):
Packit 6c4009
        """Check out the given version of the given component from a
Packit 6c4009
        tarball."""
Packit 6c4009
        if update:
Packit 6c4009
            return
Packit 6c4009
        url_map = {'binutils': 'https://ftp.gnu.org/gnu/binutils/binutils-%(version)s.tar.bz2',
Packit 6c4009
                   'gcc': 'https://ftp.gnu.org/gnu/gcc/gcc-%(version)s/gcc-%(version)s.tar.gz',
Packit 6c4009
                   'gmp': 'https://ftp.gnu.org/gnu/gmp/gmp-%(version)s.tar.xz',
Packit 6c4009
                   'linux': 'https://www.kernel.org/pub/linux/kernel/v4.x/linux-%(version)s.tar.xz',
Packit 6c4009
                   'mpc': 'https://ftp.gnu.org/gnu/mpc/mpc-%(version)s.tar.gz',
Packit 6c4009
                   'mpfr': 'https://ftp.gnu.org/gnu/mpfr/mpfr-%(version)s.tar.xz',
Packit 6c4009
                   'mig': 'https://ftp.gnu.org/gnu/mig/mig-%(version)s.tar.bz2',
Packit 6c4009
                   'gnumach': 'https://ftp.gnu.org/gnu/gnumach/gnumach-%(version)s.tar.bz2',
Packit 6c4009
                   'hurd': 'https://ftp.gnu.org/gnu/hurd/hurd-%(version)s.tar.bz2'}
Packit 6c4009
        if component not in url_map:
Packit 6c4009
            print('error: component %s coming from tarball' % component)
Packit 6c4009
            exit(1)
Packit 6c4009
        url = url_map[component] % {'version': version}
Packit 6c4009
        filename = os.path.join(self.srcdir, url.split('/')[-1])
Packit 6c4009
        response = urllib.request.urlopen(url)
Packit 6c4009
        data = response.read()
Packit 6c4009
        with open(filename, 'wb') as f:
Packit 6c4009
            f.write(data)
Packit 6c4009
        subprocess.run(['tar', '-C', self.srcdir, '-x', '-f', filename],
Packit 6c4009
                       check=True)
Packit 6c4009
        os.rename(os.path.join(self.srcdir, '%s-%s' % (component, version)),
Packit 6c4009
                  self.component_srcdir(component))
Packit 6c4009
        os.remove(filename)
Packit 6c4009
Packit 6c4009
    def load_build_state_json(self):
Packit 6c4009
        """Load information about the state of previous builds."""
Packit 6c4009
        if os.access(self.build_state_json, os.F_OK):
Packit 6c4009
            with open(self.build_state_json, 'r') as f:
Packit 6c4009
                self.build_state = json.load(f)
Packit 6c4009
        else:
Packit 6c4009
            self.build_state = {}
Packit 6c4009
        for k in ('host-libraries', 'compilers', 'glibcs'):
Packit 6c4009
            if k not in self.build_state:
Packit 6c4009
                self.build_state[k] = {}
Packit 6c4009
            if 'build-time' not in self.build_state[k]:
Packit 6c4009
                self.build_state[k]['build-time'] = ''
Packit 6c4009
            if 'build-versions' not in self.build_state[k]:
Packit 6c4009
                self.build_state[k]['build-versions'] = {}
Packit 6c4009
            if 'build-results' not in self.build_state[k]:
Packit 6c4009
                self.build_state[k]['build-results'] = {}
Packit 6c4009
            if 'result-changes' not in self.build_state[k]:
Packit 6c4009
                self.build_state[k]['result-changes'] = {}
Packit 6c4009
            if 'ever-passed' not in self.build_state[k]:
Packit 6c4009
                self.build_state[k]['ever-passed'] = []
Packit 6c4009
Packit 6c4009
    def store_build_state_json(self):
Packit 6c4009
        """Store information about the state of previous builds."""
Packit 6c4009
        self.store_json(self.build_state, self.build_state_json)
Packit 6c4009
Packit 6c4009
    def clear_last_build_state(self, action):
Packit 6c4009
        """Clear information about the state of part of the build."""
Packit 6c4009
        # We clear the last build time and versions when starting a
Packit 6c4009
        # new build.  The results of the last build are kept around,
Packit 6c4009
        # as comparison is still meaningful if this build is aborted
Packit 6c4009
        # and a new one started.
Packit 6c4009
        self.build_state[action]['build-time'] = ''
Packit 6c4009
        self.build_state[action]['build-versions'] = {}
Packit 6c4009
        self.store_build_state_json()
Packit 6c4009
Packit 6c4009
    def update_build_state(self, action, build_time, build_versions):
Packit 6c4009
        """Update the build state after a build."""
Packit 6c4009
        build_time = build_time.replace(microsecond=0)
Packit 6c4009
        self.build_state[action]['build-time'] = str(build_time)
Packit 6c4009
        self.build_state[action]['build-versions'] = build_versions
Packit 6c4009
        build_results = {}
Packit 6c4009
        for log in self.status_log_list:
Packit 6c4009
            with open(log, 'r') as f:
Packit 6c4009
                log_text = f.read()
Packit 6c4009
            log_text = log_text.rstrip()
Packit 6c4009
            m = re.fullmatch('([A-Z]+): (.*)', log_text)
Packit 6c4009
            result = m.group(1)
Packit 6c4009
            test_name = m.group(2)
Packit 6c4009
            assert test_name not in build_results
Packit 6c4009
            build_results[test_name] = result
Packit 6c4009
        old_build_results = self.build_state[action]['build-results']
Packit 6c4009
        self.build_state[action]['build-results'] = build_results
Packit 6c4009
        result_changes = {}
Packit 6c4009
        all_tests = set(old_build_results.keys()) | set(build_results.keys())
Packit 6c4009
        for t in all_tests:
Packit 6c4009
            if t in old_build_results:
Packit 6c4009
                old_res = old_build_results[t]
Packit 6c4009
            else:
Packit 6c4009
                old_res = '(New test)'
Packit 6c4009
            if t in build_results:
Packit 6c4009
                new_res = build_results[t]
Packit 6c4009
            else:
Packit 6c4009
                new_res = '(Test removed)'
Packit 6c4009
            if old_res != new_res:
Packit 6c4009
                result_changes[t] = '%s -> %s' % (old_res, new_res)
Packit 6c4009
        self.build_state[action]['result-changes'] = result_changes
Packit 6c4009
        old_ever_passed = {t for t in self.build_state[action]['ever-passed']
Packit 6c4009
                           if t in build_results}
Packit 6c4009
        new_passes = {t for t in build_results if build_results[t] == 'PASS'}
Packit 6c4009
        self.build_state[action]['ever-passed'] = sorted(old_ever_passed |
Packit 6c4009
                                                         new_passes)
Packit 6c4009
        self.store_build_state_json()
Packit 6c4009
Packit 6c4009
    def load_bot_config_json(self):
Packit 6c4009
        """Load bot configuration."""
Packit 6c4009
        with open(self.bot_config_json, 'r') as f:
Packit 6c4009
            self.bot_config = json.load(f)
Packit 6c4009
Packit 6c4009
    def part_build_old(self, action, delay):
Packit 6c4009
        """Return whether the last build for a given action was at least a
Packit 6c4009
        given number of seconds ago, or does not have a time recorded."""
Packit 6c4009
        old_time_str = self.build_state[action]['build-time']
Packit 6c4009
        if not old_time_str:
Packit 6c4009
            return True
Packit 6c4009
        old_time = datetime.datetime.strptime(old_time_str,
Packit 6c4009
                                              '%Y-%m-%d %H:%M:%S')
Packit 6c4009
        new_time = datetime.datetime.utcnow()
Packit 6c4009
        delta = new_time - old_time
Packit 6c4009
        return delta.total_seconds() >= delay
Packit 6c4009
Packit 6c4009
    def bot_cycle(self):
Packit 6c4009
        """Run a single round of checkout and builds."""
Packit 6c4009
        print('Bot cycle starting %s.' % str(datetime.datetime.utcnow()))
Packit 6c4009
        self.load_bot_config_json()
Packit 6c4009
        actions = ('host-libraries', 'compilers', 'glibcs')
Packit 6c4009
        self.bot_run_self(['--replace-sources'], 'checkout')
Packit 6c4009
        self.load_versions_json()
Packit 6c4009
        if self.get_script_text() != self.script_text:
Packit 6c4009
            print('Script changed, re-execing.')
Packit 6c4009
            # On script change, all parts of the build should be rerun.
Packit 6c4009
            for a in actions:
Packit 6c4009
                self.clear_last_build_state(a)
Packit 6c4009
            self.exec_self()
Packit 6c4009
        check_components = {'host-libraries': ('gmp', 'mpfr', 'mpc'),
Packit 6c4009
                            'compilers': ('binutils', 'gcc', 'glibc', 'linux',
Packit 6c4009
                                          'mig', 'gnumach', 'hurd'),
Packit 6c4009
                            'glibcs': ('glibc',)}
Packit 6c4009
        must_build = {}
Packit 6c4009
        for a in actions:
Packit 6c4009
            build_vers = self.build_state[a]['build-versions']
Packit 6c4009
            must_build[a] = False
Packit 6c4009
            if not self.build_state[a]['build-time']:
Packit 6c4009
                must_build[a] = True
Packit 6c4009
            old_vers = {}
Packit 6c4009
            new_vers = {}
Packit 6c4009
            for c in check_components[a]:
Packit 6c4009
                if c in build_vers:
Packit 6c4009
                    old_vers[c] = build_vers[c]
Packit 6c4009
                new_vers[c] = {'version': self.versions[c]['version'],
Packit 6c4009
                               'revision': self.versions[c]['revision']}
Packit 6c4009
            if new_vers == old_vers:
Packit 6c4009
                print('Versions for %s unchanged.' % a)
Packit 6c4009
            else:
Packit 6c4009
                print('Versions changed or rebuild forced for %s.' % a)
Packit 6c4009
                if a == 'compilers' and not self.part_build_old(
Packit 6c4009
                        a, self.bot_config['compilers-rebuild-delay']):
Packit 6c4009
                    print('Not requiring rebuild of compilers this soon.')
Packit 6c4009
                else:
Packit 6c4009
                    must_build[a] = True
Packit 6c4009
        if must_build['host-libraries']:
Packit 6c4009
            must_build['compilers'] = True
Packit 6c4009
        if must_build['compilers']:
Packit 6c4009
            must_build['glibcs'] = True
Packit 6c4009
        for a in actions:
Packit 6c4009
            if must_build[a]:
Packit 6c4009
                print('Must rebuild %s.' % a)
Packit 6c4009
                self.clear_last_build_state(a)
Packit 6c4009
            else:
Packit 6c4009
                print('No need to rebuild %s.' % a)
Packit 6c4009
        if os.access(self.logsdir, os.F_OK):
Packit 6c4009
            shutil.rmtree(self.logsdir_old, ignore_errors=True)
Packit 6c4009
            shutil.copytree(self.logsdir, self.logsdir_old)
Packit 6c4009
        for a in actions:
Packit 6c4009
            if must_build[a]:
Packit 6c4009
                build_time = datetime.datetime.utcnow()
Packit 6c4009
                print('Rebuilding %s at %s.' % (a, str(build_time)))
Packit 6c4009
                self.bot_run_self([], a)
Packit 6c4009
                self.load_build_state_json()
Packit 6c4009
                self.bot_build_mail(a, build_time)
Packit 6c4009
        print('Bot cycle done at %s.' % str(datetime.datetime.utcnow()))
Packit 6c4009
Packit 6c4009
    def bot_build_mail(self, action, build_time):
Packit 6c4009
        """Send email with the results of a build."""
Packit 6c4009
        if not ('email-from' in self.bot_config and
Packit 6c4009
                'email-server' in self.bot_config and
Packit 6c4009
                'email-subject' in self.bot_config and
Packit 6c4009
                'email-to' in self.bot_config):
Packit 6c4009
            if not self.email_warning:
Packit 6c4009
                print("Email not configured, not sending.")
Packit 6c4009
                self.email_warning = True
Packit 6c4009
            return
Packit 6c4009
Packit 6c4009
        build_time = build_time.replace(microsecond=0)
Packit 6c4009
        subject = (self.bot_config['email-subject'] %
Packit 6c4009
                   {'action': action,
Packit 6c4009
                    'build-time': str(build_time)})
Packit 6c4009
        results = self.build_state[action]['build-results']
Packit 6c4009
        changes = self.build_state[action]['result-changes']
Packit 6c4009
        ever_passed = set(self.build_state[action]['ever-passed'])
Packit 6c4009
        versions = self.build_state[action]['build-versions']
Packit 6c4009
        new_regressions = {k for k in changes if changes[k] == 'PASS -> FAIL'}
Packit 6c4009
        all_regressions = {k for k in ever_passed if results[k] == 'FAIL'}
Packit 6c4009
        all_fails = {k for k in results if results[k] == 'FAIL'}
Packit 6c4009
        if new_regressions:
Packit 6c4009
            new_reg_list = sorted(['FAIL: %s' % k for k in new_regressions])
Packit 6c4009
            new_reg_text = ('New regressions:\n\n%s\n\n' %
Packit 6c4009
                            '\n'.join(new_reg_list))
Packit 6c4009
        else:
Packit 6c4009
            new_reg_text = ''
Packit 6c4009
        if all_regressions:
Packit 6c4009
            all_reg_list = sorted(['FAIL: %s' % k for k in all_regressions])
Packit 6c4009
            all_reg_text = ('All regressions:\n\n%s\n\n' %
Packit 6c4009
                            '\n'.join(all_reg_list))
Packit 6c4009
        else:
Packit 6c4009
            all_reg_text = ''
Packit 6c4009
        if all_fails:
Packit 6c4009
            all_fail_list = sorted(['FAIL: %s' % k for k in all_fails])
Packit 6c4009
            all_fail_text = ('All failures:\n\n%s\n\n' %
Packit 6c4009
                             '\n'.join(all_fail_list))
Packit 6c4009
        else:
Packit 6c4009
            all_fail_text = ''
Packit 6c4009
        if changes:
Packit 6c4009
            changes_list = sorted(changes.keys())
Packit 6c4009
            changes_list = ['%s: %s' % (changes[k], k) for k in changes_list]
Packit 6c4009
            changes_text = ('All changed results:\n\n%s\n\n' %
Packit 6c4009
                            '\n'.join(changes_list))
Packit 6c4009
        else:
Packit 6c4009
            changes_text = ''
Packit 6c4009
        results_text = (new_reg_text + all_reg_text + all_fail_text +
Packit 6c4009
                        changes_text)
Packit 6c4009
        if not results_text:
Packit 6c4009
            results_text = 'Clean build with unchanged results.\n\n'
Packit 6c4009
        versions_list = sorted(versions.keys())
Packit 6c4009
        versions_list = ['%s: %s (%s)' % (k, versions[k]['version'],
Packit 6c4009
                                          versions[k]['revision'])
Packit 6c4009
                         for k in versions_list]
Packit 6c4009
        versions_text = ('Component versions for this build:\n\n%s\n' %
Packit 6c4009
                         '\n'.join(versions_list))
Packit 6c4009
        body_text = results_text + versions_text
Packit 6c4009
        msg = email.mime.text.MIMEText(body_text)
Packit 6c4009
        msg['Subject'] = subject
Packit 6c4009
        msg['From'] = self.bot_config['email-from']
Packit 6c4009
        msg['To'] = self.bot_config['email-to']
Packit 6c4009
        msg['Message-ID'] = email.utils.make_msgid()
Packit 6c4009
        msg['Date'] = email.utils.format_datetime(datetime.datetime.utcnow())
Packit 6c4009
        with smtplib.SMTP(self.bot_config['email-server']) as s:
Packit 6c4009
            s.send_message(msg)
Packit 6c4009
Packit 6c4009
    def bot_run_self(self, opts, action, check=True):
Packit 6c4009
        """Run a copy of this script with given options."""
Packit 6c4009
        cmd = [sys.executable, sys.argv[0], '--keep=none',
Packit 6c4009
               '-j%d' % self.parallelism]
Packit 6c4009
        cmd.extend(opts)
Packit 6c4009
        cmd.extend([self.topdir, action])
Packit 6c4009
        sys.stdout.flush()
Packit 6c4009
        subprocess.run(cmd, check=check)
Packit 6c4009
Packit 6c4009
    def bot(self):
Packit 6c4009
        """Run repeated rounds of checkout and builds."""
Packit 6c4009
        while True:
Packit 6c4009
            self.load_bot_config_json()
Packit 6c4009
            if not self.bot_config['run']:
Packit 6c4009
                print('Bot exiting by request.')
Packit 6c4009
                exit(0)
Packit 6c4009
            self.bot_run_self([], 'bot-cycle', check=False)
Packit 6c4009
            self.load_bot_config_json()
Packit 6c4009
            if not self.bot_config['run']:
Packit 6c4009
                print('Bot exiting by request.')
Packit 6c4009
                exit(0)
Packit 6c4009
            time.sleep(self.bot_config['delay'])
Packit 6c4009
            if self.get_script_text() != self.script_text:
Packit 6c4009
                print('Script changed, bot re-execing.')
Packit 6c4009
                self.exec_self()
Packit 6c4009
Packit 6c4009
Packit 6c4009
class Config(object):
Packit 6c4009
    """A configuration for building a compiler and associated libraries."""
Packit 6c4009
Packit 6c4009
    def __init__(self, ctx, arch, os_name, variant=None, gcc_cfg=None,
Packit 6c4009
                 first_gcc_cfg=None, glibcs=None, extra_glibcs=None):
Packit 6c4009
        """Initialize a Config object."""
Packit 6c4009
        self.ctx = ctx
Packit 6c4009
        self.arch = arch
Packit 6c4009
        self.os = os_name
Packit 6c4009
        self.variant = variant
Packit 6c4009
        if variant is None:
Packit 6c4009
            self.name = '%s-%s' % (arch, os_name)
Packit 6c4009
        else:
Packit 6c4009
            self.name = '%s-%s-%s' % (arch, os_name, variant)
Packit 6c4009
        self.triplet = '%s-glibc-%s' % (arch, os_name)
Packit 6c4009
        if gcc_cfg is None:
Packit 6c4009
            self.gcc_cfg = []
Packit 6c4009
        else:
Packit 6c4009
            self.gcc_cfg = gcc_cfg
Packit 6c4009
        if first_gcc_cfg is None:
Packit 6c4009
            self.first_gcc_cfg = []
Packit 6c4009
        else:
Packit 6c4009
            self.first_gcc_cfg = first_gcc_cfg
Packit 6c4009
        if glibcs is None:
Packit 6c4009
            glibcs = [{'variant': variant}]
Packit 6c4009
        if extra_glibcs is None:
Packit 6c4009
            extra_glibcs = []
Packit 6c4009
        glibcs = [Glibc(self, **g) for g in glibcs]
Packit 6c4009
        extra_glibcs = [Glibc(self, **g) for g in extra_glibcs]
Packit 6c4009
        self.all_glibcs = glibcs + extra_glibcs
Packit 6c4009
        self.compiler_glibcs = glibcs
Packit 6c4009
        self.installdir = ctx.compiler_installdir(self.name)
Packit 6c4009
        self.bindir = ctx.compiler_bindir(self.name)
Packit 6c4009
        self.sysroot = ctx.compiler_sysroot(self.name)
Packit 6c4009
        self.builddir = os.path.join(ctx.builddir, 'compilers', self.name)
Packit 6c4009
        self.logsdir = os.path.join(ctx.logsdir, 'compilers', self.name)
Packit 6c4009
Packit 6c4009
    def component_builddir(self, component):
Packit 6c4009
        """Return the directory to use for a (non-glibc) build."""
Packit 6c4009
        return self.ctx.component_builddir('compilers', self.name, component)
Packit 6c4009
Packit 6c4009
    def build(self):
Packit 6c4009
        """Generate commands to build this compiler."""
Packit 6c4009
        self.ctx.remove_recreate_dirs(self.installdir, self.builddir,
Packit 6c4009
                                      self.logsdir)
Packit 6c4009
        cmdlist = CommandList('compilers-%s' % self.name, self.ctx.keep)
Packit 6c4009
        cmdlist.add_command('check-host-libraries',
Packit 6c4009
                            ['test', '-f',
Packit 6c4009
                             os.path.join(self.ctx.host_libraries_installdir,
Packit 6c4009
                                          'ok')])
Packit 6c4009
        cmdlist.use_path(self.bindir)
Packit 6c4009
        self.build_cross_tool(cmdlist, 'binutils', 'binutils',
Packit 6c4009
                              ['--disable-gdb',
Packit 6c4009
                               '--disable-libdecnumber',
Packit 6c4009
                               '--disable-readline',
Packit 6c4009
                               '--disable-sim'])
Packit 6c4009
        if self.os.startswith('linux'):
Packit 6c4009
            self.install_linux_headers(cmdlist)
Packit 6c4009
        self.build_gcc(cmdlist, True)
Packit 6c4009
        if self.os == 'gnu':
Packit 6c4009
            self.install_gnumach_headers(cmdlist)
Packit 6c4009
            self.build_cross_tool(cmdlist, 'mig', 'mig')
Packit 6c4009
            self.install_hurd_headers(cmdlist)
Packit 6c4009
        for g in self.compiler_glibcs:
Packit 6c4009
            cmdlist.push_subdesc('glibc')
Packit 6c4009
            cmdlist.push_subdesc(g.name)
Packit 6c4009
            g.build_glibc(cmdlist, True)
Packit 6c4009
            cmdlist.pop_subdesc()
Packit 6c4009
            cmdlist.pop_subdesc()
Packit 6c4009
        self.build_gcc(cmdlist, False)
Packit 6c4009
        cmdlist.add_command('done', ['touch',
Packit 6c4009
                                     os.path.join(self.installdir, 'ok')])
Packit 6c4009
        self.ctx.add_makefile_cmdlist('compilers-%s' % self.name, cmdlist,
Packit 6c4009
                                      self.logsdir)
Packit 6c4009
Packit 6c4009
    def build_cross_tool(self, cmdlist, tool_src, tool_build, extra_opts=None):
Packit 6c4009
        """Build one cross tool."""
Packit 6c4009
        srcdir = self.ctx.component_srcdir(tool_src)
Packit 6c4009
        builddir = self.component_builddir(tool_build)
Packit 6c4009
        cmdlist.push_subdesc(tool_build)
Packit 6c4009
        cmdlist.create_use_dir(builddir)
Packit 6c4009
        cfg_cmd = [os.path.join(srcdir, 'configure'),
Packit 6c4009
                   '--prefix=%s' % self.installdir,
Packit 6c4009
                   '--build=%s' % self.ctx.build_triplet,
Packit 6c4009
                   '--host=%s' % self.ctx.build_triplet,
Packit 6c4009
                   '--target=%s' % self.triplet,
Packit 6c4009
                   '--with-sysroot=%s' % self.sysroot]
Packit 6c4009
        if extra_opts:
Packit 6c4009
            cfg_cmd.extend(extra_opts)
Packit 6c4009
        cmdlist.add_command('configure', cfg_cmd)
Packit 6c4009
        cmdlist.add_command('build', ['make'])
Packit 6c4009
        # Parallel "make install" for GCC has race conditions that can
Packit 6c4009
        # cause it to fail; see
Packit 6c4009
        # <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=42980>.  Such
Packit 6c4009
        # problems are not known for binutils, but doing the
Packit 6c4009
        # installation in parallel within a particular toolchain build
Packit 6c4009
        # (as opposed to installation of one toolchain from
Packit 6c4009
        # build-many-glibcs.py running in parallel to the installation
Packit 6c4009
        # of other toolchains being built) is not known to be
Packit 6c4009
        # significantly beneficial, so it is simplest just to disable
Packit 6c4009
        # parallel install for cross tools here.
Packit 6c4009
        cmdlist.add_command('install', ['make', '-j1', 'install'])
Packit 6c4009
        cmdlist.cleanup_dir()
Packit 6c4009
        cmdlist.pop_subdesc()
Packit 6c4009
Packit 6c4009
    def install_linux_headers(self, cmdlist):
Packit 6c4009
        """Install Linux kernel headers."""
Packit 6c4009
        arch_map = {'aarch64': 'arm64',
Packit 6c4009
                    'alpha': 'alpha',
Packit 6c4009
                    'arm': 'arm',
Packit 6c4009
                    'hppa': 'parisc',
Packit 6c4009
                    'i486': 'x86',
Packit 6c4009
                    'i586': 'x86',
Packit 6c4009
                    'i686': 'x86',
Packit 6c4009
                    'i786': 'x86',
Packit 6c4009
                    'ia64': 'ia64',
Packit 6c4009
                    'm68k': 'm68k',
Packit 6c4009
                    'microblaze': 'microblaze',
Packit 6c4009
                    'mips': 'mips',
Packit 6c4009
                    'nios2': 'nios2',
Packit 6c4009
                    'powerpc': 'powerpc',
Packit 6c4009
                    's390': 's390',
Packit 6c4009
                    'riscv32': 'riscv',
Packit 6c4009
                    'riscv64': 'riscv',
Packit 6c4009
                    'sh': 'sh',
Packit 6c4009
                    'sparc': 'sparc',
Packit 6c4009
                    'x86_64': 'x86'}
Packit 6c4009
        linux_arch = None
Packit 6c4009
        for k in arch_map:
Packit 6c4009
            if self.arch.startswith(k):
Packit 6c4009
                linux_arch = arch_map[k]
Packit 6c4009
                break
Packit 6c4009
        assert linux_arch is not None
Packit 6c4009
        srcdir = self.ctx.component_srcdir('linux')
Packit 6c4009
        builddir = self.component_builddir('linux')
Packit 6c4009
        headers_dir = os.path.join(self.sysroot, 'usr')
Packit 6c4009
        cmdlist.push_subdesc('linux')
Packit 6c4009
        cmdlist.create_use_dir(builddir)
Packit 6c4009
        cmdlist.add_command('install-headers',
Packit 6c4009
                            ['make', '-C', srcdir, 'O=%s' % builddir,
Packit 6c4009
                             'ARCH=%s' % linux_arch,
Packit 6c4009
                             'INSTALL_HDR_PATH=%s' % headers_dir,
Packit 6c4009
                             'headers_install'])
Packit 6c4009
        cmdlist.cleanup_dir()
Packit 6c4009
        cmdlist.pop_subdesc()
Packit 6c4009
Packit 6c4009
    def install_gnumach_headers(self, cmdlist):
Packit 6c4009
        """Install GNU Mach headers."""
Packit 6c4009
        srcdir = self.ctx.component_srcdir('gnumach')
Packit 6c4009
        builddir = self.component_builddir('gnumach')
Packit 6c4009
        cmdlist.push_subdesc('gnumach')
Packit 6c4009
        cmdlist.create_use_dir(builddir)
Packit 6c4009
        cmdlist.add_command('configure',
Packit 6c4009
                            [os.path.join(srcdir, 'configure'),
Packit 6c4009
                             '--build=%s' % self.ctx.build_triplet,
Packit 6c4009
                             '--host=%s' % self.triplet,
Packit 6c4009
                             '--prefix=',
Packit 6c4009
                             'CC=%s-gcc -nostdlib' % self.triplet])
Packit 6c4009
        cmdlist.add_command('install', ['make', 'DESTDIR=%s' % self.sysroot,
Packit 6c4009
                                        'install-data'])
Packit 6c4009
        cmdlist.cleanup_dir()
Packit 6c4009
        cmdlist.pop_subdesc()
Packit 6c4009
Packit 6c4009
    def install_hurd_headers(self, cmdlist):
Packit 6c4009
        """Install Hurd headers."""
Packit 6c4009
        srcdir = self.ctx.component_srcdir('hurd')
Packit 6c4009
        builddir = self.component_builddir('hurd')
Packit 6c4009
        cmdlist.push_subdesc('hurd')
Packit 6c4009
        cmdlist.create_use_dir(builddir)
Packit 6c4009
        cmdlist.add_command('configure',
Packit 6c4009
                            [os.path.join(srcdir, 'configure'),
Packit 6c4009
                             '--build=%s' % self.ctx.build_triplet,
Packit 6c4009
                             '--host=%s' % self.triplet,
Packit 6c4009
                             '--prefix=',
Packit 6c4009
                             '--disable-profile', '--without-parted',
Packit 6c4009
                             'CC=%s-gcc -nostdlib' % self.triplet])
Packit 6c4009
        cmdlist.add_command('install', ['make', 'prefix=%s' % self.sysroot,
Packit 6c4009
                                        'no_deps=t', 'install-headers'])
Packit 6c4009
        cmdlist.cleanup_dir()
Packit 6c4009
        cmdlist.pop_subdesc()
Packit 6c4009
Packit 6c4009
    def build_gcc(self, cmdlist, bootstrap):
Packit 6c4009
        """Build GCC."""
Packit 6c4009
        # libsanitizer commonly breaks because of glibc header
Packit 6c4009
        # changes, or on unusual targets.  libssp is of little
Packit 6c4009
        # relevance with glibc's own stack checking support.
Packit 6c4009
        # libcilkrts does not support GNU/Hurd (and has been removed
Packit 6c4009
        # in GCC 8, so --disable-libcilkrts can be removed once glibc
Packit 6c4009
        # no longer supports building with older GCC versions).
Packit 6c4009
        cfg_opts = list(self.gcc_cfg)
Packit 6c4009
        cfg_opts += ['--disable-libsanitizer', '--disable-libssp',
Packit 6c4009
                     '--disable-libcilkrts']
Packit 6c4009
        host_libs = self.ctx.host_libraries_installdir
Packit 6c4009
        cfg_opts += ['--with-gmp=%s' % host_libs,
Packit 6c4009
                     '--with-mpfr=%s' % host_libs,
Packit 6c4009
                     '--with-mpc=%s' % host_libs]
Packit 6c4009
        if bootstrap:
Packit 6c4009
            tool_build = 'gcc-first'
Packit 6c4009
            # Building a static-only, C-only compiler that is
Packit 6c4009
            # sufficient to build glibc.  Various libraries and
Packit 6c4009
            # features that may require libc headers must be disabled.
Packit 6c4009
            # When configuring with a sysroot, --with-newlib is
Packit 6c4009
            # required to define inhibit_libc (to stop some parts of
Packit 6c4009
            # libgcc including libc headers); --without-headers is not
Packit 6c4009
            # sufficient.
Packit 6c4009
            cfg_opts += ['--enable-languages=c', '--disable-shared',
Packit 6c4009
                         '--disable-threads',
Packit 6c4009
                         '--disable-libatomic',
Packit 6c4009
                         '--disable-decimal-float',
Packit 6c4009
                         '--disable-libffi',
Packit 6c4009
                         '--disable-libgomp',
Packit 6c4009
                         '--disable-libitm',
Packit 6c4009
                         '--disable-libmpx',
Packit 6c4009
                         '--disable-libquadmath',
Packit 6c4009
                         '--without-headers', '--with-newlib',
Packit 6c4009
                         '--with-glibc-version=%s' % self.ctx.glibc_version
Packit 6c4009
                         ]
Packit 6c4009
            cfg_opts += self.first_gcc_cfg
Packit 6c4009
        else:
Packit 6c4009
            tool_build = 'gcc'
Packit 6c4009
            cfg_opts += ['--enable-languages=c,c++', '--enable-shared',
Packit 6c4009
                         '--enable-threads']
Packit 6c4009
        self.build_cross_tool(cmdlist, 'gcc', tool_build, cfg_opts)
Packit 6c4009
Packit 6c4009
Packit 6c4009
class Glibc(object):
Packit 6c4009
    """A configuration for building glibc."""
Packit 6c4009
Packit 6c4009
    def __init__(self, compiler, arch=None, os_name=None, variant=None,
Packit 6c4009
                 cfg=None, ccopts=None):
Packit 6c4009
        """Initialize a Glibc object."""
Packit 6c4009
        self.ctx = compiler.ctx
Packit 6c4009
        self.compiler = compiler
Packit 6c4009
        if arch is None:
Packit 6c4009
            self.arch = compiler.arch
Packit 6c4009
        else:
Packit 6c4009
            self.arch = arch
Packit 6c4009
        if os_name is None:
Packit 6c4009
            self.os = compiler.os
Packit 6c4009
        else:
Packit 6c4009
            self.os = os_name
Packit 6c4009
        self.variant = variant
Packit 6c4009
        if variant is None:
Packit 6c4009
            self.name = '%s-%s' % (self.arch, self.os)
Packit 6c4009
        else:
Packit 6c4009
            self.name = '%s-%s-%s' % (self.arch, self.os, variant)
Packit 6c4009
        self.triplet = '%s-glibc-%s' % (self.arch, self.os)
Packit 6c4009
        if cfg is None:
Packit 6c4009
            self.cfg = []
Packit 6c4009
        else:
Packit 6c4009
            self.cfg = cfg
Packit 6c4009
        self.ccopts = ccopts
Packit 6c4009
Packit 6c4009
    def tool_name(self, tool):
Packit 6c4009
        """Return the name of a cross-compilation tool."""
Packit 6c4009
        ctool = '%s-%s' % (self.compiler.triplet, tool)
Packit 6c4009
        if self.ccopts and (tool == 'gcc' or tool == 'g++'):
Packit 6c4009
            ctool = '%s %s' % (ctool, self.ccopts)
Packit 6c4009
        return ctool
Packit 6c4009
Packit 6c4009
    def build(self):
Packit 6c4009
        """Generate commands to build this glibc."""
Packit 6c4009
        builddir = self.ctx.component_builddir('glibcs', self.name, 'glibc')
Packit 6c4009
        installdir = self.ctx.glibc_installdir(self.name)
Packit 6c4009
        logsdir = os.path.join(self.ctx.logsdir, 'glibcs', self.name)
Packit 6c4009
        self.ctx.remove_recreate_dirs(installdir, builddir, logsdir)
Packit 6c4009
        cmdlist = CommandList('glibcs-%s' % self.name, self.ctx.keep)
Packit 6c4009
        cmdlist.add_command('check-compilers',
Packit 6c4009
                            ['test', '-f',
Packit 6c4009
                             os.path.join(self.compiler.installdir, 'ok')])
Packit 6c4009
        cmdlist.use_path(self.compiler.bindir)
Packit 6c4009
        self.build_glibc(cmdlist, False)
Packit 6c4009
        self.ctx.add_makefile_cmdlist('glibcs-%s' % self.name, cmdlist,
Packit 6c4009
                                      logsdir)
Packit 6c4009
Packit 6c4009
    def build_glibc(self, cmdlist, for_compiler):
Packit 6c4009
        """Generate commands to build this glibc, either as part of a compiler
Packit 6c4009
        build or with the bootstrapped compiler (and in the latter case, run
Packit 6c4009
        tests as well)."""
Packit 6c4009
        srcdir = self.ctx.component_srcdir('glibc')
Packit 6c4009
        if for_compiler:
Packit 6c4009
            builddir = self.ctx.component_builddir('compilers',
Packit 6c4009
                                                   self.compiler.name, 'glibc',
Packit 6c4009
                                                   self.name)
Packit 6c4009
            installdir = self.compiler.sysroot
Packit 6c4009
            srcdir_copy = self.ctx.component_builddir('compilers',
Packit 6c4009
                                                      self.compiler.name,
Packit 6c4009
                                                      'glibc-src',
Packit 6c4009
                                                      self.name)
Packit 6c4009
        else:
Packit 6c4009
            builddir = self.ctx.component_builddir('glibcs', self.name,
Packit 6c4009
                                                   'glibc')
Packit 6c4009
            installdir = self.ctx.glibc_installdir(self.name)
Packit 6c4009
            srcdir_copy = self.ctx.component_builddir('glibcs', self.name,
Packit 6c4009
                                                      'glibc-src')
Packit 6c4009
        cmdlist.create_use_dir(builddir)
Packit 6c4009
        # glibc builds write into the source directory, and even if
Packit 6c4009
        # not intentionally there is a risk of bugs that involve
Packit 6c4009
        # writing into the working directory.  To avoid possible
Packit 6c4009
        # concurrency issues, copy the source directory.
Packit 6c4009
        cmdlist.create_copy_dir(srcdir, srcdir_copy)
Packit 6c4009
        use_usr = self.os != 'gnu'
Packit 6c4009
        prefix = '/usr' if use_usr else ''
Packit 6c4009
        cfg_cmd = [os.path.join(srcdir_copy, 'configure'),
Packit 6c4009
                   '--prefix=%s' % prefix,
Packit 6c4009
                   '--enable-profile',
Packit 6c4009
                   '--build=%s' % self.ctx.build_triplet,
Packit 6c4009
                   '--host=%s' % self.triplet,
Packit 6c4009
                   'CC=%s' % self.tool_name('gcc'),
Packit 6c4009
                   'CXX=%s' % self.tool_name('g++'),
Packit 6c4009
                   'AR=%s' % self.tool_name('ar'),
Packit 6c4009
                   'AS=%s' % self.tool_name('as'),
Packit 6c4009
                   'LD=%s' % self.tool_name('ld'),
Packit 6c4009
                   'NM=%s' % self.tool_name('nm'),
Packit 6c4009
                   'OBJCOPY=%s' % self.tool_name('objcopy'),
Packit 6c4009
                   'OBJDUMP=%s' % self.tool_name('objdump'),
Packit 6c4009
                   'RANLIB=%s' % self.tool_name('ranlib'),
Packit 6c4009
                   'READELF=%s' % self.tool_name('readelf'),
Packit 6c4009
                   'STRIP=%s' % self.tool_name('strip')]
Packit 6c4009
        if self.os == 'gnu':
Packit 6c4009
            cfg_cmd += ['MIG=%s' % self.tool_name('mig')]
Packit 6c4009
        cfg_cmd += self.cfg
Packit 6c4009
        cmdlist.add_command('configure', cfg_cmd)
Packit 6c4009
        cmdlist.add_command('build', ['make'])
Packit 6c4009
        cmdlist.add_command('install', ['make', 'install',
Packit 6c4009
                                        'install_root=%s' % installdir])
Packit 6c4009
        # GCC uses paths such as lib/../lib64, so make sure lib
Packit 6c4009
        # directories always exist.
Packit 6c4009
        mkdir_cmd = ['mkdir', '-p',
Packit 6c4009
                     os.path.join(installdir, 'lib')]
Packit 6c4009
        if use_usr:
Packit 6c4009
            mkdir_cmd += [os.path.join(installdir, 'usr', 'lib')]
Packit 6c4009
        cmdlist.add_command('mkdir-lib', mkdir_cmd)
Packit 6c4009
        if not for_compiler:
Packit 6c4009
            if self.ctx.strip:
Packit 6c4009
                cmdlist.add_command('strip',
Packit 6c4009
                                    ['sh', '-c',
Packit 6c4009
                                     ('%s $(find %s/lib* -name "*.so")' %
Packit 6c4009
                                      (self.tool_name('strip'), installdir))])
Packit 6c4009
            cmdlist.add_command('check', ['make', 'check'])
Packit 6c4009
            cmdlist.add_command('save-logs', [self.ctx.save_logs],
Packit 6c4009
                                always_run=True)
Packit 6c4009
        cmdlist.cleanup_dir('cleanup-src', srcdir_copy)
Packit 6c4009
        cmdlist.cleanup_dir()
Packit 6c4009
Packit 6c4009
Packit 6c4009
class Command(object):
Packit 6c4009
    """A command run in the build process."""
Packit 6c4009
Packit 6c4009
    def __init__(self, desc, num, dir, path, command, always_run=False):
Packit 6c4009
        """Initialize a Command object."""
Packit 6c4009
        self.dir = dir
Packit 6c4009
        self.path = path
Packit 6c4009
        self.desc = desc
Packit 6c4009
        trans = str.maketrans({' ': '-'})
Packit 6c4009
        self.logbase = '%03d-%s' % (num, desc.translate(trans))
Packit 6c4009
        self.command = command
Packit 6c4009
        self.always_run = always_run
Packit 6c4009
Packit 6c4009
    @staticmethod
Packit 6c4009
    def shell_make_quote_string(s):
Packit 6c4009
        """Given a string not containing a newline, quote it for use by the
Packit 6c4009
        shell and make."""
Packit 6c4009
        assert '\n' not in s
Packit 6c4009
        if re.fullmatch('[]+,./0-9@A-Z_a-z-]+', s):
Packit 6c4009
            return s
Packit 6c4009
        strans = str.maketrans({"'": "'\\''"})
Packit 6c4009
        s = "'%s'" % s.translate(strans)
Packit 6c4009
        mtrans = str.maketrans({'$': '$$'})
Packit 6c4009
        return s.translate(mtrans)
Packit 6c4009
Packit 6c4009
    @staticmethod
Packit 6c4009
    def shell_make_quote_list(l, translate_make):
Packit 6c4009
        """Given a list of strings not containing newlines, quote them for use
Packit 6c4009
        by the shell and make, returning a single string.  If translate_make
Packit 6c4009
        is true and the first string is 'make', change it to $(MAKE)."""
Packit 6c4009
        l = [Command.shell_make_quote_string(s) for s in l]
Packit 6c4009
        if translate_make and l[0] == 'make':
Packit 6c4009
            l[0] = '$(MAKE)'
Packit 6c4009
        return ' '.join(l)
Packit 6c4009
Packit 6c4009
    def shell_make_quote(self):
Packit 6c4009
        """Return this command quoted for the shell and make."""
Packit 6c4009
        return self.shell_make_quote_list(self.command, True)
Packit 6c4009
Packit 6c4009
Packit 6c4009
class CommandList(object):
Packit 6c4009
    """A list of commands run in the build process."""
Packit 6c4009
Packit 6c4009
    def __init__(self, desc, keep):
Packit 6c4009
        """Initialize a CommandList object."""
Packit 6c4009
        self.cmdlist = []
Packit 6c4009
        self.dir = None
Packit 6c4009
        self.path = None
Packit 6c4009
        self.desc = [desc]
Packit 6c4009
        self.keep = keep
Packit 6c4009
Packit 6c4009
    def desc_txt(self, desc):
Packit 6c4009
        """Return the description to use for a command."""
Packit 6c4009
        return '%s %s' % (' '.join(self.desc), desc)
Packit 6c4009
Packit 6c4009
    def use_dir(self, dir):
Packit 6c4009
        """Set the default directory for subsequent commands."""
Packit 6c4009
        self.dir = dir
Packit 6c4009
Packit 6c4009
    def use_path(self, path):
Packit 6c4009
        """Set a directory to be prepended to the PATH for subsequent
Packit 6c4009
        commands."""
Packit 6c4009
        self.path = path
Packit 6c4009
Packit 6c4009
    def push_subdesc(self, subdesc):
Packit 6c4009
        """Set the default subdescription for subsequent commands (e.g., the
Packit 6c4009
        name of a component being built, within the series of commands
Packit 6c4009
        building it)."""
Packit 6c4009
        self.desc.append(subdesc)
Packit 6c4009
Packit 6c4009
    def pop_subdesc(self):
Packit 6c4009
        """Pop a subdescription from the list of descriptions."""
Packit 6c4009
        self.desc.pop()
Packit 6c4009
Packit 6c4009
    def create_use_dir(self, dir):
Packit 6c4009
        """Remove and recreate a directory and use it for subsequent
Packit 6c4009
        commands."""
Packit 6c4009
        self.add_command_dir('rm', None, ['rm', '-rf', dir])
Packit 6c4009
        self.add_command_dir('mkdir', None, ['mkdir', '-p', dir])
Packit 6c4009
        self.use_dir(dir)
Packit 6c4009
Packit 6c4009
    def create_copy_dir(self, src, dest):
Packit 6c4009
        """Remove a directory and recreate it as a copy from the given
Packit 6c4009
        source."""
Packit 6c4009
        self.add_command_dir('copy-rm', None, ['rm', '-rf', dest])
Packit 6c4009
        parent = os.path.dirname(dest)
Packit 6c4009
        self.add_command_dir('copy-mkdir', None, ['mkdir', '-p', parent])
Packit 6c4009
        self.add_command_dir('copy', None, ['cp', '-a', src, dest])
Packit 6c4009
Packit 6c4009
    def add_command_dir(self, desc, dir, command, always_run=False):
Packit 6c4009
        """Add a command to run in a given directory."""
Packit 6c4009
        cmd = Command(self.desc_txt(desc), len(self.cmdlist), dir, self.path,
Packit 6c4009
                      command, always_run)
Packit 6c4009
        self.cmdlist.append(cmd)
Packit 6c4009
Packit 6c4009
    def add_command(self, desc, command, always_run=False):
Packit 6c4009
        """Add a command to run in the default directory."""
Packit 6c4009
        cmd = Command(self.desc_txt(desc), len(self.cmdlist), self.dir,
Packit 6c4009
                      self.path, command, always_run)
Packit 6c4009
        self.cmdlist.append(cmd)
Packit 6c4009
Packit 6c4009
    def cleanup_dir(self, desc='cleanup', dir=None):
Packit 6c4009
        """Clean up a build directory.  If no directory is specified, the
Packit 6c4009
        default directory is cleaned up and ceases to be the default
Packit 6c4009
        directory."""
Packit 6c4009
        if dir is None:
Packit 6c4009
            dir = self.dir
Packit 6c4009
            self.use_dir(None)
Packit 6c4009
        if self.keep != 'all':
Packit 6c4009
            self.add_command_dir(desc, None, ['rm', '-rf', dir],
Packit 6c4009
                                 always_run=(self.keep == 'none'))
Packit 6c4009
Packit 6c4009
    def makefile_commands(self, wrapper, logsdir):
Packit 6c4009
        """Return the sequence of commands in the form of text for a Makefile.
Packit 6c4009
        The given wrapper script takes arguments: base of logs for
Packit 6c4009
        previous command, or empty; base of logs for this command;
Packit 6c4009
        description; directory; PATH addition; the command itself."""
Packit 6c4009
        # prev_base is the base of the name for logs of the previous
Packit 6c4009
        # command that is not always-run (that is, a build command,
Packit 6c4009
        # whose failure should stop subsequent build commands from
Packit 6c4009
        # being run, as opposed to a cleanup command, which is run
Packit 6c4009
        # even if previous commands failed).
Packit 6c4009
        prev_base = ''
Packit 6c4009
        cmds = []
Packit 6c4009
        for c in self.cmdlist:
Packit 6c4009
            ctxt = c.shell_make_quote()
Packit 6c4009
            if prev_base and not c.always_run:
Packit 6c4009
                prev_log = os.path.join(logsdir, prev_base)
Packit 6c4009
            else:
Packit 6c4009
                prev_log = ''
Packit 6c4009
            this_log = os.path.join(logsdir, c.logbase)
Packit 6c4009
            if not c.always_run:
Packit 6c4009
                prev_base = c.logbase
Packit 6c4009
            if c.dir is None:
Packit 6c4009
                dir = ''
Packit 6c4009
            else:
Packit 6c4009
                dir = c.dir
Packit 6c4009
            if c.path is None:
Packit 6c4009
                path = ''
Packit 6c4009
            else:
Packit 6c4009
                path = c.path
Packit 6c4009
            prelims = [wrapper, prev_log, this_log, c.desc, dir, path]
Packit 6c4009
            prelim_txt = Command.shell_make_quote_list(prelims, False)
Packit 6c4009
            cmds.append('\t@%s %s' % (prelim_txt, ctxt))
Packit 6c4009
        return '\n'.join(cmds)
Packit 6c4009
Packit 6c4009
    def status_logs(self, logsdir):
Packit 6c4009
        """Return the list of log files with command status."""
Packit 6c4009
        return [os.path.join(logsdir, '%s-status.txt' % c.logbase)
Packit 6c4009
                for c in self.cmdlist]
Packit 6c4009
Packit 6c4009
Packit 6c4009
def get_parser():
Packit 6c4009
    """Return an argument parser for this module."""
Packit 6c4009
    parser = argparse.ArgumentParser(description=__doc__)
Packit 6c4009
    parser.add_argument('-j', dest='parallelism',
Packit 6c4009
                        help='Run this number of jobs in parallel',
Packit 6c4009
                        type=int, default=os.cpu_count())
Packit 6c4009
    parser.add_argument('--keep', dest='keep',
Packit 6c4009
                        help='Whether to keep all build directories, '
Packit 6c4009
                        'none or only those from failed builds',
Packit 6c4009
                        default='none', choices=('none', 'all', 'failed'))
Packit 6c4009
    parser.add_argument('--replace-sources', action='store_true',
Packit 6c4009
                        help='Remove and replace source directories '
Packit 6c4009
                        'with the wrong version of a component')
Packit 6c4009
    parser.add_argument('--strip', action='store_true',
Packit 6c4009
                        help='Strip installed glibc libraries')
Packit 6c4009
    parser.add_argument('topdir',
Packit 6c4009
                        help='Toplevel working directory')
Packit 6c4009
    parser.add_argument('action',
Packit 6c4009
                        help='What to do',
Packit 6c4009
                        choices=('checkout', 'bot-cycle', 'bot',
Packit 6c4009
                                 'host-libraries', 'compilers', 'glibcs'))
Packit 6c4009
    parser.add_argument('configs',
Packit 6c4009
                        help='Versions to check out or configurations to build',
Packit 6c4009
                        nargs='*')
Packit 6c4009
    return parser
Packit 6c4009
Packit 6c4009
Packit 6c4009
def main(argv):
Packit 6c4009
    """The main entry point."""
Packit 6c4009
    parser = get_parser()
Packit 6c4009
    opts = parser.parse_args(argv)
Packit 6c4009
    topdir = os.path.abspath(opts.topdir)
Packit 6c4009
    ctx = Context(topdir, opts.parallelism, opts.keep, opts.replace_sources,
Packit 6c4009
                  opts.strip, opts.action)
Packit 6c4009
    ctx.run_builds(opts.action, opts.configs)
Packit 6c4009
Packit 6c4009
Packit 6c4009
if __name__ == '__main__':
Packit 6c4009
    main(sys.argv[1:])