1#!/bin/bash 2 3# This is a script to build a Debian image that can run in a VM created via AVF. 4# TODOs: 5# - Add Android-specific packages via a new class 6# - Use a stable release from debian-cloud-images 7 8show_help() { 9 echo "Usage: sudo $0 [OPTION]... [FILE]" 10 echo "Builds a debian image and save it to FILE. [sudo is required]" 11 echo "Options:" 12 echo "-h Print usage and this help message and exit." 13 echo "-a ARCH Architecture of the image [default is host arch: $(uname -m)]" 14 echo "-r Release mode build" 15 echo "-w Save temp work directory [for debugging]" 16} 17 18check_sudo() { 19 if [ "$EUID" -ne 0 ]; then 20 echo "Please run as root." ; exit 1 21 fi 22} 23 24parse_options() { 25 while getopts "a:hrw" option; do 26 case ${option} in 27 h) 28 show_help ; exit 29 ;; 30 a) 31 arch="$OPTARG" 32 ;; 33 r) 34 mode=release 35 ;; 36 w) 37 save_workdir=1 38 ;; 39 *) 40 echo "Invalid option: $OPTARG" ; exit 1 41 ;; 42 esac 43 done 44 case "$arch" in 45 aarch64) 46 debian_arch="arm64" 47 ;; 48 x86_64) 49 debian_arch="amd64" 50 ;; 51 *) 52 echo "Invalid architecture: $arch" ; exit 1 53 ;; 54 esac 55 if [[ "${*:$OPTIND:1}" ]]; then 56 built_image="${*:$OPTIND:1}" 57 fi 58} 59 60prepare_build_id() { 61 local filename=build_id 62 if [ -z "${KOKORO_BUILD_NUMBER}" ]; then 63 echo eng-$(hostname)-$(date --utc) > ${filename} 64 else 65 echo ${KOKORO_BUILD_NUMBER} > ${filename} 66 fi 67 echo ${filename} 68} 69 70install_prerequisites() { 71 apt update 72 packages=( 73 apt-utils 74 automake 75 binfmt-support 76 build-essential 77 ca-certificates 78 cmake 79 curl 80 debsums 81 dosfstools 82 fai-server 83 fai-setup-storage 84 fdisk 85 git 86 libjson-c-dev 87 libtool 88 libwebsockets-dev 89 make 90 protobuf-compiler 91 python3 92 python3-libcloud 93 python3-marshmallow 94 python3-pytest 95 python3-yaml 96 qemu-user-static 97 qemu-utils 98 sudo 99 udev 100 ) 101 if [[ "$arch" == "aarch64" ]]; then 102 packages+=( 103 gcc-aarch64-linux-gnu 104 libc6-dev-arm64-cross 105 qemu-system-arm 106 ) 107 else 108 packages+=( 109 qemu-system 110 ) 111 fi 112 113 # TODO(b/365955006): remove these lines when uboot supports x86_64 EFI application 114 if [[ "$arch" == "x86_64" ]]; then 115 packages+=( 116 libguestfs-tools 117 linux-image-generic 118 ) 119 fi 120 DEBIAN_FRONTEND=noninteractive \ 121 apt install --no-install-recommends --assume-yes "${packages[@]}" 122 123 if [ ! -f $"HOME"/.cargo/bin/cargo ]; then 124 curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y 125 fi 126 127 source "$HOME"/.cargo/env 128 rustup target add "${arch}"-unknown-linux-gnu 129 cargo install cargo-license 130} 131 132download_debian_cloud_image() { 133 local ver=master 134 local prj=debian-cloud-images 135 local url="https://salsa.debian.org/cloud-team/${prj}/-/archive/${ver}/${prj}-${ver}.tar.gz" 136 local outdir="${debian_cloud_image}" 137 138 mkdir -p "${outdir}" 139 wget -O - "${url}" | tar xz -C "${outdir}" --strip-components=1 140} 141 142build_rust_binary_and_copy() { 143 pushd "$(dirname "$0")/../../guest/$1" > /dev/null 144 local release_flag= 145 local artifact_mode=debug 146 if [[ "$mode" == "release" ]]; then 147 release_flag="--release" 148 artifact_mode=release 149 fi 150 RUSTFLAGS="-C linker=${arch}-linux-gnu-gcc" cargo build \ 151 --target "${arch}-unknown-linux-gnu" \ 152 --target-dir "${workdir}/$1" ${release_flag} 153 mkdir -p "${dst}/files/usr/local/bin/$1" 154 cp "${workdir}/$1/${arch}-unknown-linux-gnu/${artifact_mode}/$1" "${dst}/files/usr/local/bin/$1/AVF" 155 chmod 777 "${dst}/files/usr/local/bin/$1/AVF" 156 157 mkdir -p "${dst}/files/usr/share/doc/$1" 158 cargo license > "${dst}/files/usr/share/doc/$1/copyright" 159 popd > /dev/null 160} 161 162build_ttyd() { 163 local ttyd_version=1.7.7 164 local url="https://github.com/tsl0922/ttyd/archive/refs/tags/${ttyd_version}.tar.gz" 165 cp -r "$(dirname "$0")/ttyd" "${workdir}/ttyd" 166 167 pushd "${workdir}" > /dev/null 168 wget "${url}" -O - | tar xz 169 cp ttyd/* ttyd-${ttyd_version}/scripts 170 pushd "$workdir/ttyd-${ttyd_version}" > /dev/null 171 bash -c "env BUILD_TARGET=${arch} ./scripts/cross-build.sh" 172 mkdir -p "${dst}/files/usr/local/bin/ttyd" 173 cp "/tmp/stage/${arch}-linux-musl/bin/ttyd" "${dst}/files/usr/local/bin/ttyd/AVF" 174 chmod 777 "${dst}/files/usr/local/bin/ttyd/AVF" 175 mkdir -p "${dst}/files/usr/share/doc/ttyd" 176 cp LICENSE "${dst}/files/usr/share/doc/ttyd/copyright" 177 popd > /dev/null 178 popd > /dev/null 179} 180 181copy_android_config() { 182 local src 183 local dst 184 src="$(dirname "$0")/fai_config" 185 dst="${config_space}" 186 187 cp -R "${src}"/* "${dst}" 188 cp "$(dirname "$0")/image.yaml" "${resources_dir}" 189 190 cp -R "$(dirname "$0")/localdebs/" "${debian_cloud_image}/" 191 build_ttyd 192 build_rust_binary_and_copy forwarder_guest 193 build_rust_binary_and_copy forwarder_guest_launcher 194 build_rust_binary_and_copy ip_addr_reporter 195 build_rust_binary_and_copy shutdown_runner 196} 197 198run_fai() { 199 local out="${built_image}" 200 make -C "${debian_cloud_image}" "image_bookworm_nocloud_${debian_arch}" 201 mv "${debian_cloud_image}/image_bookworm_nocloud_${debian_arch}.raw" "${out}" 202} 203 204extract_partitions() { 205 root_partition_num=1 206 bios_partition_num=14 207 efi_partition_num=15 208 209 loop=$(losetup -f --show --partscan $built_image) 210 dd if="${loop}p$root_partition_num" of=root_part 211 if [[ "$arch" == "x86_64" ]]; then 212 dd if="${loop}p$bios_partition_num" of=bios_part 213 fi 214 dd if="${loop}p$efi_partition_num" of=efi_part 215 losetup -d "${loop}" 216 217 sed -i "s/{root_part_guid}/$(sfdisk --part-uuid $built_image $root_partition_num)/g" vm_config.json 218 if [[ "$arch" == "x86_64" ]]; then 219 sed -i "s/{bios_part_guid}/$(sfdisk --part-uuid $built_image $bios_partition_num)/g" vm_config.json 220 fi 221 sed -i "s/{efi_part_guid}/$(sfdisk --part-uuid $built_image $efi_partition_num)/g" vm_config.json 222} 223 224clean_up() { 225 [ "$save_workdir" -eq 1 ] || rm -rf "${workdir}" 226} 227 228set -e 229trap clean_up EXIT 230 231built_image=image.raw 232workdir=$(mktemp -d) 233build_id=$(prepare_build_id) 234debian_cloud_image=${workdir}/debian_cloud_image 235debian_version=bookworm 236config_space=${debian_cloud_image}/config_space/${debian_version} 237resources_dir=${debian_cloud_image}/src/debian_cloud_images/resources 238arch="$(uname -m)" 239mode=debug 240save_workdir=0 241 242parse_options "$@" 243check_sudo 244install_prerequisites 245download_debian_cloud_image 246copy_android_config 247run_fai 248fdisk -l "${built_image}" 249images=() 250 251cp "$(dirname "$0")/vm_config.json.${arch}" vm_config.json 252 253extract_partitions 254 255if [[ "$arch" == "aarch64" ]]; then 256 images+=( 257 root_part 258 efi_part 259 ) 260# TODO(b/365955006): remove these lines when uboot supports x86_64 EFI application 261elif [[ "$arch" == "x86_64" ]]; then 262 rm -f vmlinuz initrd.img 263 virt-get-kernel -a "${built_image}" 264 mv vmlinuz* vmlinuz 265 mv initrd.img* initrd.img 266 images+=( 267 bios_part 268 root_part 269 efi_part 270 vmlinuz 271 initrd.img 272 ) 273fi 274 275# --sparse option isn't supported in apache-commons-compress 276tar czv -f images.tar.gz ${build_id} "${images[@]}" vm_config.json 277