• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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