feat(build): milestone alpha build — conditional deps; IRCKit and RoyalVNCKit disabled by default

This marks a milestone alpha build of Prole.

Key changes
- build.sh now supports conditional third‑party frameworks via env flags:
  - PROLE_ENABLE_IRCKit (default: 0)
  - PROLE_ENABLE_RoyalVNCKit (default: 0)
- When disabled, IRCKit/RoyalVNCKit are not fetched, linked, or embedded.
- Swift compile receives convenience defines for conditional compilation:
  - -D PROLE_ENABLE_IRCKit or -D PROLE_ENABLE_IRCKit_DISABLED
  - -D PROLE_ENABLE_RoyalVNCKit or -D PROLE_ENABLE_RoyalVNCKit_DISABLED
- Archive flow stabilized; optional SSL-less path for IRCKit remains available via PROLE_DISABLE_SSL=1.
- App bundle generation unchanged (plist, icons, resources, codesign).

Why
- Restore a clean build on current toolchains and allow incremental re‑enablement of optional integrations.
- Make the build resilient: dependencies can be toggled on CI or locally without modifying sources.

Usage examples
- Default (alpha baseline; both off):
  ./build.sh build --arch arm64
- Enable IRCKit only:
  PROLE_ENABLE_IRCKit=1 ./build.sh build --arch arm64
- Enable RoyalVNCKit only:
  PROLE_ENABLE_RoyalVNCKit=1 ./build.sh build --arch arm64
- Enable both:
  PROLE_ENABLE_IRCKit=1 PROLE_ENABLE_RoyalVNCKit=1 ./build.sh build --arch arm64

Notes
- IRCKit SSL-less switch remains available if needed:
  PROLE_DISABLE_SSL=1 PROLE_ENABLE_IRCKit=1 ./build.sh build --arch arm64
- Current alpha defaults keep both IRCKit and RoyalVNCKit disabled.

Build: 2025-12-05 14:21 local
This commit is contained in:
chrisfu 2025-12-05 14:22:58 -08:00
parent f93ce88d33
commit de1a34b1c2
2 changed files with 485 additions and 3 deletions

View File

@ -14,6 +14,8 @@ import AppKit
final class AppDelegate: NSObject, NSApplicationDelegate {
private var overlayWindowController: OverlayWindowController!
private var mainWindowController: MainWindowController!
private var workstationWindowController: WorkstationWindowController!
private var ircWindowController: IRCWindowController!
private var hotKeyManager: HotKeyManager!
private var serviceChecker: ServiceChecker!
private var statusItemController: StatusItemController!
@ -65,6 +67,14 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
), pfManager: pfManager)
dlog("MainWindowController created")
// Workstation window (VNC viewer) — shown to the right of Prole Status when not in status bar mode
workstationWindowController = WorkstationWindowController()
dlog("WorkstationWindowController created")
// IRC window — short vertically, long horizontally, positioned below Prole Status
ircWindowController = IRCWindowController()
dlog("IRCWindowController created")
// Create status bar item with icon and click handler
// Status bar item with a monospaced "P" and its own right‑click menu
statusItemController = StatusItemController(actions: .init(
@ -143,6 +153,8 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
private func switchToStatusBarMode() {
mode = .statusBar
mainWindowController.hide()
workstationWindowController.hide()
ircWindowController.hide()
overlayWindowController.showOverlayNow()
NSApp.setActivationPolicy(.accessory)
statusItemController.setState(statusBarVisible: statusItemController.isVisible, appWindowVisible: false)
@ -154,6 +166,12 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
mode = .appWindow
overlayWindowController.hideOverlay()
mainWindowController.show()
// Position workstation window to the right of the main window and show it
if let ref = mainWindowController.window { workstationWindowController.position(rightOf: ref) }
workstationWindowController.show()
// Position IRC window below the main window and show it
if let ref = mainWindowController.window { ircWindowController.position(below: ref) }
ircWindowController.show()
NSApp.setActivationPolicy(.regular)
statusItemController.setState(statusBarVisible: statusItemController.isVisible, appWindowVisible: true)
dlog("Switched to Application Window mode")
@ -173,9 +191,15 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
private func toggleAppWindowVisibility() {
if mainWindowController.isVisible {
mainWindowController.hide()
workstationWindowController.hide()
ircWindowController.hide()
if mode == .appWindow { mode = .statusBar }
} else {
mainWindowController.show()
if let ref = mainWindowController.window { workstationWindowController.position(rightOf: ref) }
workstationWindowController.show()
if let ref = mainWindowController.window { ircWindowController.position(below: ref) }
ircWindowController.show()
mode = .appWindow
overlayWindowController.hideOverlay()
}

View File

@ -2,7 +2,7 @@
set -euo pipefail
# Prole build script — builds a macOS .app bundle without launching Xcode
# Requirements: Xcode Command Line Tools (swiftc, codesign, plutil, lipo)
# Requirements: Xcode Command Line Tools (swiftc, codesign, plutil, lipo, xcodebuild)
APP_NAME="Prole"
BUNDLE_ID="org.prole.${APP_NAME}"
@ -19,9 +19,20 @@ MACOS_DIR="$CONTENTS_DIR/MacOS"
RESOURCES_DIR="$CONTENTS_DIR/Resources"
INFO_PLIST="$CONTENTS_DIR/Info.plist"
PARENT_DIR="$(cd "$ROOT_DIR/.." && pwd)"
LOG_DIR="$BUILD_DIR/logs"
ARCH_CURRENT="$(uname -m)" # arm64 or x86_64
# Mandatory dependency sources (auto-fetched)
URL_IRCKIT="https://github.com/FuelRats/IRCKit/archive/refs/tags/0.16.0.tar.gz"
URL_ROYALVNC="https://github.com/royalapplications/royalvnc/archive/refs/tags/1.0.1.tar.gz"
# Deps staging directories
DEPS_DIR="$BUILD_DIR/deps"
DEPS_SRC_DIR="$DEPS_DIR/src"
DEPS_OUT_DIR="$DEPS_DIR/out"
DEPS_LOGS_DIR="$DEPS_OUT_DIR/logs"
usage() {
cat <<EOF
Usage: $(basename "$0") [command] [options]
@ -43,11 +54,23 @@ Examples:
./build.sh build-universal
./build.sh run
./build.sh package
Dependencies:
This script can download, build, link, and embed IRCKit (0.16.0)
and RoyalVNCKit (1.0.1). These are optional and controlled by flags below.
Environment overrides (optional):
IRCKIT_XCFRAMEWORK If set, use this IRCKit.xcframework instead of building
ROYALVNCKIT_XCFRAMEWORK If set, use this RoyalVNCKit.xcframework instead of building
KEEP_DEPS=1 Keep the deps staging directory after the build (for debugging)
PROLE_DISABLE_SSL=1 Build IRCKit with SSL/TLS disabled (compilation condition); may also update deps to Swift‑6‑compatible NIO
PROLE_ENABLE_IRCKit=0 Enable building/linking IRCKit (0/1). Default: 0 (disabled)
PROLE_ENABLE_RoyalVNCKit=0 Enable building/linking RoyalVNCKit (0/1). Default: 0 (disabled)
EOF
}
ensure_dirs() {
mkdir -p "$BUILD_DIR" "$DIST_DIR" "$MACOS_DIR" "$RESOURCES_DIR"
mkdir -p "$BUILD_DIR" "$DIST_DIR" "$MACOS_DIR" "$RESOURCES_DIR" "$CONTENTS_DIR/Frameworks" "$DEPS_SRC_DIR" "$DEPS_OUT_DIR" "$LOG_DIR" "$DEPS_LOGS_DIR"
}
# Read a key from prole.properties (very simple parser)
@ -233,6 +256,302 @@ copy_extra_resources() {
fi
}
# --- Dependency preparation (mandatory IRCKit & RoyalVNCKit) ---
have_cmd() { command -v "$1" >/dev/null 2>&1; }
fetch_tarball() {
local url="$1"; local out_tar="$2"
echo "[deps] Fetching: $url"
if have_cmd wget; then
wget -q -O "$out_tar" "$url"
else
curl -LsSf -o "$out_tar" "$url"
fi
}
extract_tarball() {
local tarpath="$1"; local destdir="$2"
mkdir -p "$destdir"
tar -xzf "$tarpath" -C "$destdir"
}
# Run a command and tee stdout/stderr to a logfile, returning the command's exit status
log_exec() {
local logfile="$1"; shift
mkdir -p "$(dirname "$logfile")"
echo "[log] → $logfile"
"$@" 2>&1 | tee "$logfile"
return ${PIPESTATUS[0]}
}
xc_archive_one() {
local proj_dir="$1"; local scheme="$2"; local arch="$3"; local outdir="$4"
local archive_path="$outdir/${scheme}-macos-${arch}.xcarchive"
echo "[xcodebuild] archive scheme=$scheme arch=$arch"
local proj_opt=()
# If an explicit Xcode project matching the scheme exists, use it (rare for SwiftPM)
if [[ -d "$proj_dir/${scheme}.xcodeproj" ]]; then
proj_opt=( -project "${scheme}.xcodeproj" )
fi
local logfile="$LOG_DIR/xcode-archive-${scheme}-${arch}.log"
if [[ "$scheme" == "IRCKit" ]]; then logfile="$DEPS_LOGS_DIR/irckit-archive-${arch}.log"; fi
if [[ "$scheme" == "RoyalVNCKit" ]]; then logfile="$DEPS_LOGS_DIR/royalvnc-archive-${arch}.log"; fi
pushd "$proj_dir" >/dev/null || return 1
# Per-scheme extra flags / xcconfig
local extra_args=()
if [[ "$scheme" == "IRCKit" && -n ${PROLE_DISABLE_SSL:-} ]]; then
echo "[deps] PROLE_DISABLE_SSL=1: building IRCKit with PROLE_DISABLE_SSL compilation condition"
# Use a temporary xcconfig to avoid shell/argument parsing issues on the command line
local xcflags_file="$outdir/irckit-prole-disable-ssl.xcconfig"
# IMPORTANT: Keep this file strictly to valid xcconfig key/value lines (no comments or extra text)
cat > "$xcflags_file" <<'XCCONFIG'
SWIFT_ACTIVE_COMPILATION_CONDITIONS = $(inherited) PROLE_DISABLE_SSL
OTHER_SWIFT_FLAGS = $(inherited) -DPROLE_DISABLE_SSL
SWIFT_VERSION = 5.0
SWIFT_STRICT_CONCURRENCY = minimal
XCCONFIG
extra_args+=( -xcconfig "$xcflags_file" )
fi
log_exec "$logfile" xcodebuild archive \
-scheme "$scheme" \
-destination 'generic/platform=macOS' \
-configuration Release \
-archivePath "$archive_path" \
-sdk macosx \
-disableAutomaticPackageResolution \
SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES ARCHS="$arch" ONLY_ACTIVE_ARCH=NO \
SWIFT_VERSION=5.0 \
SWIFT_STRICT_CONCURRENCY=minimal \
MACOSX_DEPLOYMENT_TARGET="${MIN_MACOS}" \
${extra_args[@]:-} \
${proj_opt[@]:-} \
|| { popd >/dev/null; return 1; }
popd >/dev/null
}
build_irckit_xcframework() {
# If external override exists, trust it
if [[ -n ${IRCKIT_XCFRAMEWORK:-} && -d ${IRCKIT_XCFRAMEWORK} ]]; then
echo "[deps] Using provided IRCKit.xcframework: ${IRCKIT_XCFRAMEWORK}"
return 0
fi
local work="$DEPS_SRC_DIR/IRCKit-src"
local tar="$DEPS_DIR/irckit.tar.gz"
rm -rf "$work"; mkdir -p "$work"
fetch_tarball "$URL_IRCKIT" "$tar"
extract_tarball "$tar" "$work"
# Detect package or project root robustly
local src_root=""
# 1) If a Package.swift exists directly under work
if [[ -f "$work/Package.swift" ]]; then
src_root="$work"
fi
# 2) Else pick the first child directory that contains a Package.swift
if [[ -z "$src_root" ]]; then
for d in "$work"/*; do
[[ -d "$d" ]] || continue
if [[ -f "$d/Package.swift" ]]; then src_root="$d"; break; fi
done
fi
# 3) Else fall back to the first child directory (common for GitHub tarballs)
if [[ -z "$src_root" ]]; then
for d in "$work"/*; do
[[ -d "$d" ]] || continue
src_root="$d"; break
done
fi
if [[ -z "$src_root" ]]; then
echo "[deps] error: IRCKit source not found after extract" >&2
exit 1
fi
echo "[deps] IRCKit source root: $src_root"
pushd "$src_root" >/dev/null
# Ensure SwiftPM uses Swift 5 toolchain mode (avoid Swift 6 defaults under Xcode 16.x)
if have_cmd swift; then
echo "[deps] Forcing SwiftPM tools-version to 5.9 for IRCKit"
log_exec "$DEPS_LOGS_DIR/irckit-spm-tools-version.log" swift package tools-version --set 5.9 || true
fi
# Under SSL-less builds, strip NIOSSL from the graph and pin swift-nio to a Swift‑5–compatible release
if [[ -n ${PROLE_DISABLE_SSL:-} && -f "Package.swift" ]]; then
echo "[deps] PROLE_DISABLE_SSL=1: patching IRCKit Package.swift to remove NIOSSL and pin swift-nio to 2.40.0 (exact)"
cp -f "Package.swift" "$DEPS_LOGS_DIR/irckit-Package.swift.before" 2>/dev/null || true
# 1) Remove swift-nio-ssl package declaration lines entirely
perl -0777 -pe 's|\s*\.package\(\s*url:\s*"https://github\.com/apple/swift-nio-ssl"\s*,\s*[^\)]*\),?\n||g' -i "Package.swift" 2>/dev/null || true
# 2) Remove NIOSSL from any target dependency arrays (handles product and target name forms)
perl -0777 -pe 's|("|\[)NIOSSL(\]|"),?\s*||g; s|\[\s*,\s*\]|[]|g' -i "Package.swift" 2>/dev/null || true
perl -0777 -pe 's|\s*\.product\(name:\s*"NIOSSL"[^\)]*\),?\n||g' -i "Package.swift" 2>/dev/null || true
# 3) Pin swift-nio to an exact, Swift‑5–compatible version to avoid Swift‑6‑only codepaths
perl -0777 -pe 's|\.package\(\s*url:\s*"https://github\.com/apple/swift-nio"\s*,\s*from:\s*"[^"]+"\s*\)|.package(url: "https://github.com/apple/swift-nio", .exact("2.40.0"))|g' -i "Package.swift" 2>/dev/null || true
perl -0777 -pe 's|\.package\(\s*url:\s*"https://github\.com/apple/swift-nio"\s*,\s*\.upToNextMajor\(from:\s*"[^"]+"\)\s*\)|.package(url: "https://github.com/apple/swift-nio", .exact("2.40.0"))|g' -i "Package.swift" 2>/dev/null || true
perl -0777 -pe 's|\.package\(\s*url:\s*"https://github\.com/apple/swift-nio"\s*,\s*\.exact\([^\)]*\)\s*\)|.package(url: "https://github.com/apple/swift-nio", .exact("2.40.0"))|g' -i "Package.swift" 2>/dev/null || true
# 3b) Optionally pin swift-atomics to a Swift‑5–compatible release to help NIOConcurrencyHelpers
if ! grep -q "swift-atomics" Package.swift 2>/dev/null; then
echo "[deps] PROLE_DISABLE_SSL=1: adding swift-atomics .exact(\"1.0.2\") to Package.swift to stabilize NIOConcurrencyHelpers"
perl -0777 -pe 's|(dependencies\s*:\s*\[)|$1\n .package(url: "https://github.com/apple/swift-atomics", .exact("1.0.2")),|s' -i "Package.swift" 2>/dev/null || true
else
perl -0777 -pe 's|\.package\(\s*url:\s*"https://github\.com/apple/swift-atomics"\s*,\s*from:\s*"[^"]+"\s*\)|.package(url: "https://github.com/apple/swift-atomics", .exact("1.0.2"))|g' -i "Package.swift" 2>/dev/null || true
perl -0777 -pe 's|\.package\(\s*url:\s*"https://github\.com/apple/swift-atomics"\s*,\s*\.upToNextMajor\(from:\s*"[^"]+"\)\s*\)|.package(url: "https://github.com/apple/swift-atomics", .exact("1.0.2"))|g' -i "Package.swift" 2>/dev/null || true
fi
# 4) Ensure the package explicitly targets Swift 5 language mode if not already present
if ! grep -q "swiftLanguageVersions" Package.swift 2>/dev/null; then
echo "[deps] PROLE_DISABLE_SSL=1: injecting swiftLanguageVersions = [.v5] into Package.swift"
# Try to insert after the targets array before the closing paren of Package initializer
perl -0777 -pe 's|(targets\s*:\s*\[[\s\S]*?\])\s*\)\s*$|$1,\n swiftLanguageVersions: [.v5]\n)\n|s' -i "Package.swift" 2>/dev/null || true
fi
cp -f "Package.swift" "$DEPS_LOGS_DIR/irckit-Package.swift.after" 2>/dev/null || true
fi
# If SSL is disabled, avoid updating to latest; just resolve with our pinned/stripped graph
if have_cmd swift; then
if [[ -n ${PROLE_DISABLE_SSL:-} ]]; then
echo "[deps] PROLE_DISABLE_SSL=1: resolving IRCKit SwiftPM dependencies (no update)"
log_exec "$DEPS_LOGS_DIR/irckit-spm-resolve.log" swift package resolve || true
else
echo "[deps] Resolving IRCKit SwiftPM dependencies as pinned (no update)"
log_exec "$DEPS_LOGS_DIR/irckit-spm-resolve.log" swift package resolve || true
fi
# Best-effort diagnostics of the resolved graph
log_exec "$DEPS_LOGS_DIR/irckit-spm-show-deps.log" swift package show-dependencies || true
if [[ -f .swiftpm/resolved/Package.resolved ]]; then
cp -f .swiftpm/resolved/Package.resolved "$DEPS_LOGS_DIR/irckit-Package.resolved" || true
fi
fi
# Note: Avoid surgical source patches; rely on version pinning per user request
local build_tmp="$DEPS_OUT_DIR/IRCKit-build"
rm -rf "$build_tmp"; mkdir -p "$build_tmp"
local have_arm=0; local have_x86=0
# Determine which architectures to build for dependencies
local want_arches=(arm64 x86_64)
if [[ "${PROLE_BUILD_ARCH:-}" == "arm64" ]]; then
want_arches=(arm64)
elif [[ "${PROLE_BUILD_ARCH:-}" == "x86_64" ]]; then
want_arches=(x86_64)
fi
for dep_arch in "${want_arches[@]}"; do
if [[ "$dep_arch" == "arm64" ]]; then
if xc_archive_one "$PWD" IRCKit arm64 "$build_tmp"; then have_arm=1; fi
else
if xc_archive_one "$PWD" IRCKit x86_64 "$build_tmp"; then have_x86=1; fi
fi
done
local out_xc="$DEPS_OUT_DIR/IRCKit.xcframework"
rm -rf "$out_xc"
if [[ $have_arm -eq 1 && $have_x86 -eq 1 ]]; then
log_exec "$DEPS_LOGS_DIR/irckit-create-xcframework.log" xcodebuild -create-xcframework \
-framework "$build_tmp/IRCKit-macos-arm64.xcarchive/Products/Library/Frameworks/IRCKit.framework" \
-framework "$build_tmp/IRCKit-macos-x86_64.xcarchive/Products/Library/Frameworks/IRCKit.framework" \
-output "$out_xc"
elif [[ $have_arm -eq 1 ]]; then
log_exec "$DEPS_LOGS_DIR/irckit-create-xcframework.log" xcodebuild -create-xcframework \
-framework "$build_tmp/IRCKit-macos-arm64.xcarchive/Products/Library/Frameworks/IRCKit.framework" \
-output "$out_xc"
elif [[ $have_x86 -eq 1 ]]; then
log_exec "$DEPS_LOGS_DIR/irckit-create-xcframework.log" xcodebuild -create-xcframework \
-framework "$build_tmp/IRCKit-macos-x86_64.xcarchive/Products/Library/Frameworks/IRCKit.framework" \
-output "$out_xc"
else
echo "[deps] error: failed to archive IRCKit for any macOS arch" >&2
exit 1
fi
export IRCKIT_XCFRAMEWORK="$out_xc"
echo "[deps] Built IRCKit.xcframework at $IRCKIT_XCFRAMEWORK"
popd >/dev/null
}
build_royalvnc_xcframework() {
if [[ -n ${ROYALVNCKIT_XCFRAMEWORK:-} && -d ${ROYALVNCKIT_XCFRAMEWORK} ]]; then
echo "[deps] Using provided RoyalVNCKit.xcframework: ${ROYALVNCKIT_XCFRAMEWORK}"
return 0
fi
local work="$DEPS_SRC_DIR/RoyalVNC-src"
local tar="$DEPS_DIR/royalvnc.tar.gz"
rm -rf "$work"; mkdir -p "$work"
fetch_tarball "$URL_ROYALVNC" "$tar"
extract_tarball "$tar" "$work"
local src_root
src_root="$(find "$work" -maxdepth 1 -type d -name 'royalvnc-*' | head -n1)"
if [[ -z "$src_root" ]]; then
echo "[deps] error: RoyalVNC source not found after extract" >&2
exit 1
fi
pushd "$src_root" >/dev/null
local build_tmp="$DEPS_OUT_DIR/RoyalVNC-build"
rm -rf "$build_tmp"; mkdir -p "$build_tmp"
local have_arm=0; local have_x86=0
local want_arches=(arm64 x86_64)
if [[ "${PROLE_BUILD_ARCH:-}" == "arm64" ]]; then
want_arches=(arm64)
elif [[ "${PROLE_BUILD_ARCH:-}" == "x86_64" ]]; then
want_arches=(x86_64)
fi
for dep_arch in "${want_arches[@]}"; do
if [[ "$dep_arch" == "arm64" ]]; then
if xc_archive_one "$PWD" RoyalVNCKit arm64 "$build_tmp"; then have_arm=1; fi
else
if xc_archive_one "$PWD" RoyalVNCKit x86_64 "$build_tmp"; then have_x86=1; fi
fi
done
local out_xc="$DEPS_OUT_DIR/RoyalVNCKit.xcframework"
rm -rf "$out_xc"
if [[ $have_arm -eq 1 && $have_x86 -eq 1 ]]; then
log_exec "$DEPS_LOGS_DIR/royalvnc-create-xcframework.log" xcodebuild -create-xcframework \
-framework "$build_tmp/RoyalVNCKit-macos-arm64.xcarchive/Products/Library/Frameworks/RoyalVNCKit.framework" \
-framework "$build_tmp/RoyalVNCKit-macos-x86_64.xcarchive/Products/Library/Frameworks/RoyalVNCKit.framework" \
-output "$out_xc"
elif [[ $have_arm -eq 1 ]]; then
log_exec "$DEPS_LOGS_DIR/royalvnc-create-xcframework.log" xcodebuild -create-xcframework \
-framework "$build_tmp/RoyalVNCKit-macos-arm64.xcarchive/Products/Library/Frameworks/RoyalVNCKit.framework" \
-output "$out_xc"
elif [[ $have_x86 -eq 1 ]]; then
log_exec "$DEPS_LOGS_DIR/royalvnc-create-xcframework.log" xcodebuild -create-xcframework \
-framework "$build_tmp/RoyalVNCKit-macos-x86_64.xcarchive/Products/Library/Frameworks/RoyalVNCKit.framework" \
-output "$out_xc"
else
echo "[deps] error: failed to archive RoyalVNCKit for any macOS arch" >&2
exit 1
fi
export ROYALVNCKIT_XCFRAMEWORK="$out_xc"
echo "[deps] Built RoyalVNCKit.xcframework at $ROYALVNCKIT_XCFRAMEWORK"
popd >/dev/null
}
prepare_dependencies() {
ensure_dirs
local want_irc="${PROLE_ENABLE_IRCKit:-0}"
local want_vnc="${PROLE_ENABLE_RoyalVNCKit:-0}"
echo "[deps] Preparing frameworks — IRCKit: ${want_irc} (0=off,1=on), RoyalVNCKit: ${want_vnc} (0=off,1=on)"
if [[ "$want_irc" = "1" ]]; then
build_irckit_xcframework
if [[ ! -d "${IRCKIT_XCFRAMEWORK:-}" ]]; then
echo "[deps] error: IRCKit.xcframework is missing" >&2; exit 1; fi
else
echo "[deps] IRCKit disabled (PROLE_ENABLE_IRCKit=0); skipping build"
IRCKIT_XCFRAMEWORK=""
fi
if [[ "$want_vnc" = "1" ]]; then
build_royalvnc_xcframework
if [[ ! -d "${ROYALVNCKIT_XCFRAMEWORK:-}" ]]; then
echo "[deps] error: RoyalVNCKit.xcframework is missing" >&2; exit 1; fi
else
echo "[deps] RoyalVNCKit disabled (PROLE_ENABLE_RoyalVNCKit=0); skipping build"
ROYALVNCKIT_XCFRAMEWORK=""
fi
}
cleanup_dependencies() {
if [[ "${KEEP_DEPS:-0}" = "1" ]]; then
echo "[deps] KEEP_DEPS=1 set; preserving $DEPS_DIR"
else
rm -rf "$DEPS_DIR"
echo "[deps] Cleaned deps staging directory"
fi
}
swiftc_compile() {
local arch="$1"; shift
local out="$1"; shift
@ -242,14 +561,41 @@ swiftc_compile() {
echo "[swiftc] Building for ${arch} -> ${out}"
# Optional linking of frameworks based on flags
local want_irc="${PROLE_ENABLE_IRCKit:-0}"
local want_vnc="${PROLE_ENABLE_RoyalVNCKit:-0}"
local irc_flags=()
local vnc_flags=()
local swift_cfg_flags=()
if [[ "$want_irc" = "1" && -n "${IRCKIT_XCFRAMEWORK:-}" && -d "${IRCKIT_XCFRAMEWORK}" ]]; then
irc_flags=( -F "${IRCKIT_XCFRAMEWORK}" -framework IRCKit -Xlinker -rpath -Xlinker "@executable_path/../Frameworks" )
swift_cfg_flags+=( -D PROLE_ENABLE_IRCKit )
echo "[swiftc] Linking IRCKit from ${IRCKIT_XCFRAMEWORK}"
else
echo "[swiftc] IRCKit disabled or not present; not linking"
# Define a convenience symbol for conditional compilation if app code uses it
swift_cfg_flags+=( -D PROLE_ENABLE_IRCKit_DISABLED )
fi
if [[ "$want_vnc" = "1" && -n "${ROYALVNCKIT_XCFRAMEWORK:-}" && -d "${ROYALVNCKIT_XCFRAMEWORK}" ]]; then
vnc_flags=( -F "${ROYALVNCKIT_XCFRAMEWORK}" -framework RoyalVNCKit -Xlinker -rpath -Xlinker "@executable_path/../Frameworks" )
swift_cfg_flags+=( -D PROLE_ENABLE_RoyalVNCKit )
echo "[swiftc] Linking RoyalVNCKit from ${ROYALVNCKIT_XCFRAMEWORK}"
else
echo "[swiftc] RoyalVNCKit disabled or not present; not linking"
swift_cfg_flags+=( -D PROLE_ENABLE_RoyalVNCKit_DISABLED )
fi
# shellcheck disable=SC2046
xcrun swiftc \
log_exec "$LOG_DIR/swiftc-${arch}.log" xcrun swiftc \
-target "$target" \
-O \
-sdk "$(xcrun --show-sdk-path --sdk macosx)" \
-framework AppKit \
-framework Carbon \
-framework Network \
${swift_cfg_flags[@]:-} \
${irc_flags[@]:-} \
${vnc_flags[@]:-} \
$(/usr/bin/find "$SRC_DIR" -name "*.swift" | sort) \
-o "$out"
}
@ -259,8 +605,103 @@ codesign_app() {
xcrun codesign --force --deep -s - "$APP_DIR"
}
embed_irckit_framework() {
if [[ "${PROLE_ENABLE_IRCKit:-0}" != "1" ]]; then
return 0
fi
# Embed IRCKit.xcframework slice into Contents/Frameworks if available
if [[ -z "${IRCKIT_XCFRAMEWORK:-}" ]]; then
return 0
fi
if [[ ! -d "${IRCKIT_XCFRAMEWORK}" ]]; then
echo "[embed] IRCKIT_XCFRAMEWORK path not found: ${IRCKIT_XCFRAMEWORK}"
return 0
fi
local slice=""
# Prefer universal slice if available; otherwise match host arch
if [[ -d "${IRCKIT_XCFRAMEWORK}/macos-arm64_x86_64/IRCKit.framework" ]]; then
slice="${IRCKIT_XCFRAMEWORK}/macos-arm64_x86_64/IRCKit.framework"
else
case "$ARCH_CURRENT" in
arm64)
if [[ -d "${IRCKIT_XCFRAMEWORK}/macos-arm64/IRCKit.framework" ]]; then
slice="${IRCKIT_XCFRAMEWORK}/macos-arm64/IRCKit.framework"
fi
;;
x86_64)
if [[ -d "${IRCKIT_XCFRAMEWORK}/macos-x86_64/IRCKit.framework" ]]; then
slice="${IRCKIT_XCFRAMEWORK}/macos-x86_64/IRCKit.framework"
fi
;;
esac
fi
if [[ -z "$slice" ]]; then
echo "[embed] Could not find matching IRCKit.framework slice in xcframework"
return 0
fi
local dest="$CONTENTS_DIR/Frameworks/IRCKit.framework"
rm -rf "$dest"
echo "[embed] Embedding IRCKit.framework slice: $slice"
cp -R "$slice" "$dest"
# Sign the embedded framework
xcrun codesign --force -s - "$dest"
}
embed_royalvnckit_framework() {
if [[ "${PROLE_ENABLE_RoyalVNCKit:-0}" != "1" ]]; then
return 0
fi
# Embed RoyalVNCKit.xcframework slice into Contents/Frameworks if available
if [[ -z "${ROYALVNCKIT_XCFRAMEWORK:-}" ]]; then
return 0
fi
if [[ ! -d "${ROYALVNCKIT_XCFRAMEWORK}" ]]; then
echo "[embed] ROYALVNCKIT_XCFRAMEWORK path not found: ${ROYALVNCKIT_XCFRAMEWORK}"
return 0
fi
local slice=""
# Prefer universal slice if present; otherwise pick matching arch
if [[ -d "${ROYALVNCKIT_XCFRAMEWORK}/macos-arm64_x86_64/RoyalVNCKit.framework" ]]; then
slice="${ROYALVNCKIT_XCFRAMEWORK}/macos-arm64_x86_64/RoyalVNCKit.framework"
else
case "$ARCH_CURRENT" in
arm64)
if [[ -d "${ROYALVNCKIT_XCFRAMEWORK}/macos-arm64/RoyalVNCKit.framework" ]]; then
slice="${ROYALVNCKIT_XCFRAMEWORK}/macos-arm64/RoyalVNCKit.framework"
fi
;;
x86_64)
if [[ -d "${ROYALVNCKIT_XCFRAMEWORK}/macos-x86_64/RoyalVNCKit.framework" ]]; then
slice="${ROYALVNCKIT_XCFRAMEWORK}/macos-x86_64/RoyalVNCKit.framework"
fi
;;
esac
fi
if [[ -z "$slice" ]]; then
echo "[embed] Could not find matching RoyalVNCKit.framework slice in xcframework"
return 0
fi
local dest="$CONTENTS_DIR/Frameworks/RoyalVNCKit.framework"
rm -rf "$dest"
echo "[embed] Embedding RoyalVNCKit.framework slice: $slice"
cp -R "$slice" "$dest"
# Sign the embedded framework
xcrun codesign --force -s - "$dest"
}
build_one_arch() {
local arch="$1"
# Default clean at the start of every build
clean || true
# Constrain dependency builds to the requested architecture to avoid toolchain incompatibilities
export PROLE_BUILD_ARCH="$arch"
prepare_dependencies
ensure_dirs
gen_plist
gen_app_icns
@ -270,11 +711,22 @@ build_one_arch() {
swiftc_compile "$arch" "$bin"
mkdir -p "$MACOS_DIR"
cp "$bin" "$MACOS_DIR/${APP_NAME}"
embed_irckit_framework
embed_royalvnckit_framework
codesign_app
echo "Built: $APP_DIR"
echo "[summary] App binary: $(lipo -info "$MACOS_DIR/${APP_NAME}" 2>/dev/null || echo unknown)"
echo "[summary] Embedded frameworks:"
/usr/bin/find "$CONTENTS_DIR/Frameworks" -maxdepth 1 -type d -name "*.framework" -exec basename {} \; | sed 's/^/ - /'
cleanup_dependencies
}
build_universal() {
# Default clean at the start of every universal build
clean || true
# Build dependencies for both arches in universal build
unset PROLE_BUILD_ARCH || true
prepare_dependencies
ensure_dirs
gen_plist
local bin_arm64="$BUILD_DIR/${APP_NAME}-arm64"
@ -283,8 +735,14 @@ build_universal() {
swiftc_compile x86_64 "$bin_x86"
mkdir -p "$MACOS_DIR"
xcrun lipo -create -output "$MACOS_DIR/${APP_NAME}" "$bin_arm64" "$bin_x86"
embed_irckit_framework
embed_royalvnckit_framework
codesign_app
echo "Built universal: $APP_DIR"
echo "[summary] App binary: $(lipo -info "$MACOS_DIR/${APP_NAME}" 2>/dev/null || echo unknown)"
echo "[summary] Embedded frameworks:"
/usr/bin/find "$CONTENTS_DIR/Frameworks" -maxdepth 1 -type d -name "*.framework" -exec basename {} \; | sed 's/^/ - /'
cleanup_dependencies
}
clean() {