#!/usr/bin/env bash # 在 Linux(x86_64 宿主机)上为多个 Tauri v1 + Vue 项目打出 amd64 与 arm64 的 .deb。 # # 依赖: # - Node/npm、Rust、@tauri-apps/cli # - rustup target add aarch64-unknown-linux-gnu # - 本机 amd64:Tauri Linux 前置依赖(libwebkit2gtk-4.0-dev 等) # - 交叉 arm64:必须启用 multiarch 并安装 *:arm64 的 -dev 包,否则 glib/gdk/webkit 等 -sys 会报找不到 .pc # 见 README.md「交叉编译 arm64」或本脚本 check_arm64_pkgconfig 失败时的提示。 # # 用法: # ./scripts/build-linux-deb-all.sh # 构建自动发现的全部项目(amd64 + arm64) # ./scripts/build-linux-deb-all.sh --arch amd64 # 仅构建 amd64 # ./scripts/build-linux-deb-all.sh --arch arm64 call-client # 仅构建指定项目的 arm64 # ./scripts/build-linux-deb-all.sh --list-projects # 打印可构建项目列表 # set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" BUILD_ARCH="all" LIST_PROJECTS=0 # 统一收集 .deb 的根目录(相对 REPO_ROOT) OUTPUT_ROOT="$REPO_ROOT/dist/linux-deb" # aarch64 交叉编译时 pkg-config 查找路径(与下方 export 一致) ARM64_PKGCONFIG_DIR="/usr/lib/aarch64-linux-gnu/pkgconfig" X64_PKGCONFIG_DIR="/usr/lib/x86_64-linux-gnu/pkgconfig" AVAILABLE_PROJECTS=() POSITIONAL_ARGS=() usage() { cat </dev/null 2>&1; then echo "$project_dir" fi done shopt -u nullglob } parse_args() { POSITIONAL_ARGS=() while (($# > 0)); do case "$1" in --arch) BUILD_ARCH="${2:-}" if [[ -z "$BUILD_ARCH" ]]; then echo "错误: --arch 需要参数(all|amd64|arm64)" >&2 exit 1 fi shift 2 ;; --arch=*) BUILD_ARCH="${1#*=}" shift ;; --list-projects) LIST_PROJECTS=1 shift ;; --help|-h) usage exit 0 ;; --) shift break ;; -*) echo "错误: 未知参数 $1" >&2 usage >&2 exit 1 ;; *) break ;; esac done case "$BUILD_ARCH" in all|amd64|arm64) ;; *) echo "错误: --arch 仅支持 all|amd64|arm64,当前为 \"$BUILD_ARCH\"" >&2 exit 1 ;; esac if (($# > 0)); then POSITIONAL_ARGS=("$@") fi } check_x64_pkgconfig() { local missing=() local f for f in glib-2.0.pc gdk-3.0.pc webkit2gtk-4.0.pc; do if [[ ! -f "$X64_PKGCONFIG_DIR/$f" ]]; then missing+=( "$f" ) fi done if (( ${#missing[@]} == 0 )); then return 0 fi cat >&2 <&2 <&2 exit 1 fi echo "==> [$project_dir] $npm_cmd" (cd "$abs_project" && npm run "$npm_cmd") } ensure_tauri_icons() { local project_dir="$1" local icons_dir="$REPO_ROOT/$project_dir/src-tauri/icons" mkdir -p "$icons_dir" if [[ -f "$icons_dir/32x32.png" && -f "$icons_dir/128x128.png" && -f "$icons_dir/256x256.png" ]]; then return 0 fi echo "==> [$project_dir] 生成默认图标资源(32/128/256)" python3 - "$icons_dir" "$project_dir" <<'PY' import binascii import os import struct import sys import zlib icons_dir = sys.argv[1] project_name = sys.argv[2] if "broadcast" in project_name: color = (16, 185, 129) # green else: color = (37, 99, 235) # blue def png_chunk(tag: bytes, data: bytes) -> bytes: return ( struct.pack("!I", len(data)) + tag + data + struct.pack("!I", binascii.crc32(tag + data) & 0xFFFFFFFF) ) def write_png(path: str, size: int, rgb: tuple[int, int, int]) -> None: row = bytes(rgb) * size raw = b"".join(b"\x00" + row for _ in range(size)) ihdr = struct.pack("!IIBBBBB", size, size, 8, 2, 0, 0, 0) idat = zlib.compress(raw, 9) png = ( b"\x89PNG\r\n\x1a\n" + png_chunk(b"IHDR", ihdr) + png_chunk(b"IDAT", idat) + png_chunk(b"IEND", b"") ) with open(path, "wb") as f: f.write(png) for size in (32, 128, 256): write_png(os.path.join(icons_dir, f"{size}x{size}.png"), size, color) PY } collect_debs() { local project_dir="$1" local arch_label="$2" local triple="$3" local src="$REPO_ROOT/$project_dir/src-tauri/target/$triple/release/bundle/deb" local dest="$OUTPUT_ROOT/$project_dir/$arch_label" if [[ ! -d "$src" ]]; then echo "警告: 未找到输出目录 $src(跳过复制)" >&2 return 0 fi mkdir -p "$dest" shopt -s nullglob local files=( "$src"/*.deb ) shopt -u nullglob if (( ${#files[@]} == 0 )); then echo "警告: $src 下没有 .deb 文件" >&2 return 0 fi cp -v "${files[@]}" "$dest/" } build_project() { local project_dir="$1" ensure_tauri_icons "$project_dir" if [[ "$BUILD_ARCH" == "all" || "$BUILD_ARCH" == "amd64" ]]; then echo "" echo "######## $project_dir: amd64 ########" unset PKG_CONFIG_PATH PKG_CONFIG_LIBDIR PKG_CONFIG_SYSROOT_DIR PKG_CONFIG_ALLOW_CROSS 2>/dev/null || true check_x64_pkgconfig export PKG_CONFIG_PATH="$X64_PKGCONFIG_DIR" export PKG_CONFIG_LIBDIR="$X64_PKGCONFIG_DIR:/usr/share/pkgconfig" build_one_target "$project_dir" "build:deb:x64" collect_debs "$project_dir" "amd64" "x86_64-unknown-linux-gnu" unset PKG_CONFIG_PATH PKG_CONFIG_LIBDIR fi if [[ "$BUILD_ARCH" == "all" || "$BUILD_ARCH" == "arm64" ]]; then echo "" echo "######## $project_dir: arm64 (cross) ########" check_arm64_pkgconfig # 强制使用 aarch64 工具链,避免误用宿主机 x86_64 的 cc/ld 导致 ELF 架构不匹配。 export CC_aarch64_unknown_linux_gnu=aarch64-linux-gnu-gcc export CXX_aarch64_unknown_linux_gnu=aarch64-linux-gnu-g++ export AR_aarch64_unknown_linux_gnu=aarch64-linux-gnu-ar export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc export PKG_CONFIG_ALLOW_CROSS=1 export PKG_CONFIG_SYSROOT_DIR=/ export PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig export PKG_CONFIG_LIBDIR=/usr/lib/aarch64-linux-gnu/pkgconfig:/usr/share/pkgconfig build_one_target "$project_dir" "build:deb:arm64" collect_debs "$project_dir" "arm64" "aarch64-unknown-linux-gnu" unset CC_aarch64_unknown_linux_gnu CXX_aarch64_unknown_linux_gnu AR_aarch64_unknown_linux_gnu unset CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER unset PKG_CONFIG_ALLOW_CROSS PKG_CONFIG_SYSROOT_DIR PKG_CONFIG_PATH PKG_CONFIG_LIBDIR fi } select_projects() { local available=("${AVAILABLE_PROJECTS[@]}") if (($# == 0)); then printf '%s\n' "${available[@]}" return fi local name ok p for name in "$@"; do ok=0 for p in "${available[@]}"; do if [[ "$p" == "$name" ]]; then ok=1 break fi done if [[ "$ok" -eq 0 ]]; then echo "错误: 未知项目 \"$name\"(可用项目见 --list-projects)" >&2 exit 1 fi echo "$name" done } main() { local -a to_build parse_args "$@" mapfile -t AVAILABLE_PROJECTS < <(discover_projects) if (( ${#AVAILABLE_PROJECTS[@]} == 0 )); then echo "错误: 未发现可构建项目(需包含 build:deb:x64 与 build:deb:arm64 脚本)" >&2 exit 1 fi if [[ "$LIST_PROJECTS" -eq 1 ]]; then printf '%s\n' "${AVAILABLE_PROJECTS[@]}" return fi mapfile -t to_build < <(select_projects "${POSITIONAL_ARGS[@]}") mkdir -p "$OUTPUT_ROOT" for p in "${to_build[@]}"; do build_project "$p" done echo "" echo "==> 全部完成" echo " 构建架构: $BUILD_ARCH" echo " 统一输出目录: $OUTPUT_ROOT" echo " 每项目下: <项目名>/amd64/*.deb 与 <项目名>/arm64/*.deb" echo " Tauri 原始路径仍在各项目:" echo " src-tauri/target/x86_64-unknown-linux-gnu/release/bundle/deb/" echo " src-tauri/target/aarch64-unknown-linux-gnu/release/bundle/deb/" } main "$@"