1#!/usr/bin/env bash 2# SPDX-License-Identifier: GPL-2.0 3 4# A handy tool to launch CVD with local build or remote build. 5 6# Constants 7ACLOUD_PREBUILT="prebuilts/asuite/acloud/linux-x86/acloud" 8OPT_SKIP_PRERUNCHECK='--skip-pre-run-check' 9PRODUCT='aosp_cf_x86_64_phone' 10# Color constants 11#BOLD="$(tput bold)" # Unused 12END="$(tput sgr0)" 13GREEN="$(tput setaf 2)" 14RED="$(tput setaf 198)" 15YELLOW="$(tput setaf 3)" 16# BLUE="$(tput setaf 34)" # Unused 17 18SKIP_BUILD=false 19GCOV=false 20DEBUG=false 21KASAN=false 22EXTRA_OPTIONS=() 23 24function print_help() { 25 echo "Usage: $0 [OPTIONS]" 26 echo "" 27 echo "This script will build images and launch a Cuttlefish device." 28 echo "" 29 echo "Available options:" 30 echo " --skip-build Skip the image build step. Will build by default if in repo." 31 echo " --gcov Launch CVD with gcov enabled kernel" 32 echo " --debug Launch CVD with debug enabled kernel" 33 echo " --kasan Launch CVD with kasan enabled kernel" 34 echo " -pb <platform_build>, --platform-build=<platform_build>" 35 echo " The platform build path. Can be a local path or a remote build" 36 echo " as ab://<branch>/<build_target>/<build_id>." 37 echo " If not specified, it will use the platform build in the local" 38 echo " repo, or the default compatible platform build for the kernel." 39 echo " -sb <system_build>, --system-build=<system_build>" 40 echo " The system build path for GSI testing. Can be a local path or" 41 echo " remote build as ab://<branch>/<build_target>/<build_id>." 42 echo " If not specified, no system build will be used." 43 echo " -kb <kernel_build>, --kernel-build=<kernel_build>" 44 echo " The kernel build path. Can be a local path or a remote build" 45 echo " as ab://<branch>/<build_target>/<build_id>." 46 echo " If not specified, it will use the kernel in the local repo." 47 echo " --acloud-bin=<acloud_bin>" 48 echo " The alternative alcoud binary path." 49 echo " --cf-product=<product_type>" 50 echo " The alternative cuttlefish product type for local build." 51 echo " Will use default aosp_cf_x86_64_phone if not specified." 52 echo " --acloud-arg=<acloud_arg>" 53 echo " Additional acloud command arg. Can be repeated." 54 echo " For example --acloud-arg=--local-instance to launch a local cvd." 55 echo " -h, --help Display this help message and exit" 56 echo "" 57 echo "Examples:" 58 echo "$0" 59 echo "$0 --acloud-arg=--local-instance" 60 echo "$0 -pb ab://git_main/aosp_cf_x86_64_phone-userdebug/latest" 61 echo "$0 -pb ~/aosp-main/out/target/product/vsoc_x86_64/" 62 echo "$0 -kb ~/android-mainline/out/virtual_device_x86_64/" 63 echo "" 64 exit 0 65} 66 67function parse_arg() { 68 while test $# -gt 0; do 69 case "$1" in 70 -h|--help) 71 print_help 72 ;; 73 --skip-build) 74 SKIP_BUILD=true 75 shift 76 ;; 77 -pb) 78 shift 79 if test $# -gt 0; then 80 PLATFORM_BUILD=$1 81 else 82 print_error "platform build is not specified" 83 fi 84 shift 85 ;; 86 --platform-build=*) 87 PLATFORM_BUILD=$(echo "$1" | sed -e "s/^[^=]*=//g") 88 shift 89 ;; 90 -sb) 91 shift 92 if test $# -gt 0; then 93 SYSTEM_BUILD=$1 94 else 95 print_error "system build is not specified" 96 fi 97 shift 98 ;; 99 --system-build=*) 100 SYSTEM_BUILD=$(echo "$1" | sed -e "s/^[^=]*=//g") 101 shift 102 ;; 103 -kb) 104 shift 105 if test $# -gt 0; then 106 KERNEL_BUILD=$1 107 else 108 print_error "kernel build path is not specified" 109 fi 110 shift 111 ;; 112 --kernel-build=*) 113 KERNEL_BUILD=$(echo "$1" | sed -e "s/^[^=]*=//g") 114 shift 115 ;; 116 --acloud-arg=*) 117 EXTRA_OPTIONS+=("$(echo "$1" | sed -e "s/^[^=]*=//g")") # Use array append syntax 118 shift 119 ;; 120 --acloud-bin=*) 121 ACLOUD_BIN=$(echo "$1" | sed -e "s/^[^=]*=//g") 122 shift 123 ;; 124 --cf-product=*) 125 PRODUCT=$(echo "$1" | sed -e "s/^[^=]*=//g") 126 shift 127 ;; 128 --gcov) 129 GCOV=true 130 shift 131 ;; 132 --debug) 133 DEBUG=true 134 shift 135 ;; 136 --kasan) 137 KASAN=true 138 shift 139 ;; 140 *) 141 print_error "Unsupported flag: $1" >&2 142 ;; 143 esac 144 done 145} 146 147function adb_checker() { 148 if ! which adb &> /dev/null; then 149 print_error "adb not found!" 150 fi 151} 152 153function create_kernel_build_cmd() { 154 local cf_kernel_repo_root=$1 155 local cf_kernel_version=$2 156 157 local regex="((?<=android-)mainline|(\K\d+\.\d+(?=-stable)))|((?:android)\K\d+)" 158 local android_version 159 android_version=$(grep -oP "$regex" <(echo "$cf_kernel_version")) 160 local build_cmd="" 161 if [ -f "$cf_kernel_repo_root/common-modules/virtual-device/BUILD.bazel" ]; then 162 # support android-mainline, android16, android15, android14, android13 163 build_cmd+="tools/bazel run --config=fast" 164 if [ "$GCOV" = true ]; then 165 build_cmd+=" --gcov" 166 fi 167 if [ "$DEBUG" = true ]; then 168 build_cmd+=" --debug" 169 fi 170 if [ "$KASAN" = true ]; then 171 build_cmd+=" --kasan" 172 fi 173 build_cmd+=" //common-modules/virtual-device:virtual_device_x86_64_dist" 174 elif [ -f "$cf_kernel_repo_root/build/build.sh" ]; then 175 if [[ "$android_version" == "12" ]]; then 176 build_cmd+="BUILD_CONFIG=common/build.config.gki.x86_64 build/build.sh" 177 build_cmd+=" && " 178 build_cmd+="BUILD_CONFIG=common-modules/virtual-device/build.config.virtual_device.x86_64 build/build.sh" 179 elif [[ "$android_version" == "11" ]] || [[ "$android_version" == "4.19" ]]; then 180 build_cmd+="BUILD_CONFIG=common/build.config.gki.x86_64 build/build.sh" 181 build_cmd+=" && " 182 build_cmd+="BUILD_CONFIG=common-modules/virtual-device/build.config.cuttlefish.x86_64 build/build.sh" 183 else 184 echo "The Kernel build $cf_kernel_version is not yet supported" >&2 185 return 1 186 fi 187 else 188 echo "The Kernel build $cf_kernel_version is not yet supported" >&2 189 return 1 190 fi 191 192 echo "$build_cmd" 193} 194 195function create_kernel_build_path() { 196 local cf_kernel_version=$1 197 198 local regex="((?<=android-)mainline|(\K\d+\.\d+(?=-stable)))|((?:android)\K\d+)" 199 local android_version 200 android_version=$(grep -oP "$regex" <(echo "$cf_kernel_version")) 201 if [ "$android_version" = "mainline" ] || greater_than_or_equal_to "$android_version" "14"; then 202 # support android-mainline, android16, android15, android14 203 echo "out/virtual_device_x86_64/dist" 204 elif greater_than_or_equal_to "$android_version" "11" || [[ "$android_version" == "4.19" ]]; then 205 # support android13, android12, android11, android-4.19-stable 206 echo "out/$cf_kernel_version/dist" 207 else 208 echo "The version of this kernel build $cf_kernel_version is not supported yet" >&2 209 return 1 210 fi 211} 212 213function go_to_repo_root() { 214 current_dir="$1" 215 while [ ! -d ".repo" ] && [ "$current_dir" != "/" ]; do 216 current_dir=$(dirname "$current_dir") # Go up one directory 217 cd "$current_dir" || print_error "Failed to cd to $current_dir" 218 done 219} 220 221function greater_than_or_equal_to() { 222 local num1="$1" 223 local num2="$2" 224 225 # This regex matches strings formatted as floating-point or integer numbers 226 local num_regex="^[0]([\.][0-9]+)?$|^[1-9][0-9]*([\.][0-9]+)?$" 227 if [[ ! "$num1" =~ $num_regex ]] || [[ ! "$num2" =~ $num_regex ]]; then 228 return 1 229 fi 230 231 if [[ $(echo "$num1 >= $num2" | bc -l) -eq 1 ]]; then 232 return 0 233 else 234 return 1 235 fi 236} 237 238# Checks if target_path is within root_directory 239function is_path_in_root() { 240 local root_directory="$1" 241 local target_path="$2" 242 243 # expand the path variable, for example: 244 # "~/Documents" becomes "/home/user/Documents" 245 root_directory=$(eval echo "$root_directory") 246 target_path=$(eval echo "$target_path") 247 248 # remove the trailing slashes 249 root_directory=$(realpath -m "$root_directory") 250 target_path=$(realpath -m "$target_path") 251 252 # handles the corner case, for example: 253 # $root_directory="/home/user/Doc", $target_path="/home/user/Documents/" 254 root_directory="${root_directory}/" 255 256 if [[ "$target_path" = "$root_directory"* ]]; then 257 return 0 258 else 259 return 1 260 fi 261} 262 263function print_info() { 264 echo "[$MY_NAME]: ${GREEN}$1${END}" 265} 266 267function print_warn() { 268 echo "[$MY_NAME]: ${YELLOW}$1${END}" 269} 270 271function print_error() { 272 echo -e "[$MY_NAME]: ${RED}$1${END}" 273 cd "$OLD_PWD" || echo "Failed to cd to $OLD_PWD" 274 exit 1 275} 276 277function set_platform_repo() { 278 print_warn "Build target product '${TARGET_PRODUCT}' does not match expected '$1'" 279 local lunch_cli="source build/envsetup.sh && lunch $1" 280 if [ -f "build/release/release_configs/trunk_staging.textproto" ]; then 281 lunch_cli+="-trunk_staging-userdebug" 282 else 283 lunch_cli+="-userdebug" 284 fi 285 print_info "Setup build environment with: $lunch_cli" 286 eval "$lunch_cli" 287} 288 289function find_repo() { 290 manifest_output=$(grep -e "superproject" -e "gs-pixel" -e "private/google-modules/soc/gs" \ 291 -e "kernel/common" -e "common-modules/virtual-device" .repo/manifests/default.xml) 292 case "$manifest_output" in 293 *platform/superproject*) 294 PLATFORM_REPO_ROOT="$PWD" 295 PLATFORM_VERSION=$(grep -e "platform/superproject" .repo/manifests/default.xml | \ 296 grep -oP 'revision="\K[^"]*') 297 print_info "PLATFORM_REPO_ROOT=$PLATFORM_REPO_ROOT, PLATFORM_VERSION=$PLATFORM_VERSION" 298 if [ -z "$PLATFORM_BUILD" ]; then 299 PLATFORM_BUILD="$PLATFORM_REPO_ROOT" 300 fi 301 ;; 302 *kernel/superproject*) 303 if [[ "$manifest_output" == *common-modules/virtual-device* ]]; then 304 CF_KERNEL_REPO_ROOT="$PWD" 305 CF_KERNEL_VERSION=$(grep -e "common-modules/virtual-device" \ 306 .repo/manifests/default.xml | grep -oP 'revision="\K[^"]*') 307 print_info "CF_KERNEL_REPO_ROOT=$CF_KERNEL_REPO_ROOT, CF_KERNEL_VERSION=$CF_KERNEL_VERSION" 308 if [ -z "$KERNEL_BUILD" ]; then 309 output=$(create_kernel_build_path "$CF_KERNEL_VERSION" 2>&1) 310 if [[ $? -ne 0 ]]; then 311 print_error "$output" 312 fi 313 KERNEL_BUILD="${CF_KERNEL_REPO_ROOT}/$output" 314 print_info "KERNEL_BUILD=$KERNEL_BUILD" 315 fi 316 fi 317 ;; 318 *) 319 print_warn "Unexpected manifest output. Could not determine repository type." 320 ;; 321 esac 322} 323 324function rebuild_platform() { 325 build_cmd="m -j12" 326 print_warn "Flag --skip-build is not set. Rebuilt images at $PWD with: $build_cmd" 327 eval "$build_cmd" 328 exit_code=$? 329 if [ $exit_code -eq 0 ]; then 330 if [ -f "${ANDROID_PRODUCT_OUT}/system.img" ]; then 331 print_info "$build_cmd succeeded" 332 else 333 print_error "${ANDROID_PRODUCT_OUT}/system.img doesn't exist" 334 fi 335 else 336 print_warn "$build_cmd returned exit_code $exit_code or ${ANDROID_PRODUCT_OUT}/system.img is not found" 337 print_error "$build_cmd failed" 338 fi 339} 340 341adb_checker 342 343OLD_PWD=$PWD 344MY_NAME=$0 345 346parse_arg "$@" 347 348FULL_COMMAND_PATH=$(dirname "$PWD/$0") 349REPO_LIST_OUT=$(repo list 2>&1) 350if [[ "$REPO_LIST_OUT" == "error"* ]]; then 351 echo -e "[$MY_NAME]: ${RED}Current path $PWD is not in an Android repo. Change path to repo root.${END}" 352 go_to_repo_root "$FULL_COMMAND_PATH" 353 print_info "Changed path to $PWD" 354else 355 go_to_repo_root "$PWD" 356fi 357 358find_repo 359 360if [ "$SKIP_BUILD" = false ] && [ -n "$PLATFORM_BUILD" ] && [[ "$PLATFORM_BUILD" != ab://* ]] \ 361&& [ -d "$PLATFORM_BUILD" ]; then 362 # Check if PLATFORM_BUILD is an Android platform repo, if yes rebuild 363 cd "$PLATFORM_BUILD" || print_error "Failed to cd to $PLATFORM_BUILD" 364 PLATFORM_REPO_LIST_OUT=$(repo list 2>&1) 365 if [[ "$PLATFORM_REPO_LIST_OUT" != "error"* ]]; then 366 go_to_repo_root "$PWD" 367 if [ -z "${TARGET_PRODUCT}" ] || [[ "${TARGET_PRODUCT}" != "$PRODUCT" ]]; then 368 set_platform_repo "$PRODUCT" 369 rebuild_platform 370 PLATFORM_BUILD=${ANDROID_PRODUCT_OUT} 371 fi 372 fi 373fi 374 375if [ "$SKIP_BUILD" = false ] && [ -n "$SYSTEM_BUILD" ] && [[ "$SYSTEM_BUILD" != ab://* ]] \ 376&& [ -d "$SYSTEM_BUILD" ]; then 377 # Get GSI build 378 cd "$SYSTEM_BUILD" || print_error "Failed to cd to $SYSTEM_BUILD" 379 SYSTEM_REPO_LIST_OUT=$(repo list 2>&1) 380 if [[ "$SYSTEM_REPO_LIST_OUT" != "error"* ]]; then 381 go_to_repo_root "$PWD" 382 if [ -z "${TARGET_PRODUCT}" ] || [[ "${TARGET_PRODUCT}" != "aosp_x86_64" ]]; then 383 set_platform_repo "aosp_x86_64" 384 rebuild_platform 385 SYSTEM_BUILD="${ANDROID_PRODUCT_OUT}/system.img" 386 fi 387 fi 388fi 389 390if [ "$SKIP_BUILD" = false ] && [ -n "$KERNEL_BUILD" ] && [[ "$KERNEL_BUILD" != ab://* ]]; then 391 if [ -d "$CF_KERNEL_REPO_ROOT" ] && [ -n "$CF_KERNEL_VERSION" ] && is_path_in_root "$CF_KERNEL_REPO_ROOT" "$KERNEL_BUILD"; then 392 # Support first-build in the local kernel repository 393 target_path="$CF_KERNEL_REPO_ROOT" 394 elif [ -d $KERNEL_BUILD ]; then 395 target_path="$KERNEL_BUILD" 396 else 397 print_error "Built kernel not found. Either build the kernel or use the default kernel from the local repository" 398 fi 399 400 cd "$target_path" || print_error "Failed to cd to $target_path" 401 KERNEL_REPO_LIST_OUT=$(repo list 2>&1) 402 if [[ "$KERNEL_REPO_LIST_OUT" != "error"* ]]; then 403 go_to_repo_root "$target_path" 404 target_kernel_repo_root="$PWD" 405 target_cf_kernel_version=$(grep -e "common-modules/virtual-device" \ 406 .repo/manifests/default.xml | grep -oP 'revision="\K[^"]*') 407 408 print_info "target_kernel_repo_root=$target_kernel_repo_root, target_cf_kernel_version=$target_cf_kernel_version" 409 410 output=$(create_kernel_build_cmd $PWD $target_cf_kernel_version 2>&1) 411 if [[ $? -ne 0 ]]; then 412 print_error "$output" 413 fi 414 build_cmd="$output" 415 print_warn "Flag --skip-build is not set. Rebuild the kernel with: $build_cmd." 416 eval "$build_cmd" && print_info "$build_cmd succeeded" || print_error "$build_cmd failed" 417 else 418 print_warn "Current path $PWD is not a valid Android repo, please ensure it contains the kernel" 419 fi 420fi 421 422if [ -z "$ACLOUD_BIN" ] || ! [ -x "$ACLOUD_BIN" ]; then 423 output=$(which acloud 2>&1) 424 if [ -z "$output" ]; then 425 print_info "Use acloud binary from $ACLOUD_PREBUILT" 426 if [ -n "${PLATFORM_REPO_ROOT}" ]; then 427 ACLOUD_PREBUILT="${PLATFORM_REPO_ROOT}/${ACLOUD_PREBUILT}" 428 elif [ -n "${CF_KERNEL_REPO_ROOT}" ]; then 429 ACLOUD_PREBUILT="${CF_KERNEL_REPO_ROOT}/${ACLOUD_PREBUILT}" 430 else 431 print_error "Unable to determine repository root path from repo manifest" 432 fi 433 ACLOUD_BIN="$ACLOUD_PREBUILT" 434 else 435 print_info "Use acloud binary from $output" 436 ACLOUD_BIN="$output" 437 fi 438 439 # Check if the newly found or prebuilt ACLOUD_BIN is executable 440 if ! [ -x "$ACLOUD_BIN" ]; then 441 print_error "$ACLOUD_BIN is not executable" 442 fi 443fi 444 445acloud_cli="$ACLOUD_BIN create" 446EXTRA_OPTIONS+=("$OPT_SKIP_PRERUNCHECK") 447 448# Add in branch if not specified 449 450if [ -z "$PLATFORM_BUILD" ]; then 451 print_warn "Platform build is not specified, will use the latest aosp-main build." 452 acloud_cli+=' --branch aosp-main' 453elif [[ "$PLATFORM_BUILD" == ab://* ]]; then 454 IFS='/' read -ra array <<< "$PLATFORM_BUILD" 455 acloud_cli+=" --branch ${array[2]}" 456 457 # Check if array[3] exists before using it 458 if [ ${#array[@]} -ge 3 ] && [ -n "${array[3]}" ]; then 459 acloud_cli+=" --build-target ${array[3]}" 460 461 # Check if array[4] exists and is not 'latest' before using it 462 if [ ${#array[@]} -ge 4 ] && [ -n "${array[4]}" ] && [ "${array[4]}" != 'latest' ]; then 463 acloud_cli+=" --build-id ${array[4]}" 464 fi 465 fi 466else 467 acloud_cli+=" --local-image $PLATFORM_BUILD" 468fi 469 470if [ -z "$KERNEL_BUILD" ]; then 471 print_warn "Flag --kernel-build is not set, will not launch Cuttlefish with different kernel." 472elif [[ "$KERNEL_BUILD" == ab://* ]]; then 473 IFS='/' read -ra array <<< "$KERNEL_BUILD" 474 acloud_cli+=" --kernel-branch ${array[2]}" 475 476 # Check if array[3] exists before using it 477 if [ ${#array[@]} -ge 3 ] && [ -n "${array[3]}" ]; then 478 acloud_cli+=" --kernel-build-target ${array[3]}" 479 480 # Check if array[4] exists and is not 'latest' before using it 481 if [ ${#array[@]} -ge 4 ] && [ -n "${array[4]}" ] && [ "${array[4]}" != 'latest' ]; then 482 acloud_cli+=" --kernel-build-id ${array[4]}" 483 fi 484 fi 485else 486 acloud_cli+=" --local-kernel-image $KERNEL_BUILD" 487fi 488 489if [ -z "$SYSTEM_BUILD" ]; then 490 print_warn "System build is not specified, will not launch Cuttlefish with GSI mixed build." 491elif [[ "$SYSTEM_BUILD" == ab://* ]]; then 492 IFS='/' read -ra array <<< "$SYSTEM_BUILD" 493 acloud_cli+=" --system-branch ${array[2]}" 494 495 # Check if array[3] exists before using it 496 if [ ${#array[@]} -ge 3 ] && [ -n "${array[3]}" ]; then 497 acloud_cli+=" --system-build-target ${array[3]}" 498 499 # Check if array[4] exists and is not 'latest' before using it 500 if [ ${#array[@]} -ge 4 ] && [ -n "${array[4]}" ] && [ "${array[4]}" != 'latest' ]; then 501 acloud_cli+=" --system-build-id ${array[4]}" 502 fi 503 fi 504else 505 acloud_cli+=" --local-system-image $SYSTEM_BUILD" 506fi 507 508acloud_cli+=" ${EXTRA_OPTIONS[*]}" 509print_info "Launch CVD with command: $acloud_cli" 510eval "$acloud_cli" 511