feat: add cross-platform & cross-architecture build script
This commit is contained in:
443
build.sh
Executable file
443
build.sh
Executable file
@@ -0,0 +1,443 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m'
|
||||
|
||||
info() { echo -e "${GREEN}[INFO]${NC} $*"; }
|
||||
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
||||
error() { echo -e "${RED}[ERROR]${NC} $*"; exit 1; }
|
||||
|
||||
USAGE="
|
||||
Usage: $(basename "$0") [OPTIONS]
|
||||
|
||||
Cross-platform & cross-architecture build script for AirDisplay Client (Tauri)
|
||||
|
||||
Options:
|
||||
-p, --platform TARGET Target platform: macos, windows, linux, all (default: current OS)
|
||||
-a, --arch ARCH Target architecture: x64, arm64, armhf, universal (default: current arch)
|
||||
-t, --tool TOOL Cross-compile toolchain: cross, xwin, cargo (default: auto-detect)
|
||||
-r, --release Build in release mode (default)
|
||||
-d, --debug Build in debug mode
|
||||
-c, --clean Clean before building
|
||||
-l, --list List supported platform/arch combinations
|
||||
-h, --help Show this help message
|
||||
|
||||
Supported targets:
|
||||
macos x64, arm64, universal
|
||||
windows x64, arm64
|
||||
linux x64, arm64, armhf
|
||||
|
||||
Examples:
|
||||
$(basename "$0") Build for current platform & arch
|
||||
$(basename "$0") -p macos -a universal Universal macOS binary (x64+arm64)
|
||||
$(basename "$0") -p linux -a arm64 Linux ARM64 (e.g. Raspberry Pi 5)
|
||||
$(basename "$0") -p linux -a armhf Linux ARM32 hard-float
|
||||
$(basename "$0") -p windows -a arm64 Windows ARM64
|
||||
$(basename "$0") -p all Build for all platform+arch combos
|
||||
$(basename "$0") -p macos -a arm64 -t cross Force use cross toolchain
|
||||
$(basename "$0") -c -p linux -a arm64 Clean + build Linux ARM64
|
||||
"
|
||||
|
||||
BUILD_TYPE="release"
|
||||
CLEAN=false
|
||||
LIST=false
|
||||
TOOLCHAIN=""
|
||||
PLATFORM=""
|
||||
ARCH=""
|
||||
|
||||
detect_current_platform() {
|
||||
case "$(uname -s)" in
|
||||
Darwin) echo "macos" ;;
|
||||
Linux) echo "linux" ;;
|
||||
MINGW*|MSYS*|CYGWIN*) echo "windows" ;;
|
||||
*) error "Unsupported OS: $(uname -s)" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
detect_current_arch() {
|
||||
case "$(uname -m)" in
|
||||
x86_64|amd64) echo "x64" ;;
|
||||
arm64|aarch64) echo "arm64" ;;
|
||||
armv7l|armv7) echo "armhf" ;;
|
||||
*) echo "x64" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
CURRENT_PLATFORM=$(detect_current_platform)
|
||||
CURRENT_ARCH=$(detect_current_arch)
|
||||
PLATFORM="${CURRENT_PLATFORM}"
|
||||
ARCH="${CURRENT_ARCH}"
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-p|--platform) PLATFORM="$2"; shift 2 ;;
|
||||
-a|--arch) ARCH="$2"; shift 2 ;;
|
||||
-t|--tool) TOOLCHAIN="$2"; shift 2 ;;
|
||||
-r|--release) BUILD_TYPE="release"; shift ;;
|
||||
-d|--debug) BUILD_TYPE="debug"; shift ;;
|
||||
-c|--clean) CLEAN=true; shift ;;
|
||||
-l|--list) LIST=true; shift ;;
|
||||
-h|--help) echo "$USAGE"; exit 0 ;;
|
||||
*) error "Unknown option: $1\n$USAGE" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
VALID_ARCHS_MACOS="x64 arm64 universal"
|
||||
VALID_ARCHS_WINDOWS="x64 arm64"
|
||||
VALID_ARCHS_LINUX="x64 arm64 armhf"
|
||||
|
||||
validate_arch() {
|
||||
local platform="$1" arch="$2"
|
||||
case "$platform" in
|
||||
macos) echo "$VALID_ARCHS_MACOS" | grep -qw "$arch" || error "macos does not support arch '$arch'. Valid: $VALID_ARCHS_MACOS" ;;
|
||||
windows) echo "$VALID_ARCHS_WINDOWS" | grep -qw "$arch" || error "windows does not support arch '$arch'. Valid: $VALID_ARCHS_WINDOWS" ;;
|
||||
linux) echo "$VALID_ARCHS_LINUX" | grep -qw "$arch" || error "linux does not support arch '$arch'. Valid: $VALID_ARCHS_LINUX" ;;
|
||||
*) error "Unknown platform: $platform" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
if [[ "$LIST" == true ]]; then
|
||||
echo -e "${CYAN}Supported platform/arch combinations:${NC}"
|
||||
echo ""
|
||||
printf " %-10s %s\n" "macos" "$VALID_ARCHS_MACOS"
|
||||
printf " %-10s %s\n" "windows" "$VALID_ARCHS_WINDOWS"
|
||||
printf " %-10s %s\n" "linux" "$VALID_ARCHS_LINUX"
|
||||
echo ""
|
||||
echo -e "${CYAN}Rust targets mapping:${NC}"
|
||||
echo ""
|
||||
printf " %-20s %-30s %s\n" "PLATFORM/ARCH" "RUST TARGET" "NOTES"
|
||||
printf " %-20s %-30s %s\n" "macos/x64" "x86_64-apple-darwin" ""
|
||||
printf " %-20s %-30s %s\n" "macos/arm64" "aarch64-apple-darwin" "Apple Silicon"
|
||||
printf " %-20s %-30s %s\n" "macos/universal" "x86_64+aarch64-apple-darwin" "Merged fat binary"
|
||||
printf " %-20s %-30s %s\n" "windows/x64" "x86_64-pc-windows-msvc" ""
|
||||
printf " %-20s %-30s %s\n" "windows/arm64" "aarch64-pc-windows-msvc" "ARM64 devices"
|
||||
printf " %-20s %-30s %s\n" "linux/x64" "x86_64-unknown-linux-gnu" ""
|
||||
printf " %-20s %-30s %s\n" "linux/arm64" "aarch64-unknown-linux-gnu" "RPi 5, AWS Graviton, etc."
|
||||
printf " %-20s %-30s %s\n" "linux/armhf" "armv7-unknown-linux-gnueabihf" "RPi 3/4, 32-bit ARM"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
validate_arch "$PLATFORM" "$ARCH"
|
||||
|
||||
rust_target_for() {
|
||||
local platform="$1" arch="$2"
|
||||
case "$platform/$arch" in
|
||||
macos/x64) echo "x86_64-apple-darwin" ;;
|
||||
macos/arm64) echo "aarch64-apple-darwin" ;;
|
||||
macos/universal) echo "universal-apple-darwin" ;;
|
||||
windows/x64) echo "x86_64-pc-windows-msvc" ;;
|
||||
windows/arm64) echo "aarch64-pc-windows-msvc" ;;
|
||||
linux/x64) echo "x86_64-unknown-linux-gnu" ;;
|
||||
linux/arm64) echo "aarch64-unknown-linux-gnu" ;;
|
||||
linux/armhf) echo "armv7-unknown-linux-gnueabihf" ;;
|
||||
*) error "Unsupported combination: $platform/$arch" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
needs_cross_tool() {
|
||||
local platform="$1" arch="$2"
|
||||
local target
|
||||
target=$(rust_target_for "$platform" "$arch")
|
||||
|
||||
if [[ "$platform" != "$CURRENT_PLATFORM" ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
case "$CURRENT_PLATFORM/$CURRENT_ARCH" in
|
||||
macos/x64)
|
||||
[[ "$arch" == "arm64" ]] && return 0
|
||||
;;
|
||||
macos/arm64)
|
||||
[[ "$arch" == "x64" ]] && return 0
|
||||
;;
|
||||
*)
|
||||
local current_rust_arch
|
||||
current_rust_arch=$(rust_target_for "$CURRENT_PLATFORM" "$CURRENT_ARCH")
|
||||
[[ "$target" != "$current_rust_arch" ]] && return 0
|
||||
;;
|
||||
esac
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
detect_toolchain() {
|
||||
local platform="$1" arch="$2"
|
||||
|
||||
if [[ -n "$TOOLCHAIN" ]]; then
|
||||
echo "$TOOLCHAIN"
|
||||
return
|
||||
fi
|
||||
|
||||
if ! needs_cross_tool "$platform" "$arch"; then
|
||||
echo "native"
|
||||
return
|
||||
fi
|
||||
|
||||
case "$platform" in
|
||||
windows)
|
||||
if command -v cargo-xwin &>/dev/null; then
|
||||
echo "xwin"
|
||||
else
|
||||
echo "cross"
|
||||
fi
|
||||
;;
|
||||
linux)
|
||||
if command -v cross &>/dev/null; then
|
||||
echo "cross"
|
||||
elif command -v cargo-zigbuild &>/dev/null; then
|
||||
echo "zigbuild"
|
||||
else
|
||||
echo "cross"
|
||||
fi
|
||||
;;
|
||||
macos)
|
||||
if [[ "$arch" == "universal" ]]; then
|
||||
echo "native"
|
||||
else
|
||||
echo "native"
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo "cross"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
check_command() {
|
||||
if ! command -v "$1" &>/dev/null; then
|
||||
error "$1 is not installed. Please install it first."
|
||||
fi
|
||||
}
|
||||
|
||||
check_node_deps() {
|
||||
if [[ ! -d "node_modules" ]]; then
|
||||
info "Installing Node.js dependencies..."
|
||||
npm install
|
||||
fi
|
||||
}
|
||||
|
||||
check_rust_target() {
|
||||
local target="$1"
|
||||
if [[ "$target" == *"universal"* ]]; then
|
||||
check_rust_target "x86_64-apple-darwin"
|
||||
check_rust_target "aarch64-apple-darwin"
|
||||
return
|
||||
fi
|
||||
if ! rustup target list --installed | grep -q "$target"; then
|
||||
info "Installing Rust target: $target"
|
||||
rustup target add "$target"
|
||||
fi
|
||||
}
|
||||
|
||||
install_cross_tools() {
|
||||
local toolchain="$1"
|
||||
|
||||
case "$toolchain" in
|
||||
cross)
|
||||
if ! command -v cross &>/dev/null; then
|
||||
warn "cross not found. Installing..."
|
||||
cargo install cross --git https://github.com/cross-rs/cross
|
||||
fi
|
||||
;;
|
||||
xwin)
|
||||
if ! command -v cargo-xwin &>/dev/null; then
|
||||
warn "cargo-xwin not found. Installing..."
|
||||
cargo install cargo-xwin
|
||||
fi
|
||||
;;
|
||||
zigbuild)
|
||||
if ! command -v cargo-zigbuild &>/dev/null; then
|
||||
warn "cargo-zigbuild not found. Installing..."
|
||||
pip3 install ziglang
|
||||
cargo install cargo-zigbuild
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
do_clean() {
|
||||
info "Cleaning build artifacts..."
|
||||
npm run build -- --clearScreen false 2>/dev/null || true
|
||||
(cd src-tauri && cargo clean)
|
||||
rm -rf dist
|
||||
info "Clean complete."
|
||||
}
|
||||
|
||||
build_macos_universal() {
|
||||
info "Building macOS universal binary (x64 + arm64 → universal)..."
|
||||
check_rust_target "x86_64-apple-darwin"
|
||||
check_rust_target "aarch64-apple-darwin"
|
||||
|
||||
local tauri_args=(tauri build --target universal-apple-darwin)
|
||||
if [[ "$BUILD_TYPE" == "debug" ]]; then
|
||||
tauri_args=(tauri build --target universal-apple-darwin --debug)
|
||||
fi
|
||||
|
||||
info "Running: npm run ${tauri_args[*]}"
|
||||
npm run "${tauri_args[@]}"
|
||||
}
|
||||
|
||||
build_platform() {
|
||||
local platform="$1" arch="$2"
|
||||
|
||||
info "==========================================="
|
||||
info " Building for $platform ($arch)"
|
||||
info "==========================================="
|
||||
|
||||
if [[ "$platform/$arch" == "macos/universal" ]]; then
|
||||
build_macos_universal
|
||||
return $?
|
||||
fi
|
||||
|
||||
local rust_target
|
||||
rust_target=$(rust_target_for "$platform" "$arch")
|
||||
|
||||
local toolchain
|
||||
toolchain=$(detect_toolchain "$platform" "$arch")
|
||||
info "Using toolchain: $toolchain for $platform/$arch"
|
||||
|
||||
if [[ "$toolchain" == "native" ]]; then
|
||||
check_rust_target "$rust_target"
|
||||
local tauri_args=(tauri build --target "$rust_target")
|
||||
if [[ "$BUILD_TYPE" == "debug" ]]; then
|
||||
tauri_args+=(--debug)
|
||||
fi
|
||||
case "$platform" in
|
||||
macos) tauri_args+=(--bundles app) ;;
|
||||
windows) tauri_args+=(--bundles nsis) ;;
|
||||
linux) tauri_args+=(--bundles deb) ;;
|
||||
esac
|
||||
info "Running: npm run ${tauri_args[*]}"
|
||||
npm run "${tauri_args[@]}"
|
||||
elif [[ "$toolchain" == "cross" ]]; then
|
||||
install_cross_tools "cross"
|
||||
check_rust_target "$rust_target"
|
||||
info "Cross-compiling with cross for target: $rust_target"
|
||||
local build_flag=""
|
||||
if [[ "$BUILD_TYPE" == "release" ]]; then
|
||||
build_flag="--release"
|
||||
fi
|
||||
(cd src-tauri && cross build --target "$rust_target" $build_flag --lib)
|
||||
local tauri_args=(tauri build --target "$rust_target")
|
||||
if [[ "$BUILD_TYPE" == "debug" ]]; then
|
||||
tauri_args+=(--debug)
|
||||
fi
|
||||
case "$platform" in
|
||||
windows) tauri_args+=(--bundles nsis) ;;
|
||||
linux) tauri_args+=(--bundles deb) ;;
|
||||
esac
|
||||
info "Running: npm run ${tauri_args[*]}"
|
||||
npm run "${tauri_args[@]}"
|
||||
elif [[ "$toolchain" == "xwin" ]]; then
|
||||
install_cross_tools "xwin"
|
||||
check_rust_target "$rust_target"
|
||||
info "Cross-compiling with cargo-xwin for target: $rust_target"
|
||||
local build_flag=""
|
||||
if [[ "$BUILD_TYPE" == "release" ]]; then
|
||||
build_flag="--release"
|
||||
fi
|
||||
(cd src-tauri && cargo xwin build --target "$rust_target" $build_flag)
|
||||
local tauri_args=(tauri build --target "$rust_target" --bundles nsis)
|
||||
if [[ "$BUILD_TYPE" == "debug" ]]; then
|
||||
tauri_args+=(--debug)
|
||||
fi
|
||||
info "Running: npm run ${tauri_args[*]}"
|
||||
npm run "${tauri_args[@]}"
|
||||
elif [[ "$toolchain" == "zigbuild" ]]; then
|
||||
install_cross_tools "zigbuild"
|
||||
check_rust_target "$rust_target"
|
||||
info "Cross-compiling with cargo-zigbuild for target: $rust_target"
|
||||
local build_flag=""
|
||||
if [[ "$BUILD_TYPE" == "release" ]]; then
|
||||
build_flag="--release"
|
||||
fi
|
||||
(cd src-tauri && cargo zigbuild --target "$rust_target" $build_flag)
|
||||
local tauri_args=(tauri build --target "$rust_target")
|
||||
if [[ "$BUILD_TYPE" == "debug" ]]; then
|
||||
tauri_args+=(--debug)
|
||||
fi
|
||||
case "$platform" in
|
||||
linux) tauri_args+=(--bundles deb) ;;
|
||||
esac
|
||||
info "Running: npm run ${tauri_args[*]}"
|
||||
npm run "${tauri_args[@]}"
|
||||
else
|
||||
error "Unknown toolchain: $toolchain"
|
||||
fi
|
||||
}
|
||||
|
||||
declare -A BUILD_PLATFORMS
|
||||
|
||||
case "$PLATFORM" in
|
||||
all)
|
||||
BUILD_PLATFORMS=(
|
||||
["macos-x64"]=1
|
||||
["macos-arm64"]=1
|
||||
["macos-universal"]=1
|
||||
["windows-x64"]=1
|
||||
["windows-arm64"]=1
|
||||
["linux-x64"]=1
|
||||
["linux-arm64"]=1
|
||||
["linux-armhf"]=1
|
||||
)
|
||||
;;
|
||||
macos|windows|linux)
|
||||
key="${PLATFORM}-${ARCH}"
|
||||
BUILD_PLATFORMS=(["$key"]=1)
|
||||
;;
|
||||
*)
|
||||
error "Unknown platform: $PLATFORM. Use: macos, windows, linux, all"
|
||||
;;
|
||||
esac
|
||||
|
||||
echo -e "${CYAN}"
|
||||
echo " ╔═══════════════════════════════════════════╗"
|
||||
echo " ║ AirDisplay Client Build System ║"
|
||||
echo " ╚═══════════════════════════════════════════╝"
|
||||
echo -e "${NC}"
|
||||
info "Build type : $BUILD_TYPE"
|
||||
info "Host : $CURRENT_PLATFORM ($CURRENT_ARCH)"
|
||||
info "Targets : ${!BUILD_PLATFORMS[*]}"
|
||||
|
||||
check_command node
|
||||
check_command npm
|
||||
check_command cargo
|
||||
check_command rustup
|
||||
|
||||
check_node_deps
|
||||
|
||||
if [[ "$CLEAN" == true ]]; then
|
||||
do_clean
|
||||
fi
|
||||
|
||||
FAILED=()
|
||||
SUCCESS=()
|
||||
|
||||
for key in "${!BUILD_PLATFORMS[@]}"; do
|
||||
IFS=- read -r plat arch <<< "$key"
|
||||
if build_platform "$plat" "$arch"; then
|
||||
SUCCESS+=("$plat ($arch)")
|
||||
else
|
||||
FAILED+=("$plat ($arch)")
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
info "==========================================="
|
||||
info " Build Summary"
|
||||
info "==========================================="
|
||||
if [[ ${#SUCCESS[@]} -gt 0 ]]; then
|
||||
echo -e "${GREEN}Successful:${NC}"
|
||||
for s in "${SUCCESS[@]}"; do echo " ✓ $s"; done
|
||||
fi
|
||||
if [[ ${#FAILED[@]} -gt 0 ]]; then
|
||||
echo -e "${RED}Failed:${NC}"
|
||||
for f in "${FAILED[@]}"; do echo " ✗ $f"; done
|
||||
exit 1
|
||||
fi
|
||||
|
||||
info "All builds completed successfully!"
|
||||
Reference in New Issue
Block a user