summaryrefslogtreecommitdiff
path: root/eclass/zig-utils.eclass
diff options
context:
space:
mode:
Diffstat (limited to 'eclass/zig-utils.eclass')
-rw-r--r--eclass/zig-utils.eclass538
1 files changed, 538 insertions, 0 deletions
diff --git a/eclass/zig-utils.eclass b/eclass/zig-utils.eclass
new file mode 100644
index 000000000000..ca72e89ed6ea
--- /dev/null
+++ b/eclass/zig-utils.eclass
@@ -0,0 +1,538 @@
+# Copyright 2024 Gentoo Authors
+# Distributed under the terms of the GNU General Public License v2
+
+# @ECLASS: zig-utils.eclass
+# @MAINTAINER:
+# Eric Joldasov <bratishkaerik@landless-city.net>
+# @AUTHOR:
+# Eric Joldasov <bratishkaerik@landless-city.net>
+# @SUPPORTED_EAPIS: 8
+# @BLURB: Prepare Zig toolchain and set global variables
+# @DESCRIPTION:
+# Prepare Zig toolchain and set global variables.
+# Supports Zig 0.13+.
+# Does not set any default function, ebuilds must call them manually.
+# Generally, only "zig-utils_setup" is needed.
+#
+# Intended to be used by ebuilds that call "zig build-exe/lib/obj"
+# or "zig test" directly and by "dev-lang/zig".
+# For ebuilds with ZBS (Zig Build System), it's usually better
+# to inherit zig.eclass instead, as it has default phases-functions.
+
+if [[ -z ${_ZIG_UTILS_ECLASS} ]]; then
+_ZIG_UTILS_ECLASS=1
+
+case ${EAPI} in
+ 8) ;;
+ *) die "${ECLASS}: EAPI ${EAPI:-0} not supported" ;;
+esac
+
+inherit edo flag-o-matic linux-info
+
+# @ECLASS_VARIABLE: ZIG_SLOT
+# @PRE_INHERIT
+# @REQUIRED
+# @DESCRIPTION:
+# Zig slot that will be used in "ezig" function. Also, if
+# ZIG_OPTIONAL is empty, adds dev-lang/zig and dev-lang/zig-bin
+# dependency to BDEPEND. Must be >= "0.13".
+#
+# Example:
+# @CODE
+# ZIG_SLOT="0.13"
+# @CODE
+#
+# When a new Zig release occurs, it is advisable for maintainers to
+# check whether their ebuild supports that new version. If yes, they
+# they should bump ZIG_SLOT to the latest version; if not supported,
+# they need to patch any issues with new version and again bump
+# ZIG_SLOT. This helps to reduce dependencies on outdated Zig
+# versions.
+#
+# This policy of "1 exclusive Zig slot" will work until it
+# stabilizes enough (probably near 1.0), then it will be re-evaluated
+# and most likely changed to more common in other eclasses ZIG_MIN/
+# ZIG_MAX form.
+
+# @ECLASS_VARIABLE: ZIG_OPTIONAL
+# @PRE_INHERIT
+# @DEFAULT_UNSET
+# @DESCRIPTION:
+# If set to a non-empty value, all logic in zig-utils and
+# zig eclasses will be considered optional. No dependencies
+# will be added and no phase functions will be exported.
+#
+# For zig-utils.eclass users:
+# You have to add Zig dependency in your BDEPEND manually and call
+# at least "zig-utils_setup" before using "ezig".
+#
+# For zig.eclass users: see documentation in zig.eclass
+# instead.
+if [[ ! ${ZIG_OPTIONAL} ]]; then
+ BDEPEND="
+ || (
+ dev-lang/zig:${ZIG_SLOT}
+ dev-lang/zig-bin:${ZIG_SLOT}
+ )
+ "
+fi
+
+# @ECLASS_VARIABLE: ZIG_TARGET
+# @DEFAULT_UNSET
+# @DESCRIPTION:
+# Zig target tuple to use. Has the following format:
+# arch-os[.os_version_range]-abi[.abi_version]
+# Can be passed as:
+# * "-target " option in "zig test" or "zig build-exe/lib/obj",
+# * "-Dtarget=" option in "zig build"
+# (if project uses "std.Build.standardTargetOptions").
+#
+# Can be set by user in make.conf. If not set, then auto-generated by
+# "zig-utils_setup".
+#
+# Example:
+# @CODE
+# # Autodetected by Zig:
+# ZIG_TARGET="native"
+# # Machine running Linux x86_64 system, with glibc:
+# ZIG_TARGET="x86_64-linux-gnu"
+# # Similar to above, but versions are passed explicitly:
+# ZIG_TARGET="x86_64-linux.6.1.12...6.6.16-gnu.2.38"
+# # Machine running Linux PPC64 little-endian system, with musl
+# ZIG_TARGET="powerpc64le-linux-musl"
+# @CODE
+#
+# Note for eclass users: it is discouraged to overwrite ZIG_TARGET
+# value by ebuilds. In most cases, if you need to hardcode value for
+# -Dtarget, it's better to change "build.zig" code instead to use
+# appropriate values. For example, if some build-time executable
+# intented for host is compiled for cross-platform target, change in
+# build.zig "target" for that executable to be "b.graph.host".
+#
+# In rare cases, if you really need to hardcode ZIG_TARGET, use this
+# syntax before calling `zig-utils_setup` (or `zig_pkg_setup`) to
+# allow user override:
+# @CODE
+# pkg_setup() {
+# : "${ZIG_TARGET:=aarch64-freestanding-none}"
+# zig_pkg_setup
+# }
+# @CODE
+
+# @ECLASS_VARIABLE: ZIG_CPU
+# @DEFAULT_UNSET
+# @DESCRIPTION:
+# Zig target CPU and features to use. Has the following format:
+# family_name(\+enable_feature|\-disable_feature)*
+# Can be passed as:
+# * "-mcpu " option in "zig test" or "zig build-exe/lib/obj",
+# * "-Dcpu=" option in "zig build"
+# (if project uses "std.Build.standardTargetOptions").
+#
+# Can be set by user in make.conf. If not set, then auto-generated by
+# "zig-utils_setup".
+#
+# Example:
+# @CODE
+# # Autodetected by Zig:
+# ZIG_CPU="native"
+# # AMD Zen 2 processor
+# ZIG_CPU="znver2"
+# # x86_64 processor, X87 support enabled, SSE2 support disabled
+# ZIG_CPU="x86_64+x87-sse2"
+# @CODE
+#
+# Note for eclass users: it is discouraged to overwrite ZIG_CPU
+# value by ebuilds. In most cases, if you need to hardcode value for
+# -Dcpu, it's better to change "build.zig" code instead to use
+# appropriate values. For example, if some build-time executable
+# intented for host is compiled for cross-platform target, change in
+# build.zig "target" for that executable to be "b.graph.host".
+#
+# In rare cases, if you really need to hardcode ZIG_CPU, use this
+# syntax before calling `zig-utils_setup` (or `zig_pkg_setup`) to
+# allow user override:
+# @CODE
+# pkg_setup() {
+# : "${ZIG_CPU:=apple_m1}"
+# zig_pkg_setup
+# }
+# @CODE
+
+# @ECLASS_VARIABLE: ZIG_EXE
+# @OUTPUT_VARIABLE
+# @DESCRIPTION:
+# Absolute path to the used Zig executable.
+# Set by "zig-utils_setup"/"zig-utils_find_installation".
+#
+# Please note that when passing one flag several times with different
+# values:
+# * (only "zig build") in "-Dbar=false -Dbar" form:
+# errors due to conflict of flags,
+# * (only "zig build") in "-Dbar=false -Dbar=true" form:
+# "bar" becomes a list, which is likely not what you want,
+# * in "-fbar -fno-bar" form:
+# latest value overwrites values before.
+# Example above shows only boolean option, but it is same with other
+# types of options (enums, "std.zig.BuildId", "std.SemanticVersion",
+# integers, strings, etc.).
+
+# @ECLASS_VARIABLE: ZIG_VER
+# @OUTPUT_VARIABLE
+# @DESCRIPTION:
+# Zig version as reported in dev-lang/zig-${PV} PV part.
+# Set by "zig-utils_setup"/"zig-utils_find_installation".
+#
+# Example:
+# @CODE
+# 0.13.0
+# @CODE
+
+# @FUNCTION: zig-utils_c_env_to_zig_target
+# @USAGE: <C-style target tuple> <CFLAGS>
+# @DESCRIPTION:
+# Translates C-style target tuple (like CHOST) and CFLAGS to Zig-style
+# target tuple. For full information "zig-utils_c_env_to_zig_cpu" is
+# needed, because some information is located in different places in C
+# and Zig, for example:
+# * Moved from C target to Zig CPU: x86 and ARM families,
+# * Moved from CFLAGS to Zig tuple: ARM Thumb mode.
+#
+# Mostly used during cross-compilation to get target triple if user
+# did not set ZIG_TARGET variable, and always during bootstraping Zig.
+#
+# See ZIG_TARGET description for more information.
+zig-utils_c_env_to_zig_target() {
+ if [[ ${#} -ne 2 ]]; then
+ die "${FUNCNAME[0]}: expected 2 arguments, got ${#}"
+ fi
+ local c_tuple="${1}"
+ local c_arch="${c_tuple%%-*}"
+ local c_abi="${c_tuple##*-}"
+
+ local c_flags="${2}"
+ local c_flags_march="$(CFLAGS="${c_flags}" get-flag march)"
+
+ local arch os abi
+
+ case "${c_arch}" in
+ i?86) arch=x86;;
+ arm64) arch=aarch64;;
+ arm*)
+ if [[ "${c_flags_march}" == *-m ]]; then
+ arch=thumb
+ else
+ arch=arm
+ fi
+
+ if [[ "${c_arch}" == *eb ]]; then
+ arch+="eb"
+ fi
+ ;;
+ *) arch="${c_arch}";;
+ esac
+
+ case "${c_tuple}" in
+ *-linux*) os=linux;;
+ *-apple*) os=macos;;
+ esac
+
+ case "${c_abi}" in
+ darwin*) abi=none;;
+ *) abi="${c_abi}";;
+ esac
+
+ echo "${arch}-${os}-${abi}"
+}
+
+# @FUNCTION: zig-utils_c_env_to_zig_cpu
+# @USAGE: <C-style target tuple> <CFLAGS>
+# @DESCRIPTION:
+# Translates C-style target tuple (like CHOST) and CFLAGS to Zig-style
+# target CPU and features. For full information
+# "zig-utils_c_env_to_zig_target" is needed, because some information
+# is located in different places in C and Zig, for example:
+# * Moved from C target to Zig CPU: x86 and ARM families,
+# * Moved from CFLAGS to Zig tuple: ARM Thumb mode.
+#
+# Used to get target CPU if user did not set ZIG_CPU variable.
+#
+# See ZIG_CPU description for more information.
+zig-utils_c_env_to_zig_cpu() {
+ if [[ ${#} -ne 2 ]]; then
+ die "${FUNCNAME[0]}: expected 2 arguments, got ${#}"
+ fi
+ local c_tuple="${1}"
+ local c_arch="${c_tuple%%-*}"
+
+ local c_flags="${2}"
+ local c_flags_mabi="$(CFLAGS="${c_flags}" get-flag mabi)"
+ local c_flags_march="$(CFLAGS="${c_flags}" get-flag march)"
+ local c_flags_mcpu="$(CFLAGS="${c_flags}" get-flag mcpu)"
+ local c_flags_mfpu="$(CFLAGS="${c_flags}" get-flag mfpu)"
+
+ local base_cpu features=""
+
+ case "${c_arch}" in
+ x86_64 | i?86)
+ local c_cpu="${c_flags_march}"
+ case "${c_cpu}" in
+ "") base_cpu="${c_arch}";;
+ *) base_cpu="${c_cpu//[-.]/_}";;
+ esac
+ ;;
+ aarch64 | aarch64_be | arm*)
+ local c_cpu="${c_flags_mcpu}"
+ case "${c_cpu}" in
+ "") base_cpu=generic;;
+ *) base_cpu="${c_cpu//[-.]/_}";;
+ esac
+
+ case "${c_flags_march}" in
+ "") ;;
+ armv*)
+ local c_arm_family="${c_flags_march##arm}"
+ c_arm_family="${c_arm_family//./_}"
+ c_arm_family="${c_arm_family//-/}"
+ features+="+${c_arm_family}"
+ ;;
+ *) features+="+${c_flags_march}";;
+ esac
+
+ if [[ "${c_arch}" != aarch64* && "${c_arch}" != arm64 ]]; then
+ if [[ "${c_flags_mfpu}" == crypto-* ]]; then
+ c_flags_mfpu="${c_flags_mfpu##crypto-}"
+ features+="+crypto"
+ fi
+ if [[ "${c_flags_mfpu}" == neon-* ]]; then
+ c_flags_mfpu="${c_flags_mfpu##neon-}"
+ features+="+neon"
+ fi
+
+ case "${c_flags_mfpu}" in
+ "" | auto) ;;
+ neon) features+="+neon";;
+ fp16) features+="+fp16";;
+ fp-armv8) features+="+fp_armv8";;
+
+ vfp | vfpv2) features+="+vfp2";;
+
+ vfp3 | vfpv3) features+="+vfp3";;
+ vfpv3-fp16) features+="+vfp3sp";;
+ vfpv3-d16) features+="+vfp3d16";;
+ vfpv3-d16-fp16) features+="+vfp3d16sp";;
+ vfpv3xd) features+="+vfp3d16sp";;
+ vfpv3xd-fp16) features+="+vfp3d16sp+fp16";;
+
+ vfpv4) features+="+vfp4";;
+ vfpv4-fp16) features+="+vfp4sp";;
+ vfpv4-d16) features+="+vfp4d16";;
+ fpv4-sp-fp16) features+="+vfp4d16sp";;
+
+ fpv5-d16) features+="+fp_armv8d16+fp64";;
+ *) die -n "Unknown ARM FPU: ${c_flags_mfpu}";;
+ esac
+
+ local is_softfloat="$(CTARGET="${c_tuple}" tc-tuple-is-softfloat)"
+ case "${is_softfloat}" in
+ only | yes) features+="+soft_float";;
+ softfp | no) features+="-soft_float";;
+ *) die -n "tc-tuple-is-softfloat returned unexpected value: ${is_softfloat}"
+ esac
+ fi
+ ;;
+ riscv32 | riscv64)
+ local c_cpu="${c_flags_mcpu}"
+ case "${c_cpu}" in
+ "")
+ case "${c_arch}" in
+ riscv32) base_cpu=generic_rv32;;
+ riscv64) base_cpu=generic_rv64;;
+ esac
+ ;;
+ *) base_cpu="${c_cpu//[-.]/_}";;
+ esac
+
+ local base_isa="${c_flags_march:0:4}"
+ local extensions="${c_flags_march:4}"
+
+ case "${base_isa}" in
+ "" | rv32 | rv64) ;;
+ *) die -n "Unknown RISC-V architecture: ${base_isa}";;
+ esac
+
+ local extension
+ while read -n 1 extension; do
+ case "${extension}" in
+ "") ;;
+ g) features+="+i+m+a+f+d+zicsr+zifencei";;
+ _) die -n "Can't translate multi-letter RISC-V extensions yet";;
+ *) features+="+${extension}";;
+ esac
+ done <<< "${extensions}"
+
+ case "${c_flags_mabi}" in
+ ilp32d | lp64d) features+="+d";;
+ ilp32e | lp64e) features+="+e";;
+ ilp32f | lp64f) features+="+f";;
+ "" | ilp32 | lp64) ;;
+ *) die -n "Unknown RISC-V ABI: ${c_flags_mabi}";;
+ esac
+ ;;
+ loongarch64)
+ local c_cpu="${c_flags_march}"
+ case "${c_cpu}" in
+ "") base_cpu=generic_la64;;
+ *) base_cpu="${c_cpu//[-.]/_}";;
+ esac
+
+ case "${c_flags_mabi}" in
+ lp64d) features+="+d";;
+ lp64f) features+="+f";;
+ lp64s | "") ;;
+ *) die -n "Unknown LoongArch ABI: ${c_flags_mabi}";;
+ esac
+ ;;
+ powerpc | powerpcle | powerpc64 | powerpc64le)
+ local c_cpu="${c_flags_mcpu}"
+ case "${c_cpu}" in
+ "")
+ case "${c_arch}" in
+ powerpc | powerpcle) base_cpu=ppc;;
+ powerpc64 | powerpc64le) base_cpu=ppc64;;
+ esac
+ ;;
+ G*) base_cpu="${c_cpu//G/g}";;
+ powerpcle) base_cpu=ppc;;
+ powerpc*) base_cpu="${c_cpu//powerpc/ppc}";;
+ power*) base_cpu="${c_cpu//power/pwr}";;
+ *) base_cpu="${c_cpu//[-.]/_}";;
+ esac
+ ;;
+ *) base_cpu=generic;;
+ esac
+
+ echo "${base_cpu}${features}"
+}
+
+# @FUNCTION: zig-utils_find_installation
+# @DESCRIPTION:
+# Detects suitable Zig installation and sets ZIG_VER and ZIG_EXE
+# variables.
+#
+# See ZIG_EXE and ZIG_VER descriptions for more information.
+zig-utils_find_installation() {
+ # Adapted from https://github.com/gentoo/gentoo/pull/28986
+ # Many thanks to Florian Schmaus (Flowdalic)!
+
+ [[ -n "${ZIG_SLOT}" ]] || die "${FUNCNAME[0]}: ZIG_SLOT must be set"
+ if ver_test "${ZIG_SLOT}" -lt "0.13"; then
+ die "${ECLASS}: ZIG_SLOT must be >= 0.13, found ${ZIG_SLOT}"
+ fi
+
+ einfo "Searching Zig ${ZIG_SLOT}..."
+
+ local zig_supported_versions=(
+ "9999"
+ "0.13.1"
+ "0.13.0"
+ )
+
+ local base_path="${BROOT}/usr/bin"
+
+ local selected_path selected_ver
+ for selected_ver in "${zig_supported_versions[@]}"; do
+ # Check if candidate satisfies ZIG_SLOT condition.
+ if [[ "${selected_ver}" != "${ZIG_SLOT}"* ]]; then
+ continue
+ fi
+
+ # Prefer "dev-lang/zig" over "dev-lang/zig-bin"
+ local candidate_path
+ for candidate_path in "${base_path}"/zig{,-bin}-"${selected_ver}"; do
+ if [[ -x "${candidate_path}" ]]; then
+ selected_path="${candidate_path}"
+ break 2
+ fi
+ done
+ done
+
+ if [[ -z "${selected_path}" ]]; then
+ die "Could not find (suitable) Zig at \"${base_path}\""
+ fi
+
+ declare -g ZIG_EXE="${selected_path}"
+ declare -g ZIG_VER="${selected_ver}"
+ # Sanity check, comment from upstream:
+ # > Check libc++ linkage to make sure Zig was built correctly,
+ # > but only for "env" and "version" to avoid affecting the
+ # > startup time for build-critical commands
+ # > (check takes about ~10 μs)
+ "${ZIG_EXE}" version > /dev/null ||
+ die "Sanity check failed for \"${ZIG_EXE}\""
+}
+
+# @FUNCTION: zig-utils_setup
+# @DESCRIPTION:
+# Checks if running Linux kernel version is supported by Zig.
+# Populates ZIG_TARGET, ZIG_CPU, ZIG_EXE and ZIG_VER global
+# variables with detected values, or, if user set them already,
+# leaves as-is.
+zig-utils_setup() {
+ # Should be first because it sets ZIG_VER which might be used
+ # in the future when setting ZIG_TARGET and ZIG_CPU variables
+ # for incompatible versions.
+ if [[ -z "${ZIG_EXE}" ]]; then
+ zig-utils_find_installation
+ fi
+
+ : "${ZIG_CPU:=$(zig-utils_c_env_to_zig_cpu "${CHOST}" "${CFLAGS}")}"
+ if tc-is-cross-compiler; then
+ : "${ZIG_TARGET:=$(zig-utils_c_env_to_zig_target "${CHOST}" "${CFLAGS}")}"
+ else
+ : "${ZIG_TARGET:=native}"
+ fi
+ declare -g ZIG_CPU ZIG_TARGET
+
+ einfo "ZIG_EXE: \"${ZIG_EXE}\""
+ einfo "ZIG_VER: ${ZIG_VER}"
+ einfo "ZIG_TARGET: ${ZIG_TARGET}"
+ einfo "ZIG_CPU: ${ZIG_CPU}"
+}
+
+# @FUNCTION: ezig
+# @USAGE: [<args>...]
+# @DESCRIPTION:
+# Runs ZIG_EXE with supplied arguments. Dies if ZIG_EXE is not set or
+# if command exits with error. Respects `nonfatal`.
+#
+# Always disables progress tree. By default enables ANSI escape codes
+# (colors, etc.), user can set NO_COLOR environment variable to
+# disable them.
+#
+# Note that color support also determines how compile errors will be
+# printed: source code lines and reference traces are not available
+# when colors are disabled.
+ezig() {
+ # Sync description above and comments below with upstream's
+ # "std.io.tty.detectConfig".
+ debug-print-function "${FUNCNAME[0]}" "${@}"
+
+ if [[ -z "${ZIG_EXE}" ]] ; then
+ die "${FUNCNAME[0]}: ZIG_EXE is not set. Was 'zig-utils_setup' called before using ezig?"
+ fi
+
+ # Progress tree is helpful indicator in TTY, but unfortunately
+ # they make Portage logs harder to read in plaintext.
+ #
+ # We don't have global toggle for all Zig commands to disable
+ # progress tree, however we can emulate this using 2 steps.
+
+ # Disable progress tree and colors. Errors are now less detailed.
+ local -x TERM=dumb
+ # Re-enable colors. Errors are now yet again detailed for reading.
+ local -x CLICOLOR_FORCE=1
+ # User's NO_COLOR has more priority and can disable colors again.
+ "${ZIG_EXE}" "${@}" || die -n "Failed to run command: ${ZIG_EXE} ${@}"
+}
+fi