Author SHA1 Message Date
Local Admin 1c53e698aa build fixes
Build macOS Packages / Build macOS (Intel) (push) Failing after 15s
Build macOS Packages / Build macOS (Apple Silicon) (push) Failing after 29s
Build macOS Packages / Build macOS (Universal) (push) Skipped
2026-09-07 06:11:55 -07:00
Local Admin 23995f18e5 fix build sxriots for command line xcode
Build macOS Packages / Build macOS (Apple Silicon) (push) Failing after 2m1s
Build macOS Packages / Build macOS (Intel) (push) Failing after 2m6s
Build macOS Packages / Build macOS (Universal) (push) Skipped
2026-09-07 05:36:51 -07:00
12 changed files with 344 additions and 50 deletions
+37 -4
View File
@@ -54,10 +54,28 @@ jobs:
echo "PROJECT_ROOT=." >> "$GITHUB_ENV" echo "PROJECT_ROOT=." >> "$GITHUB_ENV"
fi fi
- name: Show Xcode - name: Select Xcode
working-directory: ${{ env.PROJECT_ROOT }} env:
XCODE_APP: ${{ vars.XCODE_APP }}
run: | run: |
xcodebuild -version ROOT="${PROJECT_ROOT:-.}"
chmod +x "$ROOT/Scripts/select_xcode.sh"
"$ROOT/Scripts/select_xcode.sh"
- name: Show toolchain
run: |
echo "HAS_FULL_XCODE=${HAS_FULL_XCODE:-unset}"
echo "DEVELOPER_DIR=${DEVELOPER_DIR:-unset}"
echo "SDKROOT=${SDKROOT:-unset}"
echo "SWIFT=${SWIFT:-unset}"
echo "xcode-select -p: $(xcode-select -p 2>/dev/null || echo unknown)"
if [ "${HAS_FULL_XCODE:-0}" = "1" ]; then
XB="${XCODEBUILD:-xcodebuild}"
"$XB" -version
else
echo "Full Xcode.app not installed; will compile with swiftc."
"${SWIFT:-$(xcrun --find swiftc)}" --version
fi
sw_vers sw_vers
uname -m uname -m
@@ -84,11 +102,18 @@ jobs:
- name: Run unit tests - name: Run unit tests
working-directory: ${{ env.PROJECT_ROOT }} working-directory: ${{ env.PROJECT_ROOT }}
run: | run: |
xcodebuild test \ if [ "${HAS_FULL_XCODE:-0}" != "1" ]; then
echo "Skipping XCTest — no Xcode.app (XCTest.framework is not part of CLT)."
exit 0
fi
XB="${XCODEBUILD:-xcodebuild}"
"$XB" test \
-project TargetPrint.xcodeproj \ -project TargetPrint.xcodeproj \
-scheme TargetPrint \ -scheme TargetPrint \
-destination "platform=macOS" \ -destination "platform=macOS" \
-only-testing:TargetPrintTests \ -only-testing:TargetPrintTests \
-jobs "$(sysctl -n hw.ncpu 2>/dev/null || echo 4)" \
-parallelizeTargets \
CODE_SIGNING_ALLOWED=NO \ CODE_SIGNING_ALLOWED=NO \
ONLY_ACTIVE_ARCH=YES ONLY_ACTIVE_ARCH=YES
@@ -171,6 +196,14 @@ jobs:
echo "PROJECT_ROOT=." >> "$GITHUB_ENV" echo "PROJECT_ROOT=." >> "$GITHUB_ENV"
fi fi
- name: Select Xcode
env:
XCODE_APP: ${{ vars.XCODE_APP }}
run: |
ROOT="${PROJECT_ROOT:-.}"
chmod +x "$ROOT/Scripts/select_xcode.sh"
"$ROOT/Scripts/select_xcode.sh"
- name: Set release names - name: Set release names
shell: bash shell: bash
run: | run: |
+10
View File
@@ -4,3 +4,13 @@
#import <cups/cups.h> #import <cups/cups.h>
#import <cups/ppd.h> #import <cups/ppd.h>
// The Swift CUPS overlay marks cupsGetPPD unavailable ("use cupsCopyDestInfo").
// SPEC §10 still requires the PPD file for AirPrint detection and vendor
// colour-bypass keys, so we call the C symbol through this wrapper.
static inline const char *TPCupsGetPPD(const char *name) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
return cupsGetPPD(name);
#pragma clang diagnostic pop
}
+2
View File
@@ -84,6 +84,8 @@ vendor-iccery/
Copy that folder to ICCery as `src-tauri/targetprint/` and spawn `TargetPrint.app/Contents/MacOS/TargetPrint --job …` from Rust (SPEC §16). Copy that folder to ICCery as `src-tauri/targetprint/` and spawn `TargetPrint.app/Contents/MacOS/TargetPrint --job …` from Rust (SPEC §16).
The `macos` runner does **not** need a full Xcode.app. If `/Applications/Xcode.app` is missing (the `xcode-select: invalid developer directory` error), the workflow compiles with **Command Line Tools** `swiftc` and the macOS SDK. Install CLT on the runner if needed: `xcode-select --install`. Optional Actions variable `XCODE_APP` only if a full Xcode is installed somewhere other than `/Applications/Xcode.app`.
Optional repository secret `MACOS_CODESIGN_IDENTITY` (Developer ID). If unset, binaries are ad-hoc signed with Hardened Runtime. Optional repository secret `MACOS_CODESIGN_IDENTITY` (Developer ID). If unset, binaries are ad-hoc signed with Hardened Runtime.
On a `v*` tag, assets are also attached to the Gitea release (the release must already exist for that tag, same pattern as ICCery). On a `v*` tag, assets are also attached to the Gitea release (the release must already exist for that tag, same pattern as ICCery).
+55 -39
View File
@@ -1,55 +1,71 @@
#!/bin/bash #!/bin/bash
# Build TargetPrint.app for one architecture: x86_64 | arm64 # Build TargetPrint.app for one architecture: x86_64 | arm64
# Prefers xcodebuild; falls back to CLT swiftc when Xcode.app is absent.
# Usage: build_macos.sh <arch> [Release|Debug] # Usage: build_macos.sh <arch> [Release|Debug]
set -euo pipefail set -euo pipefail
ARCH="${1:?arch required: x86_64 | arm64}" ARCH="${1:?arch required: x86_64 | arm64}"
CONFIGURATION="${2:-Release}" CONFIGURATION="${2:-Release}"
ROOT="$(cd "$(dirname "$0")/.." && pwd)" ROOT="$(cd "$(dirname "$0")/.." && pwd)"
PROJECT="$ROOT/TargetPrint.xcodeproj" SCRIPTS="$(dirname "$0")"
SCHEME="TargetPrint"
OUT_DIR="$ROOT/build/apps/$ARCH"
DD_DIR="$ROOT/build/derived-$ARCH"
case "$ARCH" in case "$ARCH" in
x86_64|arm64) ;; x86_64|arm64) ;;
*) echo "arch must be x86_64 or arm64 (got $ARCH)" >&2; exit 2 ;; *) echo "arch must be x86_64 or arm64 (got $ARCH)" >&2; exit 2 ;;
esac esac
echo "==> Building TargetPrint ($CONFIGURATION, $ARCH)" xcodebuild_works() {
rm -rf "$OUT_DIR" local xb="${XCODEBUILD:-}"
mkdir -p "$OUT_DIR" "$DD_DIR" if [ -n "$xb" ] && [ -x "$xb" ] && "$xb" -version >/dev/null 2>&1; then
return 0
fi
if command -v xcodebuild >/dev/null 2>&1 && xcodebuild -version >/dev/null 2>&1; then
XCODEBUILD="$(command -v xcodebuild)"
return 0
fi
return 1
}
xcodebuild \ if xcodebuild_works; then
-project "$PROJECT" \ PROJECT="$ROOT/TargetPrint.xcodeproj"
-scheme "$SCHEME" \ SCHEME="TargetPrint"
-configuration "$CONFIGURATION" \ OUT_DIR="$ROOT/build/apps/$ARCH"
-arch "$ARCH" \ DD_DIR="$ROOT/build/derived-$ARCH"
-derivedDataPath "$DD_DIR" \ # shellcheck source=lib.sh
CONFIGURATION_BUILD_DIR="$OUT_DIR" \ source "$SCRIPTS/lib.sh"
ONLY_ACTIVE_ARCH=NO \ JOBS="$(cpu_jobs)"
CODE_SIGN_IDENTITY="${MACOS_CODESIGN_IDENTITY:--}" \ echo "==> Building TargetPrint ($CONFIGURATION, $ARCH) with $XCODEBUILD (-jobs $JOBS)"
CODE_SIGNING_ALLOWED=YES \ rm -rf "$OUT_DIR"
ENABLE_HARDENED_RUNTIME=YES \ mkdir -p "$OUT_DIR" "$DD_DIR"
build "$XCODEBUILD" \
-project "$PROJECT" \
APP="$OUT_DIR/TargetPrint.app" -scheme "$SCHEME" \
test -d "$APP" -configuration "$CONFIGURATION" \
BIN="$APP/Contents/MacOS/TargetPrint" -arch "$ARCH" \
test -x "$BIN" -jobs "$JOBS" \
-parallelizeTargets \
echo "==> Signing $APP" -derivedDataPath "$DD_DIR" \
# shellcheck source=lib.sh CONFIGURATION_BUILD_DIR="$OUT_DIR" \
source "$(dirname "$0")/lib.sh" ONLY_ACTIVE_ARCH=NO \
sign_app "$APP" "$ROOT/TargetPrint.entitlements" CODE_SIGN_IDENTITY="${MACOS_CODESIGN_IDENTITY:--}" \
CODE_SIGNING_ALLOWED=YES \
echo "==> lipo -info" ENABLE_HARDENED_RUNTIME=YES \
lipo -info "$BIN" SWIFT_USE_PARALLEL_WHOLE_MODULE_OPTIMIZATION=YES \
ACTUAL="$(lipo -archs "$BIN" | tr -d '[:space:]')" build
if [[ "$ACTUAL" != "$ARCH" ]]; then APP="$OUT_DIR/TargetPrint.app"
echo "error: expected a single-arch $ARCH binary, got '$ACTUAL'" >&2 test -d "$APP"
exit 1 BIN="$APP/Contents/MacOS/TargetPrint"
test -x "$BIN"
sign_app "$APP" "$ROOT/TargetPrint.entitlements"
echo "==> lipo -info"
lipo -info "$BIN"
ACTUAL="$(lipo -archs "$BIN" | tr -d '[:space:]')"
if [[ "$ACTUAL" != "$ARCH" ]]; then
echo "error: expected a single-arch $ARCH binary, got '$ACTUAL'" >&2
exit 1
fi
echo "==> $APP"
else
echo "==> xcodebuild unavailable; using Command Line Tools swiftc"
exec "$SCRIPTS/swiftc_build.sh" "$ARCH" "$CONFIGURATION"
fi fi
echo "==> $APP"
+15 -2
View File
@@ -1,13 +1,26 @@
#!/bin/bash #!/bin/bash
# SPEC §12 — build x86_64, arm64, and Universal 2 TargetPrint.app # SPEC §12 — build x86_64, arm64, and Universal 2 TargetPrint.app
# x86_64 and arm64 compile concurrently; then lipo.
set -euo pipefail set -euo pipefail
CONFIGURATION="${1:-Release}" CONFIGURATION="${1:-Release}"
ROOT="$(cd "$(dirname "$0")/.." && pwd)" ROOT="$(cd "$(dirname "$0")/.." && pwd)"
SCRIPTS="$(dirname "$0")" SCRIPTS="$(dirname "$0")"
"$SCRIPTS/build_macos.sh" x86_64 "$CONFIGURATION" echo "==> Compiling x86_64 and arm64 in parallel"
"$SCRIPTS/build_macos.sh" arm64 "$CONFIGURATION" "$SCRIPTS/build_macos.sh" x86_64 "$CONFIGURATION" &
pid_x86=$!
"$SCRIPTS/build_macos.sh" arm64 "$CONFIGURATION" &
pid_arm=$!
status=0
wait "$pid_x86" || status=1
wait "$pid_arm" || status=1
if [ "$status" -ne 0 ]; then
echo "error: one or both architecture builds failed" >&2
exit 1
fi
"$SCRIPTS/make_universal.sh" "$SCRIPTS/make_universal.sh"
echo "==> All architectures:" echo "==> All architectures:"
+8
View File
@@ -31,6 +31,14 @@ sign_app() {
codesign --verify --verbose=2 "$app" || true codesign --verify --verbose=2 "$app" || true
} }
cpu_jobs() {
if [ -n "${JOBS:-}" ]; then
echo "$JOBS"
return
fi
sysctl -n hw.ncpu 2>/dev/null || nproc 2>/dev/null || echo 4
}
zip_app() { zip_app() {
local app="$1" local app="$1"
local dest="$2" local dest="$2"
+92
View File
@@ -0,0 +1,92 @@
#!/bin/bash
# Locate a compiler for TargetPrint.
#
# Full Xcode.app is preferred (xcodebuild). If it is missing — the usual Gitea
# macos runner state, with xcode-select pointed at Command Line Tools — we
# fall back to CLT `swiftc` + the macOS SDK, which is enough to produce
# TargetPrint.app.
set -euo pipefail
github_env() {
local key="$1" val="$2"
echo "$key=$val"
if [ -n "${GITHUB_ENV:-}" ]; then
echo "$key=$val" >> "$GITHUB_ENV"
fi
eval "export $key=\"\$val\""
}
echo "==> Toolchain probe"
echo " xcode-select -p: $(xcode-select -p 2>/dev/null || echo unset)"
echo " /Applications:"
ls -1 /Applications 2>/dev/null | sed 's/^/ /' || echo " (unreadable)"
shopt -s nullglob
candidates=()
[ -n "${XCODE_APP:-}" ] && candidates+=("$XCODE_APP")
candidates+=(
/Applications/Xcode.app
/Applications/Xcode-beta.app
/Applications/Xcode_*.app
/Applications/Xcode-*.app
)
resolve_app() {
local c="$1"
[ -n "$c" ] || return 1
if [ -x "$c/Contents/Developer/usr/bin/xcodebuild" ]; then
echo "$c"
return 0
fi
return 1
}
pick=""
for c in "${candidates[@]}"; do
if app="$(resolve_app "$c")"; then
pick="$app"
break
fi
done
if [ -n "$pick" ]; then
DEV="$pick/Contents/Developer"
echo "==> Full Xcode at $pick"
github_env HAS_FULL_XCODE 1
github_env DEVELOPER_DIR "$DEV"
github_env XCODEBUILD "$DEV/usr/bin/xcodebuild"
if command -v sudo >/dev/null 2>&1 && sudo -n true 2>/dev/null; then
sudo xcode-select -s "$DEV" || true
sudo "$DEV/usr/bin/xcodebuild" -license accept >/dev/null 2>&1 || true
sudo "$DEV/usr/bin/xcodebuild" -runFirstLaunch >/dev/null 2>&1 || true
fi
"$DEV/usr/bin/xcodebuild" -version || true
else
echo "==> No Xcode.app (this is why 'xcode-select -s /Applications/Xcode.app/...' fails)."
echo " Building with Command Line Tools swiftc + macOS SDK."
github_env HAS_FULL_XCODE 0
github_env XCODEBUILD ""
CLT="/Library/Developer/CommandLineTools"
if [ -d "$CLT" ]; then
github_env DEVELOPER_DIR "$CLT"
fi
fi
if ! SDKROOT_VAL="$(xcrun --sdk macosx --show-sdk-path 2>/dev/null)"; then
echo "No macOS SDK via xcrun. Install Command Line Tools:"
echo " xcode-select --install"
exit 1
fi
github_env SDKROOT "$SDKROOT_VAL"
if ! SWIFT_VAL="$(xcrun --find swiftc 2>/dev/null)"; then
echo "swiftc not found."
exit 1
fi
github_env SWIFT "$SWIFT_VAL"
echo " HAS_FULL_XCODE=${HAS_FULL_XCODE}"
echo " SDKROOT=${SDKROOT}"
echo " SWIFT=${SWIFT} ($("$SWIFT" --version 2>/dev/null | head -n 1))"
echo " sw_vers: $(sw_vers -productName) $(sw_vers -productVersion)"
echo " uname -m: $(uname -m)"
+118
View File
@@ -0,0 +1,118 @@
#!/bin/bash
# Build TargetPrint.app with CLT swiftc (no Xcode.app / xcodebuild required).
# Usage: swiftc_build.sh <arch> [Release|Debug]
set -euo pipefail
ARCH="${1:?arch required: x86_64 | arm64}"
CONFIGURATION="${2:-Release}"
case "$ARCH" in
x86_64|arm64) ;;
*) echo "arch must be x86_64 or arm64" >&2; exit 2 ;;
esac
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
OUT_DIR="$ROOT/build/apps/$ARCH"
APP="$OUT_DIR/TargetPrint.app"
MACOS_DIR="$APP/Contents/MacOS"
RES_DIR="$APP/Contents/Resources"
BIN="$MACOS_DIR/TargetPrint"
SDKROOT="${SDKROOT:-$(xcrun --sdk macosx --show-sdk-path)}"
SWIFT="${SWIFT:-$(xcrun --find swiftc)}"
# Match the SDK so swiftc uses the prebuilt stdlib. Targeting macos10.15
# with a modern CLT compiler forces a multi-minute stdlib rebuild and
# still may not find a compatible x86_64 runtime.
SDK_VER="$(xcrun --sdk macosx --show-sdk-version 2>/dev/null || sw_vers -productVersion)"
SDK_VER="${SDK_VER%%$'\n'*}"
TARGET="${ARCH}-apple-macos${SDK_VER}"
MIN_OS="10.15"
# shellcheck source=lib.sh
source "$(dirname "$0")/lib.sh"
JOBS="$(cpu_jobs)"
echo "==> swiftc build ($CONFIGURATION, $ARCH, $JOBS threads)"
echo " SDKROOT=$SDKROOT"
echo " SWIFT=$SWIFT"
echo " TARGET=$TARGET"
rm -rf "$APP"
mkdir -p "$MACOS_DIR" "$RES_DIR"
SOURCES=()
while IFS= read -r src; do
SOURCES+=("$src")
done < <(find "$ROOT/Sources" -name '*.swift' | LC_ALL=C sort)
if [ "${#SOURCES[@]}" -eq 0 ]; then
echo "no Swift sources under $ROOT/Sources" >&2
exit 1
fi
OPT_FLAGS=(-O -whole-module-optimization)
if [ "$CONFIGURATION" != "Release" ]; then
OPT_FLAGS=(-Onone -g)
fi
"$SWIFT" \
-sdk "$SDKROOT" \
-target "$TARGET" \
-swift-version 5 \
"${OPT_FLAGS[@]}" \
-num-threads "$JOBS" \
-j "$JOBS" \
-module-name TargetPrint \
-import-objc-header "$ROOT/Bridging-Header.h" \
-Xcc "-I${SDKROOT}/usr/include" \
-framework AppKit \
-framework Foundation \
-framework CoreGraphics \
-framework ImageIO \
-framework ApplicationServices \
-lcups \
-o "$BIN" \
"${SOURCES[@]}"
test -x "$BIN"
# Info.plist — expand the Xcode build-setting placeholder.
sed 's/\$(PRODUCT_BUNDLE_IDENTIFIER)/com.iccery.TargetPrint/g' \
"$ROOT/Info.plist" > "$APP/Contents/Info.plist"
printf 'APPL????' > "$APP/Contents/PkgInfo"
# Resources
cp "$ROOT/Resources/ICCery-logo.svg" "$RES_DIR/" 2>/dev/null || true
cp "$ROOT/Resources/app-icon.svg" "$RES_DIR/" 2>/dev/null || true
cp "$ROOT/Resources/sample.targetjob" "$RES_DIR/" 2>/dev/null || true
ICON_SRC="$ROOT/Resources/Assets.xcassets/AppIcon.appiconset/icon_1024.png"
if [ -f "$ICON_SRC" ] && command -v sips >/dev/null 2>&1; then
ICONSET="$(mktemp -d /tmp/tp-iconset-XXXXXX)"
mkdir -p "$ICONSET/AppIcon.iconset"
for pair in 16:icon_16x16 32:icon_32x32 128:icon_128x128 256:icon_256x256 512:icon_512x512; do
size="${pair%%:*}"
name="${pair##*:}"
sips -z "$size" "$size" "$ICON_SRC" --out "$ICONSET/AppIcon.iconset/${name}.png" >/dev/null &
sips -z $((size * 2)) $((size * 2)) "$ICON_SRC" --out "$ICONSET/AppIcon.iconset/${name}@2x.png" >/dev/null &
done
wait
if command -v iconutil >/dev/null 2>&1 && iconutil -c icns "$ICONSET/AppIcon.iconset" -o "$RES_DIR/AppIcon.icns" 2>/dev/null; then
echo " AppIcon.icns"
else
cp "$ICON_SRC" "$RES_DIR/AppIcon.png"
fi
rm -rf "$ICONSET"
fi
sign_app "$APP" "$ROOT/TargetPrint.entitlements"
echo "==> lipo -info"
lipo -info "$BIN"
ACTUAL="$(lipo -archs "$BIN" | tr -d '[:space:]')"
if [[ "$ACTUAL" != "$ARCH" ]]; then
echo "error: expected a single-arch $ARCH binary, got '$ACTUAL'" >&2
exit 1
fi
echo "==> $APP"
+1 -1
View File
@@ -19,7 +19,7 @@ enum AppError: Error, CustomStringConvertible {
switch self { switch self {
case .invalidJob: return .invalidJob case .invalidJob: return .invalidJob
case .unreadableTarget: return .unreadableTarget case .unreadableTarget: return .unreadableTarget
case .printer: return .printer case .printer: return .printerError
case .internalFatal: return .internalFatal case .internalFatal: return .internalFatal
} }
} }
+4 -2
View File
@@ -77,8 +77,10 @@ enum CUPSManager {
// MARK: - PPD // MARK: - PPD
static func ppdPath(forQueue name: String) -> String? { static func ppdPath(forQueue name: String) -> String? {
guard let cPath = cupsGetPPD(name) else { return nil } name.withCString { cName in
return String(cString: cPath) guard let cPath = TPCupsGetPPD(cName) else { return nil }
return String(cString: cPath)
}
} }
static func ppdContents(forQueue name: String) -> String? { static func ppdContents(forQueue name: String) -> String? {
+1 -1
View File
@@ -80,7 +80,7 @@ enum ColorMatching {
} }
static func stripColorMatchingAccessories(from panel: NSPrintPanel) { static func stripColorMatchingAccessories(from panel: NSPrintPanel) {
let accessories = panel.accessoryControllers() let accessories = panel.accessoryControllers
for ac in accessories { for ac in accessories {
let title = (ac.title ?? "") + " " + (ac.nibName ?? "") let title = (ac.title ?? "") + " " + (ac.nibName ?? "")
let hay = title.lowercased() let hay = title.lowercased()
+1 -1
View File
@@ -73,7 +73,7 @@ final class PrintEngine {
ColorMatching.apply(colorMode, to: info) ColorMatching.apply(colorMode, to: info)
do { do {
if let queue = try CUPSManager.namedQueue(printerName) ?? try CUPSManager.listQueues().first(where: { $0.name == printerName }) { if let queue = try CUPSManager.namedQueue(printerName) {
let bypass = CUPSManager.vendorColorBypass(make: queue.make, model: queue.model, ppdText: queue.ppdText) let bypass = CUPSManager.vendorColorBypass(make: queue.make, model: queue.model, ppdText: queue.ppdText)
CUPSManager.applyVendorBypass(bypass, to: info) CUPSManager.applyVendorBypass(bypass, to: info)
} }