#!/usr/bin/env python3 import argparse import collections import os import common class Component: ''' Yes, we are re-inventing a crappy dependency resolution system. I can't believe it. The hard part is that we have optional dependencies as well... e.g. buildroot optionally depends on m5 to put m5 in the root filesystem, and buildroot optionally depends on qemu to build the qcow2 version of the image. ''' def __init__(self, build_callback=None, dependencies=None): self.build_callback = build_callback if dependencies is None: self.dependencies = [] else: self.dependencies = dependencies def build(self, arch): if self.build_callback is not None: self.build_callback(arch) def build_baremetal(arch): run_cmd(['build-crosstool-ng'], arch) run_cmd(['build-baremetal'], arch) run_cmd(['build-baremetal', '--gem5'], arch) run_cmd(['build-baremetal', '--gem5', '--machine', 'RealViewPBX'], arch) def run_cmd(cmd, arch): global args cmd_abs = cmd.copy() cmd_abs[0] = os.path.join(common.root_dir, cmd[0]) cmd_abs.extend(['--arch', arch]) if args.extra_args: cmd_abs.append(args.extra_args) common.run_cmd(cmd_abs, dry_run=args.dry_run) name_to_component_map = { # Leaves without dependencies. 'baremetal': Component( lambda arch: build_baremetal(arch), ), 'buildroot': Component( lambda arch: run_cmd(['build-buildroot'], arch), ), 'buildroot-gcc': Component( lambda arch: run_cmd(['build-buildroot'], arch), ), 'copy-overlay': Component( lambda arch: run_cmd(['copy-overlay'], arch), ), 'gem5': Component( lambda arch: run_cmd(['build-gem5'], arch), ), 'gem5-debug': Component( lambda arch: run_cmd(['build-gem5', '--gem5-build-type', 'debug'], arch), ), 'gem5-fast': Component( lambda arch: run_cmd(['build-gem5', '--gem5-build-type', 'fast'], arch), ), 'linux': Component( lambda arch: run_cmd(['build-linux'], arch), ), 'modules': Component( lambda arch: run_cmd(['build-modules'], arch), ), 'm5': Component( lambda arch: run_cmd(['build-m5'], arch), ), 'qemu': Component( lambda arch: run_cmd(['build-qemu'], arch), ), 'qemu-user': Component( lambda arch: run_cmd(['build-qemu', '--userland'], arch), ), 'userland': Component( lambda arch: run_cmd(['build-userland'], arch), ), # Dependency only nodes. 'all-linux': Component(dependencies=[ 'qemu-gem5-buildroot', 'gem5-debug', 'gem5-fast', 'qemu-user', ]), 'gem5-buildroot': Component(dependencies=[ 'buildroot-gcc', 'linux', 'm5', 'overlay', 'gem5', ]), 'overlay': Component(dependencies=[ 'copy-overlay', 'modules', 'userland', 'buildroot', ]), 'qemu-buildroot': Component(dependencies=[ 'qemu', 'buildroot-gcc', 'overlay', 'linux', ]), 'qemu-gem5-buildroot': Component(dependencies=[ 'qemu', 'gem5-buildroot', ]), 'release': Component(dependencies=[ 'qemu-buildroot', ]), 'all': Component(dependencies=[ 'all-linux', 'baremetal', ]) } parser = argparse.ArgumentParser( description= '''\ Shallow helper to build everything, or a subset of everything conveniently. Our build-* scripts don't build any dependencies to make iterative development fast and more predictable. While modifying a specific component however, you will likely want to just run the individual build-* commands which: * build no dependencies, and so are fast and predictable * can take multiple options to custumize the build Without any args, build only what is necessary for https://github.com/cirosantilli/linux-kernel-module-cheat#qemu-buildroot-setup for x86_64: .... ./%(prog)s .... This is equivalent to: .... ./%(prog)s --arch x86_64 qemu-buildroot .... If `--arch` is given, build just for the given archs: .... ./%(prog)s --arch arm --arch aarch64 .... This will build `qemu-buildroot` for arm and aarch64 only, but not `x86_64`. Clean all Linux kernel builds: .... ./build --all-archs --extra-args=--clean buildroot .... ''', formatter_class=argparse.RawTextHelpFormatter, ) parser.add_argument('--all', default=False, action='store_true', help='''\ Build absolutely everything for all archs. ''') group = parser.add_mutually_exclusive_group(required=False) group.add_argument('--all-archs', default=False, action='store_true', help='''\ Build the selected components for all archs. ''') group.add_argument('--arch', choices=common.arch_choices, default=[], action='append', help='''\ Build the selected components for this arch. Select multiple arches by passing this option multiple times. Default: [{}] '''.format(common.default_arch)) parser.add_argument('--extra-args', default='', help='''\ Extra args to pass to all scripts. ''' ) parser.add_argument('components', choices=list(name_to_component_map.keys()) + [[]], default=[], nargs='*', help='''\ Which components to build. '''.format(common.default_arch)) common.add_dry_run_argument(parser) args = parser.parse_args() common.setup_dry_run_arguments(args) # Decide archs. if args.arch == []: if args.all or args.all_archs: archs = common.all_archs.copy() else: archs = set([common.default_arch]) else: archs = set() for arch in args.arch: if arch in common.arch_short_to_long_dict: arch = common.arch_short_to_long_dict[arch] archs.add(arch) # Decide components. components = args.components if args.all: components = ['all'] elif components == []: components = ['qemu-buildroot'] selected_components = [] selected_component_name_set = set() for component_name in components: todo = [component_name] while todo != []: current_name = todo.pop(0) if current_name not in selected_component_name_set: selected_component_name_set.add(current_name) component = name_to_component_map[current_name] selected_components.append(component) todo.extend(component.dependencies) # Do the build. for arch in archs: for component in selected_components: component.build(arch)