Improve toolchain caching

Instead of putting full copy of toolchain to every build directory
(/build/<release_or_build>/) create a single copy of it in
/build/toolchains/<toolchain_id> and symlink to it through a
/build/<release_or_build>/toolchain synlink

Also clean up some dead code that is not used anywhere.

Change-Id: I2959884a2e580ba55092b829e51664c5fcb19f21
Reviewed-on: https://cos-review.googlesource.com/c/cos/tools/+/33261
Tested-by: Oleksandr Tymoshenko <ovt@google.com>
Cloud-Build: GCB Service account <228075978874@cloudbuild.gserviceaccount.com>
Reviewed-by: Meena Shanmugam <meenashanmugam@google.com>
diff --git a/src/cmd/cos_kernel_devenv/devenv.sh b/src/cmd/cos_kernel_devenv/devenv.sh
index 79e5f26..407f409 100755
--- a/src/cmd/cos_kernel_devenv/devenv.sh
+++ b/src/cmd/cos_kernel_devenv/devenv.sh
@@ -12,12 +12,18 @@
 readonly TOOLCHAIN_URL_FILENAME="toolchain_path"
 readonly KERNEL_HEADERS="kernel-headers.tgz"
 readonly KERNEL_HEADERS_DIR="kernel-headers"
-readonly TOOLCHAIN_ARCHIVE="toolchain.tar.xz"
+readonly TOOLCHAIN_ARCHIVE_GCS="toolchain.tar.xz.gcs"
 readonly TOOLCHAIN_ENV_FILENAME="toolchain_env"
 ROOT_OS_RELEASE="${ROOT_MOUNT_DIR}/etc/os-release"
 readonly RETCODE_ERROR=1
-RELEASE_ID=""  # Loaded from host during execution
-BUILD_DIR="" # based on RELEASE_ID
+RELEASE_ID=""
+#
+# Individual build directory, contains kernel headers for the specific build and
+# a symlink 'toolchain' that points to the toolchain used for this particular
+# build
+#
+BUILD_DIR=""
+
 KERNEL_CONFIGS="defconfig"
 BUILD_DEBUG_PACKAGE="false"
 BUILD_HEADERS_PACKAGE="false"
@@ -96,15 +102,6 @@
 	esac
 }
 
-load_etc_os_release() {
-  if [[ ! -f "${ROOT_OS_RELEASE}" ]]; then
-    error "File ${ROOT_OS_RELEASE} not found, /etc/os-release from COS host must be mounted."
-    exit ${RETCODE_ERROR}
-  fi
-  . "${ROOT_OS_RELEASE}"
-  info "Running on COS build id ${RELEASE_ID}"
-}
-
 download_from_url() {
   local -r url="$1"
   local -r output="$2"
@@ -141,42 +138,36 @@
   info "Download finished"
 }
 
-# Get toolchain tarball from Chromium GCS bucket when
-# toolchain tarball is not found in COS GCS bucket
-get_cross_toolchain_pkg() {
-  # First, check if the toolchain path is available locally.
-  local -r tc_path_file="${ROOT_MOUNT_DIR}/etc/toolchain-path"
-  if [[ -f "${tc_path_file}" ]]; then
-    info "Found toolchain path file locally"
-    local -r tc_path="$(cat "${tc_path_file}")"
-    local -r tc_download_url="${CHROMIUMOS_SDK_GCS}/${tc_path}"
-  else
-    # Next, check if the toolchain path is available in GCS.
-    local -r tc_path_url="${COS_DOWNLOAD_GCS}/${RELEASE_ID}/${TOOLCHAIN_URL_FILENAME}"
-    info "Obtaining toolchain download URL from ${tc_path_url}"
-    local -r tc_download_url="$(curl --http1.1 -sfS "${tc_path_url}")"
-  fi
-  echo "${tc_download_url}"
-}
-
 install_cross_toolchain_pkg() {
   local -r download_url=$1
-  info "Downloading prebuilt toolchain from ${download_url}"
-  local -r pkg_name="$(basename "${download_url}")"
-  download_from_url "${download_url}" "${BUILD_DIR}/${pkg_name}"
-  # Don't unpack Rust toolchain elements because they are not needed and they
-  # use a lot of disk space.
-  tar axf "${BUILD_DIR}/${pkg_name}" -C "${BUILD_DIR}" \
-    --exclude='./usr/lib64/rustlib*' \
-    --exclude='./usr/lib64/libstd-*.so' \
-    --exclude='./lib/libstd-*.so' \
-    --exclude='./lib/librustc*' \
-    --exclude='./usr/lib64/librustc*'
-  rm "${BUILD_DIR}/${pkg_name}"
+  local -r tmpdownload="$(mktemp -d)"
+  local -r archive_name="$(basename "${download_url}")"
+  local -r pkg_name="${archive_name%%.tar.xz}"
+  local -r toolchain_dir="/build/toolchains/${pkg_name}"
+  if [[ ! -d "${toolchain_dir}" ]]; then
+    info "toolchains/Downloading prebuilt toolchain from ${download_url}"
+    download_from_url "${download_url}" "${tmpdownload}/${archive_name}"
+    # Don't unpack Rust toolchain elements because they are not needed and they
+    # use a lot of disk space.
+    mkdir -p "${toolchain_dir}"
+    info "Unpacking toolchain to ${toolchain_dir}"
+    tar axf "${tmpdownload}/${archive_name}" -C "${toolchain_dir}" \
+      --exclude='./usr/lib64/rustlib*' \
+      --exclude='./usr/lib64/libstd-*.so' \
+      --exclude='./lib/libstd-*.so' \
+      --exclude='./lib/librustc*' \
+      --exclude='./usr/lib64/librustc*'
+    rm -rf "${tmpdownload}"
+    info "Toolchain installed"
+  else
+    info "Toolchain is already cached"
+  fi
+
+  if [[ ! -L "${BUILD_DIR}/toolchain" ]]; then
+    ln -s "${toolchain_dir}" "${BUILD_DIR}/toolchain"
+  fi
 }
 
-# Set-up compilation environment using toolchain used for
-# kernel compilation
 install_release_cross_toolchain() {
   info "Downloading and installing a toolchain"
   # Get toolchain_env path from COS GCS bucket
@@ -190,7 +181,18 @@
     return ${RETCODE_ERROR}
   fi
 
-  local -r tc_download_url="${COS_DOWNLOAD_GCS}/${RELEASE_ID}/${TOOLCHAIN_ARCHIVE}"
+  # Download .gcs file with the original location of the toolchain
+  # we need the version to put it in cachable location
+  local -r tc_gcs_download_url="${COS_DOWNLOAD_GCS}/${RELEASE_ID}/${TOOLCHAIN_ARCHIVE_GCS}"
+  if ! download_from_url "${tc_gcs_download_url}" "${BUILD_DIR}/${TOOLCHAIN_ARCHIVE_GCS}"; then
+    error "Failed to download toolchain .gcs file"
+    error "Make sure build id '$RELEASE_ID' is valid"
+    return ${RETCODE_ERROR}
+  fi
+
+  local -r bucket=$(cat "${BUILD_DIR}/${TOOLCHAIN_ARCHIVE_GCS}" | grep ^bucket: | cut -d ' ' -f 2)
+  local -r path=$(cat "${BUILD_DIR}/${TOOLCHAIN_ARCHIVE_GCS}" | grep ^path: | cut -d ' ' -f 2)
+  local -r tc_download_url="https://storage.googleapis.com/$bucket/$path"
 
   # Install toolchain pkg
   install_cross_toolchain_pkg "${tc_download_url}"
@@ -245,6 +247,7 @@
   install_cross_toolchain_pkg "${tc_download_url}"
 }
 
+# Download and install kernel headers from the CI or tryjob build directory
 install_build_kernel_headers() {
   local -r bucket="$1"
 
@@ -284,8 +287,9 @@
   fi
   info "Configuring environment variables for cross-compilation"
   # CC and CXX are already set in toolchain_env
-  export PATH="${BUILD_DIR}/bin:${BUILD_DIR}/usr/bin:${PATH}"
-  export SYSROOT="${BUILD_DIR}/usr/${TOOLCHAIN_ARCH}-cros-linux-gnu"
+  TOOLCHAIN_DIR="${BUILD_DIR}/toolchain"
+  export PATH="${TOOLCHAIN_DIR}/bin:${TOOLCHAIN_DIR}/usr/bin:${PATH}"
+  export SYSROOT="${TOOLCHAIN_DIR}/usr/${TOOLCHAIN_ARCH}-cros-linux-gnu"
   export HOSTCC="x86_64-pc-linux-gnu-clang"
   export HOSTCXX="x86_64-pc-linux-gnu-clang++"
   export LD="${TOOLCHAIN_ARCH}-cros-linux-gnu-ld.lld"
@@ -560,24 +564,28 @@
   fi
   echo "Mode: $MODE"
 
-  if [[ ! -d ${BUILD_DIR} ]]; then
-    mkdir -p "${BUILD_DIR}"
-    case "$MODE" in
-      cross) install_generic_cross_toolchain ;;
-      release)
-        install_release_cross_toolchain
-        install_release_kernel_headers
-        ;;
-      build)
-        local -r bucket="${COS_CI_DOWNLOAD_GCS}/${BOARD}-release/${BUILD_ID}"
-        install_build_cross_toolchain "${bucket}"
-        install_build_kernel_headers "${bucket}"
-        ;;
-      custom)
-        install_build_cross_toolchain "${custom_bucket}"
-        install_build_kernel_headers "${custom_bucket}"
-        ;;
-    esac
+  if [[ -n "${BUILD_DIR}" ]]; then
+    if [[ ! -d "${BUILD_DIR}" ]]; then
+      mkdir -p "${BUILD_DIR}"
+      case "$MODE" in
+        cross)
+          install_generic_cross_toolchain
+          ;;
+        release)
+          install_release_cross_toolchain
+          install_release_kernel_headers
+          ;;
+        build)
+          local -r bucket="${COS_CI_DOWNLOAD_GCS}/${BOARD}-release/${BUILD_ID}"
+          install_build_cross_toolchain "${bucket}"
+          install_build_kernel_headers "${bucket}"
+          ;;
+        custom)
+          install_build_cross_toolchain "${custom_bucket}"
+          install_build_kernel_headers "${custom_bucket}"
+          ;;
+      esac
+    fi
   fi
 
   set_compilation_env