Skip to content

Open a built app without waiting for cargo - #552

Merged
ymote merged 1 commit into
mainfrom
feat/open-built-apps-without-cargo
Oct 21, 2026
Merged

ymote merged 1 commit into
mainfrom
feat/open-built-apps-without-cargo

Conversation

@ymote

@ymote ymote commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

From a checkout, opening an already-built app no longer waits for cargo. The app starts as soon as the shell's own build is known to have made the binary and nothing that build read has changed since.

Why

From a checkout, every process app's open ran cargo build --release first, even when the binary was current. Cargo's up-to-date check alone loads the whole workspace. Measured here on 21 Oct 2026, under this Mac's everyday load (another session's build, a backup client, a VM):

  • 0.6–2.1 s per open for an app already built.
  • 8.3 s for the first open after the shell starts.

That was 35–85% of each open. Reopening apps felt slow even after #549 had built everything.

What changes

  • clients::note_built: a successful build leaves a record beside its binary, <bin>.octosense-build, holding its exact command. Builds that write it: a launch's build, including one whose launch was closed meanwhile (ClientSlot::continue_launch), and the prebuild's (prebuild::run).

  • clients::build_is_fresh, checked by spawn_client. The binary starts at once (log: wm: <app>: built and unchanged since; it starts without cargo) when two things hold:

    • the record exists, matches this command, and is no older than the binary;
    • nothing the build read is newer than the binary. That covers every file in cargo's dep-info for the binary (<bin>.d: the sources and included files of every path crate it links), each of those crates' Cargo.toml, the checkout's manifest and lock, cargo's config and toolchain files on the way up from the build's directory, and the cargo and rustc it runs.

    It costs a few thousand stats. Anything newer, missing or unrecorded runs the build exactly as before, with the "building…" tile and progress.

  • What the comparison cannot see (a build script's own inputs, a changed environment, a toolchain swapped in place): the prebuild runs the same build in the background after every start, so a stale binary is rebuilt and recorded for the next open. A binary someone else rebuilt (a manual cargo build, maybe with other features) is newer than the record, so the shell's build runs again.

  • BuildStep gains program, the binary it produces. PendingApp keeps its BuildStep.

  • Docs: prebuild.rs module docs and the MAKEPAD_WM_NO_PREBUILD row in both desktop READMEs.

The sandbox is unchanged: a native app still starts under its policy. The target dir stays read-only to sandboxed apps, so no app can write a record. The six Makepad catalog apps aren't sandboxed (as before).

Measured

One hidden shell per app (MAKEPAD_WM_TEST_APP, prebuild off), the same demo profile, every app already built. Times are from the launch gesture to the app's first frame on the desk (wm: <app> client N first frame in N ms):

App main 2ad6437 this PR, first open (records the build) this PR, next open
Reference (first open after start) 9955 ms 3781 ms 336 ms
Browser 3069 ms 1938 ms 331 ms
Files 1813 ms 1818 ms 312 ms
Terminal 2796 ms 2881 ms 338 ms
Task 2776 ms 2788 ms 331 ms
Sheets 2803 ms 2780 ms 326 ms
Finance 1765 ms 1782 ms 349 ms
Route 2039 ms 1994 ms 430 ms
Image 1746 ms 1756 ms 330 ms
PDF 1739 ms 1746 ms 332 ms

Calculator, Clock, Notes, Reminders and Weather open in-process on macOS, with no build either way.

The second-run logs show the fast path for every app; Sheets' sandbox line is still there. The edit loop still builds first: after a touch of apps/image/src/main.rs, the next Image open ran cargo (Compiling makepad-image … Finished in 2.18s, first frame 3723 ms). The open after that started without cargo again (332 ms).

Testing

  • macOS, from phone/: cargo test --locked --features mobile-apps -p octosense-shell --lib -- clients::tests octosense::catalog::tests prebuild::tests sandbox::tests: 71 passed. That includes:
    • a_binary_newer_than_everything_its_build_read_starts_without_cargo: no record, then a record; a newer source, an included file with an escaped space, the crate manifest, the lock, the workspace manifest and the toolchain each force the build; a different command; a binary rebuilt after the record; a deleted input; no dep-info.
    • a_launch_starts_its_app_only_after_a_successful_build, now also checking that a successful build is recorded, a failed one is not, and a closed launch's successful build is.
  • cargo build --locked -p octosense, then the timings above.
  • Local CI gate (tools/ci-local.sh --only all --linux-host --offload): to follow.
  • Unverified: the fast path end to end on Linux and Windows. The comparison is portable, std::fs modification times, and the unit tests run on the Linux CI jobs.

This touches clients.rs in places #551 doesn't, so the two merge in either order.

🤖 Generated with Claude Code

From a checkout, every process app's open ran its release build first,
and cargo's up-to-date check alone loads the whole workspace: 0.6-2 s
for an app already built, 8 s for the first open after the shell starts,
the larger part of each open (measured 21 Oct 2026 under everyday load).

A successful build (a launch's or the prebuild's) now leaves a record
beside its binary: the exact command. An open starts the binary at once
when that record covers it and nothing the build read is newer: every
file in cargo's dep-info for the binary (the sources and included files
of every path crate it links), those crates' manifests, the checkout's
manifest and lock, cargo's config and toolchain files, and the cargo and
rustc it runs. Anything newer, missing or unrecorded runs the build as
before; the prebuild's run of the same build catches what the comparison
cannot see (a build script's own inputs, a changed environment).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ymote
ymote merged commit 1663636 into main Oct 21, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant