diff --git a/util/build_cross_gcc/build_cross_gcc.py b/util/build_cross_gcc/build_cross_gcc.py deleted file mode 100755 index 3afd4bf347..0000000000 --- a/util/build_cross_gcc/build_cross_gcc.py +++ /dev/null @@ -1,831 +0,0 @@ -#! /usr/bin/env python -# Copyright 2020 Google, Inc. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer; -# redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution; -# neither the name of the copyright holders nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import abc -import argparse -import glob -import multiprocessing -import os -import os.path -import pickle -import shutil -import subprocess -import textwrap - -SETTINGS_FILE = '.build_cross_gcc.settings' -LOG_FILE = 'build_cross_gcc.log' - -all_settings = {} -all_steps = {} - -description_paragraphs = [ - ''' - This script helps automate building a gcc based cross compiler. - The process is broken down into a series of steps which can be - executed one at a time or in arbtitrary sequences. It's assumed that - you've already downloaded the following sources into the current - directory:''', - '', - '''1. binutils''', - '''2. gcc''', - '''3. glibc''', - '''4. linux kernel''', - '''5. gdb''', - '', - ''' - The entire process can be configured with a series of settings - which are stored in a config file called {settings_file}. These - settings can generally also be set from the command line, and at run - time using step 0 of the process. Many will set themselves to - reasonable defaults if no value was loaded from a previous - configuration or a saved settings file.''', - '', - ''' - Prebaked config options can be loaded in from an external file to - make it easier to build particular cross compilers without having to - mess with a lot of options.''' - '', - ''' - When settings are listed, any setting which has a value which has - failed validation or which hasn't been set and doesn't have a - reasonable default will be marked with a X in the far left hand - column. Settings will generally refuse to be set to invalid values, - unless they were like that by default and the user refused to correct - them.''', - '', - '''This script is based on the excellent how-to here:''', - '''https://preshing.com/20141119/how-to-build-a-gcc-cross-compiler/''', - '', - ''' - Please view that webpage for a detailed explanation of what this - script does.''' - ] - -def help_text_wrapper(text): - width = shutil.get_terminal_size().columns - text = textwrap.dedent(text) - text = text.strip() - return textwrap.fill(text, width=width) - -description = '\n'.join(list(map(help_text_wrapper, description_paragraphs))) - -argparser = argparse.ArgumentParser( - formatter_class=argparse.RawDescriptionHelpFormatter, - description=description) - - -# -# Some helper utilities. -# - -def confirm(prompt): - while True: - yn = input('{} (N/y): '.format(prompt)) - if yn == '': - yn = 'n' - if yn.lower() in ('y', 'Yes'): - return True - elif yn.lower() in ('n', 'No'): - return False - - -def setup_build_dir(subdir): - build_dir_base = BuildDirBase.setting() - target = Target.setting() - if not (build_dir_base.valid and target.valid): - return False - target_build_dir = os.path.join(build_dir_base.get(), target.get()) - build_dir = os.path.join(target_build_dir, 'build-{}'.format(subdir)) - if not os.path.isdir(build_dir): - os.makedirs(build_dir) - return build_dir - -def run_commands(working_dir, *cmds): - with open(LOG_FILE, 'a') as log: - print('In working directory {:s} (log in {:s}):'.format( - working_dir, LOG_FILE)) - for cmd in cmds: - print(textwrap.fill(cmd, initial_indent=' ', - subsequent_indent=' ', - width=shutil.get_terminal_size().columns)) - print('', file=log) - print(cmd, file=log) - print('', file=log) - if subprocess.call(cmd, shell=True, cwd=working_dir, - stdout=log, stderr=subprocess.STDOUT) != 0: - return False - return True - - -# -# Settings. -# - -class MetaSetting(abc.ABCMeta): - def __new__(mcls, name, bases, d): - cls = super().__new__(mcls, name, bases, d) - key = d.get('key', None) - if key is not None: - assert('default' in d) - instance = cls() - instance.value = None - instance.valid = False - all_settings[key] = instance - return cls - -class Setting(object, metaclass=MetaSetting): - key = None - - @abc.abstractmethod - def set(self, value): - 'Validate and set the setting to "value", and return if successful.' - self.value = value - self.valid = True - return True - - def set_default(self): - 'Set this setting to its default value, and return if successful.' - return self.set(self.default) - - def set_arg(self, value): - 'Set this setting to value if not None, and return if successful.' - if value: - return self.set(value) - else: - # Nothing happened, so nothing failed. - return True - - def get(self): - 'Return the value of this setting.' - return self.value - - @abc.abstractmethod - def describe(self): - 'Return a string describing this setting.' - return '' - - @abc.abstractmethod - def add_to_argparser(self, argparser): - 'Add command line options associated with this setting.' - - @abc.abstractmethod - def set_from_args(self, args): - 'Set this setting from the command line arguments, if requested.' - return True - - @classmethod - def setting(cls): - s = all_settings[cls.key] - if not s.valid: - print('"{}" is not valid.'.format(s.key)) - return s - -class DirectorySetting(Setting): - def set(self, value): - if not os.path.exists(value): - print('Path "{:s}" does not exist.'.format(value)) - elif not os.path.isdir(value): - print('Path "{:s}" is not a directory.'.format(value)) - else: - self.value = value - self.valid = True - return self.valid - - def set_default(self): - if not self.set(self.default): - if not os.path.exists(self.default): - if confirm('Create?'): - try: - os.mkdirs(value) - assert(self.set(self.default)) - except: - print('Failed to make directory') - self.valid = False - return False - else: - self.value = self.default - self.valid = False - return False - -class Prefix(DirectorySetting): - default = os.path.join(os.environ['HOME'], 'cross') - key = 'PREFIX' - - def describe(self): - return 'Path prefix to install to.' - - def add_to_argparser(self, parser): - parser.add_argument('--prefix', help=self.describe()) - - def set_from_args(self, args): - return self.set_arg(args.prefix) - -class BuildDirBase(DirectorySetting): - default = os.getcwd() - key = 'BUILD_DIR_BASE' - - def describe(self): - return 'Path prefix for build directory(ies).' - - def add_to_argparser(self, parser): - parser.add_argument('--build-dir-base', help=self.describe()) - - def set_from_args(self, args): - return self.set_arg(args.build_dir_base) - -class Target(Setting): - key = 'TARGET' - default = None - - def set_default(self): - self.value = '(not set)' - self.valid = False - return False - - def describe(self): - return 'Tuple for the target architecture.' - - def add_to_argparser(self, parser): - parser.add_argument('--target', help=self.describe()) - - def set_from_args(self, args): - return self.set_arg(args.target) - -class LinuxArch(Setting): - key = 'LINUX_ARCH' - default = None - - def set_default(self): - self.value = '(not set)' - self.valid = False - return False - - def describe(self): - return 'The arch directory for Linux headers.' - - def add_to_argparser(self, parser): - parser.add_argument('--linux-arch', help=self.describe()) - - def set_from_args(self, args): - return self.set_arg(args.linux_arch) - -class SourceDirSetting(Setting): - def set(self, value): - if os.path.isdir(value): - self.value = value - self.valid = True - return self.valid - - def set_default(self): - matches = list(filter(os.path.isdir, glob.glob(self.pattern))) - if len(matches) == 0: - self.valid = False - return False - if len(matches) > 1: - while True: - print() - print('Multple options for "{:s}":'.format(self.key)) - choices = list(enumerate(matches)) - for number, value in choices: - print('{:>5}: {:s}'.format(number, value)) - choice = input('Which one? ') - try: - choice = choices[int(choice)][1] - except: - print('Don\'t know what to do with "{:s}".'.format(choice)) - continue - return self.set(choice) - return self.set(matches[0]) - - def describe(self): - return 'Directory with the extracted {} source.'.format(self.project) - -class BinutilsSourceDir(SourceDirSetting): - key = 'BINUTILS_SRC_DIR' - default = None - pattern = 'binutils-*' - project = 'binutils' - - def add_to_argparser(self, parser): - parser.add_argument('--binutils-src', help=self.describe()) - - def set_from_args(self, args): - return self.set_arg(args.binutils_src) - -class GccSourceDir(SourceDirSetting): - key = 'GCC_SRC_DIR' - default = None - pattern = 'gcc-*' - project = 'gcc' - - def add_to_argparser(self, parser): - parser.add_argument('--gcc-src', help=self.describe()) - - def set_from_args(self, args): - return self.set_arg(args.gcc_src) - -class GlibcSourceDir(SourceDirSetting): - key = 'GLIBC_SRC_DIR' - default = None - pattern = 'glibc-*' - project = 'glibc' - - def add_to_argparser(self, parser): - parser.add_argument('--glibc-src', help=self.describe()) - - def set_from_args(self, args): - return self.set_arg(args.glibc_src) - -class LinuxSourceDir(SourceDirSetting): - key = 'LINUX_SRC_DIR' - default = None - pattern = 'linux-*' - project = 'linux' - - def add_to_argparser(self, parser): - parser.add_argument('--linux-src', help=self.describe()) - - def set_from_args(self, args): - return self.set_arg(args.linux_src) - -class GdbSourceDir(SourceDirSetting): - key = 'GDB_SRC_DIR' - default = None - pattern = 'gdb-*' - project = 'gdb' - - def add_to_argparser(self, parser): - parser.add_argument('--gdb-src', help=self.describe()) - - def set_from_args(self, args): - return self.set_arg(args.gdb_src) - -class Parallelism(Setting): - key = 'J' - default = None - - def set(self, value): - try: - value = int(value) - except: - print('Can\'t convert "{:s}" into an integer.'.format(value)) - if value < 0: - print('Parallelism can\'t be negative.') - return False - self.value = value - self.valid = True - return self.valid - - def set_default(self): - self.set(multiprocessing.cpu_count()) - - def describe(self): - return 'The level of parellism to request from "make".' - - def add_to_argparser(self, parser): - parser.add_argument('-j', help=self.describe()) - - def set_from_args(self, args): - return self.set_arg(args.j) - - - -# -# Steps of the build process. -# - -class MetaStep(abc.ABCMeta): - def __new__(mcls, name, bases, d): - cls = super().__new__(mcls, name, bases, d) - number = d.get('number', None) - if number is not None: - all_steps[number] = cls() - return cls - -class Step(object, metaclass=MetaStep): - 'Steps to set up a cross compiling gcc.' - number = None - - @abc.abstractmethod - def run(self): - 'Execute this step.' - pass - - @abc.abstractmethod - def describe(self): - 'Return a string describing this step.' - return '' - - -class Configure(Step): - number = 0 - - def describe(self): - return 'Adjust settings.' - - def get_setting(self): - settings = list(enumerate(all_settings.items())) - all_keys = list(all_settings.keys()) - max_key_length = max([len(key) for key in all_keys]) - while True: - for number, (key, setting) in settings: - print('{}{:>4}: {:{key_len}s} - {:s}'.format( - ' ' if setting.valid else 'X', - number, key, setting.describe(), key_len=max_key_length)) - print(' {}'.format(setting.value)) - print() - key = input('Value to modify, or "done": ') - if key == "done": - save_settings() - return None - if key not in all_keys: - try: - key = settings[int(key)][1][0] - except: - print('Don\'t know what to do with "{:s}."'.format(key)) - continue - return all_settings[key] - - def run(self): - while True: - setting = self.get_setting() - if not setting: - return True - - new_value = input('New value ({:s}): '.format(setting.get())) - if new_value: - setting.set(new_value) - save_settings() - - print_settings() - return True - -class BuildBinutils(Step): - number = 1 - - def describe(self): - return 'Build binutils.' - - def run(self): - prefix = Prefix.setting() - target = Target.setting() - j = Parallelism.setting() - source_dir = BinutilsSourceDir.setting() - build_dir = setup_build_dir('binutils') - - if not all((prefix, target, j, source_dir, build_dir)): - return False - - prefix = prefix.get() - target = target.get() - j = j.get() - build_dir = os.path.abspath(build_dir) - source_dir = os.path.abspath(source_dir.get()) - - return run_commands(build_dir, - '{configure} --prefix={prefix} --target={target} ' - '--disable-multilib'.format( - configure=os.path.join(source_dir, 'configure'), - prefix=prefix, target=target), - 'make -j{j}'.format(j=j), - 'make install' - ) - -class InstallLinuxHeaders(Step): - number = 2 - - def describe(self): - return 'Install Linux headers.' - - def run(self): - source_dir = LinuxSourceDir.setting() - linux_arch = LinuxArch.setting() - prefix = Prefix.setting() - target = Target.setting() - - if not all((source_dir, linux_arch, prefix, target)): - return False - - source_dir = os.path.abspath(source_dir.get()) - linux_arch = linux_arch.get() - prefix = os.path.abspath(prefix.get()) - target = target.get() - - hdr_path = os.path.join(prefix, target) - - return run_commands(source_dir, - 'make ARCH={arch} INSTALL_HDR_PATH={hdr_path} ' - 'headers_install'.format(arch=linux_arch, hdr_path=hdr_path)) - -class Compilers(Step): - number = 3 - - def describe(self): - return 'Build C and C++ compilers.' - - def run(self): - prefix = Prefix.setting() - target = Target.setting() - j = Parallelism.setting() - source_dir = GccSourceDir.setting() - build_dir = setup_build_dir('gcc') - - if not all((prefix, target, j, source_dir, build_dir)): - return False - - prefix = prefix.get() - target = target.get() - j = j.get() - build_dir = os.path.abspath(build_dir) - source_dir = os.path.abspath(source_dir.get()) - - return run_commands(build_dir, - '{configure} --prefix={prefix} --target={target} ' - '--enable-languages=c,c++ --disable-multilib'.format( - configure=os.path.join(source_dir, 'configure'), - prefix=prefix, target=target), - 'make -j{j} all-gcc LIMITS_H_TEST=true'.format(j=j), - 'make install-gcc' - ) - -class CHeaders(Step): - number = 4 - - def describe(self): - return 'Standard C library headers and startup files.' - - def run(self): - prefix = Prefix.setting() - target = Target.setting() - j = Parallelism.setting() - source_dir = GlibcSourceDir.setting() - build_dir = setup_build_dir('glibc') - - if not all((prefix, target, j, source_dir, build_dir)): - return False - - prefix = prefix.get() - target = target.get() - j = j.get() - source_dir = os.path.abspath(source_dir.get()) - build_dir = os.path.abspath(build_dir) - - return run_commands(build_dir, - '{configure} --prefix={prefix} --build=$MACHTYPE ' - '--host={host} --target={target} --with-headers={hdr_path} ' - '--disable-multilib libc_cv_forced_unwind=yes'.format( - configure=os.path.join(source_dir, 'configure'), - prefix=os.path.join(prefix, target), - host=target, target=target, - hdr_path=os.path.join(prefix, target, 'include')), - 'make install-bootstrap-headers=yes install-headers', - 'make -j{j} csu/subdir_lib'.format(j=j), - 'install csu/crt1.o csu/crti.o csu/crtn.o {lib_path}'.format( - lib_path=os.path.join(prefix, target, 'lib')), - '{target}-gcc -nostdlib -nostartfiles -shared -x c /dev/null ' - '-o {libc_so}'.format(target=target, - libc_so=os.path.join(prefix, target, 'lib', 'libc.so')), - 'touch {stubs_h}'.format(stubs_h=os.path.join( - prefix, target, 'include', 'gnu', 'stubs.h')) - ) - -class CompilerSupportLib(Step): - number = 5 - - def describe(self): - return 'Build the compiler support library.' - - def run(self): - j = Parallelism.setting() - build_dir = setup_build_dir('gcc') - - if not all((j, build_dir)): - return False - - j = j.get() - build_dir = os.path.abspath(build_dir) - - return run_commands(build_dir, - 'make -j{j} all-target-libgcc'.format(j=j), - 'make install-target-libgcc' - ) - -class StandardCLib(Step): - number = 6 - - def describe(self): - return 'Install the standard C library.' - - def run(self): - j = Parallelism.setting() - build_dir = setup_build_dir('glibc') - - if not all((j, build_dir)): - return False - - j = j.get() - build_dir = os.path.abspath(build_dir) - - return run_commands(build_dir, - 'make -j{j}'.format(j=j), - 'make install', - ) - -class BuildGdb(Step): - number = 7 - - def describe(self): - return 'Build GDB.' - - def run(self): - prefix = Prefix.setting() - target = Target.setting() - j = Parallelism.setting() - source_dir = GdbSourceDir.setting() - build_dir = setup_build_dir('gdb') - - if not all((prefix, target, j, source_dir, build_dir)): - return False - - prefix = prefix.get() - target = target.get() - j = j.get() - source_dir = os.path.abspath(source_dir.get()) - build_dir = os.path.abspath(build_dir) - - return run_commands(build_dir, - '{configure} --prefix={prefix} --target={target} ' - '$MACHTYPE'.format(prefix=prefix, target=target, - configure=os.path.join(source_dir, 'configure')), - 'make -j{j}'.format(j=j), - 'make install' - ) - -class StandardCxxLib(Step): - number = 8 - - def describe(self): - return 'Install the standard C++ library.' - - def run(self): - j = Parallelism.setting() - build_dir = setup_build_dir('gcc') - - if not all((j, build_dir)): - return False - - j = j.get() - build_dir = os.path.abspath(build_dir) - - return run_commands(build_dir, - 'make -j{j}'.format(j=j), - 'make install' - ) - - -# -# The engine that makes it all go. -# - -def get_steps(): - while True: - print() - print('Steps:') - for _, step in sorted(all_steps.items()): - print('{:>5} {:s}'.format( - '{:d}:'.format(step.number), step.describe())) - print() - steps = input('Comma separated list of steps, or ' - '"exit", or "all" (all): ') - if not steps: - steps = 'all' - if steps == 'exit': - return [] - if steps == 'all': - keys = list([str(key) for key in all_steps.keys()]) - steps = ','.join(keys) - try: - return list([all_steps[int(i)] for i in steps.split(",")]) - except: - print('Don\'t know what to do with "{:s}"'.format(steps)) - -def print_settings(): - print() - print('Settings:') - for setting in all_settings.values(): - print('{} {} = {}'.format( - ' ' if setting.valid else 'X', setting.key, setting.value)) - -def save_settings(): - settings = {} - for setting in all_settings.values(): - if setting.valid: - settings[setting.key] = setting.get() - with open(SETTINGS_FILE, 'wb') as settings_file: - pickle.dump(settings, settings_file) - -def load_settings(): - if os.path.exists(SETTINGS_FILE): - with open(SETTINGS_FILE, 'rb') as settings_file: - settings = pickle.load(settings_file) - else: - settings = {} - - for setting in all_settings.values(): - if setting.key in settings: - setting.set(settings[setting.key]) - -def load_settings_file(path): - with open(path, 'r') as settings: - for line in settings.readlines(): - if not line: - continue - try: - key, val = line.split('=') - except: - print('Malformated line "{}" in settings file "{}".'.format( - line, path)) - return False - key = key.strip() - val = val.strip() - if key not in all_settings: - print('Unknown setting "{}" found in settings ' - 'file "{}".'.format(key, path)) - return False - setting = all_settings[key] - if not setting.set(val): - print('Failed to set "{}" to "{}" from ' - 'settings file "{}".'.format(key, val, path)) - return False - return True - - - -argparser.add_argument('--settings-file', - help='A file with name=value settings to load.') - -def main(): - # Install command line options for each setting. - for setting in all_settings.values(): - setting.add_to_argparser(argparser) - - args = argparser.parse_args() - - # Load settings from the last time we ran. Lowest priority. - load_settings() - - # If requested, read in a settings file. Medium priority. - if args.settings_file: - if not load_settings_file(args.settings_file): - return - - # Set settings based on command line options. Highest priority. - for setting in all_settings.values(): - setting.set_from_args(args) - - # If a setting is still not valid, try setting it to its default. - for setting in all_settings.values(): - if not setting.valid: - setting.set_default() - - # Print out the resulting settings. - print_settings() - - while True: - steps = get_steps() - if not steps: - return - for step in steps: - print() - print('Step {:d}: {:s}'.format(step.number, step.describe())) - print() - if not step.run(): - print() - print('Step failed, aborting.') - break - -if __name__ == "__main__": - main() diff --git a/util/build_cross_gcc/settings.aarch64 b/util/build_cross_gcc/settings.aarch64 deleted file mode 100644 index d2b21f690d..0000000000 --- a/util/build_cross_gcc/settings.aarch64 +++ /dev/null @@ -1,2 +0,0 @@ -TARGET=aarch64-linux-gnu -LINUX_ARCH=arm64 diff --git a/util/build_cross_gcc/settings.arm b/util/build_cross_gcc/settings.arm deleted file mode 100644 index 7f3eff31ec..0000000000 --- a/util/build_cross_gcc/settings.arm +++ /dev/null @@ -1,2 +0,0 @@ -TARGET=arm-linux-gnueabihf -LINUX_ARCH=arm diff --git a/util/build_cross_gcc/settings.mips b/util/build_cross_gcc/settings.mips deleted file mode 100644 index c29e4ad650..0000000000 --- a/util/build_cross_gcc/settings.mips +++ /dev/null @@ -1,2 +0,0 @@ -TARGET=mipsel-linux-gnu -LINUX_ARCH=mips diff --git a/util/build_cross_gcc/settings.power b/util/build_cross_gcc/settings.power deleted file mode 100644 index 998a2bc036..0000000000 --- a/util/build_cross_gcc/settings.power +++ /dev/null @@ -1,2 +0,0 @@ -TARGET=powerpc-linux-gnu -LINUX_ARCH=powerpc diff --git a/util/build_cross_gcc/settings.riscv b/util/build_cross_gcc/settings.riscv deleted file mode 100644 index d1910e81b1..0000000000 --- a/util/build_cross_gcc/settings.riscv +++ /dev/null @@ -1,2 +0,0 @@ -TARGET=riscv64-linux-gnu -LINUX_ARCH=riscv diff --git a/util/build_cross_gcc/settings.sparc b/util/build_cross_gcc/settings.sparc deleted file mode 100644 index cc965305a1..0000000000 --- a/util/build_cross_gcc/settings.sparc +++ /dev/null @@ -1,2 +0,0 @@ -TARGET=sparc64-linux-gnu -LINUX_ARCH=sparc