blob: f5583afc5e42bcb8ed38bfa815c5b088a3527ebc [file] [log] [blame]
#!/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 "$@"