How do I build the system?
Table of Contents
Configure first if you haven't (see How do I configure the project?). For preset / output / parallelism details, see CMake setup.
Question
How do I build ORE Studio?
Answer
Day-to-day with compass
compass build resolves the checkout's preset from ORES_PRESET in
.env and configures the build directory automatically on first use:
./compass.sh build # everything ./compass.sh build ores.iam.core # specific target(s) ./compass.sh build site # friendly alias (deploy_site) ./compass.sh build --preset linux-clang-release-make # preset override ./compass.sh build -j 4 --dry-run # jobs override / print-only
Friendly aliases: site → deploy_site, manual → deploy_manual,
org-roam-db-sync → org_roam_db_sync; any raw cmake target is also
accepted.
For documentation/agile targets, add --direct to skip cmake entirely
and run the Emacs build script directly (no cmake/vcpkg — works in light
environments): ./compass.sh build --direct site skills settings manual.
See How do I deploy the site?.
Always build via compass build (not raw cmake --build) — never
bypass it, even to "just quickly build one target". See Build
locking below for why.
Build locking
This only matters on a host running several ORE Studio worktrees at once (each their own environment) — the common case for this repo's LLM-driven workflow, where multiple environments can be building in parallel. On a single-checkout machine with nothing else building, the lock is a no-op (you always get a free slot immediately).
Where it applies: concurrent, uncoordinated cmake --build
invocations from different worktrees on the same host contend for
the same CPUs and have been observed to corrupt shared library
outputs when one build is killed mid-link while another links
against the same target. A non-direct compass build automatically
waits for one of two host-wide lock slots before invoking cmake, so
at most two environments on the host build at once, each capped to
that slot's -j:
./compass.sh build --status
reports each slot as held (with the holding environment's identity,
pid, and start time) or free, plus a tail of that slot's build log.
Every build's combined stdout/stderr is also streamed live to a
well-known path per slot, /tmp/ores-build.log.<slot> (overwritten
each run) — tail -f it directly if you want to watch a build you
(or another environment) kicked off rather than polling --status.
If both slots are busy, compass build prints a waiting message and
blocks until one frees — this is expected, not a hang; let it wait
rather than killing it or falling back to a raw cmake --build.
--jobs overrides the slot's job count if you deliberately need a
different value for one invocation. --dry-run does not take a lock
or write a log.
The actual cmake --build step (not the configure step) also runs
inside its own memory-capped app-build-<env>.slice when a user
systemd manager is available — deployed on demand from a checked-in
dash-truncated template drop-in (same mechanism as How do I launch
Claude Code inside a systemd scope?'s app-claude-.slice.d/), so an
overrunning build is killed by its own cgroup OOM instead of pushing
the whole machine into swap. Inspect with:
systemd-cgls --user-unit=app-build-<env>.slice systemctl --user show app-build-<env>.slice -p MemoryMax -p CPUWeight
Direct CMake
Only for local single-environment work where you know no other
worktree on the host is building concurrently — it bypasses the lock
entirely, so simultaneous invocations from other environments can
corrupt shared build outputs (see Build locking above). Prefer
compass build otherwise.
cmake --build --preset <preset> [--target <target_name>]
Script
compass build (projects/ores.compass/src/compass.py) wrapping
cmake --build --preset. Locking is _acquire_build_lock=/
=BUILD_LOCK_SLOTS in the same file — two flock-based slots
(-j2=/-j3=) under /tmp/ores-build.lock.<slot>, shared by every
worktree on the host. The memory cap comes from
_ensure_build_slice_deployed=/=_build_slice_name in the same file,
syncing
projects/ores.compass/src/systemd/app-build-.slice.d/50-limits.conf.
Tested by
CI builds every preset on every push.
See also
- CMake setup — preset naming, output layout, parallelism.
- How do I configure the project?
- How do I run the tests? — once the build is green.