.PHONY: \ lint test clean \ version-sync version-check \ server-run server-build server-lint server-test server-clean \ desktop-build-mac desktop-build-win desktop-build-linux \ desktop-lint desktop-test desktop-clean \ release-assets-linux release-assets-windows release-assets-macos \ _backend-lint _backend-test _backend-clean _backend-build \ _frontend-install _frontend-build _frontend-check _frontend-test _frontend-dev _frontend-clean \ _desktop-test _desktop-clean _desktop-prepare-frontend _desktop-prepare-embedfs _desktop-prepare-windows-resource \ _server-run-backend _server-run-frontend VERSION := $(shell go run ./backend/cmd/versionctl print) GIT_COMMIT ?= $(shell git rev-parse --short HEAD 2>/dev/null || printf 'unknown') BUILD_TIME ?= $(shell date -u +"%Y-%m-%dT%H:%M:%SZ") GO_LDFLAGS := -X nex/backend/pkg/buildinfo.version=$(VERSION) -X nex/backend/pkg/buildinfo.commit=$(GIT_COMMIT) -X nex/backend/pkg/buildinfo.buildTime=$(BUILD_TIME) GO_LDFLAGS_WIN := $(GO_LDFLAGS) -H=windowsgui RELEASE_DIR := build/release SERVER_LINUX_ASSET := $(shell go run ./backend/cmd/versionctl asset-name server linux amd64) SERVER_WINDOWS_ASSET := $(shell go run ./backend/cmd/versionctl asset-name server windows amd64) SERVER_DARWIN_AMD64_ASSET := $(shell go run ./backend/cmd/versionctl asset-name server darwin amd64) SERVER_DARWIN_ARM64_ASSET := $(shell go run ./backend/cmd/versionctl asset-name server darwin arm64) DESKTOP_LINUX_ASSET := $(shell go run ./backend/cmd/versionctl asset-name desktop linux) DESKTOP_WINDOWS_ASSET := $(shell go run ./backend/cmd/versionctl asset-name desktop windows) DESKTOP_MACOS_ASSET := $(shell go run ./backend/cmd/versionctl asset-name desktop macos) # ============================================ # 全局命令 # ============================================ lint: _backend-lint _frontend-check @printf 'Lint complete\n' test: _backend-test _frontend-test _desktop-test @printf 'All tests passed\n' clean: _backend-clean _frontend-clean _desktop-clean @printf 'Clean complete\n' # ============================================ # 版本管理 # ============================================ version-sync: go run ./backend/cmd/versionctl sync version-check: go run ./backend/cmd/versionctl check # ============================================ # Server 模式 # ============================================ server-run: @$(MAKE) -j2 _server-run-backend _server-run-frontend server-build: version-check _backend-build _frontend-build @printf 'Server build complete\n' server-lint: _backend-lint _frontend-check @printf 'Server lint complete\n' server-test: _backend-test _frontend-test @printf 'Server tests passed\n' server-clean: _backend-clean _frontend-clean @printf 'Server artifacts cleaned\n' _server-run-backend: @$(MAKE) -C backend run _server-run-frontend: _frontend-install cd frontend && bun run dev # ============================================ # Desktop 模式 # ============================================ desktop-build-mac: version-check _desktop-prepare-frontend _desktop-prepare-embedfs @printf 'Building macOS desktop...\n' cd backend && CGO_ENABLED=1 GOOS=darwin GOARCH=arm64 go build -ldflags "$(GO_LDFLAGS)" -o ../build/nex-mac-arm64 ./cmd/desktop cd backend && CGO_ENABLED=1 GOOS=darwin GOARCH=amd64 go build -ldflags "$(GO_LDFLAGS)" -o ../build/nex-mac-amd64 ./cmd/desktop lipo -create build/nex-mac-arm64 build/nex-mac-amd64 -output build/nex-mac-universal @printf 'Packaging macOS app bundle...\n' mkdir -p build/Nex.app/Contents/MacOS build/Nex.app/Contents/Resources cp build/nex-mac-universal build/Nex.app/Contents/MacOS/nex @if [ -f assets/icon.icns ]; then \ cp assets/icon.icns build/Nex.app/Contents/Resources/; \ else \ printf 'Missing assets/icon.icns\n'; \ fi @MIN_MACOS_VERSION=$$(vtool -show-build build/nex-mac-universal | awk '/minos / {print $$2; exit}'); \ if [ -z "$$MIN_MACOS_VERSION" ]; then \ printf 'Unable to read macOS minimum version\n'; \ exit 1; \ fi; \ go run ./backend/cmd/versionctl macos-plist "$$MIN_MACOS_VERSION" > build/Nex.app/Contents/Info.plist chmod +x build/Nex.app/Contents/MacOS/nex @printf 'macOS desktop build complete\n' desktop-build-win: version-check _desktop-prepare-frontend _desktop-prepare-embedfs _desktop-prepare-windows-resource @printf 'Building Windows desktop...\n' ifeq ($(OS),Windows_NT) powershell -NoProfile -Command "New-Item -ItemType Directory -Path 'build' -Force | Out-Null" cd backend && set "CGO_ENABLED=1"&& set "GOOS=windows"&& set "GOARCH=amd64"&& go build -ldflags "$(GO_LDFLAGS_WIN)" -o ../build/nex-win-amd64.exe ./cmd/desktop else mkdir -p build cd backend && CGO_ENABLED=1 GOOS=windows GOARCH=amd64 go build -ldflags "$(GO_LDFLAGS_WIN)" -o ../build/nex-win-amd64.exe ./cmd/desktop endif @printf 'Windows desktop build complete\n' desktop-build-linux: version-check _desktop-prepare-frontend _desktop-prepare-embedfs @printf 'Building Linux desktop...\n' cd backend && CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -ldflags "$(GO_LDFLAGS)" -o ../build/nex-linux-amd64 ./cmd/desktop @printf 'Linux desktop build complete\n' desktop-lint: _backend-lint _frontend-check @printf 'Desktop lint complete\n' desktop-test: _desktop-test @printf 'Desktop tests passed\n' desktop-clean: _desktop-clean @printf 'Desktop artifacts cleaned\n' _desktop-test: cd backend && go test ./cmd/desktop/... -v _desktop-clean: rm -rf build/ embedfs/assets embedfs/frontend-dist backend/cmd/desktop/rsrc_windows_amd64.syso _desktop-prepare-frontend: _frontend-install @printf 'Preparing frontend for desktop...\n' ifeq ($(OS),Windows_NT) powershell -NoProfile -Command "Copy-Item -LiteralPath 'frontend/.env.desktop' -Destination 'frontend/.env.production.local' -Force" cd frontend && bun run build powershell -NoProfile -Command "Remove-Item -LiteralPath 'frontend/.env.production.local' -Force -ErrorAction SilentlyContinue" else cd frontend && cp .env.desktop .env.production.local cd frontend && bun run build rm -f frontend/.env.production.local endif _desktop-prepare-embedfs: @printf 'Preparing embedded filesystem...\n' ifeq ($(OS),Windows_NT) powershell -NoProfile -Command "Remove-Item -LiteralPath 'embedfs/assets' -Recurse -Force -ErrorAction SilentlyContinue; Remove-Item -LiteralPath 'embedfs/frontend-dist' -Recurse -Force -ErrorAction SilentlyContinue; Copy-Item -LiteralPath 'assets' -Destination 'embedfs/assets' -Recurse; Copy-Item -LiteralPath 'frontend/dist' -Destination 'embedfs/frontend-dist' -Recurse" else rm -rf embedfs/assets embedfs/frontend-dist cp -r assets embedfs/assets cp -r frontend/dist embedfs/frontend-dist endif _desktop-prepare-windows-resource: @printf 'Preparing Windows executable icon...\n' ifeq ($(OS),Windows_NT) cd backend/cmd/desktop && windres -O coff -F pe-x86-64 -i icon_windows.rc -o rsrc_windows_amd64.syso else @if command -v x86_64-w64-mingw32-windres >/dev/null 2>&1; then \ cd backend/cmd/desktop && x86_64-w64-mingw32-windres -O coff -F pe-x86-64 -i icon_windows.rc -o rsrc_windows_amd64.syso; \ elif command -v windres >/dev/null 2>&1; then \ cd backend/cmd/desktop && windres -O coff -F pe-x86-64 -i icon_windows.rc -o rsrc_windows_amd64.syso; \ else \ printf 'Missing windres for Windows icon resource generation\n'; \ exit 1; \ fi endif # ============================================ # 发布资产 # ============================================ release-assets-linux: version-check desktop-build-linux rm -rf "$(RELEASE_DIR)" mkdir -p "$(RELEASE_DIR)" cd backend && CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -ldflags "$(GO_LDFLAGS)" -o ../build/nex-server-linux-amd64 ./cmd/server tar -C build -czf "$(RELEASE_DIR)/$(SERVER_LINUX_ASSET)" nex-server-linux-amd64 tar -C build -czf "$(RELEASE_DIR)/$(DESKTOP_LINUX_ASSET)" nex-linux-amd64 release-assets-windows: version-check desktop-build-win ifeq ($(OS),Windows_NT) powershell -NoProfile -Command "Remove-Item -LiteralPath '$(RELEASE_DIR)' -Recurse -Force -ErrorAction SilentlyContinue; New-Item -ItemType Directory -Path '$(RELEASE_DIR)' -Force | Out-Null" cd backend && set "CGO_ENABLED=1"&& set "GOOS=windows"&& set "GOARCH=amd64"&& go build -ldflags "$(GO_LDFLAGS_WIN)" -o ../build/nex-server-win-amd64.exe ./cmd/server powershell -NoProfile -Command "Compress-Archive -LiteralPath 'build/nex-server-win-amd64.exe' -DestinationPath '$(RELEASE_DIR)/$(SERVER_WINDOWS_ASSET)' -Force" powershell -NoProfile -Command "Compress-Archive -LiteralPath 'build/nex-win-amd64.exe' -DestinationPath '$(RELEASE_DIR)/$(DESKTOP_WINDOWS_ASSET)' -Force" else @printf 'release-assets-windows requires Windows\n' @exit 1 endif release-assets-macos: version-check desktop-build-mac rm -rf "$(RELEASE_DIR)" mkdir -p "$(RELEASE_DIR)" cd backend && CGO_ENABLED=1 GOOS=darwin GOARCH=amd64 go build -ldflags "$(GO_LDFLAGS)" -o ../build/nex-server-darwin-amd64 ./cmd/server cd backend && CGO_ENABLED=1 GOOS=darwin GOARCH=arm64 go build -ldflags "$(GO_LDFLAGS)" -o ../build/nex-server-darwin-arm64 ./cmd/server tar -C build -czf "$(RELEASE_DIR)/$(SERVER_DARWIN_AMD64_ASSET)" nex-server-darwin-amd64 tar -C build -czf "$(RELEASE_DIR)/$(SERVER_DARWIN_ARM64_ASSET)" nex-server-darwin-arm64 ditto -c -k --keepParent build/Nex.app "$(RELEASE_DIR)/$(DESKTOP_MACOS_ASSET)" # ============================================ # 共享 helper targets # ============================================ _backend-build: @$(MAKE) -C backend build _backend-lint: @$(MAKE) -C backend lint _backend-test: @$(MAKE) -C backend test _backend-clean: @$(MAKE) -C backend clean _frontend-install: cd frontend && bun install _frontend-build: _frontend-install cd frontend && bun run build _frontend-check: _frontend-install cd frontend && bun run check _frontend-test: _frontend-install cd frontend && bun run test _frontend-dev: _frontend-install cd frontend && bun run dev _frontend-clean: rm -rf frontend/dist frontend/.next frontend/coverage frontend/playwright-report frontend/test-results frontend/tsconfig.tsbuildinfo