# # Makefile # Zyquo MLX # # Author: Simon-Pierre Boucher # Mail: contact@spboucher.ai # # Command-line-only builds (no Xcode IDE, no .xcodeproj) per docs/BUILD.md. # Requires full Xcode installed as a toolchain + Metal toolchain component: # sudo xcode-select -s /Applications/Xcode.app # xcodebuild -downloadComponent metalToolchain # APP_NAME := Zyquo MLX EXEC_NAME := ZyquoMLX BUNDLE_ID := com.zyquo.mlx DIST := dist APP := $(DIST)/$(APP_NAME).app CONFIG ?= release # Use Xcode's toolchain per-command; the machine default (CLT) is untouched. XCODE_DEV_DIR := /Applications/Xcode.app/Contents/Developer DEV := DEVELOPER_DIR=$(XCODE_DEV_DIR) # Build backend per docs/BUILD.md §3.1 (RESOLVED 2026-07-30): the new # SwiftPM backend (--build-system swiftbuild, Swift 6.4) compiles the Metal # kernels and emits mlx-swift_Cmlx.bundle/default.metallib. The classic # backend silently skips Metal → broken GPU path. xcodebuild kept as fallback. BUILD_DIR := .build CONFIG_TITLE = $(shell echo $(CONFIG) | awk '{print toupper(substr($$0,1,1)) substr($$0,2)}') BIN_DIR = $(BUILD_DIR)/out/Products/$(CONFIG_TITLE) .PHONY: build build-xcodebuild app dev run icon icon-if-missing clean release dmg check-toolchain check-toolchain: @test -d $(XCODE_DEV_DIR) || { echo "error: Xcode toolchain not found at /Applications/Xcode.app — see docs/BUILD.md §2"; exit 1; } build: check-toolchain $(DEV) swift build --build-system swiftbuild --arch arm64 -c $(CONFIG) build-xcodebuild: check-toolchain $(DEV) xcodebuild build \ -scheme $(EXEC_NAME) \ -destination 'platform=OS X,arch=arm64' \ -configuration Release \ -derivedDataPath $(BUILD_DIR)/xcode \ -skipMacroValidation app: build rm -rf "$(APP)" mkdir -p "$(APP)/Contents/MacOS" "$(APP)/Contents/Resources" cp Support/Info.plist "$(APP)/Contents/Info.plist" cp "$(BIN_DIR)/$(EXEC_NAME)" "$(APP)/Contents/MacOS/$(EXEC_NAME)" @# Ship every SPM resource bundle — mlx-swift_Cmlx.bundle carries @# default.metallib; omitting it crashes GPU ops (docs/BUILD.md §3.2). @for b in "$(BIN_DIR)"/*.bundle; do \ [ -e "$$b" ] && cp -R "$$b" "$(APP)/Contents/Resources/" && echo "bundled $$(basename $$b)"; \ done; true @if [ -f assets/icon/AppIcon.icns ]; then cp assets/icon/AppIcon.icns "$(APP)/Contents/Resources/AppIcon.icns"; fi codesign --force --deep --sign - "$(APP)" @echo "→ $(APP) (ad-hoc signed)" dev: app open "$(APP)" run: app "$(APP)/Contents/MacOS/$(EXEC_NAME)" icon: @# Phase 5: SVG → PNGs → .iconset → .icns (rsvg-convert + iconutil) bash scripts/make-icon.sh # Phase 8: Developer ID signing + notarization + stapling (docs/BUILD.md §4). # Identity/profile reused from the zyquo-term pipeline (charter Phase 8). IDENTITY := Developer ID Application: Simon-Pierre Boucher (3YM54G49SN) KEYCHAIN_PROFILE := MacLustr-Notarize ENTITLEMENTS := Support/entitlements.plist ZIP := $(DIST)/ZyquoMLX.zip DMG := $(DIST)/ZyquoMLX.dmg release: build icon-if-missing rm -rf "$(APP)" mkdir -p "$(APP)/Contents/MacOS" "$(APP)/Contents/Resources" cp Support/Info.plist "$(APP)/Contents/Info.plist" cp "$(BIN_DIR)/$(EXEC_NAME)" "$(APP)/Contents/MacOS/$(EXEC_NAME)" @for b in "$(BIN_DIR)"/*.bundle; do \ [ -e "$$b" ] && cp -R "$$b" "$(APP)/Contents/Resources/" && echo "bundled $$(basename $$b)"; \ done; true cp assets/icon/AppIcon.icns "$(APP)/Contents/Resources/AppIcon.icns" @echo "=== Signing (Developer ID, hardened runtime, nested-first) ===" @# Resource bundles carry no Mach-O code (metallib + .py are data) but we @# sign any nested code defensively before the outer bundle. @find "$(APP)/Contents/Resources" -type f \( -name "*.dylib" -o -name "*.so" \) -exec \ codesign --force --options runtime --timestamp --sign "$(IDENTITY)" {} \; 2>/dev/null; true codesign --force --options runtime --timestamp \ --entitlements $(ENTITLEMENTS) \ --sign "$(IDENTITY)" "$(APP)/Contents/MacOS/$(EXEC_NAME)" codesign --force --options runtime --timestamp \ --entitlements $(ENTITLEMENTS) \ --sign "$(IDENTITY)" "$(APP)" codesign --verify --deep --strict --verbose=2 "$(APP)" @echo "=== Notarizing (profile: $(KEYCHAIN_PROFILE)) ===" rm -f "$(ZIP)" ditto -c -k --keepParent "$(APP)" "$(ZIP)" xcrun notarytool submit "$(ZIP)" --keychain-profile "$(KEYCHAIN_PROFILE)" --wait xcrun stapler staple "$(APP)" @echo "=== Verifying ===" spctl -a -vv "$(APP)" xcrun stapler validate "$(APP)" @echo "→ $(APP) (Developer ID signed, notarized, stapled)" dmg: rm -f "$(DMG)" rm -rf $(DIST)/dmg_temp mkdir -p $(DIST)/dmg_temp cp -R "$(APP)" $(DIST)/dmg_temp/ ln -s /Applications "$(DIST)/dmg_temp/Applications" hdiutil create -volname "$(APP_NAME)" -srcfolder $(DIST)/dmg_temp -ov -format UDZO "$(DMG)" rm -rf $(DIST)/dmg_temp codesign --force --sign "$(IDENTITY)" --timestamp "$(DMG)" xcrun notarytool submit "$(DMG)" --keychain-profile "$(KEYCHAIN_PROFILE)" --wait xcrun stapler staple "$(DMG)" @echo "→ $(DMG) (signed, notarized, stapled)" icon-if-missing: @test -f assets/icon/AppIcon.icns || bash scripts/make-icon.sh clean: rm -rf $(BUILD_DIR) $(DIST)