1#!/bin/bash 2 3# Note: not intended to be invoked directly, see rebuild.sh. 4# 5# Rebuilds Crosvm and its dependencies from a clean state. 6 7: ${TOOLS_DIR:="$(pwd)/tools"} 8 9setup_env() { 10 : ${SOURCE_DIR:="$(pwd)/source"} 11 : ${WORKING_DIR:="$(pwd)/working"} 12 : ${CUSTOM_MANIFEST:=""} 13 14 ARCH="$(uname -m)" 15 : ${OUTPUT_DIR:="$(pwd)/${ARCH}-linux-gnu"} 16 OUTPUT_BIN_DIR="${OUTPUT_DIR}/bin" 17 OUTPUT_ETC_DIR="${OUTPUT_DIR}/etc" 18 OUTPUT_SECCOMP_DIR="${OUTPUT_ETC_DIR}/seccomp" 19 OUTPUT_LIB_DIR="${OUTPUT_DIR}/bin" 20 21 export PATH="${PATH}:${TOOLS_DIR}:${HOME}/.local/bin" 22} 23 24set -o errexit 25set -x 26 27fatal_echo() { 28 echo "$@" 29 exit 1 30} 31 32prepare_cargo() { 33 echo Setting up cargo... 34 cd 35 rm -rf .cargo 36 # Sometimes curl hangs. When it does, retry 37 retry curl -LO \ 38 "https://static.rust-lang.org/rustup/archive/1.14.0/$(uname -m)-unknown-linux-gnu/rustup-init" 39 # echo "0077ff9c19f722e2be202698c037413099e1188c0c233c12a2297bf18e9ff6e7 *rustup-init" | sha256sum -c - 40 chmod +x rustup-init 41 ./rustup-init -y --no-modify-path 42 source $HOME/.cargo/env 43 if [[ -n "$1" ]]; then 44 rustup target add "$1" 45 fi 46 rustup component add rustfmt-preview 47 rm rustup-init 48 49 if [[ -n "$1" ]]; then 50 cat >>~/.cargo/config <<EOF 51[target.$1] 52linker = "${1/-unknown-/-}" 53EOF 54 fi 55} 56 57install_custom_scripts() { 58 # install our custom utility script used by $0 to ${TOOLS_DIR} 59 echo "Installing custom scripts..." 60 SCRIPTS_TO_INSTALL=("/static/policy-inliner.sh") 61 mkdir -p ${TOOLS_DIR} || /bin/true 62 for scr in ${SCRIPTS_TO_INSTALL[@]}; do 63 if ! [[ -f $scr ]]; then 64 >&2 echo "$scr must exist but does not" 65 exit 10 66 fi 67 chmod a+x $scr 68 cp -f $scr ${TOOLS_DIR} 69 done 70} 71 72install_packages() { 73 echo Installing packages... 74 sudo dpkg --add-architecture arm64 75 sudo apt-get update 76 sudo apt-get install -y \ 77 autoconf \ 78 automake \ 79 build-essential \ 80 "$@" \ 81 cmake \ 82 curl \ 83 gcc \ 84 g++ \ 85 git \ 86 libcap-dev \ 87 libdrm-dev \ 88 libfdt-dev \ 89 libegl1-mesa-dev \ 90 libgl1-mesa-dev \ 91 libgles2-mesa-dev \ 92 libssl-dev \ 93 libtool \ 94 libusb-1.0-0-dev \ 95 libwayland-dev \ 96 make \ 97 nasm \ 98 ninja-build \ 99 pkg-config \ 100 protobuf-compiler \ 101 python \ 102 python3 \ 103 python3-pip \ 104 xutils-dev # Needed to pacify autogen.sh for libepoxy 105 mkdir -p "${TOOLS_DIR}" 106 curl https://storage.googleapis.com/git-repo-downloads/repo > "${TOOLS_DIR}/repo" 107 chmod a+x "${TOOLS_DIR}/repo" 108 109 # Meson getting started guide mentions that the distro version is frequently 110 # outdated and recommends installing via pip. 111 pip3 install meson 112 113 # Tools for building gfxstream 114 pip3 install absl-py 115 pip3 install urlfetch 116 117 case "$(uname -m)" in 118 aarch64) 119 prepare_cargo 120 ;; 121 x86_64) 122 # Cross-compilation is x86_64 specific 123 sudo apt install -y crossbuild-essential-arm64 124 prepare_cargo aarch64-unknown-linux-gnu 125 ;; 126 esac 127} 128 129retry() { 130 for i in $(seq 5); do 131 "$@" && return 0 132 sleep 1 133 done 134 return 1 135} 136 137fetch_source() { 138 echo "Fetching source..." 139 140 mkdir -p "${SOURCE_DIR}" 141 cd "${SOURCE_DIR}" 142 143 if ! git config user.name; then 144 git config --global user.name "AOSP Crosvm Builder" 145 git config --global user.email "nobody@android.com" 146 git config --global color.ui false 147 fi 148 149 if [[ -z "${CUSTOM_MANIFEST}" ]]; then 150 # Building Crosvm currently depends using Chromium's directory scheme for subproject 151 # directories ('third_party' vs 'external'). 152 fatal_echo "CUSTOM_MANIFEST must be provided. You most likely want to provide a full path to" \ 153 "a copy of device/google/cuttlefish_vmm/${ARCH}-linux-gnu/manifest.xml." 154 fi 155 156 repo init -q -u https://android.googlesource.com/platform/manifest 157 cp "${CUSTOM_MANIFEST}" .repo/manifests 158 repo init -m "${CUSTOM_MANIFEST}" 159 repo sync 160} 161 162prepare_source() { 163 if [ "$(ls -A $SOURCE_DIR)" ]; then 164 echo "${SOURCE_DIR} is non empty. Run this from an empty directory if you wish to fetch the source." 1>&2 165 exit 2 166 fi 167 fetch_source 168} 169 170resync_source() { 171 echo "Deleting source directory..." 172 rm -rf "${SOURCE_DIR}/.*" 173 rm -rf "${SOURCE_DIR}/*" 174 fetch_source 175} 176 177compile_minijail() { 178 echo "Compiling Minijail..." 179 180 cd "${SOURCE_DIR}/external/minijail" 181 182 make -j OUT="${WORKING_DIR}" 183 184 cp "${WORKING_DIR}/libminijail.so" "${OUTPUT_LIB_DIR}" 185} 186 187compile_minigbm() { 188 echo "Compiling Minigbm..." 189 190 cd "${SOURCE_DIR}/third_party/minigbm" 191 192 # Minigbm's package config file has a default hard-coded path. Update here so 193 # that dependent packages can find the files. 194 sed -i "s|prefix=/usr\$|prefix=${WORKING_DIR}/usr|" gbm.pc 195 196 # The gbm used by upstream linux distros is not compatible with crosvm, which must use Chrome OS's 197 # minigbm. 198 local cpp_flags=() 199 local make_flags=() 200 local minigbm_drv=(${MINIGBM_DRV}) 201 for drv in "${minigbm_drv[@]}"; do 202 cpp_flags+=(-D"DRV_${drv}") 203 make_flags+=("DRV_${drv}"=1) 204 done 205 206 make -j install \ 207 "${make_flags[@]}" \ 208 CPPFLAGS="${cpp_flags[*]}" \ 209 DESTDIR="${WORKING_DIR}" \ 210 OUT="${WORKING_DIR}" \ 211 PKG_CONFIG=pkg-config 212 213 cp ${WORKING_DIR}/usr/lib/libgbm.so.1 "${OUTPUT_LIB_DIR}" 214} 215 216compile_epoxy() { 217 cd "${SOURCE_DIR}/third_party/libepoxy" 218 219 meson build \ 220 --libdir="${WORKING_DIR}/usr/lib" \ 221 --pkg-config-path="${WORKING_DIR}/usr/lib/pkgconfig" \ 222 --prefix="${WORKING_DIR}/usr" \ 223 -Dglx=no \ 224 -Dx11=false \ 225 -Degl=yes 226 227 cd build 228 229 ninja install 230 231 cp "${WORKING_DIR}"/usr/lib/libepoxy.so.0 "${OUTPUT_LIB_DIR}" 232} 233 234compile_virglrenderer() { 235 echo "Compiling VirglRenderer..." 236 237 # Note: depends on libepoxy 238 cd "${SOURCE_DIR}/third_party/virglrenderer" 239 240 # Meson doesn't like gbm's version code. 241 sed -i "s|_gbm_ver = '0.0.0'|_gbm_ver = '0'|" meson.build 242 243 # Meson needs to have dependency information for header lookup. 244 sed -i "s|cc.has_header('epoxy/egl.h')|cc.has_header('epoxy/egl.h', dependencies: epoxy_dep)|" meson.build 245 246 # Need to figure out the right way to pass this down... 247 grep "install_rpath" src/meson.build || \ 248 sed -i "s|install : true|install : true, install_rpath : '\$ORIGIN',|" src/meson.build 249 250 meson build \ 251 --libdir="${WORKING_DIR}/usr/lib" \ 252 --pkg-config-path="${WORKING_DIR}/usr/lib/pkgconfig" \ 253 --prefix="${WORKING_DIR}/usr" \ 254 -Dplatforms=egl \ 255 -Dgbm_allocation=false 256 257 cd build 258 259 ninja install 260 261 cp "${WORKING_DIR}/usr/lib/libvirglrenderer.so.1" "${OUTPUT_LIB_DIR}" 262 263 cd "${OUTPUT_LIB_DIR}" 264 ln -s -f "libvirglrenderer.so.1" "libvirglrenderer.so" 265} 266 267compile_gfxstream() { 268 echo "Compiling gfxstream..." 269 270 # Note: depends on libepoxy 271 cd "${SOURCE_DIR}/external/qemu" 272 273 # TODO: Fix or remove network unit tests that are failing in docker, 274 # so we can take out "notests" 275 python3 android/build/python/cmake.py --gfxstream_only --notests 276 local dist_dir="${SOURCE_DIR}/external/qemu/objs/distribution/emulator/lib64" 277 278 cp "${dist_dir}/libc++.so.1" "${OUTPUT_LIB_DIR}" 279 cp "${dist_dir}/libandroid-emu-shared.so" "${OUTPUT_LIB_DIR}" 280 cp "${dist_dir}/libemugl_common.so" "${OUTPUT_LIB_DIR}" 281 cp "${dist_dir}/libOpenglRender.so" "${OUTPUT_LIB_DIR}" 282 cp "${dist_dir}/libgfxstream_backend.so" "${OUTPUT_LIB_DIR}" 283} 284 285compile_crosvm() { 286 echo "Compiling Crosvm..." 287 288 source "${HOME}/.cargo/env" 289 cd "${SOURCE_DIR}/platform/crosvm" 290 291 local crosvm_features=gpu,composite-disk 292 293 if [[ $BUILD_GFXSTREAM -eq 1 ]]; then 294 crosvm_features+=,gfxstream 295 fi 296 297 RUSTFLAGS="-C link-arg=-Wl,-rpath,\$ORIGIN -C link-arg=-L${OUTPUT_LIB_DIR}" \ 298 cargo build --features ${crosvm_features} 299 300 # Save the outputs 301 cp Cargo.lock "${OUTPUT_DIR}" 302 cp target/debug/crosvm "${OUTPUT_BIN_DIR}" 303 304 cargo --version --verbose > "${OUTPUT_DIR}/cargo_version.txt" 305 rustup show > "${OUTPUT_DIR}/rustup_show.txt" 306} 307 308compile_crosvm_seccomp() { 309 # note that this depends on compile_crosvm 310 # 311 # for aarch64, this function should do nothing 312 # as the aarch64 subdirectory does not exist yet 313 # 314 echo "Processing Crosvm Seccomp..." 315 316 cd "${SOURCE_DIR}/platform/crosvm" 317 case ${ARCH} in 318 x86_64) subdir="${ARCH}" ;; 319 amd64) subdir="x86_64" ;; 320 arm64) subdir="aarch64" ;; 321 aarch64) subdir="${ARCH}" ;; 322 *) 323 echo "${ARCH} is not supported" 324 exit 15 325 esac 326 policy-inliner.sh \ 327 -p $(pwd)/seccomp/$subdir \ 328 -o ${OUTPUT_SECCOMP_DIR} \ 329 -c $(pwd)/seccomp/$subdir/common_device.policy 330} 331 332compile() { 333 echo "Compiling..." 334 mkdir -p \ 335 "${WORKING_DIR}" \ 336 "${OUTPUT_DIR}" \ 337 "${OUTPUT_BIN_DIR}" \ 338 "${OUTPUT_ETC_DIR}" \ 339 "${OUTPUT_SECCOMP_DIR}" \ 340 "${OUTPUT_LIB_DIR}" 341 342 compile_minijail 343 344 compile_minigbm 345 346 compile_epoxy 347 348 compile_virglrenderer 349 350 # TODO: Finish the aarch64 cross/native gfxstream build 351 if [[ $BUILD_GFXSTREAM -eq 1 ]]; then 352 compile_gfxstream 353 fi 354 355 compile_crosvm 356 357 compile_crosvm_seccomp 358 359 dpkg-query -W > "${OUTPUT_DIR}/builder-packages.txt" 360 repo manifest -r -o "${OUTPUT_DIR}/manifest.xml" 361 echo "Results in ${OUTPUT_DIR}" 362} 363 364aarch64_retry() { 365 MINIGBM_DRV="RADEON VC4" compile 366} 367 368aarch64_build() { 369 rm -rf "${WORKING_DIR}/*" 370 aarch64_retry 371} 372 373x86_64_retry() { 374 MINIGBM_DRV="I915 RADEON VC4" BUILD_GFXSTREAM=1 compile 375} 376 377x86_64_build() { 378 rm -rf "${WORKING_DIR}/*" 379 x86_64_retry 380} 381 382if [[ $# -lt 1 ]]; then 383 echo Choosing default config 384 set setup_env prepare_source x86_64_build 385fi 386 387echo Steps: "$@" 388 389for i in "$@"; do 390 echo $i 391 case "$i" in 392 ARCH=*) ARCH="${i/ARCH=/}" ;; 393 CUSTOM_MANIFEST=*) CUSTOM_MANIFEST="${i/CUSTOM_MANIFEST=/}" ;; 394 aarch64_build) $i ;; 395 aarch64_retry) $i ;; 396 setup_env) $i ;; 397 install_custom_scripts) $i ;; 398 install_packages) $i ;; 399 fetch_source) $i ;; 400 resync_source) $i ;; 401 prepare_source) $i ;; 402 x86_64_build) $i ;; 403 x86_64_retry) $i ;; 404 *) echo $i unknown 1>&2 405 echo usage: $0 'install_packages|prepare_source|resync_source|fetch_source|$(uname -m)_build|$(uname -m)_retry' 1>&2 406 exit 2 407 ;; 408 esac 409done 410