]> git.ipfire.org Git - thirdparty/glibc.git/blobdiff - scripts/build-many-glibcs.py
elf: Refuse to dlopen PIE objects [BZ #24323]
[thirdparty/glibc.git] / scripts / build-many-glibcs.py
index 658a22e65f86a07f11f4f3f9c77608ee4914f31f..c5821df25e0fa7e70d0e37b385b7d207b710cc14 100755 (executable)
@@ -1,6 +1,6 @@
 #!/usr/bin/python3
 # Build many configurations of glibc.
-# Copyright (C) 2016 Free Software Foundation, Inc.
+# Copyright (C) 2016-2019 Free Software Foundation, Inc.
 # This file is part of the GNU C Library.
 #
 # The GNU C Library is free software; you can redistribute it and/or
@@ -23,13 +23,14 @@ This script takes as arguments a directory name (containing a src
 subdirectory with sources of the relevant toolchain components) and a
 description of what to do: 'checkout', to check out sources into that
 directory, 'bot-cycle', to run a series of checkout and build steps,
-'host-libraries', to build libraries required by the toolchain,
-'compilers', to build cross-compilers for various configurations, or
-'glibcs', to build glibc for various configurations and run the
-compilation parts of the testsuite.  Subsequent arguments name the
-versions of components to check out (<component>-<version), for
-'checkout', or, for actions other than 'checkout' and 'bot-cycle',
-name configurations for which compilers or glibc are to be built.
+'bot', to run 'bot-cycle' repeatedly, 'host-libraries', to build
+libraries required by the toolchain, 'compilers', to build
+cross-compilers for various configurations, or 'glibcs', to build
+glibc for various configurations and run the compilation parts of the
+testsuite.  Subsequent arguments name the versions of components to
+check out (<component>-<version), for 'checkout', or, for actions
+other than 'checkout' and 'bot-cycle', name configurations for which
+compilers or glibc are to be built.
 
 """
 
@@ -45,18 +46,48 @@ import smtplib
 import stat
 import subprocess
 import sys
+import time
 import urllib.request
 
+try:
+    subprocess.run
+except:
+    class _CompletedProcess:
+        def __init__(self, args, returncode, stdout=None, stderr=None):
+            self.args = args
+            self.returncode = returncode
+            self.stdout = stdout
+            self.stderr = stderr
+
+    def _run(*popenargs, input=None, timeout=None, check=False, **kwargs):
+        assert(timeout is None)
+        with subprocess.Popen(*popenargs, **kwargs) as process:
+            try:
+                stdout, stderr = process.communicate(input)
+            except:
+                process.kill()
+                process.wait()
+                raise
+            returncode = process.poll()
+            if check and returncode:
+                raise subprocess.CalledProcessError(returncode, popenargs)
+        return _CompletedProcess(popenargs, returncode, stdout, stderr)
+
+    subprocess.run = _run
+
 
 class Context(object):
     """The global state associated with builds in a given directory."""
 
-    def __init__(self, topdir, parallelism, keep, replace_sources, action):
+    def __init__(self, topdir, parallelism, keep, replace_sources, strip,
+                 full_gcc, action):
         """Initialize the context."""
         self.topdir = topdir
         self.parallelism = parallelism
         self.keep = keep
         self.replace_sources = replace_sources
+        self.strip = strip
+        self.full_gcc = full_gcc
         self.srcdir = os.path.join(topdir, 'src')
         self.versions_json = os.path.join(self.srcdir, 'versions.json')
         self.build_state_json = os.path.join(topdir, 'build-state.json')
@@ -66,6 +97,7 @@ class Context(object):
                                                       'host-libraries')
         self.builddir = os.path.join(topdir, 'build')
         self.logsdir = os.path.join(topdir, 'logs')
+        self.logsdir_old = os.path.join(topdir, 'logs-old')
         self.makefile = os.path.join(self.builddir, 'Makefile')
         self.wrapper = os.path.join(self.builddir, 'wrapper')
         self.save_logs = os.path.join(self.builddir, 'save-logs')
@@ -80,6 +112,7 @@ class Context(object):
         self.load_versions_json()
         self.load_build_state_json()
         self.status_log_list = []
+        self.email_warning = False
 
     def get_script_text(self):
         """Return the text of this script."""
@@ -88,6 +121,7 @@ class Context(object):
 
     def exec_self(self):
         """Re-execute this script with the same arguments."""
+        sys.stdout.flush()
         os.execv(sys.executable, [sys.executable] + sys.argv)
 
     def get_build_triplet(self):
@@ -115,13 +149,10 @@ class Context(object):
 
     def add_all_configs(self):
         """Add all known glibc build configurations."""
-        # On architectures missing __builtin_trap support, these
-        # options may be needed as a workaround; see
-        # <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70216> for SH.
-        no_isolate = ('-fno-isolate-erroneous-paths-dereference'
-                      ' -fno-isolate-erroneous-paths-attribute')
         self.add_config(arch='aarch64',
-                        os_name='linux-gnu')
+                        os_name='linux-gnu',
+                        extra_glibcs=[{'variant': 'disable-multi-arch',
+                                       'cfg': ['--disable-multi-arch']}])
         self.add_config(arch='aarch64_be',
                         os_name='linux-gnu')
         self.add_config(arch='alpha',
@@ -135,15 +166,32 @@ class Context(object):
                         variant='be8',
                         gcc_cfg=['--with-arch=armv7-a'])
         self.add_config(arch='arm',
-                        os_name='linux-gnueabihf')
+                        os_name='linux-gnueabihf',
+                        gcc_cfg=['--with-float=hard', '--with-cpu=arm926ej-s'],
+                        extra_glibcs=[{'variant': 'v7a',
+                                       'ccopts': '-march=armv7-a -mfpu=vfpv3'},
+                                      {'variant': 'v7a-disable-multi-arch',
+                                       'ccopts': '-march=armv7-a -mfpu=vfpv3',
+                                       'cfg': ['--disable-multi-arch']}])
         self.add_config(arch='armeb',
-                        os_name='linux-gnueabihf')
+                        os_name='linux-gnueabihf',
+                        gcc_cfg=['--with-float=hard', '--with-cpu=arm926ej-s'])
         self.add_config(arch='armeb',
                         os_name='linux-gnueabihf',
                         variant='be8',
-                        gcc_cfg=['--with-arch=armv7-a'])
+                        gcc_cfg=['--with-float=hard', '--with-arch=armv7-a',
+                                 '--with-fpu=vfpv3'])
+        self.add_config(arch='csky',
+                        os_name='linux-gnuabiv2',
+                        variant='soft',
+                        gcc_cfg=['--disable-multilib'])
+        self.add_config(arch='csky',
+                        os_name='linux-gnuabiv2',
+                        gcc_cfg=['--with-float=hard', '--disable-multilib'])
         self.add_config(arch='hppa',
                         os_name='linux-gnu')
+        self.add_config(arch='i686',
+                        os_name='gnu')
         self.add_config(arch='ia64',
                         os_name='linux-gnu',
                         first_gcc_cfg=['--with-system-libunwind'])
@@ -154,6 +202,11 @@ class Context(object):
                         os_name='linux-gnu',
                         variant='coldfire',
                         gcc_cfg=['--with-arch=cf', '--disable-multilib'])
+        self.add_config(arch='m68k',
+                        os_name='linux-gnu',
+                        variant='coldfire-soft',
+                        gcc_cfg=['--with-arch=cf', '--with-cpu=54455',
+                                 '--disable-multilib'])
         self.add_config(arch='microblaze',
                         os_name='linux-gnu',
                         gcc_cfg=['--disable-multilib'])
@@ -172,15 +225,12 @@ class Context(object):
                         os_name='linux-gnu',
                         variant='soft',
                         gcc_cfg=['--with-mips-plt', '--with-float=soft'],
-                        glibcs=[{'variant': 'n32-soft',
-                                 'cfg': ['--without-fp']},
+                        glibcs=[{'variant': 'n32-soft'},
                                 {'variant': 'soft',
                                  'arch': 'mips',
-                                 'ccopts': '-mabi=32',
-                                 'cfg': ['--without-fp']},
+                                 'ccopts': '-mabi=32'},
                                 {'variant': 'n64-soft',
-                                 'ccopts': '-mabi=64',
-                                 'cfg': ['--without-fp']}])
+                                 'ccopts': '-mabi=64'}])
         self.add_config(arch='mips64',
                         os_name='linux-gnu',
                         variant='nan2008',
@@ -200,15 +250,12 @@ class Context(object):
                                  '--with-arch-64=mips64r2',
                                  '--with-arch-32=mips32r2',
                                  '--with-float=soft'],
-                        glibcs=[{'variant': 'n32-nan2008-soft',
-                                 'cfg': ['--without-fp']},
+                        glibcs=[{'variant': 'n32-nan2008-soft'},
                                 {'variant': 'nan2008-soft',
                                  'arch': 'mips',
-                                 'ccopts': '-mabi=32',
-                                 'cfg': ['--without-fp']},
+                                 'ccopts': '-mabi=32'},
                                 {'variant': 'n64-nan2008-soft',
-                                 'ccopts': '-mabi=64',
-                                 'cfg': ['--without-fp']}])
+                                 'ccopts': '-mabi=64'}])
         self.add_config(arch='mips64el',
                         os_name='linux-gnu',
                         gcc_cfg=['--with-mips-plt'],
@@ -221,15 +268,12 @@ class Context(object):
                         os_name='linux-gnu',
                         variant='soft',
                         gcc_cfg=['--with-mips-plt', '--with-float=soft'],
-                        glibcs=[{'variant': 'n32-soft',
-                                 'cfg': ['--without-fp']},
+                        glibcs=[{'variant': 'n32-soft'},
                                 {'variant': 'soft',
                                  'arch': 'mipsel',
-                                 'ccopts': '-mabi=32',
-                                 'cfg': ['--without-fp']},
+                                 'ccopts': '-mabi=32'},
                                 {'variant': 'n64-soft',
-                                 'ccopts': '-mabi=64',
-                                 'cfg': ['--without-fp']}])
+                                 'ccopts': '-mabi=64'}])
         self.add_config(arch='mips64el',
                         os_name='linux-gnu',
                         variant='nan2008',
@@ -249,87 +293,77 @@ class Context(object):
                                  '--with-arch-64=mips64r2',
                                  '--with-arch-32=mips32r2',
                                  '--with-float=soft'],
-                        glibcs=[{'variant': 'n32-nan2008-soft',
-                                 'cfg': ['--without-fp']},
+                        glibcs=[{'variant': 'n32-nan2008-soft'},
                                 {'variant': 'nan2008-soft',
                                  'arch': 'mipsel',
-                                 'ccopts': '-mabi=32',
-                                 'cfg': ['--without-fp']},
+                                 'ccopts': '-mabi=32'},
                                 {'variant': 'n64-nan2008-soft',
-                                 'ccopts': '-mabi=64',
-                                 'cfg': ['--without-fp']}])
+                                 'ccopts': '-mabi=64'}])
         self.add_config(arch='nios2',
                         os_name='linux-gnu')
         self.add_config(arch='powerpc',
                         os_name='linux-gnu',
-                        gcc_cfg=['--disable-multilib', '--enable-secureplt'])
+                        gcc_cfg=['--disable-multilib', '--enable-secureplt'],
+                        extra_glibcs=[{'variant': 'power4',
+                                       'ccopts': '-mcpu=power4',
+                                       'cfg': ['--with-cpu=power4']}])
         self.add_config(arch='powerpc',
                         os_name='linux-gnu',
                         variant='soft',
                         gcc_cfg=['--disable-multilib', '--with-float=soft',
-                                 '--enable-secureplt'],
-                        glibcs=[{'variant': 'soft', 'cfg': ['--without-fp']}])
+                                 '--enable-secureplt'])
         self.add_config(arch='powerpc64',
                         os_name='linux-gnu',
                         gcc_cfg=['--disable-multilib', '--enable-secureplt'])
         self.add_config(arch='powerpc64le',
                         os_name='linux-gnu',
                         gcc_cfg=['--disable-multilib', '--enable-secureplt'])
-        self.add_config(arch='powerpc',
-                        os_name='linux-gnuspe',
-                        gcc_cfg=['--disable-multilib', '--enable-secureplt',
-                                 '--enable-e500-double'],
-                        glibcs=[{'cfg': ['--without-fp']}])
-        self.add_config(arch='powerpc',
-                        os_name='linux-gnuspe',
-                        variant='e500v1',
-                        gcc_cfg=['--disable-multilib', '--enable-secureplt'],
-                        glibcs=[{'variant': 'e500v1', 'cfg': ['--without-fp']}])
+        self.add_config(arch='riscv64',
+                        os_name='linux-gnu',
+                        variant='rv64imac-lp64',
+                        gcc_cfg=['--with-arch=rv64imac', '--with-abi=lp64',
+                                 '--disable-multilib'])
+        self.add_config(arch='riscv64',
+                        os_name='linux-gnu',
+                        variant='rv64imafdc-lp64',
+                        gcc_cfg=['--with-arch=rv64imafdc', '--with-abi=lp64',
+                                 '--disable-multilib'])
+        self.add_config(arch='riscv64',
+                        os_name='linux-gnu',
+                        variant='rv64imafdc-lp64d',
+                        gcc_cfg=['--with-arch=rv64imafdc', '--with-abi=lp64d',
+                                 '--disable-multilib'])
         self.add_config(arch='s390x',
                         os_name='linux-gnu',
                         glibcs=[{},
                                 {'arch': 's390', 'ccopts': '-m31'}])
         self.add_config(arch='sh3',
-                        os_name='linux-gnu',
-                        glibcs=[{'ccopts': no_isolate}])
+                        os_name='linux-gnu')
         self.add_config(arch='sh3eb',
-                        os_name='linux-gnu',
-                        glibcs=[{'ccopts': no_isolate}])
+                        os_name='linux-gnu')
         self.add_config(arch='sh4',
-                        os_name='linux-gnu',
-                        glibcs=[{'ccopts': no_isolate}])
+                        os_name='linux-gnu')
         self.add_config(arch='sh4eb',
-                        os_name='linux-gnu',
-                        glibcs=[{'ccopts': no_isolate}])
+                        os_name='linux-gnu')
         self.add_config(arch='sh4',
                         os_name='linux-gnu',
                         variant='soft',
-                        gcc_cfg=['--without-fp'],
-                        glibcs=[{'variant': 'soft',
-                                 'cfg': ['--without-fp'],
-                                 'ccopts': no_isolate}])
+                        gcc_cfg=['--without-fp'])
         self.add_config(arch='sh4eb',
                         os_name='linux-gnu',
                         variant='soft',
-                        gcc_cfg=['--without-fp'],
-                        glibcs=[{'variant': 'soft',
-                                 'cfg': ['--without-fp'],
-                                 'ccopts': no_isolate}])
+                        gcc_cfg=['--without-fp'])
         self.add_config(arch='sparc64',
                         os_name='linux-gnu',
                         glibcs=[{},
                                 {'arch': 'sparcv9',
-                                 'ccopts': '-m32 -mlong-double-128'}])
-        self.add_config(arch='tilegx',
-                        os_name='linux-gnu',
-                        glibcs=[{},
-                                {'variant': '32', 'ccopts': '-m32'}])
-        self.add_config(arch='tilegxbe',
-                        os_name='linux-gnu',
-                        glibcs=[{},
-                                {'variant': '32', 'ccopts': '-m32'}])
-        self.add_config(arch='tilepro',
-                        os_name='linux-gnu')
+                                 'ccopts': '-m32 -mlong-double-128'}],
+                        extra_glibcs=[{'variant': 'disable-multi-arch',
+                                       'cfg': ['--disable-multi-arch']},
+                                      {'variant': 'disable-multi-arch',
+                                       'arch': 'sparcv9',
+                                       'ccopts': '-m32 -mlong-double-128',
+                                       'cfg': ['--disable-multi-arch']}])
         self.add_config(arch='x86_64',
                         os_name='linux-gnu',
                         gcc_cfg=['--with-multilib-list=m64,m32,mx32'],
@@ -338,10 +372,27 @@ class Context(object):
                                 {'arch': 'i686', 'ccopts': '-m32 -march=i686'}],
                         extra_glibcs=[{'variant': 'disable-multi-arch',
                                        'cfg': ['--disable-multi-arch']},
+                                      {'variant': 'enable-obsolete',
+                                       'cfg': ['--enable-obsolete-rpc',
+                                               '--enable-obsolete-nsl']},
+                                      {'variant': 'static-pie',
+                                       'cfg': ['--enable-static-pie']},
+                                      {'variant': 'x32-static-pie',
+                                       'ccopts': '-mx32',
+                                       'cfg': ['--enable-static-pie']},
+                                      {'variant': 'static-pie',
+                                       'arch': 'i686',
+                                       'ccopts': '-m32 -march=i686',
+                                       'cfg': ['--enable-static-pie']},
                                       {'variant': 'disable-multi-arch',
                                        'arch': 'i686',
                                        'ccopts': '-m32 -march=i686',
                                        'cfg': ['--disable-multi-arch']},
+                                      {'variant': 'enable-obsolete',
+                                       'arch': 'i686',
+                                       'ccopts': '-m32 -march=i686',
+                                       'cfg': ['--enable-obsolete-rpc',
+                                               '--enable-obsolete-nsl']},
                                       {'arch': 'i486',
                                        'ccopts': '-m32 -march=i486'},
                                       {'arch': 'i586',
@@ -404,6 +455,12 @@ class Context(object):
                 exit(1)
             self.bot_cycle()
             return
+        if action == 'bot':
+            if configs:
+                print('error: configurations specified for bot')
+                exit(1)
+            self.bot()
+            return
         if action == 'host-libraries' and configs:
             print('error: configurations specified for host-libraries')
             exit(1)
@@ -415,13 +472,15 @@ class Context(object):
             old_versions = {}
             self.build_host_libraries()
         elif action == 'compilers':
-            build_components = ('binutils', 'gcc', 'glibc', 'linux')
+            build_components = ('binutils', 'gcc', 'glibc', 'linux', 'mig',
+                                'gnumach', 'hurd')
             old_components = ('gmp', 'mpfr', 'mpc')
             old_versions = self.build_state['host-libraries']['build-versions']
             self.build_compilers(configs)
         else:
             build_components = ('glibc',)
-            old_components = ('gmp', 'mpfr', 'mpc', 'binutils', 'gcc', 'linux')
+            old_components = ('gmp', 'mpfr', 'mpc', 'binutils', 'gcc', 'linux',
+                              'mig', 'gnumach', 'hurd')
             old_versions = self.build_state['compilers']['build-versions']
             self.build_glibcs(configs)
         self.write_files()
@@ -642,13 +701,16 @@ class Context(object):
 
     def checkout(self, versions):
         """Check out the desired component versions."""
-        default_versions = {'binutils': 'vcs-2.27',
-                            'gcc': 'vcs-6',
+        default_versions = {'binutils': 'vcs-2.32',
+                            'gcc': 'vcs-9',
                             'glibc': 'vcs-mainline',
-                            'gmp': '6.1.1',
-                            'linux': '4.8.6',
-                            'mpc': '1.0.3',
-                            'mpfr': '3.1.5'}
+                            'gmp': '6.1.2',
+                            'linux': '5.0',
+                            'mpc': '1.1.0',
+                            'mpfr': '4.0.2',
+                            'mig': 'vcs-mainline',
+                            'gnumach': 'vcs-mainline',
+                            'hurd': 'vcs-mainline'}
         use_versions = {}
         explicit_versions = {}
         for v in versions:
@@ -728,6 +790,27 @@ class Context(object):
             r = self.git_checkout(component, git_url, git_branch, update)
             self.fix_glibc_timestamps()
             return r
+        elif component == 'gnumach':
+            git_url = 'git://git.savannah.gnu.org/hurd/gnumach.git'
+            git_branch = 'master'
+            r = self.git_checkout(component, git_url, git_branch, update)
+            subprocess.run(['autoreconf', '-i'],
+                           cwd=self.component_srcdir(component), check=True)
+            return r
+        elif component == 'mig':
+            git_url = 'git://git.savannah.gnu.org/hurd/mig.git'
+            git_branch = 'master'
+            r = self.git_checkout(component, git_url, git_branch, update)
+            subprocess.run(['autoreconf', '-i'],
+                           cwd=self.component_srcdir(component), check=True)
+            return r
+        elif component == 'hurd':
+            git_url = 'git://git.savannah.gnu.org/hurd/hurd.git'
+            git_branch = 'master'
+            r = self.git_checkout(component, git_url, git_branch, update)
+            subprocess.run(['autoconf'],
+                           cwd=self.component_srcdir(component), check=True)
+            return r
         else:
             print('error: component %s coming from VCS' % component)
             exit(1)
@@ -737,6 +820,9 @@ class Context(object):
         if update:
             subprocess.run(['git', 'remote', 'prune', 'origin'],
                            cwd=self.component_srcdir(component), check=True)
+            if self.replace_sources:
+                subprocess.run(['git', 'clean', '-dxfq'],
+                               cwd=self.component_srcdir(component), check=True)
             subprocess.run(['git', 'pull', '-q'],
                            cwd=self.component_srcdir(component), check=True)
         else:
@@ -753,6 +839,19 @@ class Context(object):
         # Ensure that builds do not try to regenerate generated files
         # in the source tree.
         srcdir = self.component_srcdir('glibc')
+        # These files have Makefile dependencies to regenerate them in
+        # the source tree that may be active during a normal build.
+        # Some other files have such dependencies but do not need to
+        # be touched because nothing in a build depends on the files
+        # in question.
+        for f in ('sysdeps/gnu/errlist.c',
+                  'sysdeps/mach/hurd/bits/errno.h',
+                  'sysdeps/sparc/sparc32/rem.S',
+                  'sysdeps/sparc/sparc32/sdiv.S',
+                  'sysdeps/sparc/sparc32/udiv.S',
+                  'sysdeps/sparc/sparc32/urem.S'):
+            to_touch = os.path.join(srcdir, f)
+            subprocess.run(['touch', '-c', to_touch], check=True)
         for dirpath, dirnames, filenames in os.walk(srcdir):
             for f in filenames:
                 if (f == 'configure' or
@@ -779,15 +878,19 @@ class Context(object):
         if update:
             return
         url_map = {'binutils': 'https://ftp.gnu.org/gnu/binutils/binutils-%(version)s.tar.bz2',
-                   'gcc': 'https://ftp.gnu.org/gnu/gcc/gcc-%(version)s/gcc-%(version)s.tar.bz2',
+                   'gcc': 'https://ftp.gnu.org/gnu/gcc/gcc-%(version)s/gcc-%(version)s.tar.gz',
                    'gmp': 'https://ftp.gnu.org/gnu/gmp/gmp-%(version)s.tar.xz',
-                   'linux': 'https://www.kernel.org/pub/linux/kernel/v4.x/linux-%(version)s.tar.xz',
+                   'linux': 'https://www.kernel.org/pub/linux/kernel/v%(major)s.x/linux-%(version)s.tar.xz',
                    'mpc': 'https://ftp.gnu.org/gnu/mpc/mpc-%(version)s.tar.gz',
-                   'mpfr': 'https://ftp.gnu.org/gnu/mpfr/mpfr-%(version)s.tar.xz'}
+                   'mpfr': 'https://ftp.gnu.org/gnu/mpfr/mpfr-%(version)s.tar.xz',
+                   'mig': 'https://ftp.gnu.org/gnu/mig/mig-%(version)s.tar.bz2',
+                   'gnumach': 'https://ftp.gnu.org/gnu/gnumach/gnumach-%(version)s.tar.bz2',
+                   'hurd': 'https://ftp.gnu.org/gnu/hurd/hurd-%(version)s.tar.bz2'}
         if component not in url_map:
             print('error: component %s coming from tarball' % component)
             exit(1)
-        url = url_map[component] % {'version': version}
+        version_major = version.split('.')[0]
+        url = url_map[component] % {'version': version, 'major': version_major}
         filename = os.path.join(self.srcdir, url.split('/')[-1])
         response = urllib.request.urlopen(url)
         data = response.read()
@@ -903,7 +1006,8 @@ class Context(object):
                 self.clear_last_build_state(a)
             self.exec_self()
         check_components = {'host-libraries': ('gmp', 'mpfr', 'mpc'),
-                            'compilers': ('binutils', 'gcc', 'glibc', 'linux'),
+                            'compilers': ('binutils', 'gcc', 'glibc', 'linux',
+                                          'mig', 'gnumach', 'hurd'),
                             'glibcs': ('glibc',)}
         must_build = {}
         for a in actions:
@@ -937,6 +1041,9 @@ class Context(object):
                 self.clear_last_build_state(a)
             else:
                 print('No need to rebuild %s.' % a)
+        if os.access(self.logsdir, os.F_OK):
+            shutil.rmtree(self.logsdir_old, ignore_errors=True)
+            shutil.copytree(self.logsdir, self.logsdir_old)
         for a in actions:
             if must_build[a]:
                 build_time = datetime.datetime.utcnow()
@@ -948,6 +1055,15 @@ class Context(object):
 
     def bot_build_mail(self, action, build_time):
         """Send email with the results of a build."""
+        if not ('email-from' in self.bot_config and
+                'email-server' in self.bot_config and
+                'email-subject' in self.bot_config and
+                'email-to' in self.bot_config):
+            if not self.email_warning:
+                print("Email not configured, not sending.")
+                self.email_warning = True
+            return
+
         build_time = build_time.replace(microsecond=0)
         subject = (self.bot_config['email-subject'] %
                    {'action': action,
@@ -1004,13 +1120,33 @@ class Context(object):
         with smtplib.SMTP(self.bot_config['email-server']) as s:
             s.send_message(msg)
 
-    def bot_run_self(self, opts, action):
+    def bot_run_self(self, opts, action, check=True):
         """Run a copy of this script with given options."""
         cmd = [sys.executable, sys.argv[0], '--keep=none',
                '-j%d' % self.parallelism]
+        if self.full_gcc:
+            cmd.append('--full-gcc')
         cmd.extend(opts)
         cmd.extend([self.topdir, action])
-        subprocess.run(cmd, check=True)
+        sys.stdout.flush()
+        subprocess.run(cmd, check=check)
+
+    def bot(self):
+        """Run repeated rounds of checkout and builds."""
+        while True:
+            self.load_bot_config_json()
+            if not self.bot_config['run']:
+                print('Bot exiting by request.')
+                exit(0)
+            self.bot_run_self([], 'bot-cycle', check=False)
+            self.load_bot_config_json()
+            if not self.bot_config['run']:
+                print('Bot exiting by request.')
+                exit(0)
+            time.sleep(self.bot_config['delay'])
+            if self.get_script_text() != self.script_text:
+                print('Script changed, bot re-execing.')
+                self.exec_self()
 
 
 class Config(object):
@@ -1072,6 +1208,10 @@ class Config(object):
         if self.os.startswith('linux'):
             self.install_linux_headers(cmdlist)
         self.build_gcc(cmdlist, True)
+        if self.os == 'gnu':
+            self.install_gnumach_headers(cmdlist)
+            self.build_cross_tool(cmdlist, 'mig', 'mig')
+            self.install_hurd_headers(cmdlist)
         for g in self.compiler_glibcs:
             cmdlist.push_subdesc('glibc')
             cmdlist.push_subdesc(g.name)
@@ -1100,7 +1240,17 @@ class Config(object):
             cfg_cmd.extend(extra_opts)
         cmdlist.add_command('configure', cfg_cmd)
         cmdlist.add_command('build', ['make'])
-        cmdlist.add_command('install', ['make', 'install'])
+        # Parallel "make install" for GCC has race conditions that can
+        # cause it to fail; see
+        # <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=42980>.  Such
+        # problems are not known for binutils, but doing the
+        # installation in parallel within a particular toolchain build
+        # (as opposed to installation of one toolchain from
+        # build-many-glibcs.py running in parallel to the installation
+        # of other toolchains being built) is not known to be
+        # significantly beneficial, so it is simplest just to disable
+        # parallel install for cross tools here.
+        cmdlist.add_command('install', ['make', '-j1', 'install'])
         cmdlist.cleanup_dir()
         cmdlist.pop_subdesc()
 
@@ -1109,6 +1259,7 @@ class Config(object):
         arch_map = {'aarch64': 'arm64',
                     'alpha': 'alpha',
                     'arm': 'arm',
+                    'csky': 'csky',
                     'hppa': 'parisc',
                     'i486': 'x86',
                     'i586': 'x86',
@@ -1121,9 +1272,10 @@ class Config(object):
                     'nios2': 'nios2',
                     'powerpc': 'powerpc',
                     's390': 's390',
+                    'riscv32': 'riscv',
+                    'riscv64': 'riscv',
                     'sh': 'sh',
                     'sparc': 'sparc',
-                    'tile': 'tile',
                     'x86_64': 'x86'}
         linux_arch = None
         for k in arch_map:
@@ -1144,13 +1296,50 @@ class Config(object):
         cmdlist.cleanup_dir()
         cmdlist.pop_subdesc()
 
+    def install_gnumach_headers(self, cmdlist):
+        """Install GNU Mach headers."""
+        srcdir = self.ctx.component_srcdir('gnumach')
+        builddir = self.component_builddir('gnumach')
+        cmdlist.push_subdesc('gnumach')
+        cmdlist.create_use_dir(builddir)
+        cmdlist.add_command('configure',
+                            [os.path.join(srcdir, 'configure'),
+                             '--build=%s' % self.ctx.build_triplet,
+                             '--host=%s' % self.triplet,
+                             '--prefix=',
+                             'CC=%s-gcc -nostdlib' % self.triplet])
+        cmdlist.add_command('install', ['make', 'DESTDIR=%s' % self.sysroot,
+                                        'install-data'])
+        cmdlist.cleanup_dir()
+        cmdlist.pop_subdesc()
+
+    def install_hurd_headers(self, cmdlist):
+        """Install Hurd headers."""
+        srcdir = self.ctx.component_srcdir('hurd')
+        builddir = self.component_builddir('hurd')
+        cmdlist.push_subdesc('hurd')
+        cmdlist.create_use_dir(builddir)
+        cmdlist.add_command('configure',
+                            [os.path.join(srcdir, 'configure'),
+                             '--build=%s' % self.ctx.build_triplet,
+                             '--host=%s' % self.triplet,
+                             '--prefix=',
+                             '--disable-profile', '--without-parted',
+                             'CC=%s-gcc -nostdlib' % self.triplet])
+        cmdlist.add_command('install', ['make', 'prefix=%s' % self.sysroot,
+                                        'no_deps=t', 'install-headers'])
+        cmdlist.cleanup_dir()
+        cmdlist.pop_subdesc()
+
     def build_gcc(self, cmdlist, bootstrap):
         """Build GCC."""
-        # libsanitizer commonly breaks because of glibc header
-        # changes, or on unusual targets.  libssp is of little
-        # relevance with glibc's own stack checking support.
+        # libssp is of little relevance with glibc's own stack
+        # checking support.  libcilkrts does not support GNU/Hurd (and
+        # has been removed in GCC 8, so --disable-libcilkrts can be
+        # removed once glibc no longer supports building with older
+        # GCC versions).
         cfg_opts = list(self.gcc_cfg)
-        cfg_opts += ['--disable-libsanitizer', '--disable-libssp']
+        cfg_opts += ['--disable-libssp', '--disable-libcilkrts']
         host_libs = self.ctx.host_libraries_installdir
         cfg_opts += ['--with-gmp=%s' % host_libs,
                      '--with-mpfr=%s' % host_libs,
@@ -1173,14 +1362,20 @@ class Config(object):
                          '--disable-libitm',
                          '--disable-libmpx',
                          '--disable-libquadmath',
+                         '--disable-libsanitizer',
                          '--without-headers', '--with-newlib',
                          '--with-glibc-version=%s' % self.ctx.glibc_version
                          ]
             cfg_opts += self.first_gcc_cfg
         else:
             tool_build = 'gcc'
-            cfg_opts += ['--enable-languages=c,c++', '--enable-shared',
-                         '--enable-threads']
+            # libsanitizer commonly breaks because of glibc header
+            # changes, or on unusual targets.
+            if not self.ctx.full_gcc:
+                cfg_opts += ['--disable-libsanitizer']
+            langs = 'all' if self.ctx.full_gcc else 'c,c++'
+            cfg_opts += ['--enable-languages=%s' % langs,
+                         '--enable-shared', '--enable-threads']
         self.build_cross_tool(cmdlist, 'gcc', tool_build, cfg_opts)
 
 
@@ -1244,25 +1439,16 @@ class Glibc(object):
                                                    self.compiler.name, 'glibc',
                                                    self.name)
             installdir = self.compiler.sysroot
-            srcdir_copy = self.ctx.component_builddir('compilers',
-                                                      self.compiler.name,
-                                                      'glibc-src',
-                                                      self.name)
         else:
             builddir = self.ctx.component_builddir('glibcs', self.name,
                                                    'glibc')
             installdir = self.ctx.glibc_installdir(self.name)
-            srcdir_copy = self.ctx.component_builddir('glibcs', self.name,
-                                                      'glibc-src')
         cmdlist.create_use_dir(builddir)
-        # glibc builds write into the source directory, and even if
-        # not intentionally there is a risk of bugs that involve
-        # writing into the working directory.  To avoid possible
-        # concurrency issues, copy the source directory.
-        cmdlist.create_copy_dir(srcdir, srcdir_copy)
-        cfg_cmd = [os.path.join(srcdir_copy, 'configure'),
-                   '--prefix=/usr',
-                   '--enable-add-ons',
+        use_usr = self.os != 'gnu'
+        prefix = '/usr' if use_usr else ''
+        cfg_cmd = [os.path.join(srcdir, 'configure'),
+                   '--prefix=%s' % prefix,
+                   '--enable-profile',
                    '--build=%s' % self.ctx.build_triplet,
                    '--host=%s' % self.triplet,
                    'CC=%s' % self.tool_name('gcc'),
@@ -1276,6 +1462,8 @@ class Glibc(object):
                    'RANLIB=%s' % self.tool_name('ranlib'),
                    'READELF=%s' % self.tool_name('readelf'),
                    'STRIP=%s' % self.tool_name('strip')]
+        if self.os == 'gnu':
+            cfg_cmd += ['MIG=%s' % self.tool_name('mig')]
         cfg_cmd += self.cfg
         cmdlist.add_command('configure', cfg_cmd)
         cmdlist.add_command('build', ['make'])
@@ -1283,15 +1471,20 @@ class Glibc(object):
                                         'install_root=%s' % installdir])
         # GCC uses paths such as lib/../lib64, so make sure lib
         # directories always exist.
-        cmdlist.add_command('mkdir-lib', ['mkdir', '-p',
-                                          os.path.join(installdir, 'lib'),
-                                          os.path.join(installdir,
-                                                       'usr', 'lib')])
+        mkdir_cmd = ['mkdir', '-p',
+                     os.path.join(installdir, 'lib')]
+        if use_usr:
+            mkdir_cmd += [os.path.join(installdir, 'usr', 'lib')]
+        cmdlist.add_command('mkdir-lib', mkdir_cmd)
         if not for_compiler:
+            if self.ctx.strip:
+                cmdlist.add_command('strip',
+                                    ['sh', '-c',
+                                     ('%s $(find %s/lib* -name "*.so")' %
+                                      (self.tool_name('strip'), installdir))])
             cmdlist.add_command('check', ['make', 'check'])
             cmdlist.add_command('save-logs', [self.ctx.save_logs],
                                 always_run=True)
-        cmdlist.cleanup_dir('cleanup-src', srcdir_copy)
         cmdlist.cleanup_dir()
 
 
@@ -1376,14 +1569,6 @@ class CommandList(object):
         self.add_command_dir('mkdir', None, ['mkdir', '-p', dir])
         self.use_dir(dir)
 
-    def create_copy_dir(self, src, dest):
-        """Remove a directory and recreate it as a copy from the given
-        source."""
-        self.add_command_dir('copy-rm', None, ['rm', '-rf', dest])
-        parent = os.path.dirname(dest)
-        self.add_command_dir('copy-mkdir', None, ['mkdir', '-p', parent])
-        self.add_command_dir('copy', None, ['cp', '-a', src, dest])
-
     def add_command_dir(self, desc, dir, command, always_run=False):
         """Add a command to run in a given directory."""
         cmd = Command(self.desc_txt(desc), len(self.cmdlist), dir, self.path,
@@ -1460,12 +1645,16 @@ def get_parser():
     parser.add_argument('--replace-sources', action='store_true',
                         help='Remove and replace source directories '
                         'with the wrong version of a component')
+    parser.add_argument('--strip', action='store_true',
+                        help='Strip installed glibc libraries')
+    parser.add_argument('--full-gcc', action='store_true',
+                        help='Build GCC with all languages and libsanitizer')
     parser.add_argument('topdir',
                         help='Toplevel working directory')
     parser.add_argument('action',
                         help='What to do',
-                        choices=('checkout', 'bot-cycle', 'host-libraries',
-                                 'compilers', 'glibcs'))
+                        choices=('checkout', 'bot-cycle', 'bot',
+                                 'host-libraries', 'compilers', 'glibcs'))
     parser.add_argument('configs',
                         help='Versions to check out or configurations to build',
                         nargs='*')
@@ -1478,7 +1667,7 @@ def main(argv):
     opts = parser.parse_args(argv)
     topdir = os.path.abspath(opts.topdir)
     ctx = Context(topdir, opts.parallelism, opts.keep, opts.replace_sources,
-                  opts.action)
+                  opts.strip, opts.full_gcc, opts.action)
     ctx.run_builds(opts.action, opts.configs)