blob: ecf4a945507f9250fc9cca6a5b8e5ee7b169d243 [file] [log] [blame]
#!/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/src/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_string docker_arch "$(uname -m)" "Target architectre"
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 "${ANDROID_BUILD_TOP}/device/google/cuttlefish_vmm/$(uname -m)-linux-gnu" "Output directory"
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 \
"${ANDROID_BUILD_TOP}/device/google/cuttlefish_vmm/$(uname -m)-linux-gnu/manifest.xml" \
"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
}
function container_exists() {
[[ $(docker ps -a --filter "name=^/$1$" --format '{{.Names}}') == $1 ]] && echo $1;
}
function build_on_gce() {
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 \
"${project_zone_flags[@]}" \
"${delete_instances[@]}" || \
echo Not running
gcloud compute instances create \
"${project_zone_flags[@]}" \
--boot-disk-size=200GB \
--machine-type=n1-standard-4 \
--image-family="${FLAGS_gce_source_image_family}" \
--image-project="${FLAGS_gce_source_image_project}" \
"${FLAGS_gce_instance}"
wait_for_instance "${FLAGS_gce_instance}"
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
# beta for the --internal-ip flag that may be passed via SSH_FLAGS
gcloud beta compute scp "${SSH_FLAGS[@]}" \
"${project_zone_flags[@]}" \
"${source_files[@]}" \
"${FLAGS_gce_user}@${FLAGS_gce_instance}:"
if [ ${_reuse} -eq 0 ]; then
gcloud compute ssh "${SSH_FLAGS[@]}" \
"${project_zone_flags[@]}" \
"${FLAGS_gce_user}@${FLAGS_gce_instance}" -- \
./rebuild-internal.sh install_packages
fi
gcloud compute ssh "${SSH_FLAGS[@]}" \
"${project_zone_flags[@]}" \
"${FLAGS_gce_user}@${FLAGS_gce_instance}" -- \
./rebuild-internal.sh "${gce_flags[@]}" ${_prepare_source[@]} '$(uname -m)_build'
gcloud beta compute scp --recurse "${SSH_FLAGS[@]}" \
"${project_zone_flags[@]}" \
"${FLAGS_gce_user}@${FLAGS_gce_instance}":x86_64-linux-gnu \
"${ANDROID_BUILD_TOP}/device/google/cuttlefish_vmm"
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() {
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
scp \
"${source_files[@]}" \
"${FLAGS_arm_user}@${FLAGS_arm_instance}:"
if [ ${_reuse} -eq 0 ]; then
ssh -t "${FLAGS_arm_user}@${FLAGS_arm_instance}" -- \
./rebuild-internal.sh install_packages
fi
ssh -t "${FLAGS_arm_user}@${FLAGS_arm_instance}" -- \
./rebuild-internal.sh "${arm_flags[@]}" ${_prepare_source[@]} '$(uname -m)_build'
scp -r "${FLAGS_arm_user}@${FLAGS_arm_instance}":aarch64-linux-gnu \
"${ANDROID_BUILD_TOP}/device/google/cuttlefish_vmm"
}
build_locally_using_docker() {
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
case "${FLAGS_docker_arch}" in
aarch64) ;;
x86_64) ;;
*) echo Invalid value ${FLAGS_docker_arch} for --docker_arch 1>&2
fail=1
;;
esac
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
if [[ ${_reuse} -eq 1 ]]; then
# Volume mapping are specified only when a container is created.
if [ -n "${FLAGS_docker_source}" ]; then
echo Option --docker_source may not be specified with --reuse 1>&2
fail=1
fi
if [ -n "${FLAGS_docker_working}" ]; then
echo Option --docker_working may not be specified with --reuse 1>&2
fail=1
fi
fi
if [[ "${fail}" -ne 0 ]]; then
exit "${fail}"
fi
if [[ ${_reuse} -eq 0 ]]; then
docker build \
-f ${DIR}/Dockerfile \
-t ${FLAGS_docker_image}:latest \
${DIR} \
--build-arg USER=${FLAGS_docker_user} \
--build-arg UID=${FLAGS_docker_uid}
_docker_source=()
if [ -n "${FLAGS_docker_source}" ]; then
_docker_source+=("-v ${FLAGS_docker_source}:/source:rw")
fi
_docker_working=()
if [ -n "${FLAGS_docker_working}" ]; then
_docker_working+=("-v ${FLAGS_docker_working}:/working:rw")
fi
if [[ -n "$(container_exists ${FLAGS_docker_container})" ]]; then
docker rm -f ${FLAGS_docker_container}
fi
docker run -d \
--privileged \
--name ${FLAGS_docker_container} \
-h ${FLAGS_docker_container} \
${_docker_source} \
${_docker_working} \
-v "${FLAGS_docker_output}":/output \
-v /sys/fs/cgroup:/sys/fs/cgroup:ro \
${FLAGS_docker_image}:latest
else
docker unpause ${FLAGS_docker_container}
fi
docker exec -it \
--user ${FLAGS_docker_user} \
${docker_flags[@]} \
${FLAGS_docker_container} \
/static/rebuild-internal.sh ${_prepare_source[@]} ${FLAGS_docker_arch}_build
docker pause ${FLAGS_docker_container}
}
main() {
set -o errexit
set -x
fail=0
source_files=("${DIR}"/rebuild-internal.sh)
# These must match the definitions in the Dockerfile
docker_flags=("-e SOURCE_DIR=/source" "-e WORKING_DIR=/working" "-e OUTPUT_DIR=/output" "-e TOOLS_DIR=/static/tools")
gce_flags=()
arm_flags=()
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
source_files+=("${FLAGS_manifest}")
docker_flags+=("-e CUSTOM_MANIFEST=/static/${FLAGS_docker_arch}-linux-gnu/$(basename ${FLAGS_manifest})")
gce_flags+=("CUSTOM_MANIFEST=/home/${FLAGS_gce_user}/$(basename "${FLAGS_manifest}")")
arm_flags+=("CUSTOM_MANIFEST=/home/${FLAGS_arm_user}/$(basename "${FLAGS_manifest}")")
fi
local _prepare_source=(setup_env fetch_source);
local _reuse=0
if [[ ${FLAGS_reuse} -eq ${FLAGS_TRUE} ]]; then
# neither install packages, nor sync sources; skip to building them
_prepare_source=(setup_env)
_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[@]}"