makepad/libs/relax/compiler/tests/serve
Admin b22a714840 makepad run: the Rust JIT (libs/relax) and makepad-remote
The JIT that runs makepad apps from source: lexer, front end, type checker,
lazy borrow checker (Gandalf; `--check-first` opts in), codegen, session and
warm/cold caches keyed by compiler stamp, provided crates bound late,
startup images, live patching. User-facing name is `makepad run` (binary
`makepad`, MAKEPAD_RUN_* env, ~/.makepad-run); libs/relax stays the internal
library name. `makepad check` reports rustc-style diagnostics with rustc
error codes and checks a whole workspace (`--workspace`). Runs on macOS,
Linux, Windows and Android. makepad-remote: one tool schema for apps and the
WM (app/1): grabs, captures, traces, frame profiles and tweaks.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 12:26:21 +02:00
..
fixture makepad run: the Rust JIT (libs/relax) and makepad-remote 2026-10-13 12:26:21 +02:00
.gitignore makepad run: the Rust JIT (libs/relax) and makepad-remote 2026-10-13 12:26:21 +02:00
queries.py makepad run: the Rust JIT (libs/relax) and makepad-remote 2026-10-13 12:26:21 +02:00
README.md makepad run: the Rust JIT (libs/relax) and makepad-remote 2026-10-13 12:26:21 +02:00
run.py makepad run: the Rust JIT (libs/relax) and makepad-remote 2026-10-13 12:26:21 +02:00
run.sh makepad run: the Rust JIT (libs/relax) and makepad-remote 2026-10-13 12:26:21 +02:00

Warm check server

makepad serve [--workspace DIR] [--idle-min 30] resolves and canonicalizes the workspace with Relax's resolver. It publishes PORT TOKEN PID\n in <workspace>/target/makepad-run/serve/<16 lowercase hex digits>/endpoint. Cargo target-dir overrides select <target>/makepad-run; MAKEPAD_RUN_CACHE_DIR explicitly replaces that directory. Run makepad clean from the workspace root or a member subdirectory to remove the entire makepad-run directory, including serve files and startup manifests. The directory key is the low 64 bits of Relax's Sip128 hash of the canonical root. The token is 32 OS-random bytes, hex encoded. The endpoint and lock are private files. An OS file lock prevents duplicate servers and is released on crashes; a subsequent server replaces a stale endpoint. A second live server invocation prints the endpoint and exits successfully.

All examples below are single JSON lines. Replace TOKEN and /workspace/main.rs. IDs are integers within JSON's exactly representable range. Errors have the shape {"id":1,"error":{"code":-32602,"message":"missing string pkg"}}. An invalid token returns -32001 and closes the connection. Malformed JSON returns -32700; unknown methods return -32601. Input lines are limited to 8 MiB and JSON nesting to 128. Coordinates are one-based source lines and UTF-8 byte columns, using J1's mapping for macro expansion and .rx source. Definition ranges identify the declaration token.

Protocol

Check (optional paths filters returned diagnostics; checking/invalidation stays package-wide):

{"id":1,"token":"TOKEN","method":"check","params":{"pkg":"serve_fixture"}}
{"id":1,"result":{"diagnostics":[],"summary":{"errors":0,"warnings":0,"ms":0.123,"rechecked":1,"rss_mb":42.0}}}

Diagnostics are J1 objects without modification. Summary ms and rechecked describe the last actual check/edit, including edits applied by didChange. An unchanged request returns that snapshot. rss_mb is process RSS sampled after J2 trims its caches.

Unsaved editor buffer (JSON null discards the override and reloads from disk):

{"id":2,"token":"TOKEN","method":"didChange","params":{"path":"/workspace/main.rs","text":"#![no_std]\nfn main() {}\n"}}
{"id":2,"result":null}

Overlays survive disk notifications, apply to all already-loaded packages containing the file, and are applied when a package is first loaded.

Subscribe (stays on that connection, loads the package on first use):

{"id":3,"token":"TOKEN","method":"subscribe","params":{"pkg":"serve_fixture"}}
{"id":3,"result":null}
{"event":"diagnostics","pkg":"serve_fixture","diagnostics":[],"summary":{"errors":0,"warnings":0,"ms":0.123,"rechecked":1,"rss_mb":42.0}}

Events follow disk edits, including edits first detected by a concurrent check. The watcher polls source stamps every 100 ms. Slow clients have a bounded outbound queue and a five-second write timeout.

Status:

{"id":4,"token":"TOKEN","method":"status","params":{}}
{"id":4,"result":{"workspace":"/workspace","packages":[{"pkg":"serve_fixture","loaded_ms":100.0,"last_check_ms":0.123,"items":4,"rss_mb":42.0}],"uptime_s":12.0}}

Schema:

{"id":5,"token":"TOKEN","method":"schema","params":{"pkg":"serve_fixture","path":"crate::Counter"}}
{"id":5,"result":{"kind":"struct","path":"serve_fixture::Counter","generics":[],"fields":[{"name":"value","type":"i32","docs":"Current count."}],"variants":[],"methods":[{"name":"type_id","signature":"fn type_id(&Counter) -> TypeId","docs":""},{"name":"borrow","signature":"fn borrow(&Counter) -> &Counter","docs":""},{"name":"borrow_mut","signature":"fn borrow_mut(&mut Counter) -> &mut Counter","docs":""},{"name":"from","signature":"fn from(Counter) -> Counter","docs":""},{"name":"read","signature":"fn read(&Counter) -> i32","docs":"Read the count."}],"traits":["core::any::Any","core::borrow::Borrow","core::borrow::BorrowMut","core::convert::From"],"docs":"A documented counter.\nUsed by the editor fixture.","definition":{"file":"/workspace/main.rs","line":4,"col":12,"end_line":4,"end_col":19}}}

Schema uses root-scope resolution, AST docs and generics, collected field types, and inherent/trait impls, including blanket impls whose arguments and bounds are determined by the queried type. Call-site-dependent blanket impls are omitted. .rx types additionally expose configurable fields from the crate’s retained rx-core schema as properties: [{name,type}].

Hover (pkg may be supplied to load the package before querying; otherwise an unloaded file’s nearest package manifest is used):

{"id":6,"token":"TOKEN","method":"hover","params":{"path":"/workspace/main.rs","line":16,"col":17}}
{"id":6,"result":{"kind":"local","name":"local","type":"Counter","docs":"","definition":{"file":"/workspace/main.rs","line":15,"col":9,"end_line":15,"end_col":14}}}

Local, field and method resolution reads typeck body tables, rechecking the enclosing function after J2 trims those tables. Method types substitute the resolved generic arguments. An unresolved token returns null. Packages with parse errors reject queries rather than returning locations from the previous syntax snapshot.

Definition:

{"id":7,"token":"TOKEN","method":"definition","params":{"path":"/workspace/main.rs","line":18,"col":19}}
{"id":7,"result":[{"file":"/workspace/main.rs","line":13,"col":4,"end_line":13,"end_col":10}]}

An unresolved definition returns an empty array.

Shutdown:

{"id":8,"token":"TOKEN","method":"shutdown","params":{}}
{"id":8,"result":null}

Shutdown drains outbound responses, retires sessions, and removes the exact endpoint and lock paths. Ctrl+C/SIGTERM and expiry after the configured time with no connections use the same cleanup path.

CLI

  • makepad check [--json] PKG: connect if a live endpoint authenticates; otherwise one-shot.
  • makepad check --no-serve [--json] PKG: always one-shot.
  • makepad check --serve [--json] PKG: start a detached server if necessary, with stdin closed and stdout/stderr appended to serve.log beside the endpoint; wait up to 30 minutes.
  • makepad schema [--serve] PKG ITEM
  • makepad hover [--serve] PKG FILE LINE COL
  • makepad definition [--serve] PKG FILE LINE COL

Query commands emit one JSON result, falling back to a one-shot session when no server is available; --serve explicitly requests persistent startup. Check emits J1 diagnostic lines followed by the same four-field JSON summary as one-shot, with integer milliseconds; human diagnostics and summary go to stderr. Compilation-changing options (--units, --deep, features, watch, etc.) keep their one-shot behavior and do not implicitly connect. Explicit --serve combined with those options returns an error instead of ignoring options.

Validation

Run ./run.sh /absolute/path/to/makepad. The Python socket client creates an isolated copy of the adjacent fixture, sets both Relax and Cargo offline, and retains logs and sources under the printed temporary directory. It compares diagnostic lines and summary fields with one-shot output, normalizing only milliseconds, and prints warm check/edit timings.

The compiler has process-global registries, so all package work currently runs on one server worker. Socket reads/writes run independently. This is stricter serialization than one writer per package, and avoids sharing compiler pointers between threads.

The supplementary python3 queries.py /absolute/path/to/makepad gate exercises query fallbacks without a listener, including .rx coordinates/properties, negative blanket impl coverage, and warm J2 edits over the existing check --watch pipe. It does not replace the socket gate. See REPORT.md for this implementation's validation results and deviations.