Add a container for COS kernel development
To make kernel development easier for external teams add
a cos-kernel-devenv container that can be set up for:
- building custom kernels
- building modules for specific release
- supports cross-compilation
BUG=None
TEST=None
RELEASE_NOTE=None
Change-Id: I2dcc1f07459a07373d9b3ea8a3910960b1102b0b
Reviewed-on: https://cos-review.googlesource.com/c/cos/tools/+/18550
Cloud-Build: GCB Service account <228075978874@cloudbuild.gserviceaccount.com>
Reviewed-by: Meena Shanmugam <meenashanmugam@google.com>
Tested-by: Oleksandr Tymoshenko <ovt@google.com>
diff --git a/src/cmd/cos_kernel_devenv/Dockerfile b/src/cmd/cos_kernel_devenv/Dockerfile
new file mode 100644
index 0000000..b2ce351
--- /dev/null
+++ b/src/cmd/cos_kernel_devenv/Dockerfile
@@ -0,0 +1,10 @@
+# Start from google/cloud-sdk:slim base.
+FROM google/cloud-sdk:slim
+
+RUN echo 'deb http://deb.debian.org/debian buster-backports main' > /etc/apt/sources.list.d/buster-backports.list
+RUN apt-get update && apt-get install -y make gcc git libssl-dev bc \
+ libelf-dev/buster-backports bison flex cpio dwarves/buster-backports kmod
+
+COPY ./devenv.sh /devenv.sh
+
+ENTRYPOINT ["/devenv.sh"]
diff --git a/src/cmd/cos_kernel_devenv/README.md b/src/cmd/cos_kernel_devenv/README.md
new file mode 100644
index 0000000..46b17b0
--- /dev/null
+++ b/src/cmd/cos_kernel_devenv/README.md
@@ -0,0 +1,170 @@
+# COS Kernel Devenv container
+
+## Overview
+
+## Building COS Kernel Devenv Image
+
+### Locally (for testing the image)
+
+For testing, you can simply build and test this docker container locally on your
+workstation:
+
+```shell
+ $ docker build -t gcr.io/cloud-kernel-build/cos-kernel-devenv:dev .
+```
+
+### Production (push into GCR)
+
+
+```shell
+ $ VERSION=<version> # e.g., 20171008
+ $ docker build -t gcr.io/cloud-kernel-build/cos-kernel-devenv:$VERSION .
+ $ docker build -t gcr.io/cloud-kernel-build/cos-kernel-devenv:latest .
+ $ gcloud docker -- push gcr.io/cloud-kernel-build/cos-kernel-devenv:$VERSION
+ $ gcloud docker -- push gcr.io/cloud-kernel-build/cos-kernel-devenv:latest
+```
+
+## Using COS Kernel Devenv Image
+
+### Download
+
+Get the latest version of the `cos-kernel-devenv` container by running the
+following command:
+
+```shell
+$ docker pull gcr.io/cloud-kernel-build/cos-kernel-devenv
+```
+
+### Overview
+
+`cos-kernel-devenv` provides the development environment for building Linux
+kernel or standalone kernel modules. The container supports three operation
+modes: automatic kernel build, automatic module build, and an interactive shell.
+
+The development environment includes a cross-compilation toolchain and an
+optional kernel headers. Kernel headers only provided if the environment
+replicates the environment of the specific COS build.
+
+Kernel sources or module sources are passed to the container as a volume
+and automatic modes assume that the working directory is the top-level
+build directory.
+
+In the interactive mode the contianer provides `kmake` shell function to
+simplify kernel builds. It's a wrapper around `make command with arguments
+specific for kernel build: toolchain and arch-specific. It also configures
+standard `make` specific shell variables like `CC` or `LD`.
+
+There are three ways to initialize dev environment:
+
+### Modes of Operation
+#### Generic Cross-compilation Environment
+
+Unless no arguments specifying COS build are passed to the container,
+env is initialized only with a cross-compilation toolchain
+
+```
+$ docker run --rm -ti -v $(pwd):/src -w /src gcr.io/cloud-kernel-build/cos-kernel-devenv
+... some output ...
+
+root@daf505e41e51:/src# echo $CC
+x86_64-cros-linux-gnu-clang
+
+root@daf505e41e51:/src# $CC -v
+Chromium OS 12.0_pre422132_p20210405-r9 clang version 13.0.0 (/var/tmp/portage/sys-devel/llvm-12.0_pre422132_p20210405-r9/work/llvm-12.0_pre422132_p20210405/clang cd442157cff4aad209ae532cbf031abbe10bc1df)
+Target: x86_64-cros-linux-gnu
+Thread model: posix
+InstalledDir: /build/x86_64-cros/usr/bin
+Found candidate GCC installation: /build/x86_64-cros/usr/bin/../lib/gcc/x86_64-cros-linux-gnu/10.2.0
+Selected GCC installation: /build/x86_64-cros/usr/bin/../lib/gcc/x86_64-cros-linux-gnu/10.2.0
+Candidate multilib: .;@m64
+Selected multilib: .;@m64
+
+root@daf505e41e51:/src# kmake -j48 defconfig && kmake -j48 bzImage
+... some output ...
+Kernel: arch/x86/boot/bzImage is ready (#2)
+```
+
+By default container sets environment for an x86_64 build but can also target ARM64
+builds if passed `-A arm64` command-line argument:
+
+```
+$ docker run --rm -ti -v $(pwd):/src -w /src gcr.io/cloud-kernel-build/cos-kernel-devenv -A arm64
+...
+root@099747a46b80:/src# kmake -j48 defconfig && kmake -j48 Image
+...
+ OBJCOPY arch/arm64/boot/Image
+root@099747a46b80:/src#
+```
+
+#### Officially Released COS build
+
+When passed `-R <release>` command-line argument, the container reproduces
+the build environment of that specific releases. The generated devenv can
+be used both for building a modified kernel for troubleshooting or building
+a kernel module that can be used with the specified release. The `<release>`
+argument should be in the form of `MAJOR.MINOR.PATCH`, i.e.: `16442.0.0`.
+
+```
+$ docker run --rm -ti -v $(pwd):/src -w \
+ /src gcr.io/cloud-kernel-build/cos-kernel-devenv -R 16442.0.0
+...
+root@384c89409064:/src# kmake -C $KHEADERS M=$(pwd) modules
+make: Entering directory '/build/x86_64-16442.0.0/kernel-headers/usr/src/linux-headers-5.4.120+'
+ CC [M] /src/samplemodule.o
+ Building modules, stage 2.
+ MODPOST 1 modules
+ CC [M] /src/samplemodule.mod.o
+ LD [M] /src/samplemodule.ko
+make: Leaving directory '/build/x86_64-16442.0.0/kernel-headers/usr/src/linux-headers-5.4.120+'
+root@384c89409064:/src#
+```
+#### COS CI build
+
+It's also possible to reproduce kernel dev environment for the particular CI
+build by specifying the voard and the build ID in the container's arguments:
+`-b <board> -B <buildid>`. The `<board>` argument is the board name (for
+example it's `lakitu` for COS on GCE`). The `<buildid>` is the combination of both
+milestone information and the build number, for instance: `R93-16623.0.0`
+
+For this use case it's also neccessary to pass gcloud config information to the
+container. It's required to get access to the build artifacts that are stored in
+the non-public GCS bucket.
+
+```
+$ docker run --rm -ti -v $(pwd):/src -w /src \
+ -v ~/.config/gcloud:/root/.config/gcloud \
+ gcr.io/cloud-kernel-build/cos-kernel-devenv -b lakitu -B R93-16623.0.0
+```
+### Automated Builds
+
+In addition to an interactive environment `cos-kernel-devenv` also provides
+a batch mode for building a kernel package and modules.
+
+#### Automated Kernel Build
+
+By passing `-k` command-line argument to the container developer can run an
+automated kernel build that consists of two steps: `kmake defconfig` and
+`kmake tarxz-pkg`. The kernel code is assumed to be in the working directory
+specified by `-w` docker argument.
+
+TODO(ovt): implement and document option to specify kernel configs
+
+#### Automated Kernel Build
+
+```
+$ docker run --rm -ti -v $(pwd):/src -w \
+ /src gcr.io/cloud-kernel-build/cos-kernel-devenv -R 16442.0.0 -k
+... skipped ...
+'./arch/x86/boot/bzImage' -> './tar-install/boot/vmlinuz-5.10.59'
+Tarball successfully created in ./linux-5.10.59-x86.tar.xz
+```
+
+#### Automated Modules Build
+
+```
+$ docker run --rm -ti -v $(pwd):/src -w \
+ /src gcr.io/cloud-kernel-build/cos-kernel-devenv -R 16442.0.0 -m
+... skipped ...
+ LD [M] /src/samplemodule.ko
+make: Leaving directory '/build/x86_64-16442.0.0/kernel-headers/usr/src/linux-headers-5.4.120+'
+```
diff --git a/src/cmd/cos_kernel_devenv/devenv.sh b/src/cmd/cos_kernel_devenv/devenv.sh
new file mode 100755
index 0000000..f5583af
--- /dev/null
+++ b/src/cmd/cos_kernel_devenv/devenv.sh
@@ -0,0 +1,388 @@
+#!/bin/bash
+# Create kernel development environment for COS
+
+set -o errexit
+set -o pipefail
+
+ROOT_MOUNT_DIR="${ROOT_MOUNT_DIR:-/root}"
+RETRY_COUNT=${RETRY_COUNT:-5}
+
+readonly COS_DOWNLOAD_GCS="https://storage.googleapis.com/cos-tools"
+readonly COS_CI_DOWNLOAD_GCS="gs://cos-infra-prod-artifacts-official"
+readonly CHROMIUMOS_SDK_GCS="https://storage.googleapis.com/chromiumos-sdk"
+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_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
+KERNEL_CONFIG="defconfig"
+
+BOARD=""
+BUILD_ID=""
+
+# official release, CI build, or cross-toolchain
+MODE=""
+
+CROS_TC_VERSION="2021.06.26.094653"
+CROS_TC_DATE="2021/06"
+CROS_TC_DOWNLOAD_GCS="https://storage.googleapis.com/chromiumos-sdk/"
+
+# Can be overridden by the command-line argument
+TOOLCHAIN_ARCH="x86_64"
+KERNEL_ARCH="x86"
+
+# CC and CXX will be set by set_compilation_env
+CC=""
+CXX=""
+
+_log() {
+ local -r prefix="$1"
+ shift
+ echo "[${prefix}$(date -u "+%Y-%m-%d %H:%M:%S %Z")] ""$*" >&2
+}
+
+info() {
+ _log "INFO " "$*"
+}
+
+warn() {
+ _log "WARNING " "$*"
+}
+
+error() {
+ _log "ERROR " "$*"
+}
+
+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"
+ info "Downloading from URL: ${url}"
+ info "Local download location: ${output}"
+ local attempts=0
+ until curl --http1.1 -sfS "${url}" -o "${output}"; do
+ attempts=$(( attempts + 1))
+ if (( "${attempts}" >= "${RETRY_COUNT}" )); then
+ error "Could not download from ${url}"
+ return ${RETCODE_ERROR}
+ fi
+ warn "Error downloading from ${url}, retrying"
+ sleep 1
+ done
+ info "Download finished"
+}
+
+download_from_gcs() {
+ local -r url="$1"
+ local -r output="$2"
+ info "Downloading from Google Storage: ${url}"
+ info "Local download location: ${output}"
+ local attempts=0
+ until gsutil -q cp "${url}" "${output}"; do
+ attempts=$(( attempts + 1))
+ if (( "${attempts}" >= "${RETRY_COUNT}" )); then
+ error "Could not download from ${url}"
+ return ${RETCODE_ERROR}
+ fi
+ warn "Error downloading from ${url}, retrying"
+ sleep 1
+ done
+ 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}"
+}
+
+# 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
+ local -r tc_env_file_path="${COS_DOWNLOAD_GCS}/${RELEASE_ID}/${TOOLCHAIN_ENV_FILENAME}"
+ info "Obtaining toolchain_env file from ${tc_env_file_path}"
+
+ # Download toolchain_env if present
+ if ! download_from_url "${tc_env_file_path}" "${BUILD_DIR}/${TOOLCHAIN_ENV_FILENAME}"; then
+ error "Failed to download toolchain file"
+ error "Make sure build id '$RELEASE_ID' is valid"
+ return ${RETCODE_ERROR}
+ fi
+
+ local -r tc_download_url="${COS_DOWNLOAD_GCS}/${RELEASE_ID}/${TOOLCHAIN_ARCHIVE}"
+
+ # Install toolchain pkg
+ install_cross_toolchain_pkg "${tc_download_url}"
+}
+
+install_release_kernel_headers() {
+ info "Downloading and installing a kernel headers"
+ local -r kernel_headers_file_path="${COS_DOWNLOAD_GCS}/${RELEASE_ID}/${KERNEL_HEADERS}"
+ info "Obtaining kernel headers file from ${kernel_headers_file_path}"
+
+ if ! download_from_url "${kernel_headers_file_path}" "${BUILD_DIR}/${KERNEL_HEADERS}"; then
+ return ${RETCODE_ERROR}
+ fi
+ mkdir -p "${BUILD_DIR}/${KERNEL_HEADERS_DIR}"
+ tar axf "${BUILD_DIR}/${KERNEL_HEADERS}" -C "${BUILD_DIR}/${KERNEL_HEADERS_DIR}"
+ rm -f "${BUILD_DIR}/${KERNEL_HEADERS}"
+}
+
+# Download and install toolchain from the CI or tryjob build directory
+install_build_cross_toolchain() {
+ local -r bucket="$1"
+
+ info "Downloading and installing a toolchain"
+ # Get toolchain_env path from COS GCS bucket
+ local -r tc_env_file_path="${bucket}/${TOOLCHAIN_ENV_FILENAME}"
+ local -r tc_url_file_path="${bucket}/${TOOLCHAIN_URL_FILENAME}"
+
+ info "Obtaining toolchain_env file from ${tc_env_file_path}"
+
+ # Download toolchain_env if present
+ if ! download_from_gcs "${tc_env_file_path}" "${BUILD_DIR}/${TOOLCHAIN_ENV_FILENAME}"; then
+ error "Failed to download toolchain file"
+ error "Make sure build id '$RELEASE_ID' is valid"
+ return ${RETCODE_ERROR}
+ fi
+
+ # Download toolchain_path if present
+ if ! download_from_gcs "${tc_url_file_path}" "${BUILD_DIR}/${TOOLCHAIN_URL_FILENAME}"; then
+ error "Failed to download toolchain file"
+ error "Make sure build id '$RELEASE_ID' is valid"
+ return ${RETCODE_ERROR}
+ fi
+
+ local -r tc_download_url="${CROS_TC_DOWNLOAD_GCS}$(cat ${BUILD_DIR}/${TOOLCHAIN_URL_FILENAME})"
+ if [[ -z "$tc_download_url" ]]; then
+ error "Failed to download toolchain URL file"
+ error "Make sure build id '$RELEASE_ID' is valid"
+ return ${RETCODE_ERROR}
+ fi
+
+ # Install toolchain pkg
+ install_cross_toolchain_pkg "${tc_download_url}"
+}
+
+install_build_kernel_headers() {
+ local -r bucket="$1"
+
+ info "Downloading and installing a kernel headers"
+ local -r kernel_headers_file_path="${bucket}/${KERNEL_HEADERS}"
+ info "Obtaining kernel headers file from ${kernel_headers_file_path}"
+
+ if ! download_from_gcs "${kernel_headers_file_path}" "${BUILD_DIR}/${KERNEL_HEADERS}"; then
+ return ${RETCODE_ERROR}
+ fi
+ mkdir -p "${BUILD_DIR}/${KERNEL_HEADERS_DIR}"
+ tar axf "${BUILD_DIR}/${KERNEL_HEADERS}" -C "${BUILD_DIR}/${KERNEL_HEADERS_DIR}"
+ rm -f "${BUILD_DIR}/${KERNEL_HEADERS}"
+}
+
+install_generic_cross_toolchain() {
+ info "Downloading and installing a toolchain"
+ # Download toolchain_env if present
+ local -r tc_download_url="${CROS_TC_DOWNLOAD_GCS}${CROS_TC_DATE}/${TOOLCHAIN_ARCH}-cros-linux-gnu-${CROS_TC_VERSION}.tar.xz"
+
+ # Install toolchain pkg
+ install_cross_toolchain_pkg "${tc_download_url}"
+}
+
+set_compilation_env() {
+ local -r tc_env_file_path="${COS_DOWNLOAD_GCS}/${RELEASE_ID}/${TOOLCHAIN_ENV_FILENAME}"
+ # toolchain_env file will set 'CC' and 'CXX' environment
+ # variable based on the toolchain used for kernel compilation
+ if [[ -f "${BUILD_DIR}/${TOOLCHAIN_ENV_FILENAME}" ]]; then
+ source "${BUILD_DIR}/${TOOLCHAIN_ENV_FILENAME}"
+ export CC
+ export CXX
+ else
+ export CC="${TOOLCHAIN_ARCH}-cros-linux-gnu-clang"
+ export CXX="${TOOLCHAIN_ARCH}-cros-linux-gnu-clang"
+ 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"
+ 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"
+ export HOSTLD="x86_64-pc-linux-gnu-ld.lld"
+ # export BINUTILS="${BUILD_DIR}/usr/x86_64-pc-linux-gnu/x86_64-cros-linux-gnu/binutils-bin/2.27.0"
+ export OBJCOPY=llvm-objcopy
+ export STRIP=llvm-strip
+ export KERNEL_ARCH
+ export TOOLCHAIN_ARCH
+ export LLVM_IAS=1
+ if [[ "${MODE}" = "release" || "${MODE}" = "build" || "${MODE}" = "custom" ]]; then
+ local -r headers_dir=$(ls -d ${BUILD_DIR}/${KERNEL_HEADERS_DIR}/usr/src/linux-headers*)
+ export KHEADERS="${headers_dir}"
+ fi
+ # env ARCH=x86_64 make -j47 ARCH=x86_64 CC="${CC}" CXX="${CXX}" LD="${LD}" BINUTILS="${BINUTILS}" LD="${LD}" targz-pkg
+}
+
+kmake() {
+ env ARCH=${KERNEL_ARCH} make ARCH=${KERNEL_ARCH} \
+ CC="${CC}" CXX="${CXX}" LD="${LD}" \
+ STRIP="${STRIP}" OBJCOPY="${OBJCOPY}" \
+ HOSTCC="${HOSTCC}" HOSTCXX="${HOSTCXX}" HOSTLD="${HOSTLD}" \
+ "$@"
+}
+export -f kmake
+
+kernel_build() {
+ kmake mrproper
+ kmake "$@" "${KERNEL_CONFIG}"
+ kmake "$@" tarxz-pkg
+}
+
+module_build() {
+ kmake -C "${KHEADERS}" M="$(pwd)" "$@" clean
+ kmake -C "${KHEADERS}" M="$(pwd)" "$@" modules
+}
+
+usage() {
+ echo "Usage: $0 [-k | -m] [-A <x86|arm64>] [-B <build>] [-C <config>]" 1>&2
+ echo " [-B <build> | -b <board> -R <release> | -G <GCS_bucket>]"
+ exit $RETCODE_ERROR
+}
+
+main() {
+ local build_target="shell"
+ local custom_bucket=""
+ while getopts "A:B:C:G:R:b:km" o; do
+ case "${o}" in
+ A) KERNEL_ARCH=${OPTARG} ;;
+ B) BUILD_ID=${OPTARG} ;;
+ C) KERNEL_CONFIG=${OPTARG} ;;
+ G) custom_bucket=${OPTARG} ;;
+ R) RELEASE_ID=${OPTARG} ;;
+ b) BOARD=${OPTARG} ;;
+ k) build_target="kernel" ;;
+ m) build_target="module" ;;
+ *) usage ;;
+ esac
+ done
+ shift $((OPTIND-1))
+
+ if [[ ! -z "${BOARD}" ]]; then
+ case "${BOARD}" in
+ lakitu-arm64) KERNEL_ARCH=arm64 ;;
+ *) KERNEL_ARCH=x86 ;;
+ esac
+ fi
+
+ case "${KERNEL_ARCH}" in
+ x86)
+ TOOLCHAIN_ARCH=x86_64
+ ;;
+ arm64)
+ TOOLCHAIN_ARCH=aarch64
+ ;;
+ *)
+ echo "Invalid -A value: $KERNEL_ARCH"
+ usage
+ ;;
+ esac
+
+ echo "** Kernel architecture: $KERNEL_ARCH"
+ echo "** Toolchain architecture: $TOOLCHAIN_ARCH"
+
+ if [[ -n "$RELEASE_ID" ]]; then
+ MODE="release"
+ BUILD_DIR="/build/${TOOLCHAIN_ARCH}-${RELEASE_ID}"
+ echo "** COS release: $RELEASE_ID"
+ fi
+
+ if [[ -z "$MODE" && -n "$BOARD" && -n "$BUILD_ID" ]]; then
+ MODE="build"
+ echo "** COS build: $BOARD-$BUILD_ID"
+ BUILD_DIR="/build/${BOARD}-${BUILD_ID}"
+ fi
+
+ if [[ -z "$MODE" && -n "$custom_bucket" ]]; then
+ MODE="custom"
+ BUILD_DIR="/build/$(basename "${custom_bucket}")"
+ fi
+
+ if [[ -z "$MODE" ]]; then
+ MODE="cross"
+ BUILD_DIR="/build/${TOOLCHAIN_ARCH}-cros"
+ 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
+ fi
+
+ set_compilation_env
+
+ case "${build_target}" in
+ kernel) kernel_build -j"$(nproc)" ;;
+ module) module_build -j"$(nproc)" ;;
+ *)
+ echo "Starting interactive shell for the kernel devenv"
+ /bin/bash
+ ;;
+ esac
+}
+
+main "$@"