Move the Design-tab scripts into scripts/CAD/ and name them by role

Requested by SoftFever on PR #15238: ten of these had accumulated loose in
scripts/ next to ~20 unrelated upstream ones, with names that only meant
something to whoever wrote them. They now sit in scripts/CAD/, mirroring the
src/libslic3r/CAD/ and src/slic3r/GUI/CAD/ split, and the verb in the name is
the role: build- produces a binary, start- brings something up, run- runs a
suite, check- asserts one thing against a live app.

  kernel-test.sh        -> CAD/run-kernel-tests.sh
  ladder-all.sh         -> CAD/run-all-checks.sh
  sketch-ladder.py      -> CAD/check-sketch-engine.py
  ladder-corpus.py      -> CAD/check-sketch-engine-corpus.py
  gui-ladder.py         -> CAD/check-gui-sketching.py
  offer-ladder.py       -> CAD/check-gui-context-menu.py
  mcp-sketch-smoke.py   -> CAD/check-mcp-sketch.py
  rig-build.sh          -> CAD/build-gui.sh
  docker-iter-build.sh  -> CAD/build-gui-incremental.sh
  gui-session.sh        -> CAD/start-headless-gui.sh

"Ladder" was the worst of them: it named the shape of the test (rungs of
increasing difficulty) rather than what the test proves, so nothing in the
directory listing told you which one needed a GPU and which was pure kernel.

Every reference rewritten -- the docs, the cross-calls between the scripts,
Dockerfile.deps, and the container-side /OrcaSlicer/scripts paths. The three
shell scripts resolve REPO relative to themselves and now sit one level
deeper, so that walk went from /.. to /../.. . The copies these push into a
container's /tmp were renamed to match, or the container would have kept the
old names alive.

Two runtime paths deliberately NOT renamed. /tmp/orca-rig-build.lock is a
cross-fork contract -- both forks take the same lock so two concurrent builds
serialise instead of OOMing the box, and renaming it on one side silently
removes that guard. /tmp/gui-session.log is a runtime artefact, not a script.

Added scripts/CAD/README.md: what each script proves, what it needs, and the
two constraints that have each cost a session (never build inside the GUI
container; a window manager is required or synthetic keys are ignored).

On CI, which was the other half of the request: the kernel suite is already
there and always has been. The cases are registered in
tests/libslic3r/CMakeLists.txt under if (SLIC3R_CAD), which defaults ON and no
workflow turns off, so they build into libslic3r_tests and run under ctest on
every platform via unit_tests.yml -- like any other unit test, needing no new
job. They have simply never been seen to run, because the workflows on this PR
are still awaiting maintainer approval. run-kernel-tests.sh is the local loop
over the same cases, and it is the only script here CI could run: the other
six need an OpenGL canvas and synthetic input.

Verified: scripts/CAD/run-kernel-tests.sh from its new location, all tests
passed, 2562 assertions in 190 test cases.
This commit is contained in:
Tommaso Bianchi
2026-08-28 19:34:03 +02:00
parent cdd41e230d
commit 13d5eac891
16 changed files with 118 additions and 65 deletions
+1 -1
View File
@@ -46,7 +46,7 @@ is new code. The negotiable surface is the 37 modified files.
205 `TEST_CASE` blocks across 6 new test source files. This counts assertions written, not
assertions passed — a run needs a build.
`scripts/kernel-test.sh` is the headless verification contract: it builds only
`scripts/CAD/run-kernel-tests.sh` is the headless verification contract: it builds only
`libslic3r_tests` (not the GUI app), needs no display, and exit 0 means the CAD suite
passed. It now runs with **no exclusions** — both cases that used to be quarantined (the
circle-line tangency solver abort and the internal-thread reference) are fixed.
+14 -14
View File
@@ -3,16 +3,16 @@
The build rig is two long-lived containers, `snaporca-gui` and `orcacad-gui`, one per fork. Each
mounts only its fork's build volume (`snaporca_buildcache` / `orcacad_buildcache`) at
`/OrcaSlicer/build`, its fork's `resources/`, and a shots directory — nothing else. They run the
binary; they do not build it. Rebuild with `scripts/rig-build.sh`.
binary; they do not build it. Rebuild with `scripts/CAD/build-gui.sh`.
| fork repo | project() | deps image | build volume | GUI container | binary |
|---|---|---|---|---|---|
| `snaporca` | `Snapmaker_Orca` | `snaporca-deps` | `snaporca_buildcache` | `snaporca-gui` | `snapmaker-orca` |
| `orca_cad` | `OrcaSlicer` | `orcacad-deps` | `orcacad_buildcache` | `orcacad-gui` | `orca-slicer` |
`scripts/rig-build.sh` exists alongside `scripts/docker-iter-build.sh` for one reason: it does a
`scripts/CAD/build-gui.sh` exists alongside `scripts/CAD/build-gui-incremental.sh` for one reason: it does a
target-only `ninja` into the volume the GUI rig launches from, so a session can test a single
change without a full repackage, whereas `docker-iter-build.sh` runs the full packaged build.
change without a full repackage, whereas `build-gui-incremental.sh` runs the full packaged build.
Both start a throwaway container from the deps image with the live repo mounted over the baked
tree — never build inside the GUI container (Trap 1).
@@ -32,7 +32,7 @@ reports an unknown target, and `orca-slicer` / `OrcaSlicer` have been replaced b
`snaporca-deps`, so even on the mainline fork the baked tree is the other fork's. A `cmake .`
there reconfigures the shared build dir under the wrong project name.
**Fix.** Build only via `scripts/rig-build.sh`, which starts a throwaway container from the deps
**Fix.** Build only via `scripts/CAD/build-gui.sh`, which starts a throwaway container from the deps
image with the live repo mounted over the baked tree — `src`, `resources`, `cmake`, `deps_src`,
`localization`, `CMakeLists.txt`, `version.inc` — and writes into the same volume the rig
launches from.
@@ -66,7 +66,7 @@ fork's root `CMakeLists.txt`, near line 948).
**Cause.** The deps image predates that requirement. Only the mainline (`orca_cad`) fork has
`deps_src/pybind11` and the requirement; snaporca has neither.
**Fix.** Mount `deps_src` over the baked tree — `scripts/rig-build.sh` does. Corollary: mounting a
**Fix.** Mount `deps_src` over the baked tree — `scripts/CAD/build-gui.sh` does. Corollary: mounting a
snaporca tree into an `orcacad-deps` build reproduces this error exactly.
---
@@ -83,7 +83,7 @@ its own configure, while the consumer in the root `CMakeLists.txt` (`if (NOT OCC
after the `TKFillet TKOffset` prepend (`src/libslic3r/CMakeLists.txt:599`) therefore links the
previous list and drops `TKBool`/`TKOffset`.
**Fix.** Configure twice. `scripts/rig-build.sh` runs `cmake .` twice for exactly this reason; if
**Fix.** Configure twice. `scripts/CAD/build-gui.sh` runs `cmake .` twice for exactly this reason; if
you ever configure by hand, run it twice.
---
@@ -100,7 +100,7 @@ snaporca, 319/320 in orca_cad). Every `#ifdef SLIC3R_CAD` block therefore compil
option still reads ON.
**Fix.** Always mount the live `CMakeLists.txt` and `cmake/` — never inherit them from the image.
This is why `scripts/docker-iter-build.sh`, `scripts/kernel-test.sh` and `scripts/rig-build.sh`
This is why `scripts/CAD/build-gui-incremental.sh`, `scripts/CAD/run-kernel-tests.sh` and `scripts/CAD/build-gui.sh`
all mount both.
---
@@ -109,18 +109,18 @@ all mount both.
`ninja <target>` writes `/OrcaSlicer/build/src/Release/<binary>`; only `build_linux.sh`
additionally packages to `/OrcaSlicer/build/package/bin/<binary>`. `orca_cad`'s
`scripts/gui-session.sh` defaults `BIN` to `src/Release/orca-slicer`, but snaporca's defaults to
`scripts/CAD/start-headless-gui.sh` defaults `BIN` to `src/Release/orca-slicer`, but snaporca's defaults to
`package/bin/snapmaker-orca`. So after a target-only rebuild on snaporca, launching
`gui-session.sh` with its default runs the **stale packaged** binary — the change under test is
`start-headless-gui.sh` with its default runs the **stale packaged** binary — the change under test is
invisible and the session hunts a phantom. Pass `BIN` explicitly:
docker exec -e BIN=/OrcaSlicer/build/src/Release/snapmaker-orca snaporca-gui /OrcaSlicer/scripts/gui-session.sh
docker exec -e BIN=/OrcaSlicer/build/src/Release/snapmaker-orca snaporca-gui /OrcaSlicer/scripts/CAD/start-headless-gui.sh
`scripts/rig-build.sh` prints the correct line for the current fork when it finishes.
`scripts/CAD/build-gui.sh` prints the correct line for the current fork when it finishes.
Note also that the GUI containers do **not** mount `scripts/`: `/OrcaSlicer/scripts` inside them
is the baked copy, so a local edit to `gui-session.sh` has no effect until you
`docker cp scripts/gui-session.sh <container>:/OrcaSlicer/scripts/`.
is the baked copy, so a local edit to `start-headless-gui.sh` has no effect until you
`docker cp scripts/CAD/start-headless-gui.sh <container>:/OrcaSlicer/scripts/`.
---
@@ -144,7 +144,7 @@ a real fault in your code gets past the version banner. Compare
## Trap 7 — a single-instance app plus a path-matched `pkill`
`gui-session.sh` used to kill by `"$BIN"`, while its own `app_pid()` matched by BASENAME. Launch
`start-headless-gui.sh` used to kill by `"$BIN"`, while its own `app_pid()` matched by BASENAME. Launch
with a `BIN` that differs from the running instance's path and the old process survives, keeps
the single-instance lock, and the new one exits seconds after loading fonts — then `status`
reports the *stale* pid as a healthy session. Fixed by killing on the basename; `status` now also
+1 -1
View File
@@ -144,7 +144,7 @@ def main():
text = "\n".join(lines)
# --check: prove the checked-in header IS what this generator produces, and change nothing.
# The header calls itself GENERATED and was hand-edited anyway; a claim like that is only
# worth having if something enforces it, so ladder-all.sh runs this on every gate.
# worth having if something enforces it, so run-all-checks.sh runs this on every gate.
if "--check" in sys.argv:
have = open(OUT, encoding="utf-8").read() if os.path.exists(OUT) else ""
if have == text:
+53
View File
@@ -0,0 +1,53 @@
# Design-tab scripts
Everything here supports the parametric Design tab (`src/libslic3r/CAD/`,
`src/slic3r/GUI/CAD/`). Nothing here is needed to build or run OrcaSlicer — these
are the development and verification tools for that one feature.
The verb in the name is the role:
| | |
|---|---|
| `build-…` | produce a binary |
| `start-…` | bring something up and leave it running |
| `run-…` | run a suite and report pass/fail |
| `check-…` | one specific assertion, usually driving a live app |
## Verification
| Script | What it proves | Needs |
|---|---|---|
| `run-kernel-tests.sh` | The CAD kernel builds and the Catch2 `[CadDocument]` tags pass — every case builds a document, recomputes it and asserts on real geometry. **Exit 0 is the verification contract.** | Docker only. No display. |
| `run-all-checks.sh` | Every check below, in one command. The gate before pushing a Design-tab change. | Docker + the GUI container |
| `check-sketch-engine.py` | A ladder of 2D sketches of increasing complexity, judged on loop count, closure and void attribution rather than on area. | Kernel only |
| `check-sketch-engine-corpus.py` | The same ladder graded against a systematic sample of real drawings instead of shapes we chose. | Kernel + corpus |
| `check-gui-sketching.py` | The same profiles drawn the way a person draws them — synthetic mouse gestures and typed values. | Headless GUI |
| `check-gui-context-menu.py` | That right-click is the pivot of the design gesture, and adapts to what was clicked. | Headless GUI |
| `check-mcp-sketch.py` | The sketch layer driven over the MCP socket, asserting what decides whether a profile is buildable. | Headless GUI + `SNAPORCA_MCP` |
**`run-kernel-tests.sh` is the only one CI can run.** The rest need a live
application with an OpenGL canvas and synthetic input, which hosted runners do not
have. The kernel suite itself is already in CI by an ordinary route: the cases are
registered in `tests/libslic3r/CMakeLists.txt` under `if (SLIC3R_CAD)`, so they are
part of `libslic3r_tests` and run under `ctest` on every platform like any other
unit test. This script exists for the local loop, where it is a two-minute round
trip instead of a full application build.
## Build and run
| Script | Purpose |
|---|---|
| `build-gui.sh` | Build the GUI binary in a throwaway container, writing into the build-cache volume the long-lived GUI container reads. |
| `build-gui-incremental.sh` | Incremental build against the deps-baked image, for a fast edit/compile loop. |
| `start-headless-gui.sh` | Bring the app up on a headless X display (Xvfb + a window manager), ready to drive or attach to over VNC. |
Two constraints that are not obvious and have each cost a session:
- **Never build inside the GUI container.** Its baked source tree silently
reconfigures the shared build directory and this fork's targets vanish.
- **A window manager is required.** Without one, windows are never focused, and an
unfocused GTK app ignores synthetic keys — which looks exactly like a code bug.
`docs/rig_build_traps.md` documents these and three more, with symptoms and exact
recovery commands. Read it before debugging a configure or link failure one of
these scripts reports.
@@ -9,8 +9,8 @@
# Result: edit -> rebuild in seconds-to-minutes instead of a full Docker rebuild.
#
# Usage (run on the build host, e.g. behemoth, from anywhere):
# scripts/docker-iter-build.sh
# IMAGE=orcacad-deps scripts/docker-iter-build.sh
# scripts/CAD/build-gui-incremental.sh
# IMAGE=orcacad-deps scripts/CAD/build-gui-incremental.sh
#
# On success the binary is inside the persistent volume at
# /OrcaSlicer/build/package/bin/orca-slicer (copy it out with a follow-up
@@ -18,8 +18,8 @@
# Rig build traps already paid for once each (stale project, NLopt cache, pybind11, OCCT_LIBS, SLIC3R_CAD gate): docs/rig_build_traps.md
set -euo pipefail
REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
# orcacad-deps, NOT snaporca-deps: see the note in kernel-test.sh — the wrong image
REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
# orcacad-deps, NOT snaporca-deps: see the note in run-kernel-tests.sh — the wrong image
# fails at CMake configure, not at link time.
IMAGE="${IMAGE:-orcacad-deps}"
BUILD_VOL="${BUILD_VOL:-orcacad_buildcache}"
@@ -42,10 +42,10 @@ echo "REPO=$REPO IMAGE=$IMAGE BUILD_VOL=$BUILD_VOL"
# ---- OOM guard (2026-08-21) -------------------------------------------------------------
# Two of these builds ran at once on 2026-08-21, each with ninja -j$(nproc)=16: ~36 cc1plus
# holding 42 GB of a 62 GB box -> global OOM at 21:05, a 2h28m kill storm, ssh unreachable,
# lightdm destroyed. Neither build produced a single object. scripts/rig-build.sh grew the
# lightdm destroyed. Neither build produced a single object. scripts/CAD/build-gui.sh grew the
# bounds first; every script that starts a compile needs the same three, or the guard is only
# as strong as the script you happened not to use.
# flock — the lock path is SHARED with rig-build.sh and the other fork on purpose, so
# flock — the lock path is SHARED with build-gui.sh and the other fork on purpose, so
# concurrent builds serialise instead of summing.
# -j — bounded parallelism; ~1.17 GB per cc1plus was the measured average.
# --memory — the actual guarantee: a runaway build dies in its own cgroup instead of taking
@@ -9,11 +9,11 @@
# Read that file before debugging a configure or link failure this script reports.
#
# Usage:
# scripts/rig-build.sh # configure + build the fork's GUI target
# DRY_RUN=1 scripts/rig-build.sh # print the resolved fork identity and exit, no container
# scripts/CAD/build-gui.sh # configure + build the fork's GUI target
# DRY_RUN=1 scripts/CAD/build-gui.sh # print the resolved fork identity and exit, no container
set -euo pipefail
REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
# Fork identity is DERIVED from the repo, never hardcoded, so this file is byte-identical in
# both forks and cannot be mirrored into the wrong one. Pointing a fork at the other fork's
@@ -53,7 +53,7 @@ LOCK=/tmp/orca-rig-build.lock
exec 9>"$LOCK"
if ! flock -n 9; then
echo "another fork's rig-build holds $LOCK — waiting (this is the OOM guard, not a hang)"
echo "another fork's build-gui holds $LOCK — waiting (this is the OOM guard, not a hang)"
flock 9
fi
@@ -90,9 +90,9 @@ docker run --rm \
exit \$rc
" || rc=$?
# A target-only build writes src/Release/, but this fork's gui-session.sh may default BIN to the
# A target-only build writes src/Release/, but this fork's start-headless-gui.sh may default BIN to the
# PACKAGED path that only build_linux.sh refreshes — launching with the default would then run a
# stale binary. Pass BIN explicitly. See docs/rig_build_traps.md.
echo "=== launch the rig on the binary just built ==="
echo " docker exec -e BIN=/OrcaSlicer/build/src/Release/$BIN ${PREFIX}-gui /OrcaSlicer/scripts/gui-session.sh"
echo " docker exec -e BIN=/OrcaSlicer/build/src/Release/$BIN ${PREFIX}-gui /OrcaSlicer/scripts/CAD/start-headless-gui.sh"
exit "$rc"
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
"""The OFFER ladder: prove that right-click is the pivot, and that it adapts to what was clicked.
The gesture ladder (scripts/gui-ladder.py) proved the TARGET — a complex closed profile, precise
The gesture ladder (scripts/CAD/check-gui-sketching.py) proved the TARGET — a complex closed profile, precise
in vertices, lengths, arcs and symmetry, with its voids correctly attributed. It proved it by
arming every tool with a letter key. That leaves the goal's own MECHANISM untested: the design
logic pivots on right-click, and the verbs offered are supposed to adapt to the element under the
@@ -20,7 +20,7 @@ This ladder drives the menu. Nothing here is asserted from pixels:
Run inside the rig container, with the app launched under SNAPORCA_KEYTRACE=1:
docker exec snaporca-gui python3 /OrcaSlicer/scripts/offer-ladder.py [rung ...]
docker exec snaporca-gui python3 /OrcaSlicer/scripts/CAD/check-gui-context-menu.py [rung ...]
"""
import importlib.util
import math
@@ -34,7 +34,7 @@ HERE = os.path.dirname(os.path.abspath(__file__))
# The gesture ladder owns the hand and the eye: the homography per sketch, the window lookup by
# class, the synthetic click, the typed value, the socket. Importing it is the only way those
# stay one implementation — a second copy would drift the first time a rig detail moved.
_spec = importlib.util.spec_from_file_location("gui_ladder", os.path.join(HERE, "gui-ladder.py"))
_spec = importlib.util.spec_from_file_location("gui_ladder", os.path.join(HERE, "check-gui-sketching.py"))
G = importlib.util.module_from_spec(_spec)
_spec.loader.exec_module(G)
@@ -617,7 +617,7 @@ def clicked(X, Y):
def fresh_sketch(tool):
"""Enter a sketch from a KNOWN empty state, whatever the previous rung or run left behind.
gui-ladder's enter_sketch dismisses the old session with keys, and a key is exactly what an
check-gui-sketching's enter_sketch dismisses the old session with keys, and a key is exactly what an
open value field swallows — so a session that should have been cancelled survives, the four
calibration probes land in it on top of whatever was already there, and the run dies with
"calibration expected 4 points, got 7". Cancelling through the socket cannot be swallowed:
@@ -1196,7 +1196,7 @@ RUNGS = {"kinds": rung_kinds, "vocabulary": rung_vocabulary,
def main():
if not os.path.exists(LOG):
G.die(f"no {LOG} — launch the app through scripts/gui-session.sh")
G.die(f"no {LOG} — launch the app through scripts/CAD/start-headless-gui.sh")
if "[OFFER]" not in open(LOG, errors="replace").read()[-400000:]:
print(f"note: no [OFFER] lines in {LOG} yet — the app must run with SNAPORCA_KEYTRACE=1")
want = sys.argv[1:] or list(RUNGS)
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
"""A ladder of sketches drawn the way a person draws them: mouse gestures and typed values.
WHY THIS EXISTS, next to scripts/sketch-ladder.py. That ladder proves the ENGINE — it feeds
WHY THIS EXISTS, next to scripts/CAD/check-sketch-engine.py. That ladder proves the ENGINE — it feeds
geometry through the MCP socket's add_entities_scripted and grades what comes back. The socket
path skips everything the goal actually rests on: gesture state, the auto-edit queue, snapping,
inference at gesture tolerance, and the right-click offer. A ladder that only drives the socket
@@ -11,8 +11,8 @@ here ONLY as an instrument, never as an author.
Runs INSIDE the headless rig container (Xvfb :10 + openbox + the app with SNAPORCA_MCP set):
docker cp scripts/gui-ladder.py snaporca-gui:/tmp/ && \
docker exec snaporca-gui python3 /tmp/gui-ladder.py [rung ...]
docker cp scripts/CAD/check-gui-sketching.py snaporca-gui:/tmp/ && \
docker exec snaporca-gui python3 /tmp/check-gui-sketching.py [rung ...]
With no arguments every rung runs. Exit 0 = every property held.
"""
@@ -9,7 +9,7 @@ loop: build a known profile, ask the app what it thinks it has, compare against
RUN IT AGAINST A RUNNING APP:
SNAPORCA_MCP=/tmp/mcp.sock <binary> # launch with the socket enabled
python3 scripts/mcp-sketch-smoke.py [socket] # default /tmp/mcp.sock
python3 scripts/CAD/check-mcp-sketch.py [socket] # default /tmp/mcp.sock
Exit 0 = every assertion held. Anything else prints the first mismatch and stops.
"""
@@ -20,7 +20,7 @@ agree, and that the engine's own operations preserve what they promise.
MIRROR a real closed profile, mirrored, is still exactly one closed loop
OFFSET a real closed profile, offset, is still closed
Usage: ladder-corpus.py [--sample N] [--corpus DIR]
Usage: check-sketch-engine-corpus.py [--sample N] [--corpus DIR]
"""
import argparse
@@ -16,7 +16,7 @@ Every rung asserts those. Area appears only as a cross-check, never as the verdi
Entirely 2D: sketch entities only, no extrude, revolve or any solid feature.
SNAPORCA_MCP=/tmp/mcp.sock <binary>
python3 scripts/sketch-ladder.py [socket]
python3 scripts/CAD/check-sketch-engine.py [socket]
Exit 0 = every rung held. Otherwise the first broken property is named and the run stops.
"""
@@ -6,12 +6,12 @@
# run this, read the last line, and do not push a red one. The kernel suite is the only part CI
# can carry, and it already does.
#
# scripts/ladder-all.sh # kernel + engine + corpus (every 20th) + gestures + offer
# FULL=1 scripts/ladder-all.sh # corpus over ALL 997 sheets (~25 min)
# SKIP_GUI=1 scripts/ladder-all.sh # kernel only, for a machine with no rig
# scripts/CAD/run-all-checks.sh # kernel + engine + corpus (every 20th) + gestures + offer
# FULL=1 scripts/CAD/run-all-checks.sh # corpus over ALL 997 sheets (~25 min)
# SKIP_GUI=1 scripts/CAD/run-all-checks.sh # kernel only, for a machine with no rig
#
# The rig container is expected to be up with the app running and SNAPORCA_MCP set; bring it up
# with scripts/gui-session.sh inside it. The corpus lives at /corpus in that container.
# with scripts/CAD/start-headless-gui.sh inside it. The corpus lives at /corpus in that container.
set -uo pipefail
cd "$(dirname "$0")/.." || exit 1
@@ -43,17 +43,17 @@ run_in_rig() { # copy the script in fresh, then run it ther
# 92-row array, so the last verb was unreachable (snaporca-z8rs, snaporca-ziam).
step "offer table matches the atlas" python3 docs/ux/mockups/gen_offer_table.py --check
step "kernel suite" scripts/kernel-test.sh --vol "${KVOL:-snaporca_kerneltest}"
step "kernel suite" scripts/CAD/run-kernel-tests.sh --vol "${KVOL:-snaporca_kerneltest}"
if [ -z "${SKIP_GUI:-}" ]; then
step "engine ladder (rungs 1-8, scripted geometry)" \
run_in_rig scripts/sketch-ladder.py /tmp/sketch-ladder.py
run_in_rig scripts/CAD/check-sketch-engine.py /tmp/check-sketch-engine.py
step "corpus rung (real drawings, every ${STEP}th)" \
run_in_rig scripts/ladder-corpus.py /tmp/ladder-corpus.py --corpus "$CORPUS" --step "$STEP"
run_in_rig scripts/CAD/check-sketch-engine-corpus.py /tmp/check-sketch-engine-corpus.py --corpus "$CORPUS" --step "$STEP"
step "corpus scale rung (the heaviest sheets)" \
run_in_rig scripts/ladder-corpus.py /tmp/ladder-corpus.py --corpus "$CORPUS" --scale
run_in_rig scripts/CAD/check-sketch-engine-corpus.py /tmp/check-sketch-engine-corpus.py --corpus "$CORPUS" --scale
step "gesture ladder (mouse and keyboard)" \
run_in_rig scripts/gui-ladder.py /tmp/gui-ladder.py
run_in_rig scripts/CAD/check-gui-sketching.py /tmp/check-gui-sketching.py
# The offer ladder needs TWO extra things the others do not: the app must have been launched
# with SNAPORCA_KEYTRACE=1 (its [OFFER] lines are the whole instrument), and it reads the
# generated offer table to predict what each selection should show — which is not in the
@@ -61,7 +61,7 @@ if [ -z "${SKIP_GUI:-}" ]; then
# run_in_rig puts the script, is one of the paths the ladder looks in.
docker cp src/slic3r/GUI/CAD/DesignOffer.hpp "$C:/tmp/DesignOffer.hpp" >/dev/null
step "offer ladder (right-click, the menu, the verbs behind it)" \
run_in_rig scripts/offer-ladder.py /tmp/offer-ladder.py
run_in_rig scripts/CAD/check-gui-context-menu.py /tmp/check-gui-context-menu.py
fi
echo
@@ -7,10 +7,10 @@
# CadDocument, recompute()s it and asserts on geometry.
#
# Usage:
# scripts/kernel-test.sh # [CadDocument] tags, default volume
# scripts/kernel-test.sh --tags '[CadDocument],[Sketch]'
# scripts/kernel-test.sh --vol wt_mirror # private build cache (parallel workers)
# scripts/kernel-test.sh --host tommaso@100.103.234.2 # build on a remote host
# scripts/CAD/run-kernel-tests.sh # [CadDocument] tags, default volume
# scripts/CAD/run-kernel-tests.sh --tags '[CadDocument],[Sketch]'
# scripts/CAD/run-kernel-tests.sh --vol wt_mirror # private build cache (parallel workers)
# scripts/CAD/run-kernel-tests.sh --host tommaso@100.103.234.2 # build on a remote host
#
# Parallel workers MUST pass a distinct --vol: two builds sharing one cache corrupt
# each other. A new volume pays one full build; runs after that are incremental.
@@ -22,14 +22,14 @@
# Rig build traps already paid for once each (stale project, NLopt cache, pybind11, OCCT_LIBS, SLIC3R_CAD gate): docs/rig_build_traps.md
set -euo pipefail
REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
# orcacad-deps, NOT snaporca-deps: this fork is mainline-based and needs Eigen 5.0.1,
# CGAL 5.6.3, wx 3.3.2 and Python 3.12 Development.Embed, none of which snaporca-deps has.
# With the wrong image CMake dies at configure, which is exactly why this fork went
# M1-M8 without ever compiling (see commit 1633005bba).
IMAGE="${IMAGE:-orcacad-deps}"
# Must NOT default to snaporca_buildcache: that is the other fork's volume, and pointing
# this fork at it makes the two silently trade build artefacts. docker-iter-build.sh had the
# this fork at it makes the two silently trade build artefacts. build-gui-incremental.sh had the
# identical defect and was fixed to orcacad_buildcache; this script was missed.
VOL="${BUILD_VOL:-orcacad_kerneltest}"
# No exclusions. Both cases that used to be quarantined now run: the solver SIGABRT on
@@ -64,7 +64,7 @@ if [[ -n "$HOST" ]]; then
"$REPO/src" "$REPO/tests" "$REPO/resources" "$REPO/cmake" "$REPO/scripts" \
"$REPO/CMakeLists.txt" \
"$HOST:$REMOTE/"
exec ssh "$HOST" "cd $REMOTE && scripts/kernel-test.sh --vol '$VOL' --tags '$TAGS' --image '$IMAGE'"
exec ssh "$HOST" "cd $REMOTE && scripts/CAD/run-kernel-tests.sh --vol '$VOL' --tags '$TAGS' --image '$IMAGE'"
fi
if ! docker image inspect "$IMAGE" >/dev/null 2>&1; then
@@ -84,17 +84,17 @@ if ! docker volume inspect "$VOL" >/dev/null 2>&1; then
docker volume create "$VOL" >/dev/null
fi
# tests/ is mounted too -- unlike docker-iter-build.sh, this script exists precisely to
# tests/ is mounted too -- unlike build-gui-incremental.sh, this script exists precisely to
# compile tests being edited. CMakeLists.txt and cmake/ carry the SLIC3R_CAD gate; taking
# them from the baked image instead leaves the gate off and the CAD symbols vanish.
# ---- OOM guard (2026-08-21) -------------------------------------------------------------
# Two of these builds ran at once on 2026-08-21, each with ninja -j$(nproc)=16: ~36 cc1plus
# holding 42 GB of a 62 GB box -> global OOM at 21:05, a 2h28m kill storm, ssh unreachable,
# lightdm destroyed. Neither build produced a single object. scripts/rig-build.sh grew the
# lightdm destroyed. Neither build produced a single object. scripts/CAD/build-gui.sh grew the
# bounds first; every script that starts a compile needs the same three, or the guard is only
# as strong as the script you happened not to use.
# flock — the lock path is SHARED with rig-build.sh and the other fork on purpose, so
# flock — the lock path is SHARED with build-gui.sh and the other fork on purpose, so
# concurrent builds serialise instead of summing.
# -j — bounded parallelism; ~1.17 GB per cc1plus was the measured average.
# --memory — the actual guarantee: a runaway build dies in its own cgroup instead of taking
@@ -1,11 +1,11 @@
#!/usr/bin/env bash
# Bring the headless GUI up on a VNC-served X display, ready to drive or to attach Remmina to.
#
# Runs INSIDE the long-lived GUI container (see the header of scripts/docker-iter-build.sh for how
# Runs INSIDE the long-lived GUI container (see the header of scripts/CAD/build-gui-incremental.sh for how
# that container is created). Idempotent: safe to re-run to recover a session whose app died.
#
# docker exec <container> /OrcaSlicer/scripts/gui-session.sh # launch + settle
# docker exec <container> /OrcaSlicer/scripts/gui-session.sh --status # report, change nothing
# docker exec <container> /OrcaSlicer/scripts/CAD/start-headless-gui.sh # launch + settle
# docker exec <container> /OrcaSlicer/scripts/CAD/start-headless-gui.sh --status # report, change nothing
#
# WHY THIS EXISTS. Dismissing the first-run dialogs by computing the titlebar close box from
# `xdotool getwindowgeometry --shell` and clicking it went wrong whenever the dialog had already
+2 -2
View File
@@ -2,7 +2,7 @@
# Identical system+pinned-dependency setup to scripts/Dockerfile, but STOPS after
# `build_linux.sh -dr` (no slicer/AppImage build). Produces an image with the pinned
# deps baked at /OrcaSlicer/deps/build/destdir, so the slicer can be rebuilt
# incrementally via scripts/docker-iter-build.sh without re-running the long deps build.
# incrementally via scripts/CAD/build-gui-incremental.sh without re-running the long deps build.
#
# Build once (rebuild only when deps/ changes, e.g. OCCT module flags):
# docker build -t snaporca-deps -f scripts/Dockerfile.deps .
@@ -82,7 +82,7 @@ RUN ln -sfn /OrcaSlicer/deps/build/OrcaSlicer_dep /OrcaSlicer/deps/build/destdir
# The rig's GUI runtime. This used to arrive for free because orcacad-deps was layered on
# snaporca-deps; that lineage is Trap 1 in docs/rig_build_traps.md (a baked project(Snapmaker_Orca)
# tree) and building from this Dockerfile is what removes it — along with the X stack the rig
# needs. scripts/gui-session.sh requires Xvfb and openbox (without a window manager `xdotool
# needs. scripts/CAD/start-headless-gui.sh requires Xvfb and openbox (without a window manager `xdotool
# windowactivate` aborts with "windowmanager claims not to support..."), drives the UI with
# xdotool, and captures to /shots with scrot/ImageMagick. Same set snaporca-deps carries.
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
+1 -1
View File
@@ -4005,7 +4005,7 @@ TEST_CASE("golden recipe v1 still deserialises", "[CadDocument]")
// A failure BELOW this point means the on-disk serialization format changed: some field
// in CadFeature::save/load was reordered, retyped, or removed. Fields may only ever be
// APPENDED at the end of both lists. Do NOT regenerate the fixture to make this pass --
// fix the field order instead. See scripts/kernel-test.sh and the [.regen] case.
// fix the field order instead. See scripts/CAD/run-kernel-tests.sh and the [.regen] case.
// Field-by-field assertions against expected values.
// Every field set to a distinctive non-default literal must be checked here.