|
| 1 | +#!/usr/bin/env python3 |
| 2 | + |
| 3 | +from argparse import ArgumentParser |
| 4 | +import datetime |
| 5 | +import os |
| 6 | +from pathlib import Path |
| 7 | +import shutil |
| 8 | +import subprocess |
| 9 | + |
| 10 | +BUILDROOT_VERSION = '2022.02' |
| 11 | +SUPPORTED_ARCHES = [ |
| 12 | + 'arm64', |
| 13 | + 'arm64be', |
| 14 | + 'arm', |
| 15 | + 'm68k', |
| 16 | + 'mips', |
| 17 | + 'mipsel', |
| 18 | + 'ppc32', |
| 19 | + 'ppc64', |
| 20 | + 'ppc64le', |
| 21 | + 'riscv', |
| 22 | + 's390', |
| 23 | + 'x86', |
| 24 | + 'x86_64', |
| 25 | +] |
| 26 | +RELEASE_TAG = datetime.datetime.now( |
| 27 | + tz=datetime.timezone.utc).strftime('%Y%m%d-%H%M%S') |
| 28 | +ROOT_FOLDER = Path(__file__).resolve().parent |
| 29 | +OUT_FOLDER = Path(ROOT_FOLDER, 'out') |
| 30 | +SRC_FOLDER = Path(ROOT_FOLDER, 'src') |
| 31 | + |
| 32 | + |
| 33 | +def buildroot_make(make_arg=None, **kwargs): |
| 34 | + make_cmd = ['make', f"-j{os.cpu_count()}"] |
| 35 | + if make_arg: |
| 36 | + make_cmd.append(make_arg) |
| 37 | + subprocess.run(make_cmd, **kwargs, check=True, cwd=SRC_FOLDER) |
| 38 | + |
| 39 | + |
| 40 | +def build_image(architecture, edit_config): |
| 41 | + buildroot_make('clean') |
| 42 | + |
| 43 | + config = Path(ROOT_FOLDER, f"{architecture}.config") |
| 44 | + # Python documentation notes that when subprocess.Popen()'s env parameter |
| 45 | + # is not None, the current process's envirionment is not inherited, which |
| 46 | + # causes issues because PATH is not inherited. Add BR2_DEFCONFIG to the |
| 47 | + # environment, rather than replacing it (we support Python 3.8, so we |
| 48 | + # cannot use 'os.environ | {...}'). |
| 49 | + buildroot_make('defconfig', env={**os.environ, 'BR2_DEFCONFIG': config}) |
| 50 | + if edit_config: |
| 51 | + buildroot_make('menuconfig') |
| 52 | + buildroot_make('savedefconfig') |
| 53 | + |
| 54 | + buildroot_make() |
| 55 | + |
| 56 | + OUT_FOLDER.mkdir(exist_ok=True, parents=True) |
| 57 | + |
| 58 | + images = [Path(SRC_FOLDER, 'output/images/rootfs.cpio')] |
| 59 | + # For x86_64, we also build an ext4 image for UML |
| 60 | + if architecture == 'x86_64': |
| 61 | + images.append(images[0].with_suffix('.ext4')) |
| 62 | + |
| 63 | + for image in images: |
| 64 | + if not image.exists(): |
| 65 | + raise FileNotFoundError( |
| 66 | + f"{image} could not be found! Did the build error?") |
| 67 | + zstd_cmd = [ |
| 68 | + 'zstd', '-f', '-19', '-o', |
| 69 | + Path(OUT_FOLDER, f"{architecture}-{image.name}.zst"), image |
| 70 | + ] |
| 71 | + subprocess.run(zstd_cmd, check=True) |
| 72 | + |
| 73 | + |
| 74 | +def download_and_extract_buildroot(): |
| 75 | + SRC_FOLDER.mkdir(parents=True) |
| 76 | + |
| 77 | + tarball = Path(ROOT_FOLDER, f"buildroot-{BUILDROOT_VERSION}.tar.gz") |
| 78 | + tarball.unlink(missing_ok=True) |
| 79 | + |
| 80 | + curl_cmd = [ |
| 81 | + 'curl', '-LSs', '-o', tarball, |
| 82 | + f"https://buildroot.org/downloads/{tarball.name}" |
| 83 | + ] |
| 84 | + subprocess.run(curl_cmd, check=True) |
| 85 | + |
| 86 | + sha256_cmd = ['sha256sum', '--quiet', '-c', f"{tarball.name}.sha256"] |
| 87 | + subprocess.run(sha256_cmd, check=True, cwd=ROOT_FOLDER) |
| 88 | + |
| 89 | + tar_cmd = [ |
| 90 | + 'tar', '-C', SRC_FOLDER, '--strip-components=1', '-axf', tarball |
| 91 | + ] |
| 92 | + subprocess.run(tar_cmd, check=True) |
| 93 | + |
| 94 | + tarball.unlink(missing_ok=True) |
| 95 | + |
| 96 | + |
| 97 | +def download_buildroot_if_necessary(): |
| 98 | + if SRC_FOLDER.exists(): |
| 99 | + # Make support/scripts/setlocalversion do nothing because we are in a |
| 100 | + # git repository so it will return information about this repo, not |
| 101 | + # Buildroot |
| 102 | + setlocalversion = Path(SRC_FOLDER, 'support/scripts/setlocalversion') |
| 103 | + setlocalversion.write_text('', encoding='utf-8') |
| 104 | + |
| 105 | + installed_version = subprocess.run(['make', 'print-version'], |
| 106 | + capture_output=True, |
| 107 | + check=True, |
| 108 | + cwd=SRC_FOLDER, |
| 109 | + text=True).stdout.strip() |
| 110 | + if installed_version != BUILDROOT_VERSION: |
| 111 | + shutil.rmtree(SRC_FOLDER) |
| 112 | + download_and_extract_buildroot() |
| 113 | + else: |
| 114 | + download_and_extract_buildroot() |
| 115 | + |
| 116 | + |
| 117 | +def release_images(): |
| 118 | + if not shutil.which('gh'): |
| 119 | + raise RuntimeError( |
| 120 | + "Could not find GitHub CLI ('gh') on your system, please install it to do releases!" |
| 121 | + ) |
| 122 | + |
| 123 | + gh_cmd = [ |
| 124 | + 'gh', '-R', 'ClangBuiltLinux/boot-utils', 'release', 'create', |
| 125 | + '--generate-notes', RELEASE_TAG, *list(OUT_FOLDER.iterdir()) |
| 126 | + ] |
| 127 | + subprocess.run(gh_cmd, check=True) |
| 128 | + |
| 129 | + |
| 130 | +def parse_arguments(): |
| 131 | + parser = ArgumentParser() |
| 132 | + |
| 133 | + parser.add_argument( |
| 134 | + '-a', |
| 135 | + '--architectures', |
| 136 | + choices=[*SUPPORTED_ARCHES, 'all'], |
| 137 | + default=SUPPORTED_ARCHES, |
| 138 | + help= |
| 139 | + 'The architectures to build images for. Defaults to all supported architectures.', |
| 140 | + nargs='+') |
| 141 | + parser.add_argument( |
| 142 | + '-e', |
| 143 | + '--edit-config', |
| 144 | + action='store_true', |
| 145 | + help='Edit configuration file and run savedefconfig on result') |
| 146 | + parser.add_argument( |
| 147 | + '-r', |
| 148 | + '--release', |
| 149 | + action='store_true', |
| 150 | + help=f"Create a release on GitHub (tag: {RELEASE_TAG})") |
| 151 | + |
| 152 | + return parser.parse_args() |
| 153 | + |
| 154 | + |
| 155 | +if __name__ == '__main__': |
| 156 | + args = parse_arguments() |
| 157 | + |
| 158 | + if not shutil.which('zstd'): |
| 159 | + raise RuntimeError( |
| 160 | + 'zstd could not be found on your system, please install it!') |
| 161 | + |
| 162 | + architectures = SUPPORTED_ARCHES if 'all' in args.architectures else args.architectures |
| 163 | + |
| 164 | + download_buildroot_if_necessary() |
| 165 | + for arch in architectures: |
| 166 | + build_image(arch, args.edit_config) |
| 167 | + |
| 168 | + if args.release: |
| 169 | + release_images() |
0 commit comments