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 9# Stable is usually too old for crosvm, but make sure you bump this 10# up as far as you can each time this script is touched.. 11RUST_TOOLCHAIN_VER=1.65.0 12 13setup_env() { 14 : ${SOURCE_DIR:="$(pwd)/source"} 15 : ${WORKING_DIR:="$(pwd)/working"} 16 : ${CUSTOM_MANIFEST:=""} 17 18 ARCH="$(uname -m)" 19 : ${OUTPUT_DIR:="$(pwd)/${ARCH}-linux-gnu"} 20 OUTPUT_BIN_DIR="${OUTPUT_DIR}/bin" 21 OUTPUT_ETC_DIR="${OUTPUT_DIR}/etc" 22 OUTPUT_SECCOMP_DIR="${OUTPUT_ETC_DIR}/seccomp" 23 24 export PATH="${PATH}:${TOOLS_DIR}:${HOME}/.local/bin" 25 export PKG_CONFIG_PATH="${WORKING_DIR}/usr/lib/pkgconfig" 26} 27 28set -e 29set -x 30 31fatal_echo() { 32 echo "$@" 33 exit 1 34} 35 36prepare_cargo() { 37 echo Setting up cargo... 38 cd 39 rm -rf .cargo 40 # Sometimes curl hangs. When it does, retry 41 retry curl -L \ 42 --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs -o rustup-init 43 chmod +x rustup-init 44 ./rustup-init -y --no-modify-path --default-toolchain ${RUST_TOOLCHAIN_VER} 45 source $HOME/.cargo/env 46 if [[ -n "$1" ]]; then 47 rustup target add "$1" 48 fi 49 rm -f rustup-init 50 51 if [[ -n "$1" ]]; then 52 cat >>~/.cargo/config <<EOF 53[target.$1] 54linker = "${1/-unknown-/-}" 55EOF 56 fi 57} 58 59install_packages() { 60 echo Installing packages... 61 62 sudo dpkg --add-architecture arm64 63 sudo apt-get update 64 sudo apt-get install -y \ 65 "$@" \ 66 autoconf \ 67 automake \ 68 build-essential \ 69 curl \ 70 doxygen \ 71 g++ \ 72 gcc \ 73 git \ 74 graphviz \ 75 libcap-dev \ 76 libegl1-mesa-dev \ 77 libfdt-dev \ 78 libgl1-mesa-dev \ 79 libgles2-mesa-dev \ 80 libpciaccess-dev \ 81 libssl-dev \ 82 libtool \ 83 libusb-1.0-0-dev \ 84 libwayland-bin \ 85 libwayland-dev \ 86 libxml2-dev \ 87 make \ 88 nasm \ 89 ninja-build \ 90 pkg-config \ 91 protobuf-compiler \ 92 python3 \ 93 python3-pip \ 94 texinfo \ 95 wayland-protocols \ 96 xmlto \ 97 xutils-dev # Needed to pacify autogen.sh for libepoxy 98 mkdir -p "${TOOLS_DIR}" 99 100 # Repo needs python3 but python-is-python3 package not available: 101 sudo ln -s -f /usr/bin/python3 /usr/bin/python 102 103 curl https://storage.googleapis.com/git-repo-downloads/repo > "${TOOLS_DIR}/repo" 104 chmod a+x "${TOOLS_DIR}/repo" 105 106 # Gfxstream needs a new-ish version of CMake 107 mkdir -p "${TOOLS_DIR}/cmake" 108 cd "${TOOLS_DIR}/cmake" 109 curl -O -L https://cmake.org/files/v3.22/cmake-3.22.1-linux-$(uname -m).sh 110 chmod +x cmake-3.22.1-linux-$(uname -m).sh 111 sudo ./cmake-3.22.1-linux-$(uname -m).sh --skip-license --exclude-subdir --prefix=/usr/local 112 cmake --version 113 114 # Meson getting started guide mentions that the distro version is frequently 115 # outdated and recommends installing via pip. 116 pip3 install --no-warn-script-location meson 117 118 # Tools for building gfxstream 119 pip3 install absl-py 120 pip3 install urlfetch 121 122 case "$(uname -m)" in 123 aarch64) 124 prepare_cargo 125 ;; 126 x86_64) 127 # Cross-compilation is x86_64 specific 128 sudo apt install -y crossbuild-essential-arm64 129 prepare_cargo aarch64-unknown-linux-gnu 130 ;; 131 esac 132} 133 134retry() { 135 for i in $(seq 5); do 136 "$@" && return 0 137 sleep 1 138 done 139 return 1 140} 141 142fetch_source() { 143 echo "Fetching source..." 144 145 mkdir -p "${SOURCE_DIR}" 146 cd "${SOURCE_DIR}" 147 148 if ! git config user.name; then 149 git config --global user.name "AOSP Crosvm Builder" 150 git config --global user.email "nobody@android.com" 151 git config --global color.ui false 152 fi 153 154 if [[ -z "${CUSTOM_MANIFEST}" ]]; then 155 # Building Crosvm currently depends using Chromium's directory scheme for subproject 156 # directories ('third_party' vs 'external'). 157 fatal_echo "CUSTOM_MANIFEST must be provided. You most likely want to provide a full path to" \ 158 "a copy of device/google/cuttlefish_vmm/manifest.xml." 159 fi 160 161 cp ${CUSTOM_MANIFEST} manifest.xml 162 repo init --depth=1 -q -u https://android.googlesource.com/platform/manifest -m $PWD/manifest.xml 163 repo sync 164} 165 166prepare_source() { 167 if [ "$(ls -A $SOURCE_DIR)" ]; then 168 echo "${SOURCE_DIR} is non empty. Run this from an empty directory if you wish to fetch the source." 1>&2 169 exit 2 170 fi 171 fetch_source 172} 173 174resync_source() { 175 echo "Deleting source directory..." 176 rm -rf "${SOURCE_DIR}/.*" 177 rm -rf "${SOURCE_DIR}/*" 178 fetch_source 179} 180 181# $1 = installed library filename 182debuglink() { 183 objcopy --only-keep-debug "${OUTPUT_BIN_DIR}/$1" "${OUTPUT_BIN_DIR}/$1.debug" 184 strip --strip-debug "${OUTPUT_BIN_DIR}/$1" 185 cd "${OUTPUT_BIN_DIR}" 186 objcopy --add-gnu-debuglink="$1.debug" "$1" 187 cd - 188} 189 190compile_libdrm() { 191 cd "${SOURCE_DIR}/external/libdrm" 192 193 # Ensure pkg-config file supplies rpath to dependent libraries 194 grep "install_rpath" meson.build || \ 195 sed -i "s|install : true,|install : true, install_rpath : '\$ORIGIN',|" meson.build 196 197 meson build \ 198 --libdir="${WORKING_DIR}/usr/lib" \ 199 --prefix="${WORKING_DIR}/usr" \ 200 -Damdgpu=false \ 201 -Dfreedreno=false \ 202 -Dintel=false \ 203 -Dlibkms=false \ 204 -Dnouveau=false \ 205 -Dradeon=false \ 206 -Dvc4=false \ 207 -Dvmwgfx=false 208 209 cd build 210 ninja install 211 212 cp -a "${WORKING_DIR}"/usr/lib/libdrm.so* "${OUTPUT_BIN_DIR}" 213 debuglink libdrm.so.2.4.0 214} 215 216compile_minijail() { 217 echo "Compiling Minijail..." 218 219 cd "${SOURCE_DIR}/platform/minijail" 220 221 if ! grep '^# cuttlefish_vmm-rebuild-mark' Makefile; then 222 # Link minijail-sys rust crate dynamically to minijail 223 sed -i '/BUILD_STATIC_LIBS/d' rust/minijail-sys/build.rs 224 sed -i 's,static=minijail.pic,dylib=minijail,' rust/minijail-sys/build.rs 225 226 # Use Android prebuilt C files instead of generating them 227 sed -i 's,\(.*\.gen\.c: \),DISABLED_\1,' Makefile 228 cat >>Makefile <<EOF 229libconstants.gen.c: \$(SRC)/linux-x86/libconstants.gen.c 230 @cp \$< \$@ 231libsyscalls.gen.c: \$(SRC)/linux-x86/libsyscalls.gen.c 232 @cp \$< \$@ 233# cuttlefish_vmm-rebuild-mark 234EOF 235 fi 236 237 make -j OUT="${WORKING_DIR}" 238 cp "${WORKING_DIR}"/libminijail.so "${WORKING_DIR}"/usr/lib 239 240 cp -a "${WORKING_DIR}"/usr/lib/libminijail.so "${OUTPUT_BIN_DIR}" 241 debuglink libminijail.so 242} 243 244compile_minigbm() { 245 echo "Compiling Minigbm..." 246 247 cd "${SOURCE_DIR}/platform/minigbm" 248 249 # Minigbm's package config file has a default hard-coded path. Update here so 250 # that dependent packages can find the files. 251 sed -i "s|prefix=/usr\$|prefix=${WORKING_DIR}/usr|" gbm.pc 252 253 # The gbm used by upstream linux distros is not compatible with crosvm, which must use Chrome OS's 254 # minigbm. 255 local cpp_flags=(-I/working/usr/include -I/working/usr/include/libdrm) 256 local ld_flags=(-Wl,-soname,libgbm.so.1 -Wl,-rpath,\\\$\$ORIGIN -L/working/usr/lib) 257 local make_flags=() 258 local minigbm_drv=(${MINIGBM_DRV}) 259 for drv in "${minigbm_drv[@]}"; do 260 cpp_flags+=(-D"DRV_${drv}") 261 make_flags+=("DRV_${drv}"=1) 262 done 263 264 make -j install \ 265 "${make_flags[@]}" \ 266 CPPFLAGS="${cpp_flags[*]}" \ 267 DESTDIR="${WORKING_DIR}" \ 268 LDFLAGS="${ld_flags[*]}" \ 269 OUT="${WORKING_DIR}" 270 271 cp -a "${WORKING_DIR}"/usr/lib/libminigbm.so* "${OUTPUT_BIN_DIR}" 272 cp -a "${WORKING_DIR}"/usr/lib/libgbm.so* "${OUTPUT_BIN_DIR}" 273 debuglink libminigbm.so.1.0.0 274} 275 276compile_epoxy() { 277 cd "${SOURCE_DIR}/third_party/libepoxy" 278 279 meson build \ 280 --libdir="${WORKING_DIR}/usr/lib" \ 281 --prefix="${WORKING_DIR}/usr" \ 282 -Dglx=no \ 283 -Dx11=false \ 284 -Degl=yes 285 286 cd build 287 ninja install 288 289 cp -a "${WORKING_DIR}"/usr/lib/libepoxy.so* "${OUTPUT_BIN_DIR}" 290 debuglink libepoxy.so.0.0.0 291} 292 293compile_virglrenderer() { 294 echo "Compiling VirglRenderer..." 295 296 # Note: depends on libepoxy 297 cd "${SOURCE_DIR}/third_party/virglrenderer" 298 299 # Meson needs to have dependency information for header lookup. 300 sed -i "s|cc.has_header('epoxy/egl.h')|cc.has_header('epoxy/egl.h', dependencies: epoxy_dep)|" meson.build 301 302 # Ensure pkg-config file supplies rpath to dependent libraries 303 grep "install_rpath" src/meson.build || \ 304 sed -i "s|install : true|install : true, install_rpath : '\$ORIGIN'|" src/meson.build 305 306 meson build \ 307 --libdir="${WORKING_DIR}/usr/lib" \ 308 --prefix="${WORKING_DIR}/usr" \ 309 -Dplatforms=egl \ 310 -Dminigbm_allocation=false 311 312 cd build 313 ninja install 314 315 cp -a "${WORKING_DIR}"/usr/lib/libvirglrenderer.so* "${OUTPUT_BIN_DIR}" 316 debuglink libvirglrenderer.so.1.7.7 317} 318 319compile_libffi() { 320 cd "${SOURCE_DIR}/third_party/libffi" 321 322 ./autogen.sh 323 ./configure \ 324 --prefix="${WORKING_DIR}/usr" \ 325 --libdir="${WORKING_DIR}/usr/lib" 326 make && make check && make install 327 328 cp -a "${WORKING_DIR}"/usr/lib/libffi.so* "${OUTPUT_BIN_DIR}" 329 debuglink libffi.so.7.1.0 330} 331 332compile_wayland() { 333 cd "${SOURCE_DIR}/third_party/wayland" 334 335 # Need to figure out the right way to pass this down... 336 sed -i "s|install: true\$|install: true, install_rpath : '\$ORIGIN'|" src/meson.build 337 338 meson build \ 339 --libdir="${WORKING_DIR}/usr/lib" \ 340 --prefix="${WORKING_DIR}/usr" 341 ninja -C build/ install 342 343 cp -a "${WORKING_DIR}"/usr/lib/libwayland-client.so* "${OUTPUT_BIN_DIR}" 344 debuglink libwayland-client.so.0.3.0 345} 346 347compile_gfxstream() { 348 echo "Compiling gfxstream..." 349 350 local dist_dir="${SOURCE_DIR}/hardware/google/gfxstream/build" 351 [ -d "${dist_dir}" ] && rm -rf "${dist_dir}" 352 mkdir "${dist_dir}" 353 cd "${dist_dir}" 354 355 cmake .. \ 356 -G Ninja \ 357 -DBUILD_GRAPHICS_DETECTOR=ON \ 358 -DDEPENDENCY_RESOLUTION=AOSP 359 360 ninja 361 362 chmod +x "${dist_dir}"/gfxstream_graphics_detector 363 cp -a "${dist_dir}"/gfxstream_graphics_detector "${OUTPUT_BIN_DIR}" 364 debuglink gfxstream_graphics_detector 365 366 chmod +x "${dist_dir}"/libgfxstream_backend.so 367 cp -a "${dist_dir}"/libgfxstream_backend.so "${WORKING_DIR}"/usr/lib 368 cp -a "${WORKING_DIR}"/usr/lib/libgfxstream_backend.so "${OUTPUT_BIN_DIR}" 369 debuglink libgfxstream_backend.so 370} 371 372compile_crosvm() { 373 echo "Compiling Crosvm..." 374 375 source "${HOME}/.cargo/env" 376 377 # Workaround for aosp/1412815 378 cd "${SOURCE_DIR}/platform/crosvm/protos/src" 379 if ! grep '^mod generated {$' lib.rs; then 380 cat >>lib.rs <<EOF 381mod generated { 382 include!(concat!(env!("OUT_DIR"), "/generated.rs")); 383} 384EOF 385 fi 386 sed -i "s/pub use cdisk_spec_proto::cdisk_spec/pub use generated::cdisk_spec/" lib.rs 387 388 cd "${SOURCE_DIR}/platform/crosvm" 389 390 # Workaround for minijail-sys prepending -L/usr/lib/$arch dir 391 # which breaks the preferred search path for libdrm.so 392 sed -i '0,/pkg_config::Config::new().probe("libdrm")?;/{/pkg_config::Config::new().probe("libdrm")?;/d;}' rutabaga_gfx/build.rs 393 394 # Workaround rutabaga build thinking it needs at later version of virglrenderer. 395 sed -i 's/atleast_version("1.0.0")/atleast_version("0.10.0")/g' rutabaga_gfx/build.rs 396 397 local crosvm_features=audio,gdb,gpu,composite-disk,usb,virgl_renderer 398 if [[ $BUILD_GFXSTREAM -eq 1 ]]; then 399 crosvm_features+=,gfxstream 400 fi 401 402 CROSVM_USE_SYSTEM_MINIGBM=1 \ 403 CROSVM_USE_SYSTEM_VIRGLRENDERER=1 \ 404 GFXSTREAM_PATH="${WORKING_DIR}/usr/lib" \ 405 PKG_CONFIG_PATH="${WORKING_DIR}/usr/lib/pkgconfig" \ 406 RUSTFLAGS="-C link-arg=-Wl,-rpath,\$ORIGIN -C link-arg=${WORKING_DIR}/usr/lib/libdrm.so" \ 407 cargo build --features ${crosvm_features} 408 409 # Save the outputs 410 cp Cargo.lock "${OUTPUT_DIR}" 411 cp target/debug/crosvm "${OUTPUT_BIN_DIR}" 412 debuglink crosvm 413 414 cargo --version --verbose > "${OUTPUT_DIR}/cargo_version.txt" 415 rustup show > "${OUTPUT_DIR}/rustup_show.txt" 416} 417 418compile_crosvm_seccomp() { 419 echo "Processing Crosvm Seccomp..." 420 421 cd "${SOURCE_DIR}/platform/crosvm" 422 case ${ARCH} in 423 x86_64) subdir="${ARCH}" ;; 424 amd64) subdir="x86_64" ;; 425 arm64) subdir="aarch64" ;; 426 aarch64) subdir="${ARCH}" ;; 427 *) 428 echo "${ARCH} is not supported" 429 exit 15 430 esac 431 432 inlined_policy_list="\ 433 jail/seccomp/$subdir/common_device.policy \ 434 jail/seccomp/$subdir/gpu_common.policy \ 435 jail/seccomp/$subdir/serial.policy \ 436 jail/seccomp/$subdir/net.policy \ 437 jail/seccomp/$subdir/block.policy \ 438 jail/seccomp/$subdir/vvu.policy \ 439 jail/seccomp/$subdir/vhost_user.policy \ 440 jail/seccomp/$subdir/vhost_vsock.policy" 441 for policy_file in "jail/seccomp/$subdir/"*.policy; do 442 [[ "$inlined_policy_list" = *"$policy_file"* ]] && continue 443 jail/seccomp/policy-inliner.sh $inlined_policy_list <"$policy_file" | \ 444 grep -ve "^@frequency" \ 445 >"${OUTPUT_SECCOMP_DIR}"/$(basename "$policy_file") 446 done 447} 448 449compile() { 450 echo "Compiling..." 451 mkdir -p \ 452 "${WORKING_DIR}" \ 453 "${OUTPUT_DIR}" \ 454 "${OUTPUT_BIN_DIR}" \ 455 "${OUTPUT_ETC_DIR}" \ 456 "${OUTPUT_SECCOMP_DIR}" 457 458 if [[ $BUILD_CROSVM -eq 1 ]]; then 459 compile_libdrm 460 compile_minijail 461 compile_minigbm 462 compile_epoxy 463 compile_virglrenderer 464 compile_libffi # wayland depends on it 465 compile_wayland 466 fi 467 468 if [[ $BUILD_GFXSTREAM -eq 1 ]]; then 469 compile_gfxstream 470 fi 471 472 compile_crosvm 473 compile_crosvm_seccomp 474 475 dpkg-query -W > "${OUTPUT_DIR}/builder-packages.txt" 476 echo "Results in ${OUTPUT_DIR}" 477} 478 479aarch64_retry() { 480 BUILD_CROSVM=1 BUILD_GFXSTREAM=1 compile 481} 482 483aarch64_build() { 484 rm -rf "${WORKING_DIR}/*" 485 aarch64_retry 486} 487 488x86_64_retry() { 489 MINIGBM_DRV="I915 RADEON VC4" BUILD_CROSVM=1 BUILD_GFXSTREAM=1 compile 490} 491 492x86_64_build() { 493 rm -rf "${WORKING_DIR}/*" 494 x86_64_retry 495} 496 497if [[ $# -lt 1 ]]; then 498 echo Choosing default config 499 set setup_env prepare_source x86_64_build 500fi 501 502echo Steps: "$@" 503 504for i in "$@"; do 505 echo $i 506 case "$i" in 507 ARCH=*) ARCH="${i/ARCH=/}" ;; 508 CUSTOM_MANIFEST=*) CUSTOM_MANIFEST="${i/CUSTOM_MANIFEST=/}" ;; 509 aarch64_build) $i ;; 510 aarch64_retry) $i ;; 511 setup_env) $i ;; 512 install_packages) $i ;; 513 fetch_source) $i ;; 514 resync_source) $i ;; 515 prepare_source) $i ;; 516 x86_64_build) $i ;; 517 x86_64_retry) $i ;; 518 *) echo $i unknown 1>&2 519 echo usage: $0 'install_packages|prepare_source|resync_source|fetch_source|$(uname -m)_build|$(uname -m)_retry' 1>&2 520 exit 2 521 ;; 522 esac 523done 524