This branch delivers a series of follow-up optimizations targeting Apple CI compile times and correctness in the sledtools/pika repository. Key changes include: enabling sccache as a Rust compiler wrapper in the Nix dev shell, converting Xcode version-stamp build phases from unconditional to dependency-aware with a CI skip flag, adding SSH connection multiplexing to the remote Apple CI runner, suppressing redundant tee output in remote logs, tightening CI lane file-trigger lists to avoid unnecessary cross-lane rebuilds, adding the new pika-incus-guest-role crate to all Apple build manifests, and introducing an optional xcodebuild timing summary mode for build profiling.
Tutorial Steps
Enable sccache as the default Rust compiler cache in the Nix dev shell
Intent: Accelerate Rust compilation by transparently caching compiler artifacts with sccache. When sccache is available on PATH, RUSTC_WRAPPER is set so every cargo invocation uses cached results, with a configurable cache directory defaulting to ~/.cache/pika/sccache.
@@ -857,6 +860,12 @@
+ if command -v sccache >/dev/null 2>&1; then
+ export RUSTC_WRAPPER="$(command -v sccache)"
+ export SCCACHE_DIR="''${PIKA_SCCACHE_DIR:-''${XDG_CACHE_HOME:-$HOME/.cache}/pika/sccache}"
+ mkdir -p "$SCCACHE_DIR"
+ fi
The flake.nix dev shell now includes sccache in its package set and conditionally configures it during shell initialization.
pkgs.sccache is added to the packages list so it is always available.
In the shell hook, if sccache is on PATH, three environment variables are exported:
RUSTC_WRAPPER — points to the sccache binary so Cargo delegates compilation through it.
SCCACHE_DIR — defaults to $PIKA_SCCACHE_DIR, falling back to $XDG_CACHE_HOME/pika/sccache or ~/.cache/pika/sccache.
The directory is created eagerly with mkdir -p.
This is a zero-config improvement: any developer entering the Nix shell automatically gets compilation caching. CI runners that pre-populate the cache directory across jobs will see the largest benefit.
Make Xcode version-stamp scripts dependency-aware and skippable in CI
Intent: The post-build 'Stamp version and build number' scripts for all three iOS targets (Pika, PikaNotificationService, PikaShareExtension) previously ran unconditionally on every build because basedOnDependencyAnalysis was false. This change switches them to dependency-aware execution using a sentinel output file, and adds a PIKA_XCODE_SKIP_VERSION_STAMP environment variable that CI can set to skip stamping entirely during compile-only checks.
Affected files: ios/project.yml, just/checks.just
Evidence
@@ -65,6 +65,11 @@
+ if [ "${PIKA_XCODE_SKIP_VERSION_STAMP:-0}" = "1" ]; then
+ mkdir -p "$(dirname "$SCRIPT_OUTPUT_FILE_0")"
+ : > "$SCRIPT_OUTPUT_FILE_0"
+ exit 0
+ fi
Three identical changes are applied to the Pika, PikaNotificationService, and PikaShareExtension targets in ios/project.yml:
Early-exit guard — When PIKA_XCODE_SKIP_VERSION_STAMP=1, the script creates the expected output file and exits immediately. This avoids running git and PlistBuddy during CI compile checks where version metadata is irrelevant.
Dependency analysis enabled — basedOnDependencyAnalysis is flipped from false to true, and each script declares a unique outputFiles entry (e.g., $(DERIVED_FILE_DIR)/Pika-version-stamp). Xcode can now skip the phase entirely on incremental builds when inputs have not changed.
Sentinel file creation — Both the skip path and the normal path create the sentinel output file, ensuring Xcode's dependency graph remains satisfied.
In just/checks.just, the apple-host-ios-compile recipe passes PIKA_XCODE_SKIP_VERSION_STAMP=1 so CI compile lanes benefit immediately.
Add SSH connection multiplexing to the remote Apple CI runner
Intent: Reduce SSH handshake overhead by enabling OpenSSH ControlMaster multiplexing. Subsequent SSH and SCP invocations to the same Apple host reuse the first connection for up to 60 seconds, eliminating repeated key exchange round-trips during the multi-step remote build.
The SSH wrapper script generated by prepare_ssh_binary() now includes three additional options:
ControlMaster=auto — the first connection becomes the master; subsequent ones multiplex over it.
ControlPersist=60 — the master socket stays open for 60 seconds after the last session disconnects.
ControlPath — points to a unique temp file created with mktemp -u (name only, no actual file — OpenSSH creates the socket itself).
The cleanup() trap is extended to rm -f "$ssh_control_path", ensuring the socket file does not leak on exit.
This is especially impactful for the Apple remote CI flow, which performs multiple sequential SSH round-trips (bundle push, environment setup, build execution, artifact retrieval).
Replace tee with direct append for remote CI log output
Intent: Avoid duplicating build output to stdout in the locked execution body. In CI, the outer harness already captures output; the tee was redundant and added process overhead.
Inside run_locked_body(), the output redirection is simplified from exec > >(tee -a ...) to a plain exec >> ... 2>&1. The previous approach spawned a tee subprocess to mirror output to both the log file and stdout. Since the CI runner does not consume stdout from this function, the tee was unnecessary overhead. Direct append is both simpler and faster.
Tighten CI lane file-trigger lists to reduce spurious builds
Intent: Prevent changes to files that are only relevant to one Apple lane (desktop vs. iOS) from triggering the other lane. Several script paths that were listed in both lane triggers are removed from the lane where they are not needed.
The forge_lanes.rs CI manifest previously had several script paths (apple-host-cache-key, apple-host-record-prepared-entry, lib/release-secrets.sh, pikaci-apple-host-bootstrap.sh) listed as triggers for lanes where they have no effect. These are removed:
Desktop lane loses apple-host-cache-key, apple-host-record-prepared-entry, and lib/release-secrets.sh — none of these participate in the desktop compile.
iOS lane loses apple-host-record-prepared-entry and pikaci-apple-host-bootstrap.sh.
The net effect is that a commit touching only scripts/apple-host-cache-key will trigger the iOS compile lane but not the desktop compile lane, saving an entire macOS build cycle.
Add pika-incus-guest-role crate to Apple build manifests
Intent: A new workspace crate, pika-incus-guest-role, is now a dependency in the Apple build graph. All locations that enumerate workspace crates for Apple CI — the Nix derivation inputs, the Nix allowed-package lists, and the Forge CI lane file triggers — must include it to avoid missing-source build failures.
Six insertion points are updated across three files:
flake.nix — the crate source is added to both the desktop and iOS Nix build input lists so the derivation can see the source tree.
nix/ci/apple-rust.nix — pika-incus-guest-role is added to allowedWorkspacePackages for both the desktop and iOS Nix build definitions.
crates/pikaci/src/forge_lanes.rs — the glob crates/pika-incus-guest-role/** is added to both the desktop and iOS lane trigger patterns so that changes to this crate correctly trigger rebuilds.
Add unit tests for lane-selection correctness
Intent: Codify the expected behavior of the tightened file-trigger lists with regression tests that verify desktop-only, iOS-only, and iOS-cache-key-only file changes select the correct CI lanes.
Three new unit tests are added to the forge_lanes test module:
desktop_only_branch_selects_only_desktop_apple_lane — a change to crates/pika-desktop/src/main.rs must trigger apple_desktop_compile and must not trigger apple_ios_compile.
ios_only_branch_selects_only_ios_apple_lane — a change to ios/Sources/AppManager.swift must trigger apple_ios_compile and must not trigger apple_desktop_compile.
ios_cache_key_script_selects_only_ios_apple_lane — a change to scripts/apple-host-cache-key (removed from the desktop trigger list in this branch) must only select the iOS lane.
These tests guard against future regressions in lane trigger configuration.
Add optional xcodebuild timing summary support
Intent: Allow developers and CI to opt into Xcode's -showBuildTimingSummary flag via an environment variable, enabling build-phase profiling without modifying command lines.
@@ -83,8 +84,10 @@
- if not verbose and "-quiet" not in args:
+ if not verbose and not show_timing_summary and "-quiet" not in args:
args = ["-quiet", *args]
+ if show_timing_summary and "-showBuildTimingSummary" not in args:
+ args = [*args, "-showBuildTimingSummary"]
The xcodebuild-compact wrapper gains a new environment variable PIKA_XCODEBUILD_SHOW_TIMING_SUMMARY:
When set to "1", the tool appends -showBuildTimingSummary to the xcodebuild arguments, which causes Xcode to print a per-target timing breakdown at the end of the build.
The -quiet flag is suppressed when the timing summary is requested, because -quiet would hide the summary output.
When the variable is unset or "0", behavior is unchanged.
This is useful for diagnosing which build phases dominate wall-clock time without needing full verbose output.