1#!/bin/bash 2 3# Common code to build a host image on GCE 4 5# INTERNAL_extra_source may be set to a directory containing the source for 6# extra package to build. 7 8# INTERNAL_IP can be set to --internal-ip run on a GCE instance 9# The instance will need --scope compute-rw 10 11source "${ANDROID_BUILD_TOP}/external/shflags/shflags" 12DIR="${ANDROID_BUILD_TOP}/device/google/cuttlefish_vmm" 13 14# ARM-board options 15 16DEFINE_boolean arm false "Build on an ARM board" 17DEFINE_string arm_instance "" "IP address or DNS name of an ARM system to do the secondary build" 18DEFINE_string arm_user "vsoc-01" "User to invoke on the ARM system" 19 20# Docker options 21 22DEFINE_boolean docker false "Build inside docker" 23DEFINE_boolean docker_persistent true "Build inside a privileged, persistent container (faster for iterative development)" 24DEFINE_string docker_arch "$(uname -m)" "Target architectre" 25DEFINE_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)" 26DEFINE_string docker_image "docker_vmm" "Name of docker image to build" 27DEFINE_string docker_container "docker_vmm" "Name of docker container to create" 28DEFINE_string docker_source "" "Path to sources checked out using manifest" 29DEFINE_string docker_working "" "Path to working directory" 30DEFINE_string docker_output "" "Output directory (when --docker is specified)" 31DEFINE_string docker_user "${USER}" "Docker-container user" 32DEFINE_string docker_uid "${UID}" "Docker-container user ID" 33 34# GCE options 35 36DEFINE_boolean gce false "Build on a GCE instance" 37DEFINE_string gce_project "$(gcloud config get-value project)" "Project to use" "p" 38DEFINE_string gce_source_image_family debian-10 "Image familty to use as the base" "s" 39DEFINE_string gce_source_image_project debian-cloud "Project holding the base image" "m" 40DEFINE_string gce_instance "${USER}-build" "Instance name to create for the build" "i" 41DEFINE_string gce_user cuttlefish_crosvm_builder "User name to use on GCE when doing the build" 42DEFINE_string gce_zone "$(gcloud config get-value compute/zone)" "Zone to use" "z" 43 44# Common options 45 46DEFINE_string manifest "" "Path to custom manifest to use for the build" 47DEFINE_boolean reuse false "Set to true to reuse a previously-set-up instance." 48DEFINE_boolean reuse_resync false "Reuse a previously-set-up instance, but clean and re-sync the sources. Overrides --reuse if both are specified." 49 50SSH_FLAGS=(${INTERNAL_IP}) 51 52wait_for_instance() { 53 alive="" 54 while [[ -z "${alive}" ]]; do 55 sleep 5 56 alive="$(gcloud compute ssh "${SSH_FLAGS[@]}" "$@" -- uptime || true)" 57 done 58} 59 60check_common_docker_options() { 61 if [[ -z "${FLAGS_docker_image}" ]]; then 62 echo Option --docker_image must not be empty 1>&1 63 fail=1 64 fi 65 if [[ -z "${FLAGS_docker_container}" ]]; then 66 echo Options --docker_container must not be empty 1>&2 67 fail=1 68 fi 69 if [[ -z "${FLAGS_docker_user}" ]]; then 70 echo Options --docker_user must not be empty 1>&2 71 fail=1 72 fi 73 if [[ -z "${FLAGS_docker_uid}" ]]; then 74 echo Options --docker_uid must not be empty 1>&2 75 fail=1 76 fi 77 # Volume mapping are specified only when a container is created. With 78 # --reuse, an already-created persistent container is reused, which implies 79 # that we cannot change the volume maps. For non-persistent containers, we 80 # use docker run, which creates and runs the continer in one step; in that 81 # case, we must pass the same values for --docker_source and --docker_output 82 # that we passed when we ran the non-persistent continer the first time. 83 if [[ ${_reuse} -eq 1 && ${FLAGS_docker_persistent} -eq ${FLAGS_TRUE} ]]; then 84 if [ -n "${FLAGS_docker_source}" ]; then 85 echo Option --docker_source may not be specified with --reuse and --docker_persistent 1>&2 86 fail=1 87 fi 88 if [ -n "${FLAGS_docker_working}" ]; then 89 echo Option --docker_working may not be specified with --reuse and --docker_persistent 1>&2 90 fail=1 91 fi 92 if [ -n "${FLAGS_docker_output}" ]; then 93 echo Option --docker_output may not be specified with --reuse and --docker_persistent 1>&2 94 fail=1 95 fi 96 fi 97 if [[ "${fail}" -ne 0 ]]; then 98 exit "${fail}" 99 fi 100} 101 102build_locally_using_docker() { 103 check_common_docker_options 104 case "${FLAGS_docker_arch}" in 105 aarch64) ;; 106 x86_64) ;; 107 *) echo Invalid value ${FLAGS_docker_arch} for --docker_arch 1>&2 108 fail=1 109 ;; 110 esac 111 if [[ "${fail}" -ne 0 ]]; then 112 exit "${fail}" 113 fi 114 local -i _persistent=0 115 if [[ ${FLAGS_docker_persistent} -eq ${FLAGS_TRUE} ]]; then 116 _persistent=1 117 fi 118 119 local -i _build_image=0 120 if [[ ${FLAGS_docker_build_image} -eq ${FLAGS_TRUE} ]]; then 121 _build_image=1 122 fi 123 124 local _docker_output="" 125 if [ -z "${FLAGS_docker_output}" ]; then 126 _docker_output="${ANDROID_BUILD_TOP}/device/google/cuttlefish_vmm/${FLAGS_docker_arch}-linux-gnu" 127 else 128 _docker_output="${FLAGS_docker_output}" 129 fi 130 131 local _temp="$(mktemp -d)" 132 rsync -avR "${relative_source_files[@]/#/${DIR}/./}" "${_temp}" 133 if [ -n "${custom_manifest}" ]; then 134 cp "${custom_manifest}" "${_temp}"/custom.xml 135 else 136 touch "${_temp}"/custom.xml 137 fi 138 139 ${DIR}/rebuild-docker.sh "${FLAGS_docker_image}" \ 140 "${FLAGS_docker_container}" \ 141 "${FLAGS_docker_arch}" \ 142 "${FLAGS_docker_user}" \ 143 "${FLAGS_docker_uid}" \ 144 "${_persistent}" \ 145 "x${FLAGS_docker_source}" \ 146 "x${FLAGS_docker_working}" \ 147 "x${_docker_output}" \ 148 "${_reuse}" \ 149 "${_build_image}" \ 150 "${_temp}/Dockerfile" \ 151 "${_temp}" \ 152 "${#docker_flags[@]}" "${docker_flags[@]}" \ 153 "${#_prepare_source[@]}" "${_prepare_source[@]}" 154 155 rm -rf "${_temp}" 156} 157 158function build_on_gce() { 159 check_common_docker_options 160 if [[ -z "${FLAGS_gce_instance}" ]]; then 161 echo Must specify instance 1>&2 162 fail=1 163 fi 164 if [[ -z "${FLAGS_gce_project}" ]]; then 165 echo Must specify project 1>&2 166 fail=1 167 fi 168 if [[ -z "${FLAGS_gce_zone}" ]]; then 169 echo Must specify zone 1>&2 170 fail=1 171 fi 172 if [[ "${fail}" -ne 0 ]]; then 173 exit "${fail}" 174 fi 175 project_zone_flags=(--project="${FLAGS_gce_project}" --zone="${FLAGS_gce_zone}") 176 if [ ${_reuse} -eq 0 ]; then 177 delete_instances=("${FLAGS_gce_instance}") 178 gcloud compute instances delete -q \ 179 "${delete_instances[@]}" \ 180 "${project_zone_flags[@]}" || \ 181 echo Instance does not exist 182 gcloud compute images delete -q \ 183 "${delete_instances[@]/%/-image}" \ 184 --project "${FLAGS_gce_project}" || \ 185 echo Image does not exist 186 gcloud compute disks delete -q \ 187 "${delete_instances[@]/%/-disk}" \ 188 "${project_zone_flags[@]}" || \ 189 echo Disk does not exist 190 191 gcloud compute disks create \ 192 "${delete_instances[@]/%/-disk}" \ 193 "${project_zone_flags[@]}" \ 194 --image-project="${FLAGS_gce_source_image_project}" \ 195 --image-family="${FLAGS_gce_source_image_family}" 196 gcloud compute images create \ 197 "${delete_instances[@]/%/-image}" \ 198 --source-disk "${delete_instances[@]/%/-disk}" \ 199 --project "${FLAGS_gce_project}" --source-disk-zone "${FLAGS_gce_zone}" \ 200 --licenses "https://www.googleapis.com/compute/v1/projects/vm-options/global/licenses/enable-vmx" 201 gcloud compute instances create \ 202 "${delete_instances[@]}" \ 203 "${project_zone_flags[@]}" \ 204 --image "${delete_instances[@]/%/-image}" \ 205 --boot-disk-size=200GB \ 206 --machine-type=n1-standard-8 \ 207 --min-cpu-platform "Intel Skylake" 208 209 wait_for_instance "${FLAGS_gce_instance}" "${project_zone_flags[@]}" 210 211 # install docker 212 gcloud beta compute ssh "${SSH_FLAGS[@]}" \ 213 "${project_zone_flags[@]}" \ 214 "${FLAGS_gce_user}@${FLAGS_gce_instance}" -- \ 215 'curl -fsSL https://get.docker.com | /bin/bash' 216 gcloud beta compute ssh "${SSH_FLAGS[@]}" \ 217 "${project_zone_flags[@]}" \ 218 "${FLAGS_gce_user}@${FLAGS_gce_instance}" -- \ 219 sudo usermod -aG docker "${FLAGS_gce_user}" 220 221 # beta for the --internal-ip flag that may be passed via SSH_FLAGS 222 223 gcloud beta compute ssh "${SSH_FLAGS[@]}" \ 224 "${project_zone_flags[@]}" \ 225 "${FLAGS_gce_user}@${FLAGS_gce_instance}" -- \ 226 mkdir -p '$PWD/docker $PWD/docker/source $PWD/docker/working $PWD/docker/output' 227 228 tar czv -C "${DIR}" -f - "${relative_source_files[@]}" | \ 229 gcloud beta compute ssh "${SSH_FLAGS[@]}" \ 230 "${project_zone_flags[@]}" \ 231 "${FLAGS_gce_user}@${FLAGS_gce_instance}" -- \ 232 'tar xzv -C ~/docker -f -' 233 if [ -n "${custom_manifest}" ]; then 234 gcloud beta compute scp "${SSH_FLAGS[@]}" \ 235 "${project_zone_flags[@]}" \ 236 "${custom_manifest}" \ 237 "${FLAGS_gce_user}@${FLAGS_gce_instance}:~/docker/custom.xml" 238 else 239 gcloud beta compute ssh "${SSH_FLAGS[@]}" \ 240 "${project_zone_flags[@]}" \ 241 "${FLAGS_gce_user}@${FLAGS_gce_instance}" -- \ 242 "touch ~/docker/custom.xml" 243 fi 244 fi 245 246 local _status=$(gcloud compute instances list \ 247 --project="${FLAGS_gce_project}" \ 248 --zones="${FLAGS_gce_zone}" \ 249 --filter="name=('${FLAGS_gce_instance}')" \ 250 --format=flattened | awk '/status:/ {print $2}') 251 if [ "${_status}" != "RUNNING" ] ; then 252 echo "Instance ${FLAGS_gce_instance} is not running." 253 exit 1; 254 fi 255 256 local -i _persistent=0 257 if [[ ${FLAGS_docker_persistent} -eq ${FLAGS_TRUE} ]]; then 258 _persistent=1 259 fi 260 local -i _build_image=0 261 if [[ ${FLAGS_docker_build_image} -eq ${FLAGS_TRUE} ]]; then 262 _build_image=1 263 fi 264 gcloud beta compute ssh "${SSH_FLAGS[@]}" \ 265 "${project_zone_flags[@]}" \ 266 "${FLAGS_gce_user}@${FLAGS_gce_instance}" -- \ 267 ./docker/rebuild-docker.sh "${FLAGS_docker_image}" \ 268 "${FLAGS_docker_container}" \ 269 "${FLAGS_docker_arch}" \ 270 '${USER}' \ 271 '${UID}' \ 272 "${_persistent}" \ 273 'x$PWD/docker/source' \ 274 'x$PWD/docker/working' \ 275 'x$PWD/docker/output' \ 276 "${_reuse}" \ 277 "${_build_image}" \ 278 '~/docker/Dockerfile' \ 279 '~/docker/' \ 280 "${#docker_flags[@]}" "${docker_flags[@]}" \ 281 "${#_prepare_source[@]}" "${_prepare_source[@]}" 282 283 gcloud beta compute ssh "${SSH_FLAGS[@]}" \ 284 "${project_zone_flags[@]}" \ 285 "${FLAGS_gce_user}@${FLAGS_gce_instance}" --command \ 286 'tar czv -C $PWD/docker/output -f - $(find $PWD/docker/output -printf "%P\n")' | \ 287 tar xzv -C ${DIR}/${FLAGS_docker_arch}-linux-gnu -f - 288 289 gcloud compute disks describe \ 290 "${project_zone_flags[@]}" "${FLAGS_gce_instance}" | \ 291 grep ^sourceImage: > "${DIR}"/x86_64-linux-gnu/builder_image.txt 292} 293 294function build_on_arm_board() { 295 check_common_docker_options 296 if [[ "${FLAGS_docker_arch}" != "aarch64" ]]; then 297 echo ARM board supports building only aarch64 1>&2 298 fail=1 299 fi 300 if [[ -z "${FLAGS_arm_instance}" ]]; then 301 echo Must specify IP address of ARM board 1>&2 302 fail=1 303 fi 304 if [[ -z "${FLAGS_arm_user}" ]]; then 305 echo Must specify a user account on ARM board 1>&2 306 fail=1 307 fi 308 if [[ "${fail}" -ne 0 ]]; then 309 exit "${fail}" 310 fi 311 if [[ "${_reuse}" -eq 0 ]]; then 312 ssh -t "${FLAGS_arm_user}@${FLAGS_arm_instance}" -- \ 313 rm -rf '$PWD/docker' 314 fi 315 rsync -avR -e ssh \ 316 "${relative_source_files[@]/#/${DIR}/./}" \ 317 "${FLAGS_arm_user}@${FLAGS_arm_instance}:~/docker/" 318 319 if [ -n "${custom_manifest}" ]; then 320 scp "${custom_manifest}" "${FLAGS_arm_user}@${FLAGS_arm_instance}":~/docker/custom.xml 321 else 322 ssh -t "${FLAGS_arm_user}@${FLAGS_arm_instance}" -- \ 323 "touch ~/docker/custom.xml" 324 fi 325 326 local -i _persistent=0 327 if [[ ${FLAGS_docker_persistent} -eq ${FLAGS_TRUE} ]]; then 328 _persistent=1 329 fi 330 local -i _build_image=0 331 if [[ ${FLAGS_docker_build_image} -eq ${FLAGS_TRUE} ]]; then 332 _build_image=1 333 fi 334 ssh -t "${FLAGS_arm_user}@${FLAGS_arm_instance}" -- \ 335 mkdir -p '$PWD/docker/source' '$PWD/docker/working' '$PWD/docker/output' 336 ssh -t "${FLAGS_arm_user}@${FLAGS_arm_instance}" -- \ 337 ./docker/rebuild-docker.sh "${FLAGS_docker_image}" \ 338 "${FLAGS_docker_container}" \ 339 "${FLAGS_docker_arch}" \ 340 '${USER}' \ 341 '${UID}' \ 342 "${_persistent}" \ 343 'x$PWD/docker/source' \ 344 'x$PWD/docker/working' \ 345 'x$PWD/docker/output' \ 346 "${_reuse}" \ 347 "${_build_image}" \ 348 '~/docker/Dockerfile' \ 349 '~/docker/' \ 350 "${#docker_flags[@]}" "${docker_flags[@]}" \ 351 "${#_prepare_source[@]}" "${_prepare_source[@]}" 352 353 rsync -avR -e ssh "${FLAGS_arm_user}@${FLAGS_arm_instance}":docker/output/./ \ 354 "${ANDROID_BUILD_TOP}/device/google/cuttlefish_vmm/${FLAGS_docker_arch}-linux-gnu" 355} 356 357main() { 358 set -o errexit 359 set -x 360 fail=0 361 relative_source_files=("rebuild-docker.sh" 362 "rebuild-internal.sh" 363 "Dockerfile" 364 "x86_64-linux-gnu/manifest.xml" 365 "aarch64-linux-gnu/manifest.xml" 366 "policy-inliner.sh" 367 ".dockerignore") 368 # These must match the definitions in the Dockerfile 369 docker_flags=("-eSOURCE_DIR=/source" "-eWORKING_DIR=/working" "-eOUTPUT_DIR=/output" "-eTOOLS_DIR=/static/tools") 370 371 if [[ $(( $((${FLAGS_gce}==${FLAGS_TRUE})) + $((${FLAGS_arm}==${FLAGS_TRUE})) + $((${FLAGS_docker}==${FLAGS_TRUE})) )) > 1 ]]; then 372 echo You may specify only one of --gce, --docker, or --arm 1>&2 373 exit 2 374 fi 375 376 if [[ -n "${FLAGS_manifest}" ]]; then 377 if [[ ! -f "${FLAGS_manifest}" ]]; then 378 echo custom manifest not found: ${FLAGS_manifest} 1>&1 379 exit 2 380 fi 381 custom_manifest="${FLAGS_manifest}" 382 docker_flags+=("-eCUSTOM_MANIFEST=/static/custom.xml") 383 else 384 custom_manifest="${DIR}/${FLAGS_docker_arch}-linux-gnu/manifest.xml" 385 docker_flags+=("-eCUSTOM_MANIFEST=/static/${FLAGS_docker_arch}-linux-gnu/manifest.xml") 386 fi 387 local -a _prepare_source=(setup_env fetch_source); 388 local -i _reuse=0 389 if [[ ${FLAGS_reuse} -eq ${FLAGS_TRUE} ]]; then 390 # neither install packages, nor sync sources; skip to building them 391 _prepare_source=(setup_env) 392 # unless you're setting up a non-persistent container and --docker_source is 393 # the empty string; in this case, --reuse implies --reuse_resync 394 if [[ "${FLAGS_docker_persistent}" -eq ${FLAGS_FALSE} && \ 395 -z "${FLAGS_docker_source}" ]]; then 396 _prepare_source+=(resync_source) 397 fi 398 _reuse=1 399 fi 400 if [[ ${FLAGS_reuse_resync} -eq ${FLAGS_TRUE} ]]; then 401 # do not install packages but clean and sync sources afresh 402 _prepare_source=(setup_env resync_source); 403 _reuse=1 404 fi 405 if [[ ${FLAGS_gce} -eq ${FLAGS_TRUE} ]]; then 406 build_on_gce 407 exit 0 408 gcloud compute instances delete -q \ 409 "${project_zone_flags[@]}" \ 410 "${FLAGS_gce_instance}" 411 fi 412 if [[ ${FLAGS_arm} -eq ${FLAGS_TRUE} ]]; then 413 build_on_arm_board 414 exit 0 415 fi 416 if [[ ${FLAGS_docker} -eq ${FLAGS_TRUE} ]]; then 417 build_locally_using_docker 418 exit 0 419 fi 420} 421 422FLAGS "$@" || exit 1 423main "${FLAGS_ARGV[@]}" 424