| #!/bin/bash |
| |
| # Common code to build a host image on GCE |
| |
| # INTERNAL_extra_source may be set to a directory containing the source for |
| # extra package to build. |
| |
| # INTERNAL_IP can be set to --internal-ip run on a GCE instance |
| # The instance will need --scope compute-rw |
| |
| source "${ANDROID_BUILD_TOP}/external/shflags/shflags" |
| DIR="${ANDROID_BUILD_TOP}/device/google/cuttlefish_vmm" |
| |
| # ARM-board options |
| |
| DEFINE_boolean arm false "Build on an ARM board" |
| DEFINE_string arm_instance "" "IP address or DNS name of an ARM system to do the secondary build" |
| DEFINE_string arm_user "vsoc-01" "User to invoke on the ARM system" |
| |
| # Docker options |
| |
| DEFINE_boolean docker false "Build inside docker" |
| DEFINE_boolean docker_persistent true "Build inside a privileged, persistent container (faster for iterative development)" |
| DEFINE_string docker_arch "$(uname -m)" "Target architectre" |
| DEFINE_boolean docker_build_image true "When --noreuse is specified, this flag controls building the docker image (else we assume it was built and reuse it)" |
| DEFINE_string docker_image "docker_vmm" "Name of docker image to build" |
| DEFINE_string docker_container "docker_vmm" "Name of docker container to create" |
| DEFINE_string docker_source "" "Path to sources checked out using manifest" |
| DEFINE_string docker_working "" "Path to working directory" |
| DEFINE_string docker_output "" "Output directory (when --docker is specified)" |
| DEFINE_string docker_user "${USER}" "Docker-container user" |
| DEFINE_string docker_uid "${UID}" "Docker-container user ID" |
| |
| # GCE options |
| |
| DEFINE_boolean gce false "Build on a GCE instance" |
| DEFINE_string gce_project "$(gcloud config get-value project)" "Project to use" "p" |
| DEFINE_string gce_source_image_family debian-10 "Image familty to use as the base" "s" |
| DEFINE_string gce_source_image_project debian-cloud "Project holding the base image" "m" |
| DEFINE_string gce_instance "${USER}-build" "Instance name to create for the build" "i" |
| DEFINE_string gce_user cuttlefish_crosvm_builder "User name to use on GCE when doing the build" |
| DEFINE_string gce_zone "$(gcloud config get-value compute/zone)" "Zone to use" "z" |
| |
| # Common options |
| |
| DEFINE_string manifest "" "Path to custom manifest to use for the build" |
| DEFINE_boolean reuse false "Set to true to reuse a previously-set-up instance." |
| DEFINE_boolean reuse_resync false "Reuse a previously-set-up instance, but clean and re-sync the sources. Overrides --reuse if both are specified." |
| |
| SSH_FLAGS=(${INTERNAL_IP}) |
| |
| wait_for_instance() { |
| alive="" |
| while [[ -z "${alive}" ]]; do |
| sleep 5 |
| alive="$(gcloud compute ssh "${SSH_FLAGS[@]}" "$@" -- uptime || true)" |
| done |
| } |
| |
| check_common_docker_options() { |
| if [[ -z "${FLAGS_docker_image}" ]]; then |
| echo Option --docker_image must not be empty 1>&1 |
| fail=1 |
| fi |
| if [[ -z "${FLAGS_docker_container}" ]]; then |
| echo Options --docker_container must not be empty 1>&2 |
| fail=1 |
| fi |
| if [[ -z "${FLAGS_docker_user}" ]]; then |
| echo Options --docker_user must not be empty 1>&2 |
| fail=1 |
| fi |
| if [[ -z "${FLAGS_docker_uid}" ]]; then |
| echo Options --docker_uid must not be empty 1>&2 |
| fail=1 |
| fi |
| # Volume mapping are specified only when a container is created. With |
| # --reuse, an already-created persistent container is reused, which implies |
| # that we cannot change the volume maps. For non-persistent containers, we |
| # use docker run, which creates and runs the continer in one step; in that |
| # case, we must pass the same values for --docker_source and --docker_output |
| # that we passed when we ran the non-persistent continer the first time. |
| if [[ ${_reuse} -eq 1 && ${FLAGS_docker_persistent} -eq ${FLAGS_TRUE} ]]; then |
| if [ -n "${FLAGS_docker_source}" ]; then |
| echo Option --docker_source may not be specified with --reuse and --docker_persistent 1>&2 |
| fail=1 |
| fi |
| if [ -n "${FLAGS_docker_working}" ]; then |
| echo Option --docker_working may not be specified with --reuse and --docker_persistent 1>&2 |
| fail=1 |
| fi |
| if [ -n "${FLAGS_docker_output}" ]; then |
| echo Option --docker_output may not be specified with --reuse and --docker_persistent 1>&2 |
| fail=1 |
| fi |
| fi |
| if [[ "${fail}" -ne 0 ]]; then |
| exit "${fail}" |
| fi |
| } |
| |
| build_locally_using_docker() { |
| check_common_docker_options |
| case "${FLAGS_docker_arch}" in |
| aarch64) ;; |
| x86_64) ;; |
| *) echo Invalid value ${FLAGS_docker_arch} for --docker_arch 1>&2 |
| fail=1 |
| ;; |
| esac |
| if [[ "${fail}" -ne 0 ]]; then |
| exit "${fail}" |
| fi |
| local -i _persistent=0 |
| if [[ ${FLAGS_docker_persistent} -eq ${FLAGS_TRUE} ]]; then |
| _persistent=1 |
| fi |
| |
| local -i _build_image=0 |
| if [[ ${FLAGS_docker_build_image} -eq ${FLAGS_TRUE} ]]; then |
| _build_image=1 |
| fi |
| |
| local _docker_output="" |
| if [ -z "${FLAGS_docker_output}" ]; then |
| _docker_output="${ANDROID_BUILD_TOP}/device/google/cuttlefish_vmm/${FLAGS_docker_arch}-linux-gnu" |
| else |
| _docker_output="${FLAGS_docker_output}" |
| fi |
| |
| local _temp="$(mktemp -d)" |
| rsync -avR "${relative_source_files[@]/#/${DIR}/./}" "${_temp}" |
| if [ -n "${custom_manifest}" ]; then |
| cp "${custom_manifest}" "${_temp}"/custom.xml |
| else |
| touch "${_temp}"/custom.xml |
| fi |
| |
| ${DIR}/rebuild-docker.sh "${FLAGS_docker_image}" \ |
| "${FLAGS_docker_container}" \ |
| "${FLAGS_docker_arch}" \ |
| "${FLAGS_docker_user}" \ |
| "${FLAGS_docker_uid}" \ |
| "${_persistent}" \ |
| "x${FLAGS_docker_source}" \ |
| "x${FLAGS_docker_working}" \ |
| "x${_docker_output}" \ |
| "${_reuse}" \ |
| "${_build_image}" \ |
| "${_temp}/Dockerfile" \ |
| "${_temp}" \ |
| "${#docker_flags[@]}" "${docker_flags[@]}" \ |
| "${#_prepare_source[@]}" "${_prepare_source[@]}" |
| |
| rm -rf "${_temp}" |
| } |
| |
| function build_on_gce() { |
| check_common_docker_options |
| if [[ -z "${FLAGS_gce_instance}" ]]; then |
| echo Must specify instance 1>&2 |
| fail=1 |
| fi |
| if [[ -z "${FLAGS_gce_project}" ]]; then |
| echo Must specify project 1>&2 |
| fail=1 |
| fi |
| if [[ -z "${FLAGS_gce_zone}" ]]; then |
| echo Must specify zone 1>&2 |
| fail=1 |
| fi |
| if [[ "${fail}" -ne 0 ]]; then |
| exit "${fail}" |
| fi |
| project_zone_flags=(--project="${FLAGS_gce_project}" --zone="${FLAGS_gce_zone}") |
| if [ ${_reuse} -eq 0 ]; then |
| delete_instances=("${FLAGS_gce_instance}") |
| gcloud compute instances delete -q \ |
| "${delete_instances[@]}" \ |
| "${project_zone_flags[@]}" || \ |
| echo Instance does not exist |
| gcloud compute images delete -q \ |
| "${delete_instances[@]/%/-image}" \ |
| --project "${FLAGS_gce_project}" || \ |
| echo Image does not exist |
| gcloud compute disks delete -q \ |
| "${delete_instances[@]/%/-disk}" \ |
| "${project_zone_flags[@]}" || \ |
| echo Disk does not exist |
| |
| gcloud compute disks create \ |
| "${delete_instances[@]/%/-disk}" \ |
| "${project_zone_flags[@]}" \ |
| --image-project="${FLAGS_gce_source_image_project}" \ |
| --image-family="${FLAGS_gce_source_image_family}" |
| gcloud compute images create \ |
| "${delete_instances[@]/%/-image}" \ |
| --source-disk "${delete_instances[@]/%/-disk}" \ |
| --project "${FLAGS_gce_project}" --source-disk-zone "${FLAGS_gce_zone}" \ |
| --licenses "https://www.googleapis.com/compute/v1/projects/vm-options/global/licenses/enable-vmx" |
| gcloud compute instances create \ |
| "${delete_instances[@]}" \ |
| "${project_zone_flags[@]}" \ |
| --image "${delete_instances[@]/%/-image}" \ |
| --boot-disk-size=200GB \ |
| --machine-type=n1-standard-8 \ |
| --min-cpu-platform "Intel Skylake" |
| |
| wait_for_instance "${FLAGS_gce_instance}" "${project_zone_flags[@]}" |
| |
| # install docker |
| gcloud beta compute ssh "${SSH_FLAGS[@]}" \ |
| "${project_zone_flags[@]}" \ |
| "${FLAGS_gce_user}@${FLAGS_gce_instance}" -- \ |
| 'curl -fsSL https://get.docker.com | /bin/bash' |
| gcloud beta compute ssh "${SSH_FLAGS[@]}" \ |
| "${project_zone_flags[@]}" \ |
| "${FLAGS_gce_user}@${FLAGS_gce_instance}" -- \ |
| sudo usermod -aG docker "${FLAGS_gce_user}" |
| |
| # beta for the --internal-ip flag that may be passed via SSH_FLAGS |
| |
| gcloud beta compute ssh "${SSH_FLAGS[@]}" \ |
| "${project_zone_flags[@]}" \ |
| "${FLAGS_gce_user}@${FLAGS_gce_instance}" -- \ |
| mkdir -p '$PWD/docker $PWD/docker/source $PWD/docker/working $PWD/docker/output' |
| |
| tar czv -C "${DIR}" -f - "${relative_source_files[@]}" | \ |
| gcloud beta compute ssh "${SSH_FLAGS[@]}" \ |
| "${project_zone_flags[@]}" \ |
| "${FLAGS_gce_user}@${FLAGS_gce_instance}" -- \ |
| 'tar xzv -C ~/docker -f -' |
| if [ -n "${custom_manifest}" ]; then |
| gcloud beta compute scp "${SSH_FLAGS[@]}" \ |
| "${project_zone_flags[@]}" \ |
| "${custom_manifest}" \ |
| "${FLAGS_gce_user}@${FLAGS_gce_instance}:~/docker/custom.xml" |
| else |
| gcloud beta compute ssh "${SSH_FLAGS[@]}" \ |
| "${project_zone_flags[@]}" \ |
| "${FLAGS_gce_user}@${FLAGS_gce_instance}" -- \ |
| "touch ~/docker/custom.xml" |
| fi |
| fi |
| |
| local _status=$(gcloud compute instances list \ |
| --project="${FLAGS_gce_project}" \ |
| --zones="${FLAGS_gce_zone}" \ |
| --filter="name=('${FLAGS_gce_instance}')" \ |
| --format=flattened | awk '/status:/ {print $2}') |
| if [ "${_status}" != "RUNNING" ] ; then |
| echo "Instance ${FLAGS_gce_instance} is not running." |
| exit 1; |
| fi |
| |
| local -i _persistent=0 |
| if [[ ${FLAGS_docker_persistent} -eq ${FLAGS_TRUE} ]]; then |
| _persistent=1 |
| fi |
| local -i _build_image=0 |
| if [[ ${FLAGS_docker_build_image} -eq ${FLAGS_TRUE} ]]; then |
| _build_image=1 |
| fi |
| gcloud beta compute ssh "${SSH_FLAGS[@]}" \ |
| "${project_zone_flags[@]}" \ |
| "${FLAGS_gce_user}@${FLAGS_gce_instance}" -- \ |
| ./docker/rebuild-docker.sh "${FLAGS_docker_image}" \ |
| "${FLAGS_docker_container}" \ |
| "${FLAGS_docker_arch}" \ |
| '${USER}' \ |
| '${UID}' \ |
| "${_persistent}" \ |
| 'x$PWD/docker/source' \ |
| 'x$PWD/docker/working' \ |
| 'x$PWD/docker/output' \ |
| "${_reuse}" \ |
| "${_build_image}" \ |
| '~/docker/Dockerfile' \ |
| '~/docker/' \ |
| "${#docker_flags[@]}" "${docker_flags[@]}" \ |
| "${#_prepare_source[@]}" "${_prepare_source[@]}" |
| |
| gcloud beta compute ssh "${SSH_FLAGS[@]}" \ |
| "${project_zone_flags[@]}" \ |
| "${FLAGS_gce_user}@${FLAGS_gce_instance}" --command \ |
| 'tar czv -C $PWD/docker/output -f - $(find $PWD/docker/output -printf "%P\n")' | \ |
| tar xzv -C ${DIR}/${FLAGS_docker_arch}-linux-gnu -f - |
| |
| gcloud compute disks describe \ |
| "${project_zone_flags[@]}" "${FLAGS_gce_instance}" | \ |
| grep ^sourceImage: > "${DIR}"/x86_64-linux-gnu/builder_image.txt |
| } |
| |
| function build_on_arm_board() { |
| check_common_docker_options |
| if [[ "${FLAGS_docker_arch}" != "aarch64" ]]; then |
| echo ARM board supports building only aarch64 1>&2 |
| fail=1 |
| fi |
| if [[ -z "${FLAGS_arm_instance}" ]]; then |
| echo Must specify IP address of ARM board 1>&2 |
| fail=1 |
| fi |
| if [[ -z "${FLAGS_arm_user}" ]]; then |
| echo Must specify a user account on ARM board 1>&2 |
| fail=1 |
| fi |
| if [[ "${fail}" -ne 0 ]]; then |
| exit "${fail}" |
| fi |
| if [[ "${_reuse}" -eq 0 ]]; then |
| ssh -t "${FLAGS_arm_user}@${FLAGS_arm_instance}" -- \ |
| rm -rf '$PWD/docker' |
| fi |
| rsync -avR -e ssh \ |
| "${relative_source_files[@]/#/${DIR}/./}" \ |
| "${FLAGS_arm_user}@${FLAGS_arm_instance}:~/docker/" |
| |
| if [ -n "${custom_manifest}" ]; then |
| scp "${custom_manifest}" "${FLAGS_arm_user}@${FLAGS_arm_instance}":~/docker/custom.xml |
| else |
| ssh -t "${FLAGS_arm_user}@${FLAGS_arm_instance}" -- \ |
| "touch ~/docker/custom.xml" |
| fi |
| |
| local -i _persistent=0 |
| if [[ ${FLAGS_docker_persistent} -eq ${FLAGS_TRUE} ]]; then |
| _persistent=1 |
| fi |
| local -i _build_image=0 |
| if [[ ${FLAGS_docker_build_image} -eq ${FLAGS_TRUE} ]]; then |
| _build_image=1 |
| fi |
| ssh -t "${FLAGS_arm_user}@${FLAGS_arm_instance}" -- \ |
| mkdir -p '$PWD/docker/source' '$PWD/docker/working' '$PWD/docker/output' |
| ssh -t "${FLAGS_arm_user}@${FLAGS_arm_instance}" -- \ |
| ./docker/rebuild-docker.sh "${FLAGS_docker_image}" \ |
| "${FLAGS_docker_container}" \ |
| "${FLAGS_docker_arch}" \ |
| '${USER}' \ |
| '${UID}' \ |
| "${_persistent}" \ |
| 'x$PWD/docker/source' \ |
| 'x$PWD/docker/working' \ |
| 'x$PWD/docker/output' \ |
| "${_reuse}" \ |
| "${_build_image}" \ |
| '~/docker/Dockerfile' \ |
| '~/docker/' \ |
| "${#docker_flags[@]}" "${docker_flags[@]}" \ |
| "${#_prepare_source[@]}" "${_prepare_source[@]}" |
| |
| rsync -avR -e ssh "${FLAGS_arm_user}@${FLAGS_arm_instance}":docker/output/./ \ |
| "${ANDROID_BUILD_TOP}/device/google/cuttlefish_vmm/${FLAGS_docker_arch}-linux-gnu" |
| } |
| |
| main() { |
| set -o errexit |
| set -x |
| fail=0 |
| relative_source_files=("rebuild-docker.sh" |
| "rebuild-internal.sh" |
| "Dockerfile" |
| "x86_64-linux-gnu/manifest.xml" |
| "aarch64-linux-gnu/manifest.xml" |
| "policy-inliner.sh" |
| ".dockerignore") |
| # These must match the definitions in the Dockerfile |
| docker_flags=("-eSOURCE_DIR=/source" "-eWORKING_DIR=/working" "-eOUTPUT_DIR=/output" "-eTOOLS_DIR=/static/tools") |
| |
| if [[ $(( $((${FLAGS_gce}==${FLAGS_TRUE})) + $((${FLAGS_arm}==${FLAGS_TRUE})) + $((${FLAGS_docker}==${FLAGS_TRUE})) )) > 1 ]]; then |
| echo You may specify only one of --gce, --docker, or --arm 1>&2 |
| exit 2 |
| fi |
| |
| if [[ -n "${FLAGS_manifest}" ]]; then |
| if [[ ! -f "${FLAGS_manifest}" ]]; then |
| echo custom manifest not found: ${FLAGS_manifest} 1>&1 |
| exit 2 |
| fi |
| custom_manifest="${FLAGS_manifest}" |
| docker_flags+=("-eCUSTOM_MANIFEST=/static/custom.xml") |
| else |
| custom_manifest="${DIR}/${FLAGS_docker_arch}-linux-gnu/manifest.xml" |
| docker_flags+=("-eCUSTOM_MANIFEST=/static/${FLAGS_docker_arch}-linux-gnu/manifest.xml") |
| fi |
| local -a _prepare_source=(setup_env fetch_source); |
| local -i _reuse=0 |
| if [[ ${FLAGS_reuse} -eq ${FLAGS_TRUE} ]]; then |
| # neither install packages, nor sync sources; skip to building them |
| _prepare_source=(setup_env) |
| # unless you're setting up a non-persistent container and --docker_source is |
| # the empty string; in this case, --reuse implies --reuse_resync |
| if [[ "${FLAGS_docker_persistent}" -eq ${FLAGS_FALSE} && \ |
| -z "${FLAGS_docker_source}" ]]; then |
| _prepare_source+=(resync_source) |
| fi |
| _reuse=1 |
| fi |
| if [[ ${FLAGS_reuse_resync} -eq ${FLAGS_TRUE} ]]; then |
| # do not install packages but clean and sync sources afresh |
| _prepare_source=(setup_env resync_source); |
| _reuse=1 |
| fi |
| if [[ ${FLAGS_gce} -eq ${FLAGS_TRUE} ]]; then |
| build_on_gce |
| exit 0 |
| gcloud compute instances delete -q \ |
| "${project_zone_flags[@]}" \ |
| "${FLAGS_gce_instance}" |
| fi |
| if [[ ${FLAGS_arm} -eq ${FLAGS_TRUE} ]]; then |
| build_on_arm_board |
| exit 0 |
| fi |
| if [[ ${FLAGS_docker} -eq ${FLAGS_TRUE} ]]; then |
| build_locally_using_docker |
| exit 0 |
| fi |
| } |
| |
| FLAGS "$@" || exit 1 |
| main "${FLAGS_ARGV[@]}" |