Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e0b1719921 | ||
|
|
fe68c5c01d | ||
|
|
1c53e698aa | ||
|
|
23995f18e5 |
@@ -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: |
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ This directory is the **native AppKit implementation** of [`SPEC.md`](SPEC.md).
|
|||||||
|
|
||||||
| Item | Value |
|
| Item | Value |
|
||||||
|------|--------|
|
|------|--------|
|
||||||
| macOS | 10.15 Catalina → 26+ Tahoe |
|
| macOS | Intel **10.15** Catalina · Apple Silicon **11.0** Big Sur → 26+ Tahoe |
|
||||||
| Architecture | Universal 2 (Intel + Apple Silicon) |
|
| Architecture | Universal 2 (Intel + Apple Silicon) |
|
||||||
| Xcode | Command Line Tools / full Xcode (Swift 5) |
|
| Xcode | Command Line Tools / full Xcode (Swift 5) |
|
||||||
| Sandbox | Hardened Runtime **on**, App Sandbox **off** |
|
| Sandbox | Hardened Runtime **on**, App Sandbox **off** |
|
||||||
@@ -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).
|
||||||
|
|||||||
+57
-39
@@ -1,55 +1,73 @@
|
|||||||
#!/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:--}" \
|
MIN_OS="$(min_os_for_arch "$ARCH")"
|
||||||
CODE_SIGNING_ALLOWED=YES \
|
echo "==> Building TargetPrint ($CONFIGURATION, $ARCH, min $MIN_OS) with $XCODEBUILD (-jobs $JOBS)"
|
||||||
ENABLE_HARDENED_RUNTIME=YES \
|
rm -rf "$OUT_DIR"
|
||||||
build
|
mkdir -p "$OUT_DIR" "$DD_DIR"
|
||||||
|
"$XCODEBUILD" \
|
||||||
APP="$OUT_DIR/TargetPrint.app"
|
-project "$PROJECT" \
|
||||||
test -d "$APP"
|
-scheme "$SCHEME" \
|
||||||
BIN="$APP/Contents/MacOS/TargetPrint"
|
-configuration "$CONFIGURATION" \
|
||||||
test -x "$BIN"
|
-arch "$ARCH" \
|
||||||
|
-jobs "$JOBS" \
|
||||||
echo "==> Signing $APP"
|
-parallelizeTargets \
|
||||||
# shellcheck source=lib.sh
|
-derivedDataPath "$DD_DIR" \
|
||||||
source "$(dirname "$0")/lib.sh"
|
CONFIGURATION_BUILD_DIR="$OUT_DIR" \
|
||||||
sign_app "$APP" "$ROOT/TargetPrint.entitlements"
|
ONLY_ACTIVE_ARCH=NO \
|
||||||
|
MACOSX_DEPLOYMENT_TARGET="$MIN_OS" \
|
||||||
echo "==> lipo -info"
|
CODE_SIGN_IDENTITY="${MACOS_CODESIGN_IDENTITY:--}" \
|
||||||
lipo -info "$BIN"
|
CODE_SIGNING_ALLOWED=YES \
|
||||||
ACTUAL="$(lipo -archs "$BIN" | tr -d '[:space:]')"
|
ENABLE_HARDENED_RUNTIME=YES \
|
||||||
if [[ "$ACTUAL" != "$ARCH" ]]; then
|
SWIFT_USE_PARALLEL_WHOLE_MODULE_OPTIMIZATION=YES \
|
||||||
echo "error: expected a single-arch $ARCH binary, got '$ACTUAL'" >&2
|
build
|
||||||
exit 1
|
APP="$OUT_DIR/TargetPrint.app"
|
||||||
|
test -d "$APP"
|
||||||
|
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"
|
|
||||||
|
|||||||
@@ -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:"
|
||||||
|
|||||||
@@ -31,6 +31,23 @@ 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
|
||||||
|
}
|
||||||
|
|
||||||
|
# Intel: Catalina 10.15. Apple Silicon did not exist until Big Sur 11.0.
|
||||||
|
min_os_for_arch() {
|
||||||
|
case "$1" in
|
||||||
|
x86_64) echo "${MACOSX_DEPLOYMENT_TARGET_X86_64:-10.15}" ;;
|
||||||
|
arm64) echo "${MACOSX_DEPLOYMENT_TARGET_ARM64:-11.0}" ;;
|
||||||
|
*) echo "10.15" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
zip_app() {
|
zip_app() {
|
||||||
local app="$1"
|
local app="$1"
|
||||||
local dest="$2"
|
local dest="$2"
|
||||||
|
|||||||
@@ -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)"
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
#!/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)}"
|
||||||
|
|
||||||
|
# shellcheck source=lib.sh
|
||||||
|
source "$(dirname "$0")/lib.sh"
|
||||||
|
JOBS="$(cpu_jobs)"
|
||||||
|
MIN_OS="$(min_os_for_arch "$ARCH")"
|
||||||
|
TARGET="${ARCH}-apple-macos${MIN_OS}"
|
||||||
|
|
||||||
|
echo "==> swiftc build ($CONFIGURATION, $ARCH, $JOBS threads)"
|
||||||
|
echo " SDKROOT=$SDKROOT"
|
||||||
|
echo " SWIFT=$SWIFT"
|
||||||
|
echo " TARGET=$TARGET (min $MIN_OS)"
|
||||||
|
|
||||||
|
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"
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import AppKit
|
import AppKit
|
||||||
|
import UniformTypeIdentifiers
|
||||||
|
|
||||||
/// SPEC §9 — three-pane window: thumbnails | canvas | inspector.
|
/// SPEC §9 — three-pane window: thumbnails | canvas | inspector.
|
||||||
|
|
||||||
@@ -211,7 +212,15 @@ final class PreviewWindowController: NSWindowController {
|
|||||||
let panel = NSOpenPanel()
|
let panel = NSOpenPanel()
|
||||||
panel.allowsMultipleSelection = true
|
panel.allowsMultipleSelection = true
|
||||||
panel.canChooseDirectories = false
|
panel.canChooseDirectories = false
|
||||||
panel.allowedFileTypes = ["tif", "tiff", "targetjob", "json"]
|
if #available(macOS 12.0, *) {
|
||||||
|
var types: [UTType] = [.tiff, .json]
|
||||||
|
if let jobType = UTType(filenameExtension: "targetjob") {
|
||||||
|
types.append(jobType)
|
||||||
|
}
|
||||||
|
panel.allowedContentTypes = types
|
||||||
|
} else {
|
||||||
|
panel.allowedFileTypes = ["tif", "tiff", "targetjob", "json"]
|
||||||
|
}
|
||||||
panel.begin { result in
|
panel.begin { result in
|
||||||
if result == .OK { completion(panel.urls) }
|
if result == .OK { completion(panel.urls) }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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? {
|
||||||
|
|||||||
@@ -80,13 +80,18 @@ 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 cls = NSStringFromClass(type(of: ac))
|
||||||
|
let title = (ac.title ?? "") + " " + (ac.nibName ?? "") + " " + cls
|
||||||
let hay = title.lowercased()
|
let hay = title.lowercased()
|
||||||
if hay.contains("color matching") || hay.contains("colour matching") || hay.contains("colormatch") {
|
if hay.contains("color matching") || hay.contains("colour matching") || hay.contains("colormatch") {
|
||||||
panel.removeAccessoryController(ac)
|
// SDK types this as NSViewController; removeAccessoryController
|
||||||
TPLog.info("Removed print-panel accessory: \(title)")
|
// requires NSPrintPanelAccessorizing (SPEC §7.2).
|
||||||
|
if let accessor = ac as? (NSViewController & NSPrintPanelAccessorizing) {
|
||||||
|
panel.removeAccessoryController(accessor)
|
||||||
|
TPLog.info("Removed print-panel accessory: \(title)")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user