Files
OrcaSlicer/docs/rig_build_traps.md
T
Tommaso Bianchi cfc2555c3a Design: a verb's address is data, so the toolbar widget can stop existing
snaporca-7ih's remaining half. Both flyout factories registered their verbs INSIDE the
widget-building loop, so the ~40 retired tool buttons had to be constructed and then
Hide()n: skipping construction would have deleted 42 offer verbs (26 fly:<family>#<row>
+ 16 Shift+keys) while their rows still rendered and did nothing when picked.

Register first, build second. The addresses are pure data; the widget is one door onto
them, not their owner. A family absent from kBarKeep now returns before any wxWindow is
made. The keep-list stays a one-line data decision, not a structural one.

And close the class of bug for good: the constructor now verifies, once, that every verb
the atlas marks wired resolves to a real registration, logging each break and asserting in
debug. Rows that render and do nothing have shipped three times (edit_feature and sk_move
with action:null, then this) and are invisible from either side alone.

Verified on the snaporca rig by walking the offer, not by reading the code — all four
at-risk address kinds run with no widget behind them: fly:design_rect#2 drew an OBLIQUE
rectangle (the third variant, not the family's first), key:S+E opened Extrude with its
10 mm gizmo, fly:material#4 opened Thicken. Hover hints, icons and nesting intact. This
fork is code-identical here bar the two permitted DropDown divergences; it still owes a
build of its own (snaporca-5pl).

Two hints were wrong and are fixed: Cut said "Split the body with a plane", colliding with
the Split verb one row away and pointing at a card for a value the canvas already offers as
a draggable arrow; Split never said its plane comes from a picked face.

Also, because it blocked the verification and will block the next one: gui-session.sh
killed by full path while its own app_pid() matched by basename, so a differently-pathed
instance survived, held the single-instance lock, and got reported as a healthy session —
a Jul-30 binary nearly passed as this build. It now kills by basename and prints which
binary is actually on screen. Traps 6 and 7 documented.
2026-08-02 08:37:44 +02:00

7.9 KiB

Rig build traps

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.

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 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. 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).

Every trap below has already cost about a session to re-derive, once each. They are recorded now so no fresh session pays them again. Symptoms, causes, and exact recovery commands follow.


Trap 1 — never configure inside the GUI container

Symptom. After building inside the GUI container, the fork's targets no longer exist; ninja reports an unknown target, and orca-slicer / OrcaSlicer have been replaced by snapmaker-orca / Snapmaker_Orca.

Cause. The GUI image's baked /OrcaSlicer tree is the Jun-13 Snapmaker-derived source (project(Snapmaker_Orca), executable snapmaker-orca). orcacad-deps is layered on 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 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.


Trap 2 — stale NLopt_DIR in CMakeCache

Symptom. Configure fails with Cannot find NLopt library 'nlopt_cxx' in '<prefix>/lib'.

Cause. cmake/modules/FindNLopt.cmake:26 is set(NLopt_DIR $ENV{NLOPT}). With NLOPT unset that expands to set(NLopt_DIR) — zero arguments — which unsets the normal variable and lets a leftover CACHE entry of the same name (e.g. <prefix>/lib/cmake/nlopt) show through the following if(NOT NLopt_DIR). The else() branch then searches for nlopt_cxx under ${NLopt_DIR}/lib with NO_DEFAULT_PATH, while the deps prefix ships plain nlopt.

Fix. From inside the build dir:

cmake -U NLopt_DIR -U NLopt_LIBS .

Do not sed the entry out of CMakeCache.txt — deleting a line breaks the cache parser.


Trap 3 — the image lacks deps_src/pybind11

Symptom. Configure aborts with pybind11 headers not found in /OrcaSlicer/deps_src/pybind11. Did you initialize submodules? (the FATAL_ERROR guarding PYBIND11_SOURCE_DIR in the mainline 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 snaporca tree into an orcacad-deps build reproduces this error exactly.


Trap 4 — OCCT_LIBS lags one configure

Symptom. A wall of undefined references to TopOpeBRepBuild symbols. It reads as a broken OCCT installation. It is not.

Cause. src/libslic3r/CMakeLists.txt:603 does set(OCCT_LIBS "${OCCT_LIBS}" CACHE INTERNAL "OCCT toolkits linked by libslic3r") at the END of its own configure, while the consumer in the root CMakeLists.txt (if (NOT OCCT_LIBS)foreach (_tk IN LISTS OCCT_LIBS)) reads whatever is already in the cache. The first reconfigure 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 you ever configure by hand, run it twice.


Trap 5 — SLIC3R_CAD=ON in the cache, macro never defined

Symptom. The build succeeds and links, but the Design tab is simply absent — or it fails with class GLCanvas3D has no member named set_design_sketch_tool.

Cause. The cache carries SLIC3R_CAD=ON, but the root CMakeLists.txt actually configured is a stale baked copy that predates the gate and never runs add_definitions(-DSLIC3R_CAD) (the gate is if (SLIC3R_CAD) / add_definitions(-DSLIC3R_CAD) in the root list — line 179/180 in snaporca, 319/320 in orca_cad). Every #ifdef SLIC3R_CAD block therefore compiles out while the 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 all mount both.


The binary the rig actually launches

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 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 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

scripts/rig-build.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/.


Trap 6 — src/Release/ resolves resources to build/resources, which may not exist

The binary derives resources_dir() from its own location, so the src/Release/ one looks in /OrcaSlicer/build/resources while the packaged one looks inside build/package/. Only the packaging step creates the latter; nothing creates the former. Without it the app fails every Failed to add custom font ".../build/resources/fonts/…", logs Health check is not running, and exits 255 with nothing on stdout — which reads exactly like a crash in whatever you just changed. Measured 2026-08-02: an hour was nearly spent bisecting a GUI change that was fine.

build/ is the shared cache volume, so one symlink fixes it permanently, and pointing it at the bind-mounted repo tree means the rig also picks up new resources/images/*.svg without a rebuild:

docker exec <fork>-gui ln -sfn /OrcaSlicer/resources /OrcaSlicer/build/resources

Tell the two apart before debugging: a resource failure dies in the first second with no window; a real fault in your code gets past the version banner. Compare ~/.config/<App>/log/<newest>.log.0 against a known-good run — 47 lines versus 340 is the tell.

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 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 prints binary : $(readlink -f /proc/<pid>/exe). Read that line before trusting a screenshot.