#!/bin/bash
# Copyright 1999-2025 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2

CROSSDEV_VER="@CDEVPV@"

cd /
umask 0022 #159111

: ${ROOT:=/}
if [[ ${ROOT} != "/" ]] ; then
	echo "Sorry, but crossdev does not support ROOT." 1>&2
	exit 2
fi

EPREFIX="@GENTOO_PORTAGE_EPREFIX@"
if [[ ${EPREFIX} == "@"GENTOO_PORTAGE_EPREFIX"@" ]] ; then
	EPREFIX=""
fi
EROOT=${ROOT%/}"${EPREFIX}"/
: ${PORTAGE_CONFIGROOT:=${EROOT}}
CONFIGROOT="${PORTAGE_CONFIGROOT}/etc/portage"

# Prevent functions.sh from complaining about command line arguments like -C,
# which has a different meaning for crossdev.
declare -a ARGV=( "${@}" )
set --
source "${EPREFIX}"/lib/gentoo/functions.sh || exit 1
set -- "${ARGV[@]}"

esyslog() { :; }
die_logs() {
	echo
	eerror "error: $1"

	shift
	local log
	eerror
	eerror "If you file a bug, please attach the following logfiles:"
	eerror "${PORT_LOGDIR}/${CROSSDEV_OVERLAY_CATEGORY}-info.log"
	for log in "$@" ; do
		eerror "${log}"
	done
	exit 1
}
die() { die_logs "$*"; }
has() { [[ " ${@:2} " == *" $1 "* ]]; }
xmkdir() { mkdir "$@" || die "could not mkdir $*"; }

usage() {
	local exit_status=${1:-0}
	shift
cat << EOF
Usage: ${HILITE}crossdev${NORMAL} ${GOOD}[options]${NORMAL} ${BRACKET}--target TARGET${NORMAL}

Options:
    ${GOOD}-L,  --llvm${NORMAL}              Use LLVM/Clang as a cross compiler
    ${GOOD}--b, --binutils${NORMAL} ver      Specify version of binutils to use
    ${GOOD}--g, --gcc${NORMAL} ver           Specify version of gcc to use
    ${GOOD}--k, --kernel${NORMAL} ver        Specify version of kernel headers to use
    ${GOOD}--l, --libc${NORMAL} ver          Specify version of libc to use
                                Note: versions support depend atom syntaxes:
                                 e.g. ">=2.20" "~4.6.1" "=2.13.1-r3"
    ${GOOD}-A, --abis${NORMAL} abis          Specify ABIs to build, first one is the default
    ${GOOD}--host-abi abi${NORMAL}           Specify the ABI of the compiler itself
    ${GOOD}--env${NORMAL} env                Specify env settings for all packages (see below)
    ${GOOD}--[bdgkl]env${NORMAL} env         Specify env settings for binutils/gdb/gcc/kernel/libc
                                Note: make sure to quote: 'VAR="some value"\nFOO="cow"'
    ${GOOD}--[bdgkl]cat${NORMAL} category    Use binutils/gdb/gcc/kernel/libc package from category
    ${GOOD}--[bdgkl]pkg${NORMAL} pkg         Use binutils/gdb/gcc/kernel/libc package with given name
    ${GOOD}-f, --force${NORMAL}              I don't need a seat belt!
    ${GOOD}-S, --stable${NORMAL}             Use latest stable versions as default
    ${GOOD}-C, --clean${NORMAL} target       Uninstall specified target
    ${GOOD}-P, --portage${NORMAL} opts       Options to pass to emerge (see emerge(1))
    ${GOOD}--with[out]-headers${NORMAL}      Build C library headers before C compiler?
    ${GOOD}--show-fail-log${NORMAL}          If the build fails, dump the failing log

Overlay Options:
    ${GOOD}-oS, --overlays${NORMAL} list     Space delimited list of overlays to search
                                [default: \`portageq repositories_configuration\`]
    ${GOOD}-oO, --ov-output${NORMAL} path    Parent directory of overlay to write crossdev package links
                                [default: uses repo with name 'crossdev', or
                                 'cross-\${CTARGET}', or falls back to first
                                 from --overlays list]
    ${GOOD}-ob, --ov-binutils${NORMAL} path  Overlay for binutils ebuilds [default: search]
    ${GOOD}-od, --ov-gdb${NORMAL} path       Overlay for gdb ebuilds [default: search]
    ${GOOD}-og, --ov-gcc${NORMAL} path       Overlay for gcc ebuilds [default: search]
    ${GOOD}-ok, --ov-kernel${NORMAL} path    Overlay for kernel ebuilds [default: search]
    ${GOOD}-ol, --ov-libc${NORMAL} path      Overlay for C library ebuilds [default: search]
    ${GOOD}-ox, --ov-extra${NORMAL} path     Overlay for extra packages [default: search]

Stage Options:
    ${GOOD}-s0, --stage0${NORMAL}            Build just binutils
    ${GOOD}-s1, --stage1${NORMAL}            Also build a bare C compiler (no C library/
                                C++/shared GCC libs/C++ exceptions/etc...)
    ${GOOD}-s2, --stage2${NORMAL}            Also build kernel headers
    ${GOOD}-s3, --stage3${NORMAL}            Also build the C library
    ${GOOD}-s4, --stage4${NORMAL}            Also build a full compiler [default]
                                (shared libs GCC/various lang frontends/etc...)

External Tooling Options:
    ${GOOD}--show-target-cfg${NORMAL}        Display target settings that crossdev will use
    ${GOOD}--init-target${NORMAL}            Setup config/overlay/etc... files only

Extra Fun (must be run after above stages):
    ${GOOD}--ex-only${NORMAL}                Skip the stage steps above
    ${GOOD}--ex-gcc${NORMAL}                 Build extra gcc targets (ada/d/fortran/etc...)
    ${GOOD}--ex-gdb${NORMAL}                 Build a cross gdb
    ${GOOD}--ex-pkg${NORMAL} pkg             Build extra packages (may be used multiple times)

LLVM/Clang Specific Options (--llvm):
    ${GOOD}--r, --crt${NORMAL} ver           Specify version of compiler-rt to use
    ${GOOD}--c, --ccw${NORMAL} ver           Specify version of clang-crossdev-wrapper to use
    ${GOOD}--[rc]env${NORMAL} env            Specify env settings for compiler-rt/clang-crossdev-wrapper
    ${GOOD}--[rc]cat${NORMAL} category       Use compiler-rt/clang-crossdev-wrapper package from category
    ${GOOD}--[rc]pkg${NORMAL} pkg            Use compiler-rt/clang-crossdev-wrapper package with given name
    ${GOOD}-or, --ov-crt${NORMAL} path       Overlay for compiler-rt ebuilds [default: search]
    ${GOOD}-oc, --ov-ccw${NORMAL} path       Overlay for clang-crossdev-wrapper ebuilds [default: search]

${BRACKET}Target (-t)${NORMAL} takes a tuple ${BRACKET}ARCHITECTURE-VENDOR-OS-LIBC${NORMAL}; see 'crossdev -t help'
EOF
	[[ -n $* ]] && echo && eerror "Error: $*"
	exit ${exit_status}
}
STAGE_BINUTILS=0
STAGE_C_ONLY=1
STAGE_C_KERNEL=2
STAGE_LIBC=3
STAGE_C_CPP=4
STAGE_DEFAULT=${STAGE_C_CPP}
STAGE_DISP=(
	"binutils"
	"C compiler only"
	"kernel headers"
	"C compiler & libc"
	"C/C++ compiler"
)

parse_target() {
	CTARGET=${1#cross-}
	if [[ ${CTARGET} == "cross-*" ]] ; then
		CTARGET=${1#cross-}
	elif [[ ${CTARGET} == "cross_llvm-*" ]] ; then
		CTARGET=${1#cross_llvm-}
		LLVM="yes"
	fi

	[[ -z ${CTARGET} ]] && usage 1

	if [[ ${CTARGET} == "help" ]] ; then
		cat <<-EOF
		Target (-t) takes a tuple ARCHITECTURE-VENDOR-OS-LIBC:

		Supported Architectures (ARCHITECTURE):
		   - alpha
		   - arm / armeb / aarch64
		   - hppa (parisc)
		   - ia64
		   - i386 / i486 / i586 / i686 (x86)
		   - loongarch64
		   - m68k
		   - mips / mipsel / mips64 / mips64el
		   - or1k
		   - powerpc (ppc) / powerpc64 (ppc64)
		   - riscv32 / riscv64
		   - sparc / sparc64
		   - s390 / s390x
		   - sh / sh[1-5] / sh64
		   - x86_64 (amd64)
		Supported C Libraries (LIBC):
		   - gnu (glibc)
		   - klibc       [prob wont work]
		   - musl
		   - newlib      [bare metal/no operating system]
		   - uclibc      [not all arches are ported]
		Special Targets (full tuple):
		   - avr      http://www.nongnu.org/avr-libc/
		   - bfin     http://blackfin.uclinux.org/
		   - h8300    http://h8300-hms.sourceforge.net/
		   - mingw64  http://mingw-w64.sourceforge.net/
		   - mmix     http://www-cs-faculty.stanford.edu/~knuth/mmix.html
		   - msp430   http://www.ti.com/msp430
		   - nds32    http://github.com/nds32
		   - nios2    http://www.altera.com/products/ip/processors/nios2/ni2-index.html
		   - xc16x    http://www.infineon.com/
		   - ppu / spu (cell) [Cell/Playstation 3 targets]
		Softfloat toolchains:
		   Include 'softfloat' in the 'vendor' field
		   e.g. armeb-softfloat-linux-uclibc  powerpc-booya_softfloat-linux-gnu
		EOF
		exit 0
	fi

	# Allow for laziness
	if [[ ${CTARGET} != *-* ]] ; then
		# Handle aliases
		local aliases=()
		case ${CTARGET} in
			cell)    aliases=( ppu spu-elf );;
			mingw64) aliases=( x86_64-w64-mingw32 );;
		esac
		if [[ ${#aliases[@]} -eq 1 ]] ; then
			CTARGET=${aliases[0]}
		elif [[ ${#aliases[@]} -gt 1 ]] ; then
			einfo "The '${CTARGET}' target is really an alias for the '${aliases}' target(s)"
			local alias
			for alias in ${aliases} ; do
				${CROSSDEV} -t ${alias} || exit 1
			done
			exit 0
		fi

		# First translate portage ARCH to actual tuple
		case ${CTARGET} in
			amd64)      CTARGET="x86_64";;
			parisc*)    CTARGET=${CTARGET/parisc/hppa};;
			ppc|ppc64)  CTARGET=${CTARGET/ppc/powerpc};;
			x86)        CTARGET="i686";;
		esac

		# Then add common suffixes
		case ${CTARGET} in
			i?86|x86_64)
				CTARGET="${CTARGET}-pc-linux-gnu";;
			s390*)
				CTARGET="${CTARGET}-ibm-linux-gnu";;
			arm64*)
				CTARGET="aarch${CTARGET#arm}-unknown-linux-gnu";;
			armv[67]*)
				CTARGET="${CTARGET}-unknown-linux-gnueabihf";;
			arm*)
				CTARGET="${CTARGET}-unknown-linux-gnueabi";;
			aarch64*|alpha*|cris*|hppa*|ia64*|loongarch*|m68*|mips*|powerpc*|riscv*|sparc*|sh*|tile*)
				CTARGET="${CTARGET}-unknown-linux-gnu";;
			bfin*|h8300*|msp430*|nds32*|nios2*|spu*|xc16x*)
				CTARGET="${CTARGET}-elf";;
		esac
	fi

	# Figure out an acceptable portage ARCH for this target
	case ${CTARGET} in
		aarch64*)   TARCH=arm64;;
		alpha*)     TARCH=alpha;;
		arm*)       TARCH=arm;;
		hppa*)      TARCH=hppa;;
		ia64*)      TARCH=ia64;;
		i?86*)      TARCH=x86;;
		loongarch*) TARCH=loong;;
		m68*)       TARCH=m68k;;
		mips*)      TARCH=mips;;
		powerpc64*) TARCH=ppc64;;
		powerpc*)   TARCH=ppc;;
		riscv*)     TARCH=riscv;;
		sparc*)     TARCH=sparc;;
		s390*)      TARCH=s390;;
		sh*)        TARCH=sh;;
		x86_64*)    TARCH=amd64;;
		*)          TARCH="*";;
	esac
	case ${CTARGET} in
		*-freebsd*) TARCH="${TARCH}-fbsd";;
	esac

	# Now account for all the "special" architectures out there
	case ${CTARGET} in
		# this is a linux+ target, not microcontroller (below)
		avr32*) :;;

		avr*)
			KPKG="[none]"
			LCAT="dev-embedded"
			LPKG="avr-libc"
			GUSE+=" -fortran -go" # doesn't work
			MULTILIB_USE="yes" #377039
			BUSE+=" cxx"
			WITH_DEF_HEADERS="no"
			;;

		bpf*)
			# Doesn't support C++ and friends
			STAGE_DEFAULT=${STAGE_C_ONLY}
			# No linux-headers needed/required
			WITH_DEF_HEADERS="no"
			;;

		# Has no glibc support yet (or even ABI defined). Can
		# only compile linux kernel:
		# https://parisc.wiki.kernel.org/index.php/Userspace64
		hppa64*)
			STAGE_DEFAULT=${STAGE_C_ONLY}
			WITH_DEF_HEADERS="no";;

		ia64*)
			# gcc's libgcc needs glibc headers for unwinder.
			# Thus gcc-stage1 can't be built without glibc headers.
			WITH_DEF_HEADERS="yes"
			;;

		loongarch*)
			# have to bring in the experimental LoongArch patchset before
			# everything is upstream
			KUSE+=" experimental-loong"
			LUSE+=" experimental-loong"
			;;

		# added in bug #609602
		mmix*)
			KPKG="[none]"
			LPKG="newlib"
			STAGE_DEFAULT=${STAGE_LIBC}
			WITH_DEF_HEADERS="no"
			;;

		# Offload targets
		nvptx*)
			KPKG="[none]"
			BPKG="nvptx-tools"
			LPKG="newlib"
			# Offloading targets are only really useful with
			# openmp, unless doing standalone bare minimum testing.
			# They also often want Fortran.
			STAGE_DEFAULT=${STAGE_C_CPP}
			# We can't force these on via GFORCE because they need
			# to be disabled for stage1.
			GUSE+=" cxx openmp fortran"
			# https://gcc.gnu.org/legacy-ml/gcc/2018-03/msg00122.html
			GMASK+=" ada d go graphite modula2 rust"

			GMASK+=" default-stack-clash-protection hardened ssp"
			GUSE+=" -hardened" #687598, needs -fstack-check=specific support
			GUSE+=" -default-stack-clash-protection -ssp" # SSP isn't supported for freestanding anyway
			# Offloading uses LTO to stream data, we need to make sure
			# we support whatever compression the host compiler uses
			# for that.
			GUSE+=" zlib zstd"

			# We need multilib for openmp to be built w/ -mgomp
			MULTILIB_USE="yes"
			WITH_DEF_HEADERS="no"
			;;

		# due to upstream lameness, build C/C++ at first glance
		*-cygwin)
			GUSE_DISABLE_STAGE_1+=" cxx"
			;;

		# these are the mingw64 targets that binutils seems to use
		x86_64-*-mingw*|*-w64-mingw*)
			KPKG="[none]";
			LCAT="dev-util"
			LPKG="mingw64-runtime"
			WITH_DEF_HEADERS="yes" # gcc can't bootstrap without headers: bug #693770
			GMASK+=" default-stack-clash-protection hardened"
			GUSE+=" -default-stack-clash-protection -hardened" # gcc ICEs as libssp/ssp.c:186:1: internal compiler error: in seh_emit_stackalloc
			;;

		mingw*|*-mingw*)
			# bug #584858
			die "'${CTARGET}' target is not supported anymore, use i686-w64-mingw32"
			;;

		spu*)
			TARCH=ppc64
			KPKG="[none]"
			LPKG="newlib"
			;;
		ppu*)
			TARCH=ppc64
			;;

		mips64*-gnuabin32|mipsisa64*-gnuabin32)
			[[ ${MULTILIB_ABIS} == "default" ]] && MULTILIB_ABIS="n32"
			;;
		mips64*-gnuabi64|mipsisa64*-gnuabi64|mips64*-gnuabin64|mipsisa64*-gnuabin64)
			[[ ${MULTILIB_ABIS} == "default" ]] && MULTILIB_ABIS="n64"
			;;
		mips64*-gnuabi32|mipsisa64*-gnuabi32|mips64*-gnuabio32|mipsisa64*-gnuabio32)
			[[ ${MULTILIB_ABIS} == "default" ]] && MULTILIB_ABIS="o32"
			;;
	esac

	# Tweak packages based upon CTARGET
	case ${CTARGET} in
		# Normal Linux host, just diff libc
		*-dietlibc)
			LPKG="dietlibc"
			LCAT="dev-libs"
			;;
		*-gnu*)
			LPKG="glibc"
			# gcc would not find -lpthread without static libraries while building
			# its copy of libgomp.
			LUSE+=" static-libs"
			;;
		*-klibc)
			LPKG="klibc"
			;;
		*-uclibc*)
			LPKG="uclibc-ng"
			;;
		*-uclinux)
			LPKG="uclibc-ng"
			;;
		*-musl*)
			LPKG="musl"
			;;
		*-llvm*)
			LPKG="llvm-libc"
			;;

		# Windows targets
		*-cygwin)
			LCAT="dev-libs"
			LPKG="cygwin"
			KPKG="[none]"
			;;

		# Bare metal targets
		*-newlib|*-elf|*-eabi|*-rtems*)
			LPKG="newlib"
			KPKG="[none]"
			GMASK+=" default-stack-clash-protection hardened ssp"
			GUSE+=" cxx -openmp" #489798
			GUSE+=" -fortran" #589672, needs syscalls
			GUSE+=" -hardened" #687598, needs -fstack-check=specific support
			GUSE+=" -default-stack-clash-protection -ssp" # SSP isn't supported for freestanding anyway
			MULTILIB_USE="yes" #407275
			WITH_DEF_HEADERS="no"
			;;

		# Now for the BSDs ...
		*-freebsd*)
			LCAT="sys-freebsd" LPKG="freebsd-lib"
			KPKG="[none]"
			;;
	esac

	# Target-specific defaults (workarounds for upstream bugs). User
	# can override them by passing 'USE=foo crossdev ...'. Useful when
	# one wants to fix upstream bugs.

	local sanitizer_support=no
	# Whitelist asan on explicitly supported arches for linux.
	# Broken examples:
	# - musl libc
	# - bare metal targets
	# Untested examples:
	# - *BSD
	case ${CTARGET} in
		# glibc targets
		*-gnu*)
			case ${CTARGET} in
				mips64*|mipsisa64*)
					# has some support code, fails to build
					;;
				x86_64*|i?86*|arm*|aarch64*|powerpc*)
					sanitizer_support=yes
					;;
			esac
			;;
	esac
	if [[ ${sanitizer_support} = "no" ]]; then
		GUSE+=" -sanitize"
	fi

	local vtv_support=no
	# Whitelist vtv on explicitly supported arches for linux.
	# Broken examples:
	# - musl libc
	# - bare metal targets
	# - powerpc
	# Untested examples:
	# - *BSD
	case ${CTARGET} in
		# glibc targets
		*-gnu*)
			case ${CTARGET} in
				x86_64*|i?86*|arm*|aarch64)
					vtv_support=yes
					;;
			esac
			;;
	esac
	if [[ ${vtv_support} = "no" ]]; then
		GUSE+=" -vtv"
	fi

	local pie_support=yes
	# Blacklist rare targets that:
	# 1. don't support -fPIC in compiler
	# 2. have -fPIE broken
	case ${CTARGET} in
		# [1.]: no -fPIC flag support:
		#       check as '$CC -fPIC -c -x c - </dev/null'
		avr*|mmix*)
			pie_support=no
			;;
		# [2.] mingw32 startup code is broken: bug #644930
		#      at least on i686-w64-mingw32 and x86_64-w64-mingw32
		mingw*|*-mingw*) pie_support=no
			;;
		# Many bare-metal targets don't work with pie as-is
		*-elf|*-eabi)
			# mips can't generate freestanding PIC:
			#   cc1: error: position-independent code requires ‘-mabicalls’
			# arm firmware packages don't expect pie-by-default:
			#   https://lists.gnupg.org/pipermail/gnuk-users/2018-August/000086.html
			#   https://github.com/gl-sergei/u2f-token/issues/14
			pie_support=no
			;;
	esac
	# Running a hardened profile on the host forces pie #831165
	if [[ ${pie_support} == "no" ]]; then
		# pie is >=gcc-6, nopie is <gcc-6
		GMASK+=" pie -nopie"
		GFORCE+=" nopie"
	fi

	local ssp_support=yes
	# Blacklist rare targets that:
	# 1. don't support -fstack-protector in compiler
	# 2. have -fstack-protector broken
	case ${CTARGET} in
		# [1.]: no -fstack-protector flag support:
		#       check as '$CC -fstack-protector -c -x c - </dev/null'
		alpha*|avr*|hppa*|ia64*|nvptx*|mmix*|nios2*) ssp_support=no;;
	esac
	# Running a hardened profile on the host forces ssp #831165
	if [[ ${ssp_support} == "no" ]]; then
		# ssp is >=gcc-6, nossp is <gcc-6
		# blacklist hardened too because it'll force -fstack-clash-protection
		GMASK+=" default-stack-clash-protection ssp hardened -nossp"
		GFORCE+=" nossp"
	fi

	# Configure the cet USE flag to match the profiles. We especially
	# don't want to enable it on i?86.
	case ${CTARGET} in
		x86_64-*)
			BFORCE+=" cet"
			GUSE+=" cet"
			LUSE+=" cet"
			;;
		*)
			BMASK+=" cet"
			GMASK+=" cet"
			LMASK+=" cet"
			;;
	esac
}

parse_repo_config() {
	# Ugh, ini parser here we come.
	# [DEFAULT]
	# main-repo = gentoo
	# [crossdev]
	# location = /usr/local/portage
	# masters = gentoo
	# priority = 0
	local repo_config=$(portageq repositories_configuration "${EROOT}")
	local flat_config=$(echo "${repo_config}" | gawk '
		function push(arr, idx, ele) {
			if (idx in arr)
				arr[idx][length(arr[idx]) + 1] = ele
			else
				arr[idx][0] = ele
		}
		{
			if ($1 == "main-repo") {
				main_repo = $NF
			} else if ($1 ~ /^\[/) {
				if (repo_name && loc)
					push(repos, prio, repo_name ":" loc)
				repo_name = gensub(/\[([^\]]*)\]/, "\\1", 1, $1)
				loc = prio = ""
			} else if ($1 == "priority") {
				prio = $3
			} else if ($1 == "location") {
				loc = $3
			}
		}
		END {
			push(repos, prio, repo_name ":" loc)

			print(main_repo)
			asorti(repos, prios)
			for (prio in prios)
				for (repo in repos[prios[prio]])
					print(repos[prios[prio]][repo])
		}
	')

	MAIN_REPO_NAME=$(echo "${flat_config}" | head -1)
	REPO_CONFIG=$(echo "${flat_config}" | sed 1d)
	MAIN_REPO_PATH=$(echo "${REPO_CONFIG}" | sed -n "/^${MAIN_REPO_NAME}:/s,^[^:]*:,,p")
	if [[ -z ${SEARCH_OVERLAYS} ]] ; then
		# see if user gave us an overlay search list, otherwise
		# default to whatever is configured in portage
		SEARCH_OVERLAYS=$(echo "${REPO_CONFIG}" | sed -e "/^${MAIN_REPO_NAME}:/d" -e 's,^[^:]*:,,')
	fi
}

# Used by --show-repo-cfg to share the parser logic with other tools.
show_repo_cfg() {
	local var=$1
	unset SEARCH_OVERLAYS
	parse_repo_config
	echo "${!var}"
	exit 0
}

setup_portage_vars() {
	local arch=${ARCH} arch_set=${ARCH+set}
	local chost=${CHOST} chost_set=${CHOST+set}
	local abi
	unset ARCH CHOST
	eval $(portageq envvar -v PORT_LOGDIR PORTAGE_TMPDIR ARCH CHOST)
	parse_repo_config

	# keep the original values, but be aware of the native ones
	HARCH=${ARCH}
	[[ ${arch_set} == "set" ]] && ARCH=${arch} || unset ARCH
	HCHOST=${CHOST}
	[[ ${chost_set} == "set" ]] && CHOST=${chost} || unset CHOST

	# Collect all CHOST* visible for host. We need to make sure
	# not to clobber any of native CHOSTs.
	HCHOSTS=(${HCHOST})
	for abi in $(portageq envvar ABI MULTILIB_ABIS DEFAULT_ABI); do
		HCHOSTS+=($(portageq envvar CHOST_${abi}))
	done
	# uniq
	HCHOSTS=(
		$(printf "%s\n" "${HCHOSTS[@]}" | sort -u)
	)

	# see if user told us where to write things, otherwise
	# set up some repos.conf magic if possible.  if not,
	# install our stuff to the first overlay in the list.
	if [[ -z ${CROSSDEV_OVERLAY} ]] ; then
		local repo_path repo_name
		for repo_name in "${CROSSDEV_OVERLAY_CATEGORY}" crossdev ; do
			repo_path=$(echo "${REPO_CONFIG}" | sed -n "/^${repo_name}:/s,^[^:]*:,,p")
			if [[ -n ${repo_path} ]] ; then
				CROSSDEV_OVERLAY=${repo_path}
				# if we end up installing our stuff, save the name of the repo
				# for further use in metadata/layout.conf
				CROSSDEV_OVERLAY_NAME=${repo_name}
				break
			fi
		done

		: ${CROSSDEV_OVERLAY:=$(echo "${SEARCH_OVERLAYS}" | head -1)}
	else
		# Check if there is a repos.conf entry whose
		# location matches ${CROSSDEV_OVERLAY}
		local repo_name conf_base i=""

		repo_name="$(echo "${REPO_CONFIG}" | sed -n "\|:${CROSSDEV_OVERLAY}$|s,:.*,,p")"
		if [[ -n ${repo_name} ]]; then
			# Match the name specified in the corresponding repos.conf entry
			CROSSDEV_OVERLAY_NAME="${repo_name}"
		else
			# Derive the name
			: ${CROSSDEV_OVERLAY_NAME:="$(basename "${CROSSDEV_OVERLAY}")"}
			# Note that there is no repos.conf entry and set up the path for the conf file
			# We'll create the conf file itself in set_metadata()
			conf_base="${CONFIGROOT}/repos.conf/${CROSSDEV_OVERLAY_NAME}"
			while [[ -e "${conf_base}${i}.conf" ]]; do
				# If there is a file collision with the conf file we target,
				# we'll first append '1' before '.conf' and try.
				# If that doesn't work, we'll retry with an incremented number
				# Incrementing an empty string returns '1' in the shell
				i=$((i + 1))
			done
			CROSSDEV_OVERLAY_CREATE_REPOS_CONF="${conf_base}${i}.conf"
		fi
	fi

	# make sure we have a valid logdir
	: ${PORT_LOGDIR:=${EPREFIX}/var/log/portage}
	xmkdir -p "${PORT_LOGDIR}"
}

uninstall() {
	local d f

	setup_portage_vars

	ewarn "Uninstalling target '${CTARGET}' ..."

	# clean out portage config files
	if [[ -d ${CROSSDEV_OVERLAY}/${CROSSDEV_OVERLAY_CATEGORY} ]]; then
		rm -r "${CROSSDEV_OVERLAY}"/${CROSSDEV_OVERLAY_CATEGORY}
		# if we remove all the package in the category,
		# might as well remove the category itself
		sed -e "/${CROSSDEV_OVERLAY_CATEGORY}/d" \
		    -i "${CROSSDEV_OVERLAY}"/profiles/categories
	fi
	# If profiles/categories is empty, see if we can remove the output overlay entirely
	# The conservative criteria for removal are as follows:
	# - The profiles/categories is empty
	# - metadata/layout.conf is managed by crossdev
	# - The only files in the overlay are profiles/categories and metadata/layout.conf
	# Otherwise, we leave the overlay alone
	if [[ ! -s "${CROSSDEV_OVERLAY}"/profiles/categories ]]; then
		# Check if layout.conf is managed by crossdev
		if grep -qs "^${AUTOGEN_TAG}" "${CROSSDEV_OVERLAY}"/metadata/layout.conf; then
			# Check that there are no other files
			local i=0
			while IFS="" read -d $'\0' -r; do
				i=$((i + 1))
			done < <(find "${CROSSDEV_OVERLAY}" -type f -print0)
			# Remove the overlay if we can
			[[ ${i} -eq 2 ]] && rm -r "${CROSSDEV_OVERLAY}"
		fi
	fi
	# Remove the repos.conf entry if the output overlay is completely removed
	if [[ ! -e "${CROSSDEV_OVERLAY}" ]]; then
		# There is no easy way to reverse map an overlay entry
		# to a particular file under repos.conf
		# We'll scan every conf file for a match on repo name and
		# remove if the file is managed by crossdev
		while IFS="" read -d $'\0' -r f ; do
			grep -qs "^${AUTOGEN_TAG}" "${f}" || continue
			if grep -qs "^\[${CROSSDEV_OVERLAY_NAME}\]$" "${f}"; then
				rm -f "${f}"
				break
			fi
		done < <(find "${CONFIGROOT}"/repos.conf -type f -print0)
	fi
	# crossdev stopped creating 'package.keywords' in Jan 2020
	for f in package.{accept_keywords,env,mask,keywords,use} profile/package.use.{force,mask} ; do
		f="${CONFIGROOT}/${f}"
		rm -f "${f}"/${CROSSDEV_OVERLAY_CATEGORY}
		rmdir "${f}" 2>/dev/null
	done
	rm -rf "${CONFIGROOT}"/env/${CROSSDEV_OVERLAY_CATEGORY}
	rmdir "${CONFIGROOT}"/env 2>/dev/null
	rm -f "${EPREFIX}"/etc/revdep-rebuild/05${CROSSDEV_OVERLAY_CATEGORY}
	rmdir "${EPREFIX}"/etc/revdep-rebuild 2>/dev/null

	if [[ "${LLVM}" == "yes" ]] ; then
		rm ${EROOT}/etc/clang/cross/${CTARGET}.cfg
		rmdir ${EROOT}/etc/clang/cross 2>/dev/null
	fi

	# Unmerge all toolchain packages for this target.
	emerge -q --rage-clean "${CROSSDEV_OVERLAY_CATEGORY}/*"

	# clean out known toolchain files (binutils/gcc)
	for f in \
		addr2line ar as c++filt dlltool dllwrap dwp embedspu \
		gcc-{ar,nm,ranlib} gccbug gcov-{dump,tool} gprof ld nm \
		objcopy objdump ranlib readelf size strings strip windmc windres
	do
		rm -f "${EPREFIX}"/usr/bin/${CTARGET}-${f}
	done
	rm -f "${EPREFIX}"/usr/${CTARGET}/{sys-include,usr}
	rmdir "${EPREFIX}"/usr/${CTARGET}/{include/asm,include} 2>/dev/null
	rm -f "${EPREFIX}"/usr/bin/${CTARGET}-{gcc,{c,g}++,cpp,gfortran,gcov}
	rm -f "${EPREFIX}"/etc/env.d/{binutils,gcc}/config-${CTARGET}

	# clean out files from crossdev itself
	[[ -e ${EPREFIX}/var/db/pkg/${CROSSDEV_OVERLAY_CATEGORY} ]] && rmdir "${EPREFIX}"/var/db/pkg/${CROSSDEV_OVERLAY_CATEGORY}
	rm -f "${EPREFIX}"/usr/bin/${CTARGET}-{emerge,ebuild,fix-root,pkg-config} "${EPREFIX}"/usr/bin/emerge-${CTARGET}

	for f in make.{conf,globals,profile} ; do
		f="${EPREFIX}/usr/${CTARGET}/etc/${f}"
		[[ -L ${f} ]] && rm -f ${f}
		f="${EPREFIX}/usr/${CTARGET}/etc/portage/${f##*/}"
		[[ -L ${f} ]] && rm -f ${f}
	done
	find "${EPREFIX}"/usr/share/crossdev/etc/ -type f | \
	while read f ; do
		f1=${f}
		[[ ! -e ${f1} ]] && continue
		m1=$(set -- `md5sum ${f1}`; echo $1)
		f2=${EPREFIX}/usr/${CTARGET}${f#/usr/share/crossdev}
		[[ ! -e ${f2} ]] && continue
		m2=$(set -- `md5sum ${f2}`; echo $1)
		if [[ ${m1} == ${m2} ]] ; then
			rm -f ${f2}
		fi
	done

	# clean out the sysroot, prompting the user if need be
	for d in "${EPREFIX}"/usr/lib/gcc{,-lib}/${CTARGET} "${EPREFIX}"/usr/${CTARGET} ; do
		if [[ ! -d ${d} ]] ; then
			rm -f "${d}"
		else
			if [[ ${FORCE} == "no" ]] ; then
				find "${d}" -type d -depth -exec rmdir {} + 2>/dev/null && continue
				printf "${d}: directory still exists; remove recursively? [y/N] "
				local ans
				read ans
				[[ ${ans} == [Yy]* ]] && rm -rf "${d}"
			else
				rm -rf "${d}"
			fi
		fi
	done
}

set_withval() {
	local withval varname
	varname=${*#--with-}
	varname=${varname#--without-}
	varname=${varname%%=*}
	if [[ $* == *=* ]] ; then
		withval=${*#*=}
	else
		[[ $* == --with-* ]] && withval="yes" || withval="no"
	fi

	echo WITH_`echo ${varname} | tr '[:lower:]' '[:upper:]'`=\"${withval}\"
}

is_stage() { [[ ${STAGE} -ge $1 ]] ; }
is_s0()    { is_stage 0 ; }
is_s1()    { is_stage 1 ; }
is_s2()    { is_stage 2 ; }
is_s3()    { is_stage 3 ; }
is_s4()    { is_stage 4 ; }
is_s5()    { is_stage 5 ; }

with_headers()   { [[ ${WITH_HEADERS}   == "yes" ]] ; }

ex_fast()    { [[ ${EX_FAST}    == "yes" ]] ; }
ex_gcc()     { [[ ${EX_GCC}     == "yes" ]] ; }
ex_gdb()     { [[ ${EX_GDB}     == "yes" ]] ; }
ex_pkgs()    { [[ ${#XPKGS[@]} -gt 0     ]] ; }

# For each extra package, call $@ with the X* vars set up properly.
for_each_extra_pkg() {
	local pkg i

	for (( i = 0; i < ${#XPKGS[@]}; ++i )) ; do
		# Since the user gave us full atoms, pull them apart here.
		pkg=${XPKGS[i]}
		if [[ ${pkg} != */* ]]; then
			usage 1 "arguments to --ex-pkg must be CATEGORY/PN"
		fi

		XCAT=${pkg%/*} \
		XPKG=${pkg#*/} \
		XVER=${XVERS[i]} \
		XUSE=${XUSES[i]} \
		XENV=${XENVS[i]} \
		XOVL=${XOVLS[i]} \
		XMASK=${XMASKS[i]} \
		XFORCE=${XFORCES[i]} \
		"$@"
	done
}

hr() {
	local c=${COLUMNS:-0}
	if [[ ${c} -eq 0 ]] ; then
		c=$(stty size 2> /dev/null)
		[[ -z ${c} ]] \
			&& c=50 \
			|| c=${c##* }
	fi
	local ext=${1:-  _  -  ~  -}
	local sext=${ext//?/ }
	local br=$(printf "%$((c + ${#ext}))s")
	local banner=${br//${sext}/${ext}}
	echo "${banner:0:${c}}"
}
ver_get_op() {
	local op ver=$1
	[[ ${ver} == "["* ]] && return
	op=${ver%%[0-9]*}
	[[ -n ${op} ]] && echo "${op}"
}
ver_chop_op() {
	local op ver=$1
	op=$(ver_get_op "${ver}")
	echo "${ver##${op}}"
}
pretty_atom() {
	local mid=$1 ver=$2
	printf '%s%s%s' "$(ver_get_op "${ver}")" "${mid}" "$(ver_chop_op "${ver}")"
}

##################
### setup vars ###
CROSSDEV=$0
EOPTS=
UOPTS=
TARCH=
HARCH=
HCHOST=
HCHOSTS=()
CTARGET=
MULTILIB_ABIS="default"
MULTILIB_USE=""
HOST_ABI="default"
STAGE=""
AENV=""
# Only GMASK/GFORCE are currently used
BCAT="sys-devel"  ; BPKG="binutils"      ; BVER="" BUSE="" BENV="" BOVL="" BMASK="" BFORCE=""
GCAT="sys-devel"  ; GPKG="gcc"           ; GVER="" GUSE="" GENV="" GOVL="" GMASK="" GFORCE=""
KCAT="sys-kernel" ; KPKG="linux-headers" ; KVER="" KUSE="" KENV="" KOVL="" KMASK="" KFORCE=""
LCAT="sys-libs"   ; LPKG="[none]"        ; LVER="" LUSE="" LENV="" LOVL="" LMASK="" LFORCE=""
DCAT="dev-debug"  ; DPKG="gdb"           ; DVER="" DUSE="" DENV="" DOVL="" DMASK="" DFORCE=""
RCAT="llvm-runtimes" ; RPKG="compiler-rt"   ; RVER="" RUSE="" RENV="" ROVL="" RMASK="" RFORCE=""
CCAT="sys-devel"  ; CPKG="clang-crossdev-wrappers" ; CVER="" CUSE="" CENV="" COVL="" CMASK="" CFORCE=""
XPKGS=() XVERS=() XUSES=() XENVS=() XOVLS=() XMASKS=() XFORCES=()
DEFAULT_VER="[latest]"
SEARCH_OVERLAYS=""
CROSSDEV_OVERLAY=""
CROSSDEV_OVERLAY_NAME=""
CROSSDEV_OVERLAY_CATEGORY_PREFIX="cross-"
CROSSDEV_OVERLAY_CREATE_REPOS_CONF=""
AUTOGEN_TAG="# Autogenerated and managed by crossdev"
# These flags are always disabled for cross-gcc; either usually/always broken, or
# not tested, or doesn't make sense, or no one simply cares about them
GUSE_DISABLE="-d -objc -objc++ -objc-gc -vtv"
# These are disabled only for stage1 gcc. Normally need libc presence.
GUSE_DISABLE_STAGE_1="${GUSE_DISABLE} -fortran -go -jit -cxx -openmp -sanitize -zstd -zlib"
# These are also disabled for stage2, but could be used later if dependencies
# are installed into ${SYSROOT}:
# - sanitize needs crypt.h: #799707
GUSE_DISABLE_STAGE_2="${GUSE_DISABLE} -sanitize"

# mingw64-runtime needs a stage2 compiler to build libraries: https://bugs.gentoo.org/751295
LUSE_DISABLE="-libraries"

# Past history of WITH_DEF_HEADERS:
# - ????-2005: "no"
# - 2005-2019: "yes": #227065 gcc-4.3+ is a pita w/out headers
# - 2019-????: "no" again: glibc does not really support headers install
#              at least on riscv-*: #686248
WITH_HEADERS="COW"
WITH_DEF_HEADERS="no"
EX_FAST="no"
EX_GCC="no"
EX_GDB="no"
FORCE="no"
SET_X="no"
ACTION="install"
SHOW_FAIL_LOG="no"
SHOW_TARGET_CFG="no"
INIT_TARGET_ONLY="no"
LLVM="no"

while [[ $# -gt 0 ]] ; do
	case $1 in
	-V|--version)      echo "crossdev-${CROSSDEV_VER}"; exit 0;;
	-t|--target)       shift; parse_target $1;;
	--b|--binutils)    shift; BVER=$1;;
	--benv)            shift; BENV=$1;;
	-ob|--ov-binutils) shift; BOVL=$1;;
	--bcat)            shift; BCAT=$1;;
	--bpkg)            shift; BPKG=$1;;
	--d|--gdb)         shift; DVER=$1;;
	--denv)            shift; DENV=$1;;
	-od|--ov-gdb)      shift; DOVL=$1;;
	--dcat)            shift; DCAT=$1;;
	--dpkg)            shift; DPKG=$1;;
	--g|--gcc)         shift; GVER=$1;;
	--genv)            shift; GENV=$1;;
	-og|--ov-gcc)      shift; GOVL=$1;;
	--gcat)            shift; GCAT=$1;;
	--gpkg)            shift; GPKG=$1;;
	--k|--kernel)      shift; KVER=$1;;
	--kenv)            shift; KENV=$1;;
	-ok|--ov-kernel)   shift; KOVL=$1;;
	--kcat)            shift; KCAT=$1;;
	--kpkg)            shift; KPKG=$1;;
	--l|--libc)        shift; LVER=$1;;
	--lenv)            shift; LENV=$1;;
	-ol|--ov-libc)     shift; LOVL=$1;;
	--lcat)            shift; LCAT=$1;;
	--lpkg)            shift; LPKG=$1;;
	--r|--crt)         shift; RVER=$1;;
	--renv)            shift; RENV=$1;;
	-or|--ov-crt)      shift; ROVL=$1;;
	--rcat)            shift; RCAT=$1;;
	--rpkg)            shift; RPKG=$1;;
	--c|--ccw)         shift; CVER=$1;;
	--cenv)            shift; CENV=$1;;
	-oc|--ov-ccw)      shift; COVL=$1;;
	--ccat)            shift; CCAT=$1;;
	--cpkg)            shift; CPKG=$1;;
	-ox|--ov-extra)    shift; XOVLS+=( "$1" );;
	--env)             shift; AENV=$1;;
	-L|--llvm)         LLVM="yes";;
	-A|--abis)         shift; MULTILIB_ABIS=$1;;
	--host-abi)        shift; HOST_ABI=$1;;
	-S|--stable)       DEFAULT_VER="[stable]";;
	-C|--clean)        shift; parse_target $1; ACTION="uninstall";;
	-s?|--stage?)      STAGE=${1:0-1};;
	-oS|--overlays)    shift; SEARCH_OVERLAYS=$1;;
	-oO|--ov-output)   shift; CROSSDEV_OVERLAY=$1;;
	--ex-only)         EX_FAST="yes";;
	--ex-gcc)          EX_GCC="yes";;
	--ex-gdb)          EX_GDB="yes";;
	--ex-pkg)          shift; XPKGS+=( "$1" );;
	--with-*)          eval $(set_withval $1);;
	--without-*)       eval $(set_withval $1);;
	-f|--force)        FORCE="yes";;
	-x)                SET_X="yes";;
	--show-target-cfg) SHOW_TARGET_CFG="yes";;
	--init-target)     INIT_TARGET_ONLY="yes";;
	--show-fail-log)   SHOW_FAIL_LOG="yes";;
	--show-repo-cfg)   show_repo_cfg "$2";;
	-P|--portage)      UOPTS="${UOPTS} $2"; shift;;
	-h|--help)         usage;;
	-*)                eerror "UNKNOWN OPTION: '$1'" ; usage 1;;
	*)                 parse_target $1;;
	esac
	shift
done

if [[ "${LLVM}" == "yes" ]] ; then
	WITH_HEADERS="yes"
	CROSSDEV_OVERLAY_CATEGORY_PREFIX="cross_llvm-"
    # To avoid bug reports
    [[ "${LPKG}" == "glibc" ]] && die "LLVM/Clang cannot currently compile glibc"
fi

CROSSDEV_OVERLAY_CATEGORY="${CROSSDEV_OVERLAY_CATEGORY_PREFIX}${CTARGET}"

[[ ${SET_X} == "yes" ]] && set -x
case ${ACTION} in
uninstall) uninstall; exit 0;;
esac

for _ in "${XPKGS[@]}"; do
	XVERS+=( "${DEFAULT_VER}" )
done

BVER=${BVER:-${DEFAULT_VER}}
GVER=${GVER:-${DEFAULT_VER}}
KVER=${KVER:-${DEFAULT_VER}}
LVER=${LVER:-${DEFAULT_VER}}
DVER=${DVER:-${DEFAULT_VER}}
STAGE=${STAGE:-${STAGE_DEFAULT}}
[[ -z ${CTARGET} ]] && usage 1
for with in HEADERS ; do
	var=WITH_${with} defvar=WITH_DEF_${with}
	[[ ${!var} == "COW" ]] && eval ${var}=${!defvar}
done

# ::gentoo has no stable 'mingw64-runtime' or 'newlib'.
# Let's default to 'latest' unless user specified something else.
if [[ ${LPKG} == "mingw64-runtime" && ${LVER} == "[stable]" ]]; then
	LVER="[latest]"
fi
if [[ ${LPKG} == "newlib" && ${LVER} == "[stable]" ]]; then
	LVER="[latest]"
fi

RVER="[latest]"

show_target_cfg() {
	local pkgs crosspkgs=()

	pkgs=(
		binutils B
		gcc G
		kernel K
		libc L
	)
	ex_gdb && pkgs+=( gdb D )

	if ex_pkgs ; then
		show_extra_pkg() {
			echo "ex_${XPKG}_category=${XCAT}"
			echo "ex_${XPKG}_pn=${XPKG}"
			crosspkgs+=( "ex_${XPKG}" )
		}
		for_each_extra_pkg show_extra_pkg
		echo "extrapkgs='${crosspkgs[*]}'"
	fi

	echo "arch=${TARCH}"
	echo "target=${CTARGET}"
	echo "category=${CROSSDEV_OVERLAY_CATEGORY}"
	while [[ ${#pkgs[@]} -gt 0 ]] ; do
		local pkg=${pkgs[0]}
		local v=${pkgs[1]}
		local vcat="${v}CAT"
		local vpkg="${v}PKG"
		local ocat="${pkg}_category"
		local opkg="${pkg}_pn"

		if [[ ${!vpkg} != "[none]" ]] ; then
			echo "${ocat}=${!vcat}"
			echo "${opkg}=${!vpkg}"
		else
			printf '%s=\n' "${ocat}" "${opkg}"
		fi
		crosspkgs+=( ${pkg} )
		pkgs=( ${pkgs[@]:2} )
	done
	echo "crosspkgs='${crosspkgs[*]}'"

	exit 0
}
[[ ${SHOW_TARGET_CFG} == "yes" ]] && show_target_cfg

setup_portage_vars
if [[ -z ${CROSSDEV_OVERLAY} ]] ; then
	eerror "You need to specify an output overlay.  Please use --ov-output, or consult"
	eerror "https://wiki.gentoo.org/wiki/Custom_repository for more details."
	exit 1
fi

if [[ ${HCHOST} == "${CTARGET}" ]] ; then
	eerror "Refusing to create a cross-compiler using the same"
	eerror "target name as your host utils."
	exit 1
fi

for hchost in "${HCHOSTS[@]}"; do
	if [[ ${hchost} == "${CTARGET}" ]] ; then
		eerror "Refusing to create a cross-compiler using the same"
		eerror "target name as your host utils."
		eerror "Consider using sys-devel/multilib-gcc-wrapper package."
		exit 1
	fi
done

# grab user settings
d="${CONFIGROOT}/crossdev/${CTARGET}"
for v in MULTILIB_ABIS USE BVER GVER KVER LVER DVER STAGE CFLAGS LDFLAGS ASFLAGS ; do
	if [[ -e ${d}/${v} ]] ; then
		# yes, quotes are needed in this instance (export $var="...")
		export ${v}="$(<"${d}"/${v})"
		einfo "Restoring user setting '${v}' to '${!v}'"
	fi
done
if [[ -e ${d}/env ]] ; then
	einfo "Restoring generic user env settings"
	source "${d}"/env
fi

# parse multilib settings until profiles are sane
load_multilib_env() {
	local var=$1
	# TODO: avoid eval+subshell and use subshell only. It will make error
	# handling simpler.
	local eval_result=failed
	eval $(
	# see what target to parse
	CTARGET=${!var}

	# clean the env in our subshell
	unset ${!CFLAGS_*} ${!CHOST_*} ${!CTARGET_*} ${!LDFLAGS_*} ${!LIBDIR_*}

	# ask multilib.eclass to tell us how things work
	inherit() { :; }
	for p in ${SEARCH_OVERLAYS} ${MAIN_REPO_PATH} ; do
		p+="/eclass/multilib.eclass"
		if [[ -e ${p} ]] ; then
			EAPI=7 . "${p}"
			break
		fi
	done
	unset DEFAULT_ABI
	if [[ ${MULTILIB_ABIS} == "default" ]] ; then
		unset MULTILIB_ABIS
		single_abi=true
	else
		single_abi=false
	fi
	multilib_env || die "die 'could not load multilib settings for ${var}'"
	${single_abi} && MULTILIB_ABIS=${DEFAULT_ABI}

	# output the desired env
	for v in ${!CFLAGS_*} ${!CHOST_*} ${!CTARGET_*} ${!LDFLAGS_*} ${!LIBDIR_*} ; do
		echo ${v}=\'${!v}\'
	done
	# output the variables that are not uniquely named
	[[ ${var} == "CTARGET" ]] && d='' || d='_'
	for v in MULTILIB_ABIS DEFAULT_ABI ; do
		echo ${d}${v}=\'${!v}\'
	done

	# make sure all ABIs have valid vars
	def_CFLAGS=
	def_LIBDIR="lib"
	def_LDFLAGS=
	for v in CFLAGS LIBDIR LDFLAGS ; do
		d="def_${v}"
		for a in ${MULTILIB_ABIS} ; do
			_v="${v}_${a}"
			[[ ${!_v+set} == "set" ]] && continue
			echo ${_v}=\'${!d}\'
		done
	done
	echo "eval_result='succeeded'"
	)
	[[ $eval_result = succeeded ]] || die "could not load multilib settings for '${var}'"
}
# Load settings for the host.
MULTILIB_ABIS=${HOST_ABI} load_multilib_env HCHOST
HOST_ABI=${_MULTILIB_ABIS}
# Load settings for the target.
load_multilib_env CTARGET

DEFAULT_ABI=${MULTILIB_ABIS%% *}
if [[ -z ${MULTILIB_USE} ]] ; then
	if [[ $(set -- ${MULTILIB_ABIS}; echo $#) -eq 1 ]] ; then
		MULTILIB_USE="no"
	else
		MULTILIB_USE="yes"
	fi
fi

#####################
### do the emerge ###
info() {
hr -
[[ "${LLVM}" == "yes" ]] && einfo "Using LLVM/Clang as cross compiler, experimental!"
einfo "crossdev version:      ${CROSSDEV_VER}"
einfo "Host Portage ARCH:     ${HARCH}"
einfo "Host Portage System:   ${HCHOST} (${HCHOSTS[*]})"
einfo "Target Portage ARCH:   ${TARCH}"
einfo "Target System:         ${CTARGET}"
einfo "Stage:                 ${STAGE} (${STAGE_DISP[${STAGE}]})"
einfo "USE=multilib:          ${MULTILIB_USE}"
[[ ${DEFAULT_ABI} != "${MULTILIB_ABIS}" ]] && def_out=" (default: ${DEFAULT_ABI})" || def_out=
einfo "Target ABIs:           ${MULTILIB_ABIS}${def_out}"
echo
ex_fast || {
is_s0 && {
[[ "${LLVM}" == "yes" ]] || einfo "binutils:              `pretty_atom ${BPKG}- ${BVER}`"
}
is_s1 && {
	if [[ "${LLVM}" == "yes" ]] ; then
		einfo "compiler-rt:           `pretty_atom ${RPKG}- ${RVER}`"
	else
		einfo "gcc:                   `pretty_atom ${GPKG}- ${GVER}`"
	fi
}
is_s2 && {
[[ ${KPKG} != "[none]" ]] && \
einfo "headers:               `pretty_atom ${KPKG}- ${KVER}`"
}
is_s3 && {
einfo "libc:                  `pretty_atom ${LPKG}- ${LVER}`"
}
}
ex_gcc && {
einfo "Extra: gcc pass:       DO IT"
}
ex_gdb && {
einfo "Extra: gdb:            DO IT"
}
ex_pkgs && {
einfo "Extra:                 ${XPKGS[*]}"
}
echo
einfo "CROSSDEV_OVERLAY:      ${CROSSDEV_OVERLAY}"
einfo "PORT_LOGDIR:           ${PORT_LOGDIR}"
einfo "PORTAGE_CONFIGROOT:    ${PORTAGE_CONFIGROOT}"
einfo "Portage flags:         ${UOPTS}"
hr
}
# avoid pipe since `einfo` will not use color :(
info
if [[ ${INIT_TARGET_ONLY} != "yes" ]] ; then
	(
	info
	emerge -v --info
	) >& "${PORT_LOGDIR}"/${CROSSDEV_OVERLAY_CATEGORY}-info.log || exit 1
fi

####################################
### Fix up portage files / paths ###

check_trailing_newline() { #267132
	[[ -e $1 ]] || return 0
	if [[ `tail -c 1 "$1" | wc -l` == *0* ]] ; then
		ewarn "Autofixing mangled file: $1"
		echo >> "$1"
	fi
}
_set_portage_file() {
	local pkg=$1 output=$2
	[[ ! -f ${output} ]] && output+="/${CROSSDEV_OVERLAY_CATEGORY}"
	[[ -e ${output} ]] && sed -i -e "/^${CROSSDEV_OVERLAY_CATEGORY}\/${pkg}/d" ${output}
	check_trailing_newline ${output}
	echo ${output}
}
set_keywords() {
	local pkg=$1 ver=$2 output
	[[ -z ${pkg} ]] && return 0
	output=$(_set_portage_file ${pkg} package.accept_keywords)

	if [[ ${ver} == "["*"]" ]] || [[ -z ${ver} ]] ; then
		local keywords=""
		case ${ver} in
			"[stable]") keywords="${TARCH}";;
			*)          keywords="${TARCH} ~${TARCH}";;
		esac
		[[ "${TARCH}" != "${HARCH}" ]] && keywords="${keywords} -${HARCH} -~${HARCH}"
		echo "${CROSSDEV_OVERLAY_CATEGORY}/${pkg} ${keywords}" >> ${output}
	else
		local op=$(ver_get_op "${ver}")
		if [[ -n ${op} ]] ; then
			# user has been explicit in the version they desire
			ver=$(ver_chop_op "${ver}")
			echo "${CROSSDEV_OVERLAY_CATEGORY}/${pkg} -*" >> ${output}
			echo "${op}${CROSSDEV_OVERLAY_CATEGORY}/${pkg}-${ver} * ~* **" >> ${output}
			if [[ ${ver} != "9999" ]] ; then
				# Disable live versions unless exactly requested.
				output=$(_set_portage_file ${pkg} package.mask)
				echo ">=${CROSSDEV_OVERLAY_CATEGORY}/${pkg}-9999" >> ${output}
			fi
		else
			echo "${CROSSDEV_OVERLAY_CATEGORY}/${pkg} * ~* **" >> ${output}
			output=$(_set_portage_file ${pkg} package.mask)
			echo ">${CROSSDEV_OVERLAY_CATEGORY}/${pkg}-${ver}" >> ${output}
		fi
	fi
}
set_use() {
	local pkg=$1 output use=${@:2}
	[[ -z ${use} ]] && return 0
	output=$(_set_portage_file ${pkg} package.use)
	echo "${CROSSDEV_OVERLAY_CATEGORY}/${pkg} ${use}" >> ${output}
}
set_use_force() {
	local pkg=$1 output use=${@:2}
	[[ -z ${use} ]] && return 0
	output=$(_set_portage_file ${pkg} profile/package.use.force)
	echo "${CROSSDEV_OVERLAY_CATEGORY}/${pkg} ${use}" >> ${output}
}
set_use_mask() {
	local pkg=$1 output use=${@:2}
	[[ -z ${use} ]] && return 0
	output=$(_set_portage_file ${pkg} profile/package.use.mask)
	echo "${CROSSDEV_OVERLAY_CATEGORY}/${pkg} ${use}" >> ${output}
}
set_links() {
	local cat=$1 pkg=$2 ovl=$3
	local s srcdir=${MAIN_REPO_PATH} d

	d="${CROSSDEV_OVERLAY}"/${CROSSDEV_OVERLAY_CATEGORY}/${pkg}
	# if auto searching and something is already set, leave it be
	if [[ -z ${ovl} ]] && [[ -e ${d} ]] ; then #211386 #347389
		einfo "leaving ${cat}/${pkg} in ${CROSSDEV_OVERLAY}"
		return
	fi

	rm -f "${d}"
	if [[ -e ${d} ]] ; then
		eerror "${d} still exists and isn't a symlink !?"
		exit 1
	fi
	for s in ${ovl} ${SEARCH_OVERLAYS} ; do
		if [[ -d ${s}/${cat}/${pkg} ]] ; then
			# Versions in an additional non-crossdev overlay may not have the right keywords.
			# e.g. suppose we have sys-devel/gcc in an overlay with just ~arch keywords.
			# (Or not be keyworded for our target arch at all!)
			local search_kw_string=

			case "${LVER}" in
				"[stable]")
					search_kw_string="[^~]${TARCH}"
					;;
				*)
					search_kw_string="${TARCH}"
					;;
			esac

			# TODO: Import eapi7-ver.eclass and prefer the best version
			# rather than just the first we find.
			if grep -qre "KEYWORDS=.*${search_kw_string}" "${s}/${cat}/${pkg}" ; then
				# ... so only skip if we found one which is at least stable.
				srcdir=${s}
				einfo "getting ${cat}/${pkg} from ${srcdir}"
				break
			fi
		fi
	done
	ln -s "${srcdir}"/${cat}/${pkg} "${d}"
}
set_env() {
	local l=$1 pkg=$2 env=$3 output
	shift ; shift

	# We have to ignore collisions in the build-id tree because it's
	# easy to create a cross-program that is the same.  For example,
	# if we try to build gdb for both arm-eabi and cross-arm-linux-gnueabi,
	# then these gdb's might have the same build-id and try to install
	# into the same paths.  Ignoring the collisions isn't great since
	# updating one can drop the symlink for another, but for now, it's
	# the best we've got without implementing reference counting on
	# installed paths in the PM.

	output="env/${CROSSDEV_OVERLAY_CATEGORY}/${pkg}.conf"
	cat <<-EOF > "${output}"
	SYMLINK_LIB=no
	COLLISION_IGNORE="\${COLLISION_IGNORE} /usr/lib/debug/.build-id"
	$(printf '%b' "${env}")
	$(printf '%b' "${AENV}")
	EOF

	# We need to differentiate between the host and target ABI just like
	# we have to handle the difference between CHOST and CTARGET.  For
	# the headers and library packages, we want the ABI to be the target.
	# For the compiler tools (as/ld/cc/gdb/etc...), we want the ABI to be
	# the host.
	local TARGET_ABI=$(set -- ${MULTILIB_ABIS}; echo $1)
	case ${l} in
	K|L)
		# Target packages.
		local ABI=${TARGET_ABI}
		# Use MULTILIB_ABIS & DEFAULT_ABI from env.
		;;
	*)
		# Host packages.
		cat <<-EOF >> "${output}"
		TARGET_ABI='${TARGET_ABI}'
		TARGET_MULTILIB_ABIS='${MULTILIB_ABIS}'
		TARGET_DEFAULT_ABI='${DEFAULT_ABI}'
		EOF
		local ABI=${HOST_ABI}
		local MULTILIB_ABIS=${ABI}
		local DEFAULT_ABI=${ABI}
		;;
	esac
	local v
	for v in ${!CFLAGS_*} ${!CHOST_*} ${!CTARGET_*} ${!LDFLAGS_*} ${!LIBDIR_*} ABI MULTILIB_ABIS DEFAULT_ABI ; do
		echo "${v}='${!v}'"
	done >> "${output}"

	output=$(_set_portage_file ${pkg} package.env)
	echo "${CROSSDEV_OVERLAY_CATEGORY}/${pkg} ${CROSSDEV_OVERLAY_CATEGORY}/${pkg}.conf" >> ${output}
    [[ "${LLVM}" == "yes" ]] && echo "${CROSSDEV_OVERLAY_CATEGORY}/${pkg} ${CROSSDEV_OVERLAY_CATEGORY}/llvm.conf" >> ${output}
}
set_portage() {
	local l=$1
	eval set -- \${${l}CAT} \${${l}PKG} \"\${${l}VER}\" \"\${${l}ENV}\" \"\${${l}OVL}\" \"\${${l}MASK}\" \"\${${l}FORCE}\"
	local cat=$1 pkg=$2 ver=$3 env=$4 ovl=$5 mask=$6 force=$7
	shift 7
	local use=$*

	[[ ${pkg} == "[none]" ]] && return 0

	case ${CTARGET} in
		# avr requires multilib, that provides
		# libgcc for all sub-architectures #378387
		avr*)
			mask+=" -multilib"
			force+=" multilib"
			;;
		nvptx*|*-newlib|*-elf|*-eabi)
			# nvptx: needs multilib because of https://gcc.gnu.org/legacy-ml/gcc-help/2020-01/msg00106.html.
			mask+=" -multilib"
			force+=" multilib"
			;;
		*)
			mask+=" multilib"
			;;
	esac

	set_use_mask ${pkg} "${mask}"
	set_use_force ${pkg} "${force}"
	set_keywords ${pkg} "${ver}"
	set_use ${pkg} ${use}
	set_links ${cat} ${pkg} "${ovl}"
	set_env ${l} ${pkg} "${env}"
}
set_metadata() {
	# for people who have eclasses spread over their overlays, generate
	# a layout.conf file so portage can find them.  this is a crapshoot
	# when diff overlay sources have conflicting eclasses, but nothing
	# we really can do about that.
	local meta=${CROSSDEV_OVERLAY}/metadata
	local repo_name
	local layout=${meta}/layout.conf
	local f d name masters thin_manifests="false"

	# See if this repo is already named.
	f="${CROSSDEV_OVERLAY}/profiles/repo_name"
	repo_name=$(cat "${f}" 2>/dev/null)
	: ${repo_name:="${CROSSDEV_OVERLAY_NAME}"}

	xmkdir -p "${meta}"
	if [[ -e ${layout} ]] ; then
		if ! grep -qs "^${AUTOGEN_TAG}" "${layout}" ; then
			einfo "leaving metadata/layout.conf alone in ${CROSSDEV_OVERLAY}"
			return
		fi

		# We are managing it, so blow it away
		rm -f "${layout}"
	fi

	# build up a list of possible repos where we can pull from
	for d in "${BOVL}" "${GOVL}" "${KOVL}" "${LOVL}" "${ROVL}" "${DOVL}" ${SEARCH_OVERLAYS} "${MAIN_REPO_PATH}" ; do
		[[ -z ${d} ]] && continue

		name=
		if [[ -e ${d}/metadata/layout.conf ]] ; then
			name=$(awk '$1 == "repo-name" { print $3 }' "${d}/metadata/layout.conf")
		fi
		if [[ -z ${name} && -e ${d}/profiles/repo_name ]] ; then
			name=$(<"${d}"/profiles/repo_name)
		fi
		[[ -z ${name} ]] && continue
		# Don't list ourselves as a master.
		[[ ${name} == "${repo_name}" ]] && continue

		# If this repo has an eclass dir, mark it as a master.
		# Note: portage reads the masters list in reverse order,
		#       so we have to prepare it the same way.
		if [[ -d ${d}/eclass ]] ; then
			has ${name} ${masters} || masters="${name} ${masters}"
		fi

		# If one of the overlays uses thin manifests, then turn it on
		if [[ -z ${this_manifests} ]] && has ${name} ${masters} && \
			sed \
				-e 's:#.*::' \
				-e 's:^[[:space:]]*::' \
				-e 's:[[:space:]]*$::' \
				-e 's:[[:space:]]*=[[:space:]]*:=:' \
				"${d}/metadata/layout.conf" 2>/dev/null | \
			gawk -F= '{
					if ($1 == "use-manifests") um = $2
					if ($1 == "thin-manifests") tm = $2
				}
				END {
					exit !(um != "false" && tm == "true")
				}'
		then
			einfo "enabling thin-manifests due to ${d}"
			this_manifests="use-manifests = true\nthin-manifests = true"
		fi
	done

	# write out that layout.conf!
	cat <<-EOF > "${layout}" || die "could not write ${layout}"
	${AUTOGEN_TAG}
	# Delete the above line if you want to manage this file yourself
	masters = ${masters% }
	repo-name = ${repo_name}
	$(printf '%b' "${this_manifests}")
	EOF

	if [[ ! -f "${CROSSDEV_OVERLAY}"/profiles/repo_name ]] ; then
		cat <<-EOF > "${CROSSDEV_OVERLAY}"/profiles/repo_name || die "could not write ${CROSSDEV_OVERLAY}/profiles/repo_name"
		${repo_name}
		EOF
	fi

	if [[ ! -f "${CROSSDEV_OVERLAY}"/profiles/eapi ]] ; then
		cat <<-EOF > "${CROSSDEV_OVERLAY}"/profiles/eapi || die "could not write ${CROSSDEV_OVERLAY}/profiles/eapi"
		8
		EOF
	fi

	# If there is no repos.conf entry for the output overlay, create one here
	if [[ -n ${CROSSDEV_OVERLAY_CREATE_REPOS_CONF} ]]; then
		cat <<-EOF > "${CROSSDEV_OVERLAY_CREATE_REPOS_CONF}" || die "could not create the repo conf"
		${AUTOGEN_TAG}
		[${repo_name}]
		location = ${CROSSDEV_OVERLAY}
		masters = ${masters% }
		auto-sync = no
		EOF
	fi
}

xmkdir -p "${CROSSDEV_OVERLAY}"/{${CROSSDEV_OVERLAY_CATEGORY},profiles}
f="${CROSSDEV_OVERLAY}"/profiles/categories
check_trailing_newline "${f}"
grep -qs "^${CROSSDEV_OVERLAY_CATEGORY}$" "${f}" \
	|| echo ${CROSSDEV_OVERLAY_CATEGORY} >> "${f}"

xmkdir -p "${CONFIGROOT}"
cd "${CONFIGROOT}" || die "wtf!?"
for f in package.{accept_keywords,env,mask,use} env/${CROSSDEV_OVERLAY_CATEGORY} profile/package.use.{force,mask} ; do
	[[ -f ${f} ]] && die "please convert ${CONFIGROOT}/${f} to a directory"
	xmkdir -p "${f}"
	rm -f "${f}/${CROSSDEV_OVERLAY_CATEGORY}"
done

pkglist=( K L )
if [[ ${LLVM} == "yes" ]] ; then
	pkglist+=( R C )
else
	pkglist+=( B G D )
fi
for pkg in ${pkglist[@]}; do
	set_portage ${pkg}
done

for_each_extra_pkg set_portage X
set_metadata

xmkdir -p "${EPREFIX}"/etc/revdep-rebuild
cat > "${EPREFIX}"/etc/revdep-rebuild/05${CROSSDEV_OVERLAY_CATEGORY} << EOF
# Generated by crossdev-${CROSSDEV_VER}
# Ignore ${CTARGET} root, https://bugs.gentoo.org/182601.
SEARCH_DIRS_MASK="${EPREFIX}/usr/${CTARGET}"
EOF

hr

#######################################
### Create links for helper scripts ###

xmkdir -p "${EPREFIX}"/usr/${CTARGET}
LLVM="${LLVM}" emerge-wrapper --target ${CTARGET} --init || exit 1

#############################################################
### Create directories usually created by sys-apps/baselayout
###
### Why we do that at all:
### For multilib-aware targets (ppc64, s390x, sparc64, x86_64), Gentoo
### normally uses libdir=lib64.
### For crossdev, it means /lib and /usr/lib does not get created at all
### but gcc relies on their presence by refering to =/lib64 as
### =/usr/lib/../lib64 when builds itself (see https://bugs.gentoo.org/652724)
###
### Thus we create non-symlinked layout early.
xmkdir -p "${EPREFIX}"/usr/${CTARGET}/lib
xmkdir -p "${EPREFIX}"/usr/${CTARGET}/usr/lib
###
### We need some special riscv sauce here similar as in baselayout. Ugly.
### step 1: set up all multilib libdirs
### step 2: set up the compat symlink of the default abi for non-multilib
case ${CTARGET} in
	riscv*)
			xmkdir -p "${EPREFIX}"/usr/${CTARGET}/lib64
			xmkdir -p "${EPREFIX}"/usr/${CTARGET}/lib64/lp64
			xmkdir -p "${EPREFIX}"/usr/${CTARGET}/lib64/lp64d
			xmkdir -p "${EPREFIX}"/usr/${CTARGET}/usr/lib64
			xmkdir -p "${EPREFIX}"/usr/${CTARGET}/usr/lib64/lp64
			xmkdir -p "${EPREFIX}"/usr/${CTARGET}/usr/lib64/lp64d
			xmkdir -p "${EPREFIX}"/usr/${CTARGET}/lib32
			xmkdir -p "${EPREFIX}"/usr/${CTARGET}/lib32/ilp32
			xmkdir -p "${EPREFIX}"/usr/${CTARGET}/lib32/ilp32d
			xmkdir -p "${EPREFIX}"/usr/${CTARGET}/usr/lib32
			xmkdir -p "${EPREFIX}"/usr/${CTARGET}/usr/lib32/ilp32
			xmkdir -p "${EPREFIX}"/usr/${CTARGET}/usr/lib32/ilp32d
			;;&
	riscv64*)
			rmdir "${EPREFIX}"/usr/${CTARGET}/lib64/${DEFAULT_ABI}
			ln -s . "${EPREFIX}"/usr/${CTARGET}/lib64/${DEFAULT_ABI}
			rmdir "${EPREFIX}"/usr/${CTARGET}/usr/lib64/${DEFAULT_ABI}
			ln -s . "${EPREFIX}"/usr/${CTARGET}/usr/lib64/${DEFAULT_ABI}
			;;
	riscv32*)
			rmdir "${EPREFIX}"/usr/${CTARGET}/lib32/${DEFAULT_ABI}
			ln -s ../lib "${EPREFIX}"/usr/${CTARGET}/lib32/${DEFAULT_ABI}
			rmdir "${EPREFIX}"/usr/${CTARGET}/usr/lib32/${DEFAULT_ABI}
			ln -s ../lib "${EPREFIX}"/usr/${CTARGET}/usr/lib32/${DEFAULT_ABI}
			;;
esac

# HOSTCC is used by linux-headers to compile fixdeps program for CBUILD
if [[ "${LLVM}" == "yes" ]] ; then
	cat <<-EOF > "${CONFIGROOT}/env/${CROSSDEV_OVERLAY_CATEGORY}/llvm.conf"
	AR=llvm-ar
	AS="${CTARGET}-clang -c"
	CC="${CTARGET}-clang"
	CROSS_COMPILE="${CTARGET}-"
	CXX="${CTARGET}-clang++"
	DLLTOOL=llvm-dlltool
	HOSTCC="${CC:=clang}"
	HOSTCXX="${CXX:=clang++}"
	LD=ld.lld
	LLVM=1
	NM=llvm-nm
	OBJCOPY=llvm-objcopy
	RANLIB=llvm-ranlib
	READELF=llvm-readelf
	STRIP=llvm-strip
	EOF
fi

#################
emerged_with_use() {
	local pkg=$1 use=$2
	grep -qs ${use} "${EPREFIX}"/var/db/pkg/${CROSSDEV_OVERLAY_CATEGORY}/${pkg}-*/USE
}
# Force package rebuild if any of passed USE-flag is set otherwise install package only if flag is missing.
# $1    - pkg
# $2... - USE flags to check
set_eopts_on_pkg_status() {
	local pkg=$1 flag
	shift

	for flag in "$@"; do
		if emerged_with_use "${pkg}" "${flag}"; then
			# Force rebuild
			EOPTS=${EOPTS_DEF}
			return
		fi
	done
	# Install if missing
	EOPTS=${EOPTS_UP}
}

doemerge() {
	local category="${CROSSDEV_OVERLAY_CATEGORY}"
	local pn=$1
	local atom="${category}/${pn}"

	[[ ${pn} == "[none]" ]] && return 0

	set_use ${pn} ${USE} $( [[ ${MULTILIB_USE} == "no" ]] && echo - )multilib

	[[ ${INIT_TARGET_ONLY} == "yes" ]] && return 0

	local logfile=${PORT_LOGDIR}/${category}
	[[ -z $2 ]] \
		&& logfile=${logfile}-${pn}.log \
		|| logfile=${logfile}-$2.log

	einfo "Log: ${logfile}"
	ebegin "Emerging ${CROSSDEV_OVERLAY_CATEGORY_PREFIX}${2:-${pn}}"

	if has -v ${UOPTS} || has -p ${UOPTS} || has -vp ${UOPTS} || has -pv ${UOPTS} ; then
		SHOW_FAIL_LOG="no"
		emerge ${atom} ${EOPTS} 2>&1 | tee "${logfile}"
	else
		emerge ${atom} ${EOPTS} >& "${logfile}"
	fi
	local _pipestatus=${PIPESTATUS[*]}
	if [[ "${_pipestatus// /}" -ne 0 ]] ; then
		[[ ${SHOW_FAIL_LOG} == "yes" ]] && cat "${logfile}"

		local d
		for d in "${PORTAGE_TMPDIR}"/portage/${atom}*/work/ ; do
			[[ -d ${d} ]] || continue
			pushd "${d}" >/dev/null
			mkdir -p ../temp # sanity!
			find -name config.log | \
				tar cf - --files-from=- | \
				xz > ../temp/${pn}-config.logs.tar.xz
			popd >/dev/null
		done
		xz -zkf "${logfile}"

		die_logs "${pn} failed :(" \
			"${logfile}.xz" \
			"${PORTAGE_TMPDIR}/portage/${atom}*/temp/${pn}-config.logs.tar.xz"
	fi
	eend 0
}

# We include the '-u' so that we don't re-emerge packages. Avoid
# using --nodeps as packages have more host depends nowadays (like
# gcc wanting updated mpfr/gmp).  Don't use --oneshot anymore to
# follow normal emerge behavior; people can pass the -1 to portage
# themselves if they want that.
EOPTS_DEF="${UOPTS}"
EOPTS_UP="${EOPTS_DEF} -u"
EOPTS=${EOPTS_UP}
# keep things like --ask from screwing us up
export EMERGE_DEFAULT_OPTS="--quiet-build=n"

# screw random strictness in cross-compilers
export FEATURES="${FEATURES} -stricter"

if [[ "${LLVM}" == "yes" ]]; then
	CLANG_CROSS_CFG_DIR=/etc/clang/cross
	[[ -d "${CLANG_CROSS_CFG_DIR}" ]] || mkdir -p "${CLANG_CROSS_CFG_DIR}"

	export CLANG_CROSS_CFG="${CLANG_CROSS_CFG_DIR}/${CTARGET}.cfg"
	# Force --unwindlib=none for now
	cat <<-EOF > "${CLANG_CROSS_CFG}"
	--rtlib=compiler-rt
	--sysroot=/usr/${CTARGET}
	--target=${CTARGET}
	--unwindlib=libunwind
	--stdlib=libc++
	-fuse-ld=lld
	EOF
	# Workaround until LLVM libc supports dynamic linking and SSP
	[[ "${LPKG}" == "llvm-libc" ]] && cat <<-EOF >> "${CLANG_CROSS_CFG}"
	-static
	-fno-stack-protector
	EOF
fi

# maybe someday this work, but that day != today
USE="${USE} -selinux"

if ! ex_fast ; then

	# stage 0: binutils
	if [[ "${LLVM}" == "yes" ]] ; then
		if ! portageq has_version / "llvm-core/llvm" ; then
			eerror "LLVM is not installed"
			exit 1
		fi

		best_ver=$(portageq best_version "${EPREFIX}"/ llvm-core/llvm)
		llvm_use=$(portageq metadata "${EPREFIX}"/ installed "${best_ver}" USE)

		llvm_arch=""
		case ${CTARGET} in
			x86*) llvm_arch="X86" ;;
			arm*) llvm_arch="ARM" ;;
			aarch64*) llvm_arch="AArch64" ;;
			riscv*) llvm_arch="RISCV" ;;
			mips*) llvm_arch="Mips" ;;
			loongarch*) llvm_arch="LoongArch" ;;
			powerpc*) llvm_arch="PowerPC" ;;
			sparc*) llvm_arch="Sparc" ;;
		esac

		supported_arch=0
		for flag in ${llvm_use} ; do
			if [[ ${flag} == llvm_targets_* ]] ; then
				target=${flag#llvm_targets_}
				[[ ${llvm_arch} == ${target} ]] && supported_arch=1
			fi
		done

		[[ ${supported_arch} -eq 0 ]] && die "Target architecture not supported by installed LLVM toolchain"

		USE="${CUSE}" doemerge ${CPKG}
	else
		USE="${BUSE}" doemerge ${BPKG}
	fi

	# stage1: bare C compiler
	if is_s1 ; then

		# first install headers if requested
		if with_headers ; then
			# install kernel headers (since the C library often uses them)
			USE="${KUSE} ${USE} headers-only" \
			CROSSCOMPILE_OPTS="headers-only" \
				doemerge ${KPKG} ${KPKG}-quick

			if [[ -n ${LPKG} ]] ; then
				# install C library headers
				# we have to use --nodeps as glibc itself might have
				# a dependency on newer gcc versions that we don't
				# care about at this point -- we aren't compiling yet
				USE="${LUSE} ${USE} ${LUSE_DISABLE} headers-only" \
				CROSSCOMPILE_OPTS="headers-only" \
				EOPTS="${EOPTS} --nodeps" \
					doemerge ${LPKG} ${LPKG}-headers
			fi
		fi

		# then finally get around to the C compiler
		if [[ "${LLVM}" == "yes" ]]; then
			# Compile compiler-rt
			USE="${RUSE} ${USE}" \
			CROSSCOMPILE_OPTS="" \
				doemerge ${RPKG}
		else
			USE="${GUSE} ${USE} ${GUSE_DISABLE_STAGE_1}" \
			CROSSCOMPILE_OPTS="" \
				doemerge ${GPKG} ${GPKG}-stage1
		fi

	fi

	# stage2: kernel headers
	if is_s2 ; then
		set_eopts_on_pkg_status ${KPKG} crosscompile_opts_headers-only headers-only

		USE="${KUSE} ${USE}" \
		CROSSCOMPILE_OPTS="" \
			doemerge ${KPKG}
	fi

	# stage3: full C library (headers/libs/etc...)
	if is_s3 ; then
		[[ -z ${LPKG} ]] && die "Invalid target '${CTARGET}': unknown libc"

		set_eopts_on_pkg_status ${LPKG} crosscompile_opts_headers-only headers-only

		USE="${LUSE} ${USE} ${LUSE_DISABLE}" \
		CROSSCOMPILE_OPTS="" \
			doemerge ${LPKG}
	fi

	# stage4: full compiler (C/C++/etc...)
	if is_s4 && [[ "${LLVM}" != "yes" ]] ; then
		EOPTS="${EOPTS_UP} --newuse" \
		USE="${GUSE} ${USE} ${GUSE_DISABLE_STAGE_2}" \
			doemerge ${GPKG} ${GPKG}-stage2
	fi
fi

# all the extra things (like debuggers)
EOPTS="${EOPTS_UP} --newuse"
ex_gcc && USE="${GUSE} ${USE}" doemerge ${GPKG} ${GPKG}-extra
ex_gdb && USE="${DUSE} ${USE}" doemerge ${DPKG}
if ex_pkgs ; then
	for pkg in "${XPKGS[@]}" ; do
		doemerge "${pkg#*/}"
	done
fi

exit 0
