#!/bin/bash # Toolchain packages are treated a bit specially, since they take a # while to build and are generally more complicated to build they are # only built via catalyst and everyone else installs them as binpkgs. TOOLCHAIN_PKGS=( sys-devel/binutils sys-devel/gcc sys-kernel/linux-headers sys-libs/glibc ) # Portage profile to use for building out the cross compiler's SYSROOT. # This is only used as an intermediate step to be able to use the cross # compiler to build a full native toolchain. Packages are not uploaded. declare -A CROSS_PROFILES CROSS_PROFILES["x86_64-cros-linux-gnu"]="coreos:coreos/amd64/generic" CROSS_PROFILES["aarch64-cros-linux-gnu"]="coreos:coreos/arm64/generic" # Map board names to CHOSTs and portage profiles. This is the # definitive list, there is assorted code new and old that either # guesses or hard-code these. All that should migrate to this list. declare -A BOARD_CHOSTS BOARD_PROFILES BOARD_CHOSTS["amd64-usr"]="x86_64-cros-linux-gnu" BOARD_PROFILES["amd64-usr"]="coreos:coreos/amd64/generic" BOARD_CHOSTS["arm64-usr"]="aarch64-cros-linux-gnu" BOARD_PROFILES["arm64-usr"]="coreos:coreos/arm64/generic" BOARD_NAMES=( "${!BOARD_CHOSTS[@]}" ) # Declare the above globals as read-only to avoid accidental conflicts. declare -r \ TOOLCHAIN_PKGS \ CROSS_PROFILES \ BOARD_CHOSTS \ BOARD_NAMES \ BOARD_PROFILES ### Generic metadata fetching functions ### # map CHOST to portage ARCH, list came from crossdev # Usage: get_portage_arch chost get_portage_arch() { case "$1" in aarch64*) echo arm64;; alpha*) echo alpha;; arm*) echo arm;; hppa*) echo hppa;; ia64*) echo ia64;; i?86*) echo x86;; m68*) echo m68k;; mips*) echo mips;; powerpc64*) echo ppc64;; powerpc*) echo ppc;; sparc*) echo sparc;; s390*) echo s390;; sh*) echo sh;; x86_64*) echo amd64;; *) die "Unknown CHOST '$1'";; esac } # map CHOST to kernel ARCH # Usage: get_kernel_arch chost get_kernel_arch() { case "$1" in aarch64*) echo arm64;; alpha*) echo alpha;; arm*) echo arm;; hppa*) echo parisc;; ia64*) echo ia64;; i?86*) echo x86;; m68*) echo m68k;; mips*) echo mips;; powerpc*) echo powerpc;; sparc64*) echo sparc64;; sparc*) echo sparc;; s390*) echo s390;; sh*) echo sh;; x86_64*) echo x86;; *) die "Unknown CHOST '$1'";; esac } get_board_list() { local IFS=$'\n\t ' sort <<<"${BOARD_NAMES[*]}" } get_chost_list() { local IFS=$'\n\t ' sort -u <<<"${BOARD_CHOSTS[*]}" } get_profile_list() { local IFS=$'\n\t ' sort -u <<<"${BOARD_PROFILES[*]}" } # Usage: get_board_arch board [board...] get_board_arch() { local board for board in "$@"; do get_portage_arch $(get_board_chost "${board}") done } # Usage: get_board_chost board [board...] get_board_chost() { local board for board in "$@"; do if [[ ${#BOARD_CHOSTS["$board"]} -ne 0 ]]; then echo "${BOARD_CHOSTS["$board"]}" else die "Unknown board '$board'" fi done } # Usage: get_board_profile board [board...] get_board_profile() { local board for board in "$@"; do if [[ ${#BOARD_PROFILES["$board"]} -ne 0 ]]; then echo "${BOARD_PROFILES["$board"]}" else die "Unknown board '$board'" fi done } # Usage: get_board_binhost [-t] board [version...] # -t: toolchain only, full rebuilds re-using toolchain pkgs # If no versions are specified the current and SDK versions are used. get_board_binhost() { local toolchain_only=0 board ver if [[ "$1" == "-t" ]]; then toolchain_only=1 shift fi board="$1" shift if [[ $# -eq 0 ]]; then set -- "${FLATCAR_SDK_VERSION}" "${FLATCAR_VERSION_ID}" fi for ver in "$@"; do if [[ $toolchain_only -eq 0 ]]; then echo "${FLATCAR_DEV_BUILDS}/boards/${board}/${ver}/pkgs/" fi echo "${FLATCAR_DEV_BUILDS}/boards/${board}/${ver}/toolchain/" done } get_sdk_arch() { get_portage_arch $(uname -m) } get_sdk_profile() { echo "coreos:coreos/$(get_sdk_arch)/sdk" } get_sdk_libdir() { # Looking for LIBDIR_amd64 or similar portageq envvar "LIBDIR_$(get_sdk_arch)" } # Usage: get_sdk_binhost [version...] # If no versions are specified the current and SDK versions are used. get_sdk_binhost() { local arch=$(get_sdk_arch) ver if [[ $# -eq 0 ]]; then set -- "${FLATCAR_SDK_VERSION}" "${FLATCAR_VERSION_ID}" fi for ver in "$@"; do echo "${FLATCAR_DEV_BUILDS}/sdk/${arch}/${ver}/pkgs/" echo "${FLATCAR_DEV_BUILDS}/sdk/${arch}/${ver}/toolchain/" done } # Usage: get_cross_pkgs chost [chost2...] get_cross_pkgs() { local cross_chost native_pkg for cross_chost in "$@"; do for native_pkg in "${TOOLCHAIN_PKGS[@]}"; do echo "${native_pkg/*\//cross-${cross_chost}/}" done done } # Get portage arguments restricting toolchains to binary packages only. get_binonly_args() { local pkgs=( "${TOOLCHAIN_PKGS[@]}" $(get_cross_pkgs "$@") ) echo "${pkgs[@]/#/--useoldpkg-atoms=}" "${pkgs[@]/#/--rebuild-exclude=}" } ### Toolchain building utilities ### # Create the crossdev overlay and repos.conf entry. # crossdev will try to setup this itself but doesn't do everything needed # to make the newer repos.conf based configuration system happy. This can # probably go away if crossdev itself is improved. configure_crossdev_overlay() { local root="$1" local location="$2" # may be called from either catalyst (root) or update_chroot (user) local sudo="env" if [[ $(id -u) -ne 0 ]]; then sudo="sudo -E" fi $sudo mkdir -p "${root}${location}/"{profiles,metadata} echo "x-crossdev" | \ $sudo tee "${root}${location}/profiles/repo_name" > /dev/null $sudo tee "${root}${location}/metadata/layout.conf" > /dev/null < /dev/null </dev/null <&2 return 1 fi # Only call crossdev to regenerate configs if something has changed if ! cmp --quiet - "${cross_cfg}" <<<"${cross_cfg_data}" then $sudo crossdev "${cross_flags[@]}" --init-target $sudo tee "${cross_cfg}" <<<"${cross_cfg_data}" >/dev/null fi # Check if any packages need to be built from source. If so do a full # bootstrap via crossdev, otherwise just install the binaries (if enabled). # It is ok to build gdb from source outside of crossdev. if emerge "${emerge_flags[@]}" \ --pretend "${cross_pkgs[@]}" | grep -q '^\[ebuild' then $sudo crossdev "${cross_flags[@]}" --stage4 else $sudo emerge "${emerge_flags[@]}" \ "cross-${cross_chost}/gdb" "${cross_pkgs[@]}" fi # Setup environment and wrappers for our shiny new toolchain gcc_set_latest_profile "${cross_chost}" $sudo CC_QUIET=1 ccache-config --install-links "${cross_chost}" $sudo CC_QUIET=1 sysroot-config --install-links "${cross_chost}" } # Build/install toolchain dependencies into the cross sysroot for a # given CHOST. This is required to build target board toolchains since # the target sysroot under /build/$BOARD is incomplete at this stage. # Usage: build_cross_toolchain chost [--portage-opts....] install_cross_libs() { local cross_chost="$1"; shift local ROOT="/usr/${cross_chost}" local package_provided="$ROOT/etc/portage/profile/package.provided" # may be called from either catalyst (root) or setup_board (user) local sudo="env" if [[ $(id -u) -ne 0 ]]; then sudo="sudo -E" fi CBUILD="$(portageq envvar CBUILD)" \ CHOST="${cross_chost}" \ ROOT="$ROOT" \ SYSROOT="$ROOT" \ _configure_sysroot "${CROSS_PROFILES[${cross_chost}]}" # In order to get a dependency list we must calculate it before # updating package.provided. Otherwise portage will no-op. $sudo rm -f "${package_provided}/cross-${cross_chost}" local cross_deps=$(ROOT="$ROOT" _get_dependency_list \ "$@" "${TOOLCHAIN_PKGS[@]}" | $sudo tee \ "$ROOT/etc/portage/cross-${cross_chost}-depends") # Add toolchain to packages.provided since they are on the host system if [[ -f "${package_provided}" ]]; then # emerge-wrapper is trying a similar trick but doesn't work $sudo rm -f "${package_provided}" fi $sudo mkdir -p "${package_provided}" local native_pkg cross_pkg cross_pkg_version for native_pkg in "${TOOLCHAIN_PKGS[@]}"; do cross_pkg="${native_pkg/*\//cross-${cross_chost}/}" cross_pkg_version=$(portageq match / "${cross_pkg}") echo "${native_pkg%/*}/${cross_pkg_version#*/}" done | $sudo tee "${package_provided}/cross-${cross_chost}" >/dev/null # OK, clear as mud? Install those dependencies now! PORTAGE_CONFIGROOT="$ROOT" $sudo emerge --root="$ROOT" --sysroot="$ROOT" "$@" -u $cross_deps } # Get the latest GCC profile for a given CHOST # The extra flag can be blank, hardenednopie, and so on. See gcc-config -l # Usage: gcc_get_latest_profile chost [extra] gcc_get_latest_profile() { local prefix="${1}-" local suffix="${2+-$2}" local status gcc-config -l | cut -d' ' -f3 | grep "^${prefix}[0-9\\.]*${suffix}$" | tail -n1 # return 1 if anything in the above pipe failed for status in ${PIPESTATUS[@]}; do [[ $status -eq 0 ]] || return 1 done } # Update to the latest GCC profile for a given CHOST if required # The extra flag can be blank, hardenednopie, and so on. See gcc-config -l # Usage: gcc_set_latest_profile chost [extra] gcc_set_latest_profile() { local latest=$(gcc_get_latest_profile "$@") if [[ -z "${latest}" ]]; then echo "Failed to detect latest gcc profile for $1" >&2 return 1 fi # may be called from either catalyst (root) or upgrade_chroot (user) local sudo="env" if [[ $(id -u) -ne 0 ]]; then sudo="sudo -E" fi $sudo gcc-config "${latest}" }