You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
synctv/script/build.sh

980 lines
35 KiB
Bash

#!/bin/bash
set -e
# Color definitions
readonly COLOR_RED='\033[0;31m'
readonly COLOR_GREEN='\033[0;32m'
readonly COLOR_YELLOW='\033[0;33m'
readonly COLOR_BLUE='\033[0;34m'
readonly COLOR_PURPLE='\033[0;35m'
readonly COLOR_CYAN='\033[0;36m'
readonly COLOR_LIGHT_GRAY='\033[0;37m'
readonly COLOR_DARK_GRAY='\033[1;30m'
readonly COLOR_LIGHT_RED='\033[1;31m'
readonly COLOR_LIGHT_GREEN='\033[1;32m'
readonly COLOR_RESET='\033[0m'
# Default values
readonly DEFAULT_SOURCE_DIR="$(pwd)"
readonly DEFAULT_RESULT_DIR="${DEFAULT_SOURCE_DIR}/build"
readonly DEFAULT_BUILD_CONFIG="${DEFAULT_SOURCE_DIR}/build.config.sh"
readonly DEFAULT_CGO_ENABLED="1"
readonly DEFAULT_CC="gcc"
readonly DEFAULT_CXX="g++"
readonly DEFAULT_CGO_CROSS_COMPILER_DIR="${DEFAULT_SOURCE_DIR}/cross"
readonly DEFAULT_CGO_FLAGS="-O2 -g0 -pipe"
readonly DEFAULT_CGO_LDFLAGS="-s"
readonly DEFAULT_LDFLAGS="-s -w"
readonly DEFAULT_CGO_DEPS_VERSION="v0.4.6"
1 year ago
readonly DEFAULT_TTY_WIDTH="40"
# Go environment variables
readonly GOHOSTOS="$(go env GOHOSTOS)"
readonly GOHOSTARCH="$(go env GOHOSTARCH)"
readonly GOHOSTPLATFORM="${GOHOSTOS}/${GOHOSTARCH}"
# --- Function Declarations ---
# Prints help information about build configuration.
function printBuildConfigHelp() {
echo -e "${COLOR_YELLOW}You can customize the build configuration using the following functions (defined in ${DEFAULT_BUILD_CONFIG}):${COLOR_RESET}"
echo -e " ${COLOR_LIGHT_GREEN}parseDepArgs${COLOR_RESET} - Parse dependency arguments."
echo -e " ${COLOR_LIGHT_GREEN}printDepHelp${COLOR_RESET} - Print dependency help information."
echo -e " ${COLOR_LIGHT_GREEN}printDepEnvHelp${COLOR_RESET} - Print dependency environment variable help."
echo -e " ${COLOR_LIGHT_GREEN}initDepPlatforms${COLOR_RESET} - Initialize dependency platforms."
echo -e " ${COLOR_LIGHT_GREEN}initDep${COLOR_RESET} - Initialize dependencies."
}
# Prints help information about environment variables.
function printEnvHelp() {
echo -e "${COLOR_CYAN}Environment Variables:${COLOR_RESET}"
echo -e " ${COLOR_CYAN}SOURCE_DIR${COLOR_RESET} - Set the source directory (default: ${DEFAULT_SOURCE_DIR})."
echo -e " ${COLOR_CYAN}RESULT_DIR${COLOR_RESET} - Set the build result directory (default: ${DEFAULT_RESULT_DIR})."
echo -e " ${COLOR_CYAN}BUILD_CONFIG${COLOR_RESET} - Set the build configuration file (default: ${DEFAULT_BUILD_CONFIG})."
echo -e " ${COLOR_CYAN}BIN_NAME${COLOR_RESET} - Set the binary name (default: source directory basename)."
echo -e " ${COLOR_CYAN}PLATFORM${COLOR_RESET} - Set the target platform(s) (default: host platform, supports: all, linux, linux/arm*, ...)."
echo -e " ${COLOR_CYAN}DISABLE_MICRO${COLOR_RESET} - Disable building micro variants."
echo -e " ${COLOR_CYAN}CGO_ENABLED${COLOR_RESET} - Enable or disable CGO (default: ${DEFAULT_CGO_ENABLED})."
echo -e " ${COLOR_CYAN}HOST_CC${COLOR_RESET} - Set the host C compiler (default: ${DEFAULT_CC})."
echo -e " ${COLOR_CYAN}HOST_CXX${COLOR_RESET} - Set the host C++ compiler (default: ${DEFAULT_CXX})."
echo -e " ${COLOR_CYAN}FORCE_CC${COLOR_RESET} - Force the use of a specific C compiler."
echo -e " ${COLOR_CYAN}FORCE_CXX${COLOR_RESET} - Force the use of a specific C++ compiler."
echo -e " ${COLOR_CYAN}*_ALLOWED_PLATFORM${COLOR_RESET} - Set allowed platforms for a specific OS (e.g., LINUX_ALLOWED_PLATFORM=\"linux/amd64\")."
echo -e " ${COLOR_CYAN}CGO_*_ALLOWED_PLATFORM${COLOR_RESET} - Set allowed platforms for CGO for a specific OS (e.g., CGO_LINUX_ALLOWED_PLATFORM=\"linux/amd64\")."
echo -e " ${COLOR_CYAN}GH_PROXY${COLOR_RESET} - Set the GitHub proxy mirror (e.g., https://mirror.ghproxy.com/)."
if declare -f printDepEnvHelp >/dev/null; then
echo -e "${COLOR_LIGHT_GRAY}$(getSeparator)${COLOR_RESET}"
echo -e "${COLOR_CYAN}Dependency Environment Variables:${COLOR_RESET}"
printDepEnvHelp
fi
}
# Prints help information about command-line arguments.
function printHelp() {
echo -e "${COLOR_BLUE}Usage:${COLOR_RESET}"
echo -e " $(basename "$0") [options]"
echo -e ""
echo -e "${COLOR_BLUE}Options:${COLOR_RESET}"
echo -e " ${COLOR_BLUE}-h, --help${COLOR_RESET} - Display this help message."
echo -e " ${COLOR_BLUE}-eh, --env-help${COLOR_RESET} - Display help information about environment variables."
echo -e " ${COLOR_BLUE}--disable-cgo${COLOR_RESET} - Disable CGO support."
echo -e " ${COLOR_BLUE}--source-dir=<dir>${COLOR_RESET} - Specify the source directory (default: ${DEFAULT_SOURCE_DIR})."
echo -e " ${COLOR_BLUE}--more-go-cmd-args='<args>'${COLOR_RESET} - Pass additional arguments to the 'go build' command."
echo -e " ${COLOR_BLUE}--disable-micro${COLOR_RESET} - Disable building micro architecture variants."
echo -e " ${COLOR_BLUE}--ldflags='<flags>'${COLOR_RESET} - Set linker flags (default: \"${DEFAULT_LDFLAGS}\")."
echo -e " ${COLOR_BLUE}--platforms=<platforms>${COLOR_RESET} - Specify target platform(s) (default: host platform, supports: all, linux, linux/arm*, ...)."
echo -e " ${COLOR_BLUE}--result-dir=<dir>${COLOR_RESET} - Specify the build result directory (default: ${DEFAULT_RESULT_DIR})."
echo -e " ${COLOR_BLUE}--tags='<tags>'${COLOR_RESET} - Set build tags."
echo -e " ${COLOR_BLUE}--show-all-targets${COLOR_RESET} - Display all supported target platforms."
echo -e " ${COLOR_BLUE}--github-proxy-mirror=<url>${COLOR_RESET} - Use a GitHub proxy mirror (e.g., https://mirror.ghproxy.com/)."
echo -e " ${COLOR_BLUE}--force-gcc=<path>${COLOR_RESET} - Force the use of a specific C compiler."
echo -e " ${COLOR_BLUE}--force-g++=<path>${COLOR_RESET} - Force the use of a specific C++ compiler."
echo -e " ${COLOR_BLUE}--host-cc=<path>${COLOR_RESET} - Specify the host C compiler (default: ${DEFAULT_CC})."
echo -e " ${COLOR_BLUE}--host-cxx=<path>${COLOR_RESET} - Specify the host C++ compiler (default: ${DEFAULT_CXX})."
if declare -f printDepHelp >/dev/null; then
echo -e "${COLOR_PURPLE}$(getSeparator)${COLOR_RESET}"
echo -e "${COLOR_PURPLE}Dependency Options:${COLOR_RESET}"
printDepHelp
fi
echo -e "${COLOR_DARK_GRAY}$(getSeparator)${COLOR_RESET}"
printBuildConfigHelp
}
# Sets a variable to a default value if it's not already set.
# Arguments:
# $1: Variable name.
# $2: Default value.
function setDefault() {
local var_name="$1"
local default_value="$2"
[[ -z "${!var_name}" ]] && eval "${var_name}=\"${default_value}\"" || true
}
# Appends tags to the TAGS variable.
# Arguments:
# $1: Tags to append.
function addTags() {
[[ -n "${1}" ]] && TAGS="${TAGS} ${1}" || true
}
# Appends linker flags to the LDFLAGS variable.
# Arguments:
# $1: Linker flags to append.
function addLDFLAGS() {
[[ -n "${1}" ]] && LDFLAGS="${LDFLAGS} ${1}" || true
}
# Appends build arguments to the BUILD_ARGS variable.
# Arguments:
# $1: Build arguments to append.
function addBuildArgs() {
[[ -n "${1}" ]] && BUILD_ARGS="${BUILD_ARGS} ${1}" || true
}
# Fixes and validates command-line arguments and sets default values.
function fixArgs() {
setDefault "SOURCE_DIR" "${DEFAULT_SOURCE_DIR}"
source_dir="$(cd "${SOURCE_DIR}" && pwd)"
setDefault "BIN_NAME" "$(basename "${SOURCE_DIR}")"
setDefault "RESULT_DIR" "${DEFAULT_RESULT_DIR}"
mkdir -p "${RESULT_DIR}"
result_dir="$(cd "${RESULT_DIR}" && pwd)"
echo -e "${COLOR_BLUE}Source directory: ${COLOR_GREEN}${source_dir}${COLOR_RESET}"
echo -e "${COLOR_BLUE}Build result directory: ${COLOR_GREEN}${result_dir}${COLOR_RESET}"
setDefault "CGO_CROSS_COMPILER_DIR" "$DEFAULT_CGO_CROSS_COMPILER_DIR"
mkdir -p "${CGO_CROSS_COMPILER_DIR}"
cgo_cross_compiler_dir="$(cd "${CGO_CROSS_COMPILER_DIR}" && pwd)"
setDefault "PLATFORMS" "${GOHOSTPLATFORM}"
setDefault "DISABLE_MICRO" ""
setDefault "HOST_CC" "${DEFAULT_CC}"
setDefault "HOST_CXX" "${DEFAULT_CXX}"
setDefault "FORCE_CC" ""
setDefault "FORCE_CXX" ""
setDefault "GH_PROXY" ""
setDefault "TAGS" ""
setDefault "LDFLAGS" "${DEFAULT_LDFLAGS}"
setDefault "BUILD_ARGS" ""
setDefault "CGO_DEPS_VERSION" "${DEFAULT_CGO_DEPS_VERSION}"
}
# Checks if CGO is enabled.
# Returns:
# 0: CGO is enabled.
# 1: CGO is disabled.
function isCGOEnabled() {
[[ "${CGO_ENABLED}" == "1" ]]
}
# Downloads a file from a URL and extracts it.
# Arguments:
# $1: URL of the file to download.
# $2: Directory to extract the file to.
# $3: Optional. File type (e.g., "tgz", "zip"). If not provided, it's extracted from the URL.
function downloadAndUnzip() {
local url="$1"
local file="$2"
local type="${3:-$(echo "${url}" | sed 's/.*\.//g')}"
mkdir -p "${file}"
file="$(cd "${file}" && pwd)"
echo -e "${COLOR_BLUE}Downloading ${COLOR_CYAN}\"${url}\"${COLOR_BLUE} to ${COLOR_GREEN}\"${file}\"${COLOR_RESET}"
rm -rf "${file}"/*
local start_time=$(date +%s)
case "${type}" in
"tgz" | "gz")
curl -sL "${url}" | tar -xf - -C "${file}" --strip-components 1 -z
;;
"bz2")
curl -sL "${url}" | tar -xf - -C "${file}" --strip-components 1 -j
;;
"xz")
curl -sL "${url}" | tar -xf - -C "${file}" --strip-components 1 -J
;;
"lzma")
curl -sL "${url}" | tar -xf - -C "${file}" --strip-components 1 --lzma
;;
"zip")
curl -sL "${url}" -o "${file}/tmp.zip"
unzip -o "${file}/tmp.zip" -d "${file}" -q
rm -f "${file}/tmp.zip"
;;
*)
echo -e "${COLOR_RED}Unsupported compression type: ${type}${COLOR_RESET}"
return 1
;;
esac
local end_time=$(date +%s)
echo -e "${COLOR_GREEN}Download and extraction successful (took $((end_time - start_time))s)${COLOR_RESET}"
}
# --- Platform Management ---
declare -A allowed_platforms
allowed_platforms=(
["linux"]="linux/386,linux/amd64,linux/arm,linux/arm64,linux/loong64,linux/mips,linux/mips64,linux/mips64le,linux/mipsle,linux/ppc64,linux/ppc64le,linux/riscv64,linux/s390x"
["darwin"]="darwin/amd64,darwin/arm64"
["windows"]="windows/386,windows/amd64,windows/arm,windows/arm64"
)
declare -A cgo_allowed_platforms
cgo_allowed_platforms=(
["linux"]="linux/386,linux/amd64,linux/arm,linux/arm64,linux/loong64,linux/mips,linux/mips64,linux/mips64le,linux/mipsle,linux/ppc64le,linux/riscv64,linux/s390x"
["windows"]="windows/386,windows/amd64"
)
# Adds platforms to the allowed platforms list.
# Arguments:
# $1: Comma-separated list of platforms to add.
function addToAllowedPlatforms() {
local platforms="$1"
local platform=""
for platform in ${platform//,/ }; do
local os="${platform%%/*}"
if [[ -z "${allowed_platforms[${os}]}" ]]; then
allowed_platforms[${os}]="${platform}"
else
allowed_platforms[${os}]="${allowed_platforms[${os}]},${platform}"
fi
done
}
# Adds platforms to the CGO allowed platforms list.
# Arguments:
# $1: Comma-separated list of platforms to add.
function addToCGOAllowedPlatforms() {
local platforms="$1"
local platform=""
for platform in ${platforms//,/ }; do
local os="${platform%%/*}"
if [[ -z "${cgo_allowed_platforms[${os}]}" ]]; then
cgo_allowed_platforms[${os}]="${platform}"
else
cgo_allowed_platforms[${os}]="${cgo_allowed_platforms[${os}]},${platform}"
fi
done
}
# Removes platforms from the allowed platforms list.
# Arguments:
# $1: Comma-separated list of platforms to remove.
function deleteFromAllowedPlatforms() {
local platforms="$1"
local platform=""
for platform in ${platforms//,/ }; do
local os="${platform%%/*}"
if [[ -n "${allowed_platforms[${os}]}" ]]; then
allowed_platforms[${os}]=$(echo "${allowed_platforms[${os}]}" | sed "s|${platform}$||g" | sed "s|${platform},||g")
fi
done
}
# Removes platforms from the CGO allowed platforms list.
# Arguments:
# $1: Comma-separated list of platforms to remove.
function deleteFromCGOAllowedPlatforms() {
local platforms="$1"
local platform=""
for platform in ${platforms//,/ }; do
local os="${platform%%/*}"
if [[ -n "${cgo_allowed_platforms[${os}]}" ]]; then
cgo_allowed_platforms[${os}]=$(echo "${cgo_allowed_platforms[${os}]}" | sed "s|${platform}$||g" | sed "s|${platform},||g")
fi
done
}
# Initializes the host platforms.
function initHostPlatforms() {
addToAllowedPlatforms "${GOHOSTOS}/${GOHOSTARCH}"
addToCGOAllowedPlatforms "${GOHOSTOS}/${GOHOSTARCH}"
}
# Removes duplicate platforms from a comma-separated list.
# Arguments:
# $1: Comma-separated list of platforms.
# Returns:
# Comma-separated list of platforms with duplicates removed.
function removeDuplicatePlatforms() {
local all_platforms="$1"
all_platforms="$(echo "${all_platforms}" | tr ',' '\n' | sort | uniq | paste -s -d ',' -)"
all_platforms="${all_platforms#,}"
all_platforms="${all_platforms%,}"
echo "${all_platforms}"
}
# Initializes the platforms based on environment variables and allowed platforms.
function initPlatforms() {
setDefault "CGO_ENABLED" "${DEFAULT_CGO_ENABLED}"
unset -v CURRENT_ALLOWED_PLATFORM
ALLOWED_PLATFORM=""
CGO_ALLOWED_PLATFORM=""
for os in "${!allowed_platforms[@]}"; do
ALLOWED_PLATFORM="${ALLOWED_PLATFORM},${allowed_platforms[${os}]}"
if [[ -n "${cgo_allowed_platforms[${os}]}" ]]; then
CGO_ALLOWED_PLATFORM="${CGO_ALLOWED_PLATFORM},${cgo_allowed_platforms[${os}]}"
fi
done
ALLOWED_PLATFORM=$(removeDuplicatePlatforms "${ALLOWED_PLATFORM}")
CGO_ALLOWED_PLATFORM=$(removeDuplicatePlatforms "${CGO_ALLOWED_PLATFORM}")
isCGOEnabled && CURRENT_ALLOWED_PLATFORM="${CGO_ALLOWED_PLATFORM}" || CURRENT_ALLOWED_PLATFORM="${ALLOWED_PLATFORM}"
for os in "${!allowed_platforms[@]}"; do
local var="${os^^}_ALLOWED_PLATFORM"
local cgo_var="CGO_${var}"
eval "CURRENT_${var}=\"${!var}\""
eval "CURRENT_${cgo_var}=\"${!cgo_var}\""
done
if declare -f initDepPlatforms >/dev/null; then
initDepPlatforms
fi
}
# Checks if a platform is allowed.
# Arguments:
# $1: Target platform to check.
# $2: Optional. List of allowed platforms. If not provided, CURRENT_ALLOWED_PLATFORM is used.
# Returns:
# 0: Platform is allowed.
# 1: Platform is not allowed.
# 2: Platform is not allowed for CGO.
function checkPlatform() {
local target_platform="$1"
local current_allowed_platform="${2:-${CURRENT_ALLOWED_PLATFORM}}"
if [[ "${current_allowed_platform}" =~ (^|,)${target_platform}($|,) ]]; then
return 0
elif isCGOEnabled && [[ "${ALLOWED_PLATFORM}" =~ (^|,)${target_platform}($|,) ]]; then
return 2
else
return 1
fi
}
# Checks if a list of platforms are allowed.
# Arguments:
# $1: Comma-separated list of platforms to check.
# Returns:
# 0: All platforms are allowed.
# 1: At least one platform is not allowed.
# 2: At least one platform is not allowed for CGO.
# 3: Error checking platforms.
function checkPlatforms() {
local platforms="$1"
for platform in ${platforms//,/ }; do
case $(
checkPlatform "${platform}"
echo $?
) in
0)
continue
;;
1)
echo -e "${COLOR_RED}Platform not supported: ${platform}${COLOR_RESET}"
return 1
;;
2)
echo -e "${COLOR_RED}Platform not supported for CGO: ${platform}${COLOR_RESET}"
return 2
;;
*)
echo -e "${COLOR_RED}Error checking platform: ${platform}${COLOR_RESET}"
return 3
;;
esac
done
return 0
}
# --- CGO Dependencies ---
declare -A cgo_deps
cgo_deps=(
["CC"]=""
["CXX"]=""
["MORE_CGO_CFLAGS"]=""
["MORE_CGO_CXXFLAGS"]=""
["MORE_CGO_LDFLAGS"]=""
)
# Initializes CGO dependencies based on the target operating system and architecture.
# Arguments:
# $1: Target operating system (GOOS).
# $2: Target architecture (GOARCH).
# $3: Optional. Micro architecture variant.
# Returns:
# 0: CGO dependencies initialized successfully.
# 1: Error initializing CGO dependencies.
function initCGODeps() {
local goos="$1"
local goarch="$2"
local micro="$3"
if [[ -n "${FORCE_CC}" ]] && [[ -n "${FORCE_CXX}" ]]; then
cgo_deps["CC"]="${FORCE_CC}"
cgo_deps["CXX"]="${FORCE_CXX}"
return 0
elif [[ -n "${FORCE_CC}" ]] || [[ -n "${FORCE_CXX}" ]]; then
echo -e "${COLOR_RED}Both FORCE_CC and FORCE_CXX must be set at the same time.${COLOR_RESET}"
return 1
fi
if ! isCGOEnabled; then
echo -e "${COLOR_RED}Try init CGO, but CGO is not enabled.${COLOR_RESET}"
return 1
fi
case "${GOHOSTOS}" in
"linux" | "darwin")
case "${GOHOSTARCH}" in
"amd64" | "arm64" | "arm" | "ppc64le" | "riscv64" | "s390x")
initDefaultCGODeps "$@"
;;
*)
if [[ "${goos}" == "${GOHOSTOS}" ]] && [[ "${goarch}" == "${GOHOSTARCH}" ]]; then
initHostCGODeps "$@"
else
echo -e "${COLOR_LIGHT_RED}CGO is not supported for ${goos}/${goarch}.${COLOR_RESET}"
return 1
fi
;;
esac
;;
*)
if [[ "${goos}" == "${GOHOSTOS}" ]] && [[ "${goarch}" == "${GOHOSTARCH}" ]]; then
initHostCGODeps "$@"
else
echo -e "${COLOR_RED}CGO is not supported for ${goos}/${goarch}.${COLOR_RESET}"
return 1
fi
;;
esac
local cc_command cc_options
read -r cc_command cc_options <<<"${cgo_deps["CC"]}"
cc_command="$(command -v "${cc_command}")"
if [[ "${cc_command}" != /* ]]; then
cgo_deps["CC"]="$(cd "$(dirname "${cc_command}")" && pwd)/$(basename "${cc_command}")"
[[ -n "${cc_options}" ]] && cgo_deps["CC"]="${cgo_deps["CC"]} ${cc_options}"
fi
local cxx_command cxx_options
read -r cxx_command cxx_options <<<"${cgo_deps["CXX"]}"
cxx_command="$(command -v "${cxx_command}")"
if [[ "${cxx_command}" != /* ]]; then
cgo_deps["CXX"]="$(cd "$(dirname "${cxx_command}")" && pwd)/$(basename "${cxx_command}")"
[[ -n "${cxx_options}" ]] && cgo_deps["CXX"]="${cgo_deps["CXX"]} ${cxx_options}"
fi
}
# Initializes CGO dependencies for the host platform.
function initHostCGODeps() {
cgo_deps["CC"]="${HOST_CC}"
cgo_deps["CXX"]="${HOST_CXX}"
}
# Initializes default CGO dependencies based on the target operating system, architecture, and micro architecture.
# Arguments:
# $1: Target operating system (GOOS).
# $2: Target architecture (GOARCH).
# $3: Optional. Micro architecture variant.
function initDefaultCGODeps() {
local goos="$1"
local goarch="$2"
local micro="$3"
local unamespacer="${GOHOSTOS}-${GOHOSTARCH}"
[[ "${GOHOSTARCH}" == "arm" ]] && unamespacer="${GOHOSTOS}-arm32v7"
case "${goos}" in
"linux")
case "${micro}" in
"hardfloat")
micro="hf"
;;
"softfloat")
micro="sf"
;;
esac
case "${goarch}" in
"386")
initLinuxCGO "i686" ""
;;
"amd64")
initLinuxCGO "x86_64" ""
;;
"arm")
[[ "${micro}" == "5" ]] && initLinuxCGO "armv5" "eabi" || initLinuxCGO "armv${micro}" "eabihf"
;;
"arm64")
initLinuxCGO "aarch64" ""
;;
"mips")
[[ "${micro}" == "hf" ]] && micro="" || micro="sf"
initLinuxCGO "mips" "" "${micro}"
;;
"mipsle")
[[ "${micro}" == "hf" ]] && micro="" || micro="sf"
initLinuxCGO "mipsel" "" "${micro}"
;;
"mips64")
[[ "${micro}" == "hf" ]] && micro="" || micro="sf"
initLinuxCGO "mips64" "" "${micro}"
;;
"mips64le")
[[ "${micro}" == "hf" ]] && micro="" || micro="sf"
initLinuxCGO "mips64el" "" "${micro}"
;;
"ppc64")
initLinuxCGO "powerpc64" ""
;;
"ppc64le")
initLinuxCGO "powerpc64le" ""
;;
"riscv64")
initLinuxCGO "riscv64" ""
;;
"s390x")
initLinuxCGO "s390x" ""
;;
"loong64")
initLinuxCGO "loongarch64" ""
;;
*)
if [[ "${goos}" == "${GOHOSTOS}" ]] && [[ "${goarch}" == "${GOHOSTARCH}" ]]; then
initHostCGODeps "$@"
else
echo -e "${COLOR_RED}CGO is not supported for ${goos}/${goarch}.${COLOR_RESET}"
return 1
fi
;;
esac
;;
"windows")
case "${goarch}" in
"386")
initWindowsCGO "i686"
;;
"amd64")
initWindowsCGO "x86_64"
;;
*)
if [[ "${goos}" == "${GOHOSTOS}" ]] && [[ "${goarch}" == "${GOHOSTARCH}" ]]; then
initHostCGODeps "$@"
else
echo -e "${COLOR_RED}CGO is not supported for ${goos}/${goarch}.${COLOR_RESET}"
return 1
fi
;;
esac
;;
*)
if [[ "${goos}" == "${GOHOSTOS}" ]] && [[ "${goarch}" == "${GOHOSTARCH}" ]]; then
initHostCGODeps "$@"
else
echo -e "${COLOR_RED}CGO is not supported for ${goos}/${goarch}.${COLOR_RESET}"
return 1
fi
;;
esac
}
# Initializes CGO dependencies for Linux.
# Arguments:
# $1: Architecture prefix (e.g., "i686", "x86_64").
# $2: Optional. ABI (e.g., "eabi", "eabihf").
# $3: Optional. Micro architecture variant.
function initLinuxCGO() {
local arch_prefix="$1"
local abi="$2"
local micro="$3"
local cc_var="CC_LINUX_${arch_prefix^^}${abi^^}${micro^^}"
local cxx_var="CXX_LINUX_${arch_prefix^^}${abi^^}${micro^^}"
if [[ -z "${!cc_var}" ]] && [[ -z "${!cxx_var}" ]]; then
local cross_compiler_name="${arch_prefix}-linux-musl${abi}${micro}-cross"
if command -v "${arch_prefix}-linux-musl${abi}${micro}-gcc" >/dev/null 2>&1 &&
command -v "${arch_prefix}-linux-musl${abi}${micro}-g++" >/dev/null 2>&1; then
eval "${cc_var}=\"${arch_prefix}-linux-musl${abi}${micro}-gcc\""
eval "${cxx_var}=\"${arch_prefix}-linux-musl${abi}${micro}-g++\""
elif [[ -x "${cgo_cross_compiler_dir}/${cross_compiler_name}/bin/${arch_prefix}-linux-musl${abi}${micro}-gcc" ]] &&
[[ -x "${cgo_cross_compiler_dir}/${cross_compiler_name}/bin/${arch_prefix}-linux-musl${abi}${micro}-g++" ]]; then
eval "${cc_var}=\"${cgo_cross_compiler_dir}/${cross_compiler_name}/bin/${arch_prefix}-linux-musl${abi}${micro}-gcc\""
eval "${cxx_var}=\"${cgo_cross_compiler_dir}/${cross_compiler_name}/bin/${arch_prefix}-linux-musl${abi}${micro}-g++\""
else
downloadAndUnzip "${GH_PROXY}https://github.com/zijiren233/musl-cross-make/releases/download/${CGO_DEPS_VERSION}/${cross_compiler_name}-${unamespacer}.tgz" \
"${cgo_cross_compiler_dir}/${cross_compiler_name}"
eval "${cc_var}=\"${cgo_cross_compiler_dir}/${cross_compiler_name}/bin/${arch_prefix}-linux-musl${abi}${micro}-gcc\""
eval "${cxx_var}=\"${cgo_cross_compiler_dir}/${cross_compiler_name}/bin/${arch_prefix}-linux-musl${abi}${micro}-g++\""
fi
elif [[ -z "${!cc_var}" ]] || [[ -z "${!cxx_var}" ]]; then
echo -e "${COLOR_RED}Both ${cc_var} and ${cxx_var} must be set.${COLOR_RESET}"
return 1
fi
cgo_deps["CC"]="${!cc_var} -static --static"
cgo_deps["CXX"]="${!cxx_var} -static --static"
return 0
}
# Initializes CGO dependencies for Windows.
# Arguments:
# $1: Architecture prefix (e.g., "i686", "x86_64").
function initWindowsCGO() {
local arch_prefix="$1"
local cc_var="CC_WINDOWS_${arch_prefix^^}"
local cxx_var="CXX_WINDOWS_${arch_prefix^^}"
if [[ -z "${!cc_var}" ]] && [[ -z "${!cxx_var}" ]]; then
local cross_compiler_name="${arch_prefix}-w64-mingw32-cross"
if command -v "${arch_prefix}-w64-mingw32-gcc" >/dev/null 2>&1 &&
command -v "${arch_prefix}-w64-mingw32-g++" >/dev/null 2>&1; then
eval "${cc_var}=\"${arch_prefix}-w64-mingw32-gcc\""
eval "${cxx_var}=\"${arch_prefix}-w64-mingw32-g++\""
elif [[ -x "${cgo_cross_compiler_dir}/${cross_compiler_name}/bin/${arch_prefix}-w64-mingw32-gcc" ]] &&
[[ -x "${cgo_cross_compiler_dir}/${cross_compiler_name}/bin/${arch_prefix}-w64-mingw32-g++" ]]; then
eval "${cc_var}=\"${cgo_cross_compiler_dir}/${cross_compiler_name}/bin/${arch_prefix}-w64-mingw32-gcc\""
eval "${cxx_var}=\"${cgo_cross_compiler_dir}/${cross_compiler_name}/bin/${arch_prefix}-w64-mingw32-g++\""
else
downloadAndUnzip "${GH_PROXY}https://github.com/zijiren233/musl-cross-make/releases/download/${CGO_DEPS_VERSION}/${cross_compiler_name}-${unamespacer}.tgz" \
"${cgo_cross_compiler_dir}/${cross_compiler_name}"
eval "${cc_var}=\"${cgo_cross_compiler_dir}/${cross_compiler_name}/bin/${arch_prefix}-w64-mingw32-gcc\""
eval "${cxx_var}=\"${cgo_cross_compiler_dir}/${cross_compiler_name}/bin/${arch_prefix}-w64-mingw32-g++\""
fi
elif [[ -z "${!cc_var}" ]] || [[ -z "${!cxx_var}" ]]; then
echo -e "${COLOR_RED}Both ${cc_var} and ${cxx_var} must be set.${COLOR_RESET}"
return 1
fi
cgo_deps["CC"]="${!cc_var} -static --static"
cgo_deps["CXX"]="${!cxx_var} -static --static"
return 0
}
# Checks if a platform supports Position Independent Executables (PIE).
# Arguments:
# $1: Target platform.
# Returns:
# 0: Platform supports PIE.
# 1: Platform does not support PIE.
function supportPIE() {
local platform="$1"
[ ! $(isCGOEnabled) ] &&
[[ "${platform}" != "linux/386" ]] &&
[[ "${platform}" != "linux/arm" ]] &&
[[ "${platform}" != "linux/loong64" ]] &&
[[ "${platform}" != "linux/riscv64" ]] &&
[[ "${platform}" != "linux/s390x" ]] ||
return 1
[[ "${platform}" != "linux/mips"* ]] &&
[[ "${platform}" != "linux/ppc64" ]] &&
[[ "${platform}" != "openbsd"* ]] &&
[[ "${platform}" != "freebsd"* ]] &&
[[ "${platform}" != "netbsd"* ]]
}
# --- Utility Functions ---
# Gets a separator line based on the terminal width.
# Returns:
# A string of "-" characters with the length of the terminal width.
function getSeparator() {
1 year ago
local width=$(tput cols 2>/dev/null || echo $DEFAULT_TTY_WIDTH)
local separator=""
for ((i = 0; i < width; i++)); do
separator+="-"
done
echo $separator
}
# --- Build Functions ---
# Builds a target for a specific platform and micro architecture variant.
# Arguments:
# $1: Target platform (e.g., "linux/amd64").
# $2: Target name (e.g., binary name).
function buildTarget() {
local platform="$1"
local target_name="$2"
local goos="${platform%/*}"
local goarch="${platform#*/}"
local ext=""
[[ "${goos}" == "windows" ]] && ext=".exe"
local target_file="${result_dir}/${target_name}-${goos}-${goarch}${ext}"
local build_mode=""
supportPIE "${platform}" && build_mode="-buildmode=pie"
local build_env=(
"CGO_ENABLED=${CGO_ENABLED}"
"GOOS=${goos}"
"GOARCH=${goarch}"
)
echo -e "${COLOR_LIGHT_GRAY}$(getSeparator)${COLOR_RESET}"
buildTargetWithMicro "" "${build_env[@]}"
if [ -n "${DISABLE_MICRO}" ]; then
return 0
fi
# Build micro architecture variants based on the target architecture.
case "${goarch}" in
"386")
echo
buildTargetWithMicro "sse2" "${build_env[@]}"
echo
buildTargetWithMicro "softfloat" "${build_env[@]}"
;;
"arm")
echo
buildTargetWithMicro "5" "${build_env[@]}"
echo
buildTargetWithMicro "6" "${build_env[@]}"
echo
buildTargetWithMicro "7" "${build_env[@]}"
;;
"amd64")
echo
buildTargetWithMicro "v1" "${build_env[@]}"
echo
buildTargetWithMicro "v2" "${build_env[@]}"
echo
buildTargetWithMicro "v3" "${build_env[@]}"
echo
buildTargetWithMicro "v4" "${build_env[@]}"
;;
"mips" | "mipsle" | "mips64" | "mips64le")
echo
buildTargetWithMicro "hardfloat" "${build_env[@]}"
echo
buildTargetWithMicro "softfloat" "${build_env[@]}"
;;
"ppc64" | "ppc64le")
echo
buildTargetWithMicro "power8" "${build_env[@]}"
echo
buildTargetWithMicro "power9" "${build_env[@]}"
;;
"wasm")
echo
buildTargetWithMicro "satconv" "${build_env[@]}"
echo
buildTargetWithMicro "signext" "${build_env[@]}"
;;
esac
}
# Builds a target for a specific platform, micro architecture variant, and build environment.
# Arguments:
# $1: Micro architecture variant (e.g., "sse2", "softfloat").
# $2: Array of build environment variables.
function buildTargetWithMicro() {
local micro="$1"
local build_env=("${@:2}")
local goos="${platform%/*}"
local goarch="${platform#*/}"
local ext=""
[[ "${goos}" == "windows" ]] && ext=".exe"
local target_file="${result_dir}/${bin_name}-${goos}-${goarch}${micro:+"-$micro"}${ext}"
local default_target_file="${result_dir}/${bin_name}-${goos}-${goarch}${ext}"
# Set micro architecture specific environment variables.
case "${goarch}" in
"386")
build_env+=("GO386=${micro}")
isCGOEnabled && initCGODeps "${goos}" "${goarch}" "${micro}"
;;
"arm")
build_env+=("GOARM=${micro}")
isCGOEnabled && initCGODeps "${goos}" "${goarch}" "${micro:-6}"
;;
"amd64")
build_env+=("GOAMD64=${micro}")
isCGOEnabled && initCGODeps "${goos}" "${goarch}" "${micro}"
;;
"mips" | "mipsle")
build_env+=("GOMIPS=${micro}")
isCGOEnabled && initCGODeps "${goos}" "${goarch}" "${micro:-hardfloat}"
;;
"mips64" | "mips64le")
build_env+=("GOMIPS64=${micro}")
isCGOEnabled && initCGODeps "${goos}" "${goarch}" "${micro:-hardfloat}"
;;
"ppc64" | "ppc64le")
build_env+=("GOPPC64=${micro}")
isCGOEnabled && initCGODeps "${goos}" "${goarch}" "${micro}"
;;
"wasm")
build_env+=("GOWASM=${micro}")
isCGOEnabled && initCGODeps "${goos}" "${goarch}" "${micro}"
;;
*)
isCGOEnabled && initCGODeps "${goos}" "${goarch}" "${micro}"
;;
esac
# Set CGO specific environment variables.
if isCGOEnabled; then
build_env+=("CGO_CFLAGS=${DEFAULT_CGO_FLAGS} ${cgo_deps["MORE_CGO_CFLAGS"]}")
build_env+=("CGO_CXXFLAGS=${DEFAULT_CGO_FLAGS} ${cgo_deps["MORE_CGO_CXXFLAGS"]}")
build_env+=("CGO_LDFLAGS=${DEFAULT_CGO_LDFLAGS} ${cgo_deps["MORE_CGO_LDFLAGS"]}")
build_env+=("CC=${cgo_deps["CC"]}")
build_env+=("CXX=${cgo_deps["CXX"]}")
fi
echo -e "${COLOR_PURPLE}Building ${goos}/${goarch}${micro:+/${micro}}...${COLOR_RESET}"
echo "${build_env[@]}"
env "${build_env[@]}" go build -tags "${TAGS}" -ldflags "${LDFLAGS}" -trimpath ${BUILD_ARGS} ${build_mode} -o "${target_file}" "${source_dir}"
echo -e "${COLOR_LIGHT_GREEN}Build successful: ${goos}/${goarch}${micro:+ ${micro}}${COLOR_RESET}"
}
# Expands platform patterns (e.g., "linux/*") to a list of supported platforms.
# Arguments:
# $1: Comma-separated list of platforms, potentially containing patterns.
# Returns:
# Comma-separated list of expanded platforms.
function expandPlatforms() {
local platforms="$1"
local expanded_platforms=""
local platform=""
for platform in ${platforms//,/ }; do
if [[ "${platform}" == "all" ]]; then
echo "${CURRENT_ALLOWED_PLATFORM}"
return 0
elif [[ "${platform}" == *\** ]]; then
local tmp_var=""
for tmp_var in ${CURRENT_ALLOWED_PLATFORM//,/ }; do
[[ "${tmp_var}" == ${platform} ]] && expanded_platforms="${expanded_platforms} ${tmp_var}"
done
elif [[ "${platform}" != */* ]]; then
expanded_platforms="${expanded_platforms} $(expandPlatforms "${platform}/*")"
else
expanded_platforms="${expanded_platforms} ${platform}"
fi
done
removeDuplicatePlatforms "${expanded_platforms}"
}
# Performs the automatic build process for the specified platforms.
# Arguments:
# $1: Comma-separated list of platforms to build for.
function autoBuild() {
local platforms=$(expandPlatforms "$1")
checkPlatforms "${platforms}" || return 1
if declare -f initDep >/dev/null; then
initDep
fi
for platform in ${platforms//,/ }; do
buildTarget "${platform}" "${BIN_NAME}"
done
}
# Checks if the build configuration file has been loaded.
# Returns:
# 0: Build configuration file has been loaded.
# 1: Build configuration file has not been loaded.
function loadedBuildConfig() {
if [[ -n "${load_build_config}" ]]; then
return 0
fi
return 1
}
# Loads the build configuration file if it exists.
function loadBuildConfig() {
if [[ -f "${BUILD_CONFIG:=$DEFAULT_BUILD_CONFIG}" ]]; then
source "$BUILD_CONFIG"
load_build_config="true"
fi
}
# --- Main Script ---
loadBuildConfig
initHostPlatforms
# Parse command-line arguments.
while [[ $# -gt 0 ]]; do
case "${1}" in
-h | --help)
printHelp
exit 0
;;
-eh | --env-help)
printEnvHelp
exit 0
;;
--disable-cgo)
CGO_ENABLED="0"
;;
--source-dir=*)
SOURCE_DIR="${1#*=}"
;;
--more-go-cmd-args=*)
addBuildArgs "${1#*=}"
;;
--disable-micro)
DISABLE_MICRO="true"
;;
--ldflags=*)
addLDFLAGS "${1#*=}"
;;
--platforms=*)
PLATFORMS="${1#*=}"
;;
--result-dir=*)
RESULT_DIR="${1#*=}"
;;
--tags=*)
addTags "${1#*=}"
;;
--show-all-targets)
initPlatforms
echo "${CURRENT_ALLOWED_PLATFORM}"
exit 0
;;
--github-proxy-mirror=*)
GH_PROXY="${1#*=}"
;;
--force-gcc=*)
FORCE_CC="${1#*=}"
;;
--force-g++=*)
FORCE_CXX="${1#*=}"
;;
--host-cc=*)
HOST_CC="${1#*=}"
;;
--host-cxx=*)
HOST_CXX="${1#*=}"
;;
*)
if declare -f parseDepArgs >/dev/null && parseDepArgs "$1"; then
shift
continue
fi
echo -e "${COLOR_RED}Invalid option: $1${COLOR_RESET}"
exit 1
;;
esac
shift
done
fixArgs
initPlatforms
autoBuild "${PLATFORMS}"