# visionA-local — Makefile(骨架,M1-1) # # 這份 Makefile 目前所有 targets 都只是 placeholder,實際內容會在後續任務逐步填入: # - M1-2 複製 server core # - M1-4 複製 frontend # - M1-8 build-server / build-frontend 實作 # - M1-9 visiona-local (Wails) shell # - M1-11 payload-macos # - M1-12 installer-macos (dmg) # - M2+ Windows / Linux / CI # # 詳見 .autoflow/04-architecture/build-pipeline.md SHELL := /bin/bash VERSION ?= v0.1.0-dev BUILD_TIME := $(shell date -u +%Y-%m-%dT%H:%M:%SZ) OS := $(shell uname -s | tr A-Z a-z) DIST := dist PAYLOAD := visiona-local/payload .PHONY: help \ vendor-sync vendor-python vendor-wheels vendor-ffmpeg vendor-ytdlp \ vendor-python-windows vendor-wheels-windows vendor-ffmpeg-windows vendor-ytdlp-windows \ vendor-python-linux vendor-wheels-linux vendor-ffmpeg-linux vendor-ytdlp-linux \ server build-server \ frontend build-frontend build-embed \ payload payload-macos payload-windows payload-linux \ stage-macos stage-windows \ wails-macos wails-windows wails-linux \ dmg exe appimage \ dev dev-mock test lint fmt clean # ── 幫助 ─────────────────────────────────────────────────────────── help: ## 列出所有 make targets @echo "visionA-local — available targets (M1-1 skeleton)" @echo "" @echo " 依賴同步:" @echo " vendor-sync 下載 python-build-standalone / wheels / ffmpeg / yt-dlp → vendor/" @echo "" @echo " Build(單元):" @echo " server build Go server binary (→ dist/visiona-local-server)" @echo " frontend pnpm build Next.js 靜態產物 (→ frontend/out)" @echo "" @echo " Payload 準備:" @echo " payload-macos stage macOS payload → visiona-local/payload/" @echo " payload-windows stage Windows payload" @echo " payload-linux stage Linux payload" @echo "" @echo " Wails 應用 build:" @echo " wails-macos wails build darwin/amd64" @echo " wails-windows wails build windows/amd64" @echo " wails-linux wails build linux/amd64" @echo "" @echo " 安裝檔打包:" @echo " dmg macOS .dmg(dmgbuild)" @echo " exe Windows .exe(Inno Setup)" @echo " appimage Linux .AppImage" @echo "" @echo " 工具:" @echo " clean 清除 dist/ payload/" @echo "" @echo "Note: 目前所有 target 都是 placeholder(只印 TODO),尚未實作。" # ── 依賴同步 ─────────────────────────────────────────────────────── PYTHON_VERSION := 3.12.9 PBS_RELEASE := 20250317 PBS_URL_DARWIN := https://github.com/astral-sh/python-build-standalone/releases/download/$(PBS_RELEASE)/cpython-$(PYTHON_VERSION)+$(PBS_RELEASE)-x86_64-apple-darwin-install_only.tar.gz FFMPEG_URL_DARWIN := https://evermeet.cx/ffmpeg/getrelease/ffmpeg/zip YTDLP_URL_DARWIN := https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_macos vendor-sync: vendor-python vendor-wheels vendor-ffmpeg vendor-ytdlp ## 下載所有第三方依賴到 vendor/(不進 git,第三輪決策 Q-D=D2) @echo "==> vendor-sync 完成" vendor-python: ## 下載 python-build-standalone tarball → vendor/python/darwin/ @mkdir -p vendor/python/darwin @if [ ! -f vendor/python/darwin/python.tar.gz ]; then \ echo "==> 下載 python-build-standalone $(PYTHON_VERSION)+$(PBS_RELEASE) (macOS x86_64 install_only)..."; \ curl -fL -o vendor/python/darwin/python.tar.gz "$(PBS_URL_DARWIN)"; \ echo "==> tarball 大小:$$(du -sh vendor/python/darwin/python.tar.gz | cut -f1)"; \ else \ echo "==> python tarball 已存在,跳過下載 ($$(du -sh vendor/python/darwin/python.tar.gz | cut -f1))"; \ fi vendor-wheels: ## 同步 wheels → vendor/wheels/darwin/(內部 wheel 從 visiona-local/wheels 複製,公開相依用 pip download) @mkdir -p vendor/wheels/darwin @echo "==> 同步內部 wheels(KneronPLUS 等)..." @if [ -d visiona-local/wheels/macos ]; then \ cp visiona-local/wheels/macos/*.whl vendor/wheels/darwin/ 2>/dev/null || true; \ fi @echo "==> 從 PyPI 下載公開相依 wheels (cp312, macosx_x86_64)..." @pip3 download \ --only-binary=:all: \ --platform macosx_10_9_x86_64 \ --platform macosx_11_0_x86_64 \ --platform macosx_12_0_x86_64 \ --python-version 3.12 \ --implementation cp \ --dest vendor/wheels/darwin \ numpy opencv-python-headless pyusb requests || echo "WARN: pip download 部分失敗(詳見上方訊息)" @echo "==> wheels 總覽:" @ls -1 vendor/wheels/darwin/*.whl 2>/dev/null | wc -l | xargs -I{} echo " 共 {} 個 wheel" @du -sh vendor/wheels/darwin vendor-ffmpeg: ## 下載 ffmpeg static build (macOS x86_64) → vendor/ffmpeg/darwin/(預設要求 LGPL,必要時可用 VISIONA_ALLOW_GPL_FFMPEG=1 暫時放行 GPL) @mkdir -p vendor/ffmpeg/darwin @if [ -f vendor/ffmpeg/darwin/ffmpeg ]; then \ echo "==> ffmpeg 已存在,跳過 ($$(du -sh vendor/ffmpeg/darwin/ffmpeg | cut -f1))"; \ else \ echo "==> 下載 ffmpeg static build (macOS) from evermeet.cx..."; \ curl -fL -o /tmp/ffmpeg-latest.zip "$(FFMPEG_URL_DARWIN)"; \ unzip -o /tmp/ffmpeg-latest.zip -d vendor/ffmpeg/darwin/; \ rm -f /tmp/ffmpeg-latest.zip; \ chmod +x vendor/ffmpeg/darwin/ffmpeg; \ echo "==> ffmpeg 大小:$$(du -sh vendor/ffmpeg/darwin/ffmpeg | cut -f1)"; \ vendor/ffmpeg/darwin/ffmpeg -version 2>&1 | head -3; \ echo "==> 授權檢查:"; \ if vendor/ffmpeg/darwin/ffmpeg -version 2>&1 | grep -q -- '--enable-gpl'; then \ if [ "$${VISIONA_ALLOW_GPL_FFMPEG:-0}" = "1" ]; then \ echo " !! WARNING: 此 build 含 --enable-gpl(GPL build) !!"; \ echo " !! VISIONA_ALLOW_GPL_FFMPEG=1 已啟用,暫時允許(僅限本地驗收,不可發佈) !!"; \ else \ echo "!! 錯誤:此 build 含 --enable-gpl,違反 LGPL 策略 !!"; \ echo "!! macOS 暫無現成 LGPL binary 來源,需自行 build 或用 VISIONA_ALLOW_GPL_FFMPEG=1 暫時放行 !!"; \ rm -f vendor/ffmpeg/darwin/ffmpeg; \ exit 1; \ fi; \ else \ echo " OK: 未偵測到 --enable-gpl"; \ fi; \ fi vendor-ytdlp: ## 下載 yt-dlp standalone (macOS) → vendor/yt-dlp/darwin/ @mkdir -p vendor/yt-dlp/darwin @if [ ! -f vendor/yt-dlp/darwin/yt-dlp ]; then \ echo "==> 下載 yt-dlp (macOS)..."; \ curl -fL -o vendor/yt-dlp/darwin/yt-dlp "$(YTDLP_URL_DARWIN)"; \ chmod +x vendor/yt-dlp/darwin/yt-dlp; \ echo "==> yt-dlp 大小:$$(du -sh vendor/yt-dlp/darwin/yt-dlp | cut -f1)"; \ vendor/yt-dlp/darwin/yt-dlp --version 2>&1 | head -1; \ else \ echo "==> yt-dlp 已存在,跳過 ($$(du -sh vendor/yt-dlp/darwin/yt-dlp | cut -f1))"; \ fi # ── Build(單元) ────────────────────────────────────────────────── server: build-server ## alias for build-server build-server: build-embed ## build Go server binary → dist/visiona-local-server(會先 build frontend + embed) @mkdir -p $(DIST) cd server && go build -o ../$(DIST)/visiona-local-server . @echo "built: $(DIST)/visiona-local-server" build-server-windows: build-embed ## 交叉/原生 build Windows server → payload/windows/bin/visiona-local-server.exe @mkdir -p payload/windows/bin cd server && GOOS=windows GOARCH=amd64 go build -o ../payload/windows/bin/visiona-local-server.exe . @echo "built: payload/windows/bin/visiona-local-server.exe" build-server-linux: build-embed ## 交叉/原生 build Linux server → payload/linux/bin/visiona-local-server @mkdir -p payload/linux/bin cd server && GOOS=linux GOARCH=amd64 go build -o ../payload/linux/bin/visiona-local-server . @echo "built: payload/linux/bin/visiona-local-server" frontend: build-frontend ## alias for build-frontend build-frontend: ## pnpm build → frontend/out/ @echo "==> pnpm build frontend..." cd frontend && pnpm install --frozen-lockfile && pnpm build @echo "==> frontend/out 完成:" @du -sh frontend/out build-embed: build-frontend ## 同步 frontend/out → server/web/out 供 go:embed @echo "==> 同步 frontend/out → server/web/out..." rm -rf server/web/out mkdir -p server/web/out cp -R frontend/out/. server/web/out/ @du -sh server/web/out # ── Payload 準備 ─────────────────────────────────────────────────── payload: payload-$(OS) ## 依當前 OS 準備 payload payload-macos: build-server vendor-python vendor-wheels vendor-ffmpeg vendor-ytdlp ## 準備 macOS payload → payload/darwin/(含 python runtime + wheels + ffmpeg + yt-dlp) @echo "==> 建立 macOS payload (binary + models + scripts + python + wheels + ffmpeg + yt-dlp)..." rm -rf payload/darwin mkdir -p payload/darwin/bin payload/darwin/data payload/darwin/scripts payload/darwin/python payload/darwin/wheels cp dist/visiona-local-server payload/darwin/bin/ cp vendor/ffmpeg/darwin/ffmpeg payload/darwin/bin/ cp vendor/yt-dlp/darwin/yt-dlp payload/darwin/bin/ chmod +x payload/darwin/bin/ffmpeg payload/darwin/bin/yt-dlp cp -R server/data/* payload/darwin/data/ cp -R server/scripts/* payload/darwin/scripts/ cp vendor/python/darwin/python.tar.gz payload/darwin/python/ @cp vendor/wheels/darwin/*.whl payload/darwin/wheels/ 2>/dev/null || true @echo "==> macOS payload 完成:" @du -sh payload/darwin @echo "" @echo "payload/darwin 結構:" @find payload/darwin -maxdepth 2 -type d | sed 's|^| |' @echo "" @echo "payload/darwin/python:" @ls -lh payload/darwin/python @echo "payload/darwin/wheels:" @ls -1 payload/darwin/wheels | head -20 stage-macos: payload-macos ## 將 payload/darwin/ 放到 Wails build/darwin/Resources/ @echo "==> 放置 payload 到 Wails build/darwin/Resources..." rm -rf visiona-local/build/darwin/Resources mkdir -p visiona-local/build/darwin/Resources cp -R payload/darwin/. visiona-local/build/darwin/Resources/ @echo "==> stage 完成:" @du -sh visiona-local/build/darwin/Resources # ── M4:Windows vendor + payload + wails + exe ───────────────────── # 注意:wails-windows 與 exe 必須在 Windows runner 上跑;在 macOS 上會明確 fail。 # payload-windows / vendor-*-windows 是 curl 下載,跨平台可跑(server.exe 步驟除外)。 PBS_URL_WINDOWS := https://github.com/astral-sh/python-build-standalone/releases/download/$(PBS_RELEASE)/cpython-$(PYTHON_VERSION)+$(PBS_RELEASE)-x86_64-pc-windows-msvc-install_only.tar.gz FFMPEG_URL_WINDOWS := https://github.com/BtbN/FFmpeg-Builds/releases/latest/download/ffmpeg-master-latest-win64-gpl.zip YTDLP_URL_WINDOWS := https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp.exe vendor-python-windows: ## 下載 python-build-standalone Windows x86_64 → vendor/python/windows/ @mkdir -p vendor/python/windows @if [ ! -f vendor/python/windows/python.tar.gz ]; then \ echo "==> 下載 python-build-standalone $(PYTHON_VERSION)+$(PBS_RELEASE) (Windows x86_64 install_only)..."; \ curl -fL -o vendor/python/windows/python.tar.gz "$(PBS_URL_WINDOWS)"; \ echo "==> tarball 大小:$$(du -sh vendor/python/windows/python.tar.gz | cut -f1)"; \ else \ echo "==> python (Windows) tarball 已存在,跳過 ($$(du -sh vendor/python/windows/python.tar.gz | cut -f1))"; \ fi vendor-wheels-windows: ## 同步 Windows wheels → vendor/wheels/windows/ @mkdir -p vendor/wheels/windows @echo "==> 同步內部 wheels (Windows, KneronPLUS 等)..." @if [ -d visiona-local/wheels/windows ]; then \ cp visiona-local/wheels/windows/*.whl vendor/wheels/windows/ 2>/dev/null || true; \ fi @echo "==> 從 PyPI 下載公開相依 wheels (cp312, win_amd64)..." @PY=""; \ for candidate in "$$VISIONA_PYTHON" "py -3" python3 python; do \ [ -z "$$candidate" ] && continue; \ resolved=$$(command -v $${candidate%% *} 2>/dev/null || true); \ case "$$resolved" in *WindowsApps*) continue ;; esac; \ if $$candidate --version >/dev/null 2>&1; then PY="$$candidate"; break; fi; \ done; \ if [ -z "$$PY" ]; then \ echo "WARN: 找不到真實 python(WindowsApps stub 不算),跳過 PyPI 下載(僅使用內部 wheels)"; \ else \ echo "==> 使用 $$PY -m pip"; \ $$PY -m pip download \ --only-binary=:all: \ --platform win_amd64 \ --python-version 3.12 \ --implementation cp \ --dest vendor/wheels/windows \ numpy opencv-python-headless pyusb requests || echo "WARN: pip download 部分失敗(詳見上方訊息)"; \ fi @echo "==> Windows wheels 總覽:" @ls -1 vendor/wheels/windows/*.whl 2>/dev/null | wc -l | xargs -I{} echo " 共 {} 個 wheel" @du -sh vendor/wheels/windows 2>/dev/null || true vendor-ffmpeg-windows: ## 下載 ffmpeg Windows static build → vendor/ffmpeg/windows/ @mkdir -p vendor/ffmpeg/windows @if [ -f vendor/ffmpeg/windows/ffmpeg.exe ]; then \ echo "==> ffmpeg.exe 已存在,跳過"; \ else \ echo "==> 下載 ffmpeg Windows build from BtbN..."; \ echo "!! WARNING: BtbN 為 GPL build;license 由 PM 最終確認 !!"; \ curl -fL -o vendor/ffmpeg/windows/ffmpeg-win.zip "$(FFMPEG_URL_WINDOWS)"; \ PY=""; \ for candidate in "$$VISIONA_PYTHON" "py -3" python3 python; do \ [ -z "$$candidate" ] && continue; \ resolved=$$(command -v $${candidate%% *} 2>/dev/null || true); \ case "$$resolved" in *WindowsApps*) continue ;; esac; \ if $$candidate --version >/dev/null 2>&1; then PY="$$candidate"; break; fi; \ done; \ if [ -z "$$PY" ]; then echo "ERROR: 需要真實 python 來解壓 zip(WindowsApps stub 無法使用)"; exit 1; fi; \ echo "==> 使用 $$PY 解壓 ffmpeg zip"; \ $$PY -c "import zipfile, shutil; z=zipfile.ZipFile('vendor/ffmpeg/windows/ffmpeg-win.zip'); \ members=[n for n in z.namelist() if n.endswith('/bin/ffmpeg.exe')]; \ assert members, 'ffmpeg.exe not found in zip'; \ src=z.open(members[0]); dst=open('vendor/ffmpeg/windows/ffmpeg.exe','wb'); \ shutil.copyfileobj(src, dst); src.close(); dst.close(); z.close()" || { echo "ERROR: python 解壓失敗"; exit 1; }; \ rm -f vendor/ffmpeg/windows/ffmpeg-win.zip; \ [ -f vendor/ffmpeg/windows/ffmpeg.exe ] || { echo "ERROR: ffmpeg.exe 沒被寫出"; exit 1; }; \ echo "==> ffmpeg.exe 大小:$$(du -sh vendor/ffmpeg/windows/ffmpeg.exe | cut -f1)"; \ fi vendor-ytdlp-windows: ## 下載 yt-dlp.exe → vendor/yt-dlp/windows/ @mkdir -p vendor/yt-dlp/windows @if [ ! -f vendor/yt-dlp/windows/yt-dlp.exe ]; then \ echo "==> 下載 yt-dlp.exe..."; \ curl -fL -o vendor/yt-dlp/windows/yt-dlp.exe "$(YTDLP_URL_WINDOWS)"; \ echo "==> yt-dlp.exe 大小:$$(du -sh vendor/yt-dlp/windows/yt-dlp.exe | cut -f1)"; \ else \ echo "==> yt-dlp.exe 已存在,跳過"; \ fi payload-windows: build-server-windows vendor-python-windows vendor-wheels-windows vendor-ffmpeg-windows vendor-ytdlp-windows ## 準備 Windows payload → payload/windows/ @echo "==> 建立 Windows payload (binary + models + scripts + python + wheels + ffmpeg + yt-dlp)..." @# 注意:不 rm -rf payload/windows,因為 build-server-windows 已先把 .exe 放進去 mkdir -p payload/windows/bin payload/windows/data payload/windows/scripts payload/windows/python payload/windows/wheels @if [ ! -f payload/windows/bin/visiona-local-server.exe ]; then \ echo "!! ERROR: payload/windows/bin/visiona-local-server.exe 不存在,build-server-windows 可能失敗 !!"; \ exit 1; \ fi cp vendor/ffmpeg/windows/ffmpeg.exe payload/windows/bin/ cp vendor/yt-dlp/windows/yt-dlp.exe payload/windows/bin/ cp -R server/data/. payload/windows/data/ cp -R server/scripts/. payload/windows/scripts/ cp vendor/python/windows/python.tar.gz payload/windows/python/ @cp vendor/wheels/windows/*.whl payload/windows/wheels/ 2>/dev/null || true @echo "==> Windows payload 完成:" @du -sh payload/windows @echo "" @echo "payload/windows 結構:" @find payload/windows -maxdepth 2 -type d | sed 's|^| |' stage-windows: payload-windows ## 將 payload/windows/ 放到 Wails build/windows/Resources/ @echo "==> 放置 payload 到 Wails build/windows/Resources..." rm -rf visiona-local/build/windows/Resources mkdir -p visiona-local/build/windows/Resources cp -R payload/windows/. visiona-local/build/windows/Resources/ @echo "==> stage 完成:" @du -sh visiona-local/build/windows/Resources # ── M5:Linux vendor + payload + wails + AppImage ────────────────── # 注意:wails-linux 與 appimage 必須在 Linux runner 上跑;在 macOS 上會明確 fail。 # payload-linux / vendor-*-linux 是 curl 下載,跨平台可跑(server 步驟除外)。 PBS_URL_LINUX := https://github.com/astral-sh/python-build-standalone/releases/download/$(PBS_RELEASE)/cpython-$(PYTHON_VERSION)+$(PBS_RELEASE)-x86_64-unknown-linux-gnu-install_only.tar.gz FFMPEG_URL_LINUX := https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz YTDLP_URL_LINUX := https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_linux vendor-python-linux: ## 下載 python-build-standalone Linux x86_64 → vendor/python/linux/ @mkdir -p vendor/python/linux @if [ ! -f vendor/python/linux/python.tar.gz ]; then \ echo "==> 下載 python-build-standalone $(PYTHON_VERSION)+$(PBS_RELEASE) (Linux x86_64 install_only)..."; \ curl -fL -o vendor/python/linux/python.tar.gz "$(PBS_URL_LINUX)"; \ echo "==> tarball 大小:$$(du -sh vendor/python/linux/python.tar.gz | cut -f1)"; \ else \ echo "==> python (Linux) tarball 已存在,跳過 ($$(du -sh vendor/python/linux/python.tar.gz | cut -f1))"; \ fi vendor-wheels-linux: ## 同步 Linux wheels → vendor/wheels/linux/ @mkdir -p vendor/wheels/linux @echo "==> 同步內部 wheels (Linux, KneronPLUS 等)..." @if [ -d visiona-local/wheels/linux ]; then \ cp visiona-local/wheels/linux/*.whl vendor/wheels/linux/ 2>/dev/null || true; \ fi @echo "==> 從 PyPI 下載公開相依 wheels (cp312, manylinux2014_x86_64)..." @pip3 download \ --only-binary=:all: \ --platform manylinux2014_x86_64 \ --python-version 3.12 \ --implementation cp \ --dest vendor/wheels/linux \ numpy opencv-python-headless pyusb requests || echo "WARN: pip download 部分失敗(詳見上方訊息)" @echo "==> Linux wheels 總覽:" @ls -1 vendor/wheels/linux/*.whl 2>/dev/null | wc -l | xargs -I{} echo " 共 {} 個 wheel" @du -sh vendor/wheels/linux 2>/dev/null || true vendor-ffmpeg-linux: ## 下載 ffmpeg Linux static build → vendor/ffmpeg/linux/ @mkdir -p vendor/ffmpeg/linux @if [ -f vendor/ffmpeg/linux/ffmpeg ]; then \ echo "==> ffmpeg (Linux) 已存在,跳過 ($$(du -sh vendor/ffmpeg/linux/ffmpeg | cut -f1))"; \ else \ echo "==> 下載 ffmpeg static build (Linux x86_64) from johnvansickle..."; \ echo "!! WARNING: johnvansickle build 為 GPL build;正式發佈前需改用 LGPL 來源 !!"; \ curl -fL -o /tmp/ffmpeg-linux.tar.xz "$(FFMPEG_URL_LINUX)"; \ mkdir -p /tmp/ffmpeg-linux-extract; \ tar xf /tmp/ffmpeg-linux.tar.xz -C /tmp/ffmpeg-linux-extract --strip-components=1; \ cp /tmp/ffmpeg-linux-extract/ffmpeg vendor/ffmpeg/linux/; \ chmod +x vendor/ffmpeg/linux/ffmpeg; \ rm -rf /tmp/ffmpeg-linux* ; \ echo "==> ffmpeg (Linux) 大小:$$(du -sh vendor/ffmpeg/linux/ffmpeg | cut -f1)"; \ if [ "$${VISIONA_ALLOW_GPL_FFMPEG:-0}" != "1" ]; then \ echo " ⚠️ 提醒:此 build 為 GPL,僅限本地驗收,發佈前請改用 LGPL build"; \ fi; \ fi vendor-ytdlp-linux: ## 下載 yt-dlp (Linux) → vendor/yt-dlp/linux/ @mkdir -p vendor/yt-dlp/linux @if [ ! -f vendor/yt-dlp/linux/yt-dlp ]; then \ echo "==> 下載 yt-dlp (Linux)..."; \ curl -fL -o vendor/yt-dlp/linux/yt-dlp "$(YTDLP_URL_LINUX)"; \ chmod +x vendor/yt-dlp/linux/yt-dlp; \ echo "==> yt-dlp (Linux) 大小:$$(du -sh vendor/yt-dlp/linux/yt-dlp | cut -f1)"; \ else \ echo "==> yt-dlp (Linux) 已存在,跳過"; \ fi payload-linux: build-server-linux vendor-python-linux vendor-wheels-linux vendor-ffmpeg-linux vendor-ytdlp-linux ## 準備 Linux payload → payload/linux/ @echo "==> 建立 Linux payload (binary + models + scripts + python + wheels + ffmpeg + yt-dlp)..." mkdir -p payload/linux/bin payload/linux/data payload/linux/scripts payload/linux/python payload/linux/wheels @if [ ! -f payload/linux/bin/visiona-local-server ]; then \ echo "!! ERROR: payload/linux/bin/visiona-local-server 不存在,build-server-linux 可能失敗 !!"; \ exit 1; \ fi @chmod +x payload/linux/bin/visiona-local-server @cp vendor/ffmpeg/linux/ffmpeg payload/linux/bin/ 2>/dev/null && chmod +x payload/linux/bin/ffmpeg || echo "!! WARN: ffmpeg 缺失" @cp vendor/yt-dlp/linux/yt-dlp payload/linux/bin/ 2>/dev/null && chmod +x payload/linux/bin/yt-dlp || echo "!! WARN: yt-dlp 缺失" @if [ -d server/data ]; then cp -R server/data/. payload/linux/data/; fi @if [ -d server/scripts ]; then cp -R server/scripts/. payload/linux/scripts/; fi @cp vendor/python/linux/python.tar.gz payload/linux/python/ 2>/dev/null || echo "!! WARN: python tarball 缺失" @cp vendor/wheels/linux/*.whl payload/linux/wheels/ 2>/dev/null || true @echo "==> Linux payload 完成:" @du -sh payload/linux 2>/dev/null || true @echo "" @echo "payload/linux 結構:" @find payload/linux -maxdepth 2 -type d 2>/dev/null | sed 's|^| |' # ── Wails build ──────────────────────────────────────────────────── wails-macos: stage-macos ## wails build darwin/amd64 → visiona-local/build/bin/visiona-local.app cd visiona-local && wails build -platform darwin/amd64 -clean cp -R visiona-local/build/darwin/Resources/bin \ visiona-local/build/darwin/Resources/data \ visiona-local/build/darwin/Resources/scripts \ visiona-local/build/darwin/Resources/python \ visiona-local/build/darwin/Resources/wheels \ visiona-local/build/bin/visiona-local.app/Contents/Resources/ codesign --force --deep --sign - visiona-local/build/bin/visiona-local.app codesign --verify --verbose visiona-local/build/bin/visiona-local.app @du -sh visiona-local/build/bin/visiona-local.app wails-windows: stage-windows ## ⚠️ 必須在 Windows runner 上執行:wails build -platform windows/amd64 @case "$$(uname -s 2>/dev/null)" in \ MINGW*|CYGWIN*|MSYS*) : ;; \ *) \ echo ""; \ echo "❌ wails-windows 只能在 Windows runner 上 build(偵測到 $$(uname -s))"; \ echo " 請在 Windows 機器上執行此 target,或用 CI 的 Windows runner"; \ echo " 若只是要檢查前置步驟,可單獨跑 make stage-windows"; \ echo ""; \ exit 1 ;; \ esac cd visiona-local && wails build -platform windows/amd64 -clean @du -sh visiona-local/build/bin/visiona-local.exe wails-linux: payload-linux ## ⚠️ 必須在 Linux runner 上執行:wails build -platform linux/amd64 @if [ "$$(uname -s)" != "Linux" ]; then \ echo ""; \ echo "❌ wails-linux 只能在 Linux 上 build(偵測到 $$(uname -s))"; \ echo " 請在 Linux x86_64 runner(GitHub Actions ubuntu-latest 即可)執行"; \ echo " 若只是要檢查前置步驟,可單獨跑 make payload-linux"; \ echo ""; \ exit 1; \ fi cd visiona-local && wails build -platform linux/amd64 -clean @du -sh visiona-local/build/bin/visiona-local # ── 安裝檔打包 ───────────────────────────────────────────────────── dmg: wails-macos ## hdiutil UDZO → dist/visiona-local.dmg mkdir -p $(DIST) rm -f $(DIST)/visiona-local.dmg hdiutil create -volname "visionA-local" \ -srcfolder visiona-local/build/bin/visiona-local.app \ -ov -format UDZO \ $(DIST)/visiona-local.dmg @du -sh $(DIST)/visiona-local.dmg @file $(DIST)/visiona-local.dmg exe: wails-windows ## ⚠️ 必須在 Windows 上跑:Inno Setup → dist/visiona-local-*-windows-x64.exe @ISCC_BIN="$$ISCC"; \ if [ -z "$$ISCC_BIN" ]; then \ if command -v iscc > /dev/null 2>&1; then ISCC_BIN=iscc; \ elif command -v iscc.exe > /dev/null 2>&1; then ISCC_BIN=iscc.exe; \ else \ for p in "/c/Program Files (x86)/Inno Setup 6/ISCC.exe" \ "/c/Program Files/Inno Setup 6/ISCC.exe" \ "$$LOCALAPPDATA/Programs/Inno Setup 6/ISCC.exe" \ "$$USERPROFILE/AppData/Local/Programs/Inno Setup 6/ISCC.exe" \ "/c/Program Files (x86)/Inno Setup 5/ISCC.exe"; do \ if [ -f "$$p" ]; then ISCC_BIN="$$p"; break; fi; \ done; \ fi; \ fi; \ ISCC_OK=0; \ if [ -n "$$ISCC_BIN" ]; then \ if [ -e "$$ISCC_BIN" ] || command -v "$$ISCC_BIN" > /dev/null 2>&1; then ISCC_OK=1; fi; \ fi; \ if [ "$$ISCC_OK" = "0" ]; then \ echo ""; \ echo "❌ Inno Setup Compiler (iscc) 未安裝 / 找不到"; \ echo " 已嘗試偵測的路徑:"; \ echo " - \$$ISCC 環境變數"; \ echo " - PATH 上的 iscc / iscc.exe"; \ echo " - /c/Program Files (x86)/Inno Setup 6/ISCC.exe"; \ echo " - /c/Program Files/Inno Setup 6/ISCC.exe"; \ echo " 請從 https://jrsoftware.org/isdl.php 下載並安裝 Inno Setup 6"; \ echo " 或設定 ISCC 環境變數指向 ISCC.exe 絕對路徑"; \ echo ""; \ exit 1; \ fi; \ echo "==> 使用 ISCC: $$ISCC_BIN"; \ mkdir -p $(DIST); \ echo "==> 執行 iscc(cwd: $$(pwd))..."; \ "$$ISCC_BIN" installer/windows/visiona-local.iss; \ ISCC_RC=$$?; \ echo "==> iscc exit code: $$ISCC_RC"; \ if [ $$ISCC_RC -ne 0 ]; then exit $$ISCC_RC; fi @echo "==> 產出:" @ls -lh $(DIST)/ 2>/dev/null || echo "dist 目錄不存在" @ls -lh $(DIST)/visiona-local-*-windows-x64.exe 2>/dev/null || echo "未找到 .exe 產出檔" appimage: wails-linux ## ⚠️ 必須在 Linux 上跑:build-appimage.sh → dist/visiona-local-*-linux-x64.AppImage @if [ "$$(uname -s)" != "Linux" ]; then \ echo ""; \ echo "❌ appimage 只能在 Linux 上 build(偵測到 $$(uname -s))"; \ echo " 需要 appimagetool,請在 Linux x86_64 runner 執行"; \ echo ""; \ exit 1; \ fi @mkdir -p $(DIST) VERSION=$(VERSION) bash installer/linux/build-appimage.sh @echo "==> 產出:" @ls -lh $(DIST)/visiona-local-*-linux-x64.AppImage 2>/dev/null || echo "未找到產出檔" # ── 開發(placeholder) ──────────────────────────────────────────── dev: @echo "TODO: make -j2 dev-server dev-frontend(開發模式)" dev-mock: @echo "TODO: make -j2 dev-mock-server dev-frontend(Mock 模式)" test: @echo "TODO: go test + pnpm test" lint: @echo "TODO: go vet + pnpm lint" fmt: @echo "TODO: go fmt" # ── 清理 ─────────────────────────────────────────────────────────── clean: ## 清除 dist/ 與 payload/ 產物 @echo "Cleaning dist/ and payload artifacts..." rm -rf $(DIST) rm -rf $(PAYLOAD) @mkdir -p $(DIST) $(PAYLOAD) @touch $(DIST)/.gitkeep $(PAYLOAD)/.gitkeep @echo "Done."