Skip to content

de-bespoke build - #519

Open
michalhosna wants to merge 25 commits into
mainfrom
build-system-cpm
Open

de-bespoke build#519
michalhosna wants to merge 25 commits into
mainfrom
build-system-cpm

Conversation

@michalhosna

@michalhosna michalhosna commented Jul 27, 2026

Copy link
Copy Markdown
Member

Very much a draft that needs more passes.

Goal

Make the repo easier to understand and work with for newcomers.

  • Make the build system less bespoke — lean more heavily on CMake, so overrides
    and extension points are predictable.
  • Get rid of submodules.
    • Submodules are a pain to manage.
    • Yes, I know about submodule.recurse=true. Still a pain.
    • There is a reason package managers exist and people use them instead of git
      submodules.
    • We support prebuilts — a build that never touches that subtree still drags
      the source along. Optional submodule checkout is worse.
  • Make the scripts easier to use and understand. Less walls of text.

Breaking changes

  • Breaks existing scripts, and possibly the compile cache (ccache still works).
  • The goal here is cleanup, so breaking backwards compatibility seems ok-ish.

This change is Reviewable

@michalhosna
michalhosna requested review from afrind and gmarzot and removed request for gmarzot July 28, 2026 11:30
@michalhosna

Copy link
Copy Markdown
Member Author

@afrind @gmarzot What do you think about this proposal?

At least high-level, switch to CPM, better support for custom CMake profiles etc.

@gmarzot

gmarzot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

@afrind @gmarzot What do you think about this proposal?

At least high-level, switch to CPM, better support for custom CMake profiles etc.

I see the benefits of some simplification, and improved organization. I neither love nor hate the submodule approach. I think a full walk-thru would help and being clear on some key workflows. one nagging problem that is handled to some extent in the current model (not perfect) is pr validation against the dependency profile, including our submodules, that exist in main. the other workflow difficulty is the case where you have changes in moqx that depend some unmerged work in moxygen . oh also note now openmoq/moxygen maintains artifacts for snapshot-latest and full tagged releases only... we may want to reexamine that approach too.

Comment thread cmake/dependencies.cmake Outdated
# on the next reconfigure of an existing build dir. A cached pin would silently
# shadow the file's value.
set(MOXYGEN_REPOSITORY "openmoq/moxygen")
set(MOXYGEN_REV "a1c42e1a0e0305484eb6cbcfd8c228a4685cade1")

@michalhosna michalhosna Jul 29, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would point to this as one of the motivating fixes.

This is now just a plain text file. Rebase, merging, diffing now works as with any ordinary file.

Conceptually, submodules work as this simple pointer, but them being special makes them more difficult to work with. Because

  1. Commands don't recurse by default.
  2. The submodule state lives in multiple-places that can desync (.gitmodules, .git/config, and the actual working tree)
  3. Different tools shows different diffs.

@michalhosna

Copy link
Copy Markdown
Member Author

I think the PR CI workflow should stay mostly unchanged, except, I always use the oxygen version specified in the source, never fallback on latest.

If not prebuilt is found, the fallback is to build from source, not to use latest.

In the spirit of CI should be reproducible I think falling back to latest snapshot is just wrong, you are building different code then what's specified in the source code.
I added caching so that the builds are reasonably fasts, even for source build.
Also for most cases, the prebuilt should still win, as the CI runs on merge result, and main should most of the time have a pin that's prebuilt.

The main problem is "drift between moxygen merges and moqx's daily moxygen-sync.". But the solution for this is keeping more prebuilts in the moxygen repo, not building with possibly broken version of moxygen.

@michalhosna michalhosna changed the title Draft: de-bespoke build de-bespoke build Aug 4, 2026
@michalhosna
michalhosna force-pushed the build-system-cpm branch 3 times, most recently from c3418da to 551f2b8 Compare August 7, 2026 06:51
The pin is the one thing every other piece reads: the prebuilt fetcher, the
superbuild, and the moqx build all resolve moxygen from it.
MOXYGEN_REV starts where deps/moxygen points, not at whatever the branch was
cut from.
Inert until CMakeLists.txt includes it.
The rev-to-release resolution and its consistency checks are documented in the
file header.
- The from-source alternative to the prebuilt, for revs and platforms with no
  published tarball and for instrumented sanitizer builds.
- moqx itself is not built here; only the origin of the moxygen prefix differs
  between the two modes.
- Inert until scripts/configure.sh --moxygen from-source drives it.
- Pins live in cmake/dependencies.cmake.
- moxygen is consumed as an installed CMake package: the published prebuilt or
  a superbuild prefix.
- binaryDir is build/<presetName> so profiles coexist.
- No .a preference in CMAKE_FIND_LIBRARY_SUFFIXES. folly-targets.cmake hardcodes
  libglog.so, so an .a-preferring Glog resolution loads both copies and glog's
  double flag registration crashes under ASan.
- GFLAGS_SHARED=ON retires the tests' whole-archive workaround, which only
  existed to force flag registration out of the static gflags.
- The module path and the policy floor are build-level requirements, not
  per-preset choices: a bare `cmake -S .` needs them too.
- compile_commands.json is exported because lint needs it and Ninja does not
  write it unasked.
- A missing system -dev package surfaced deep inside the folly stack rather
  than as the install-system-deps.sh hint.
Only the build lifecycle scripts, install-system-deps.sh and moqx-run.sh are
daily entry points.
The rest move next to their audience, and every caller moves with them.
- With CPM the build dir's CMakeCache is the only state, so the setup-deps
  dispatcher and its marker files go.
- --moxygen is explicit: a default would silently pick between an hours-long
  stack build and an uninstrumented prebuilt.
- prebuilt-with-fallback is the entry point. It probes the fetcher directly
  rather than reading a failed moqx configure as "no prebuilt".
- A sanitizer preset refuses any moxygen not instrumented to match. ASan deps
  under a TSan moqx link two clashing runtimes.
- The variables --moxygen and --moxygen-dir own are refused as -D arguments;
  last-wins would defeat the interlock above.
- Job count is -j, then MOQX_BUILD_JOBS, then a RAM-derated default for
  sanitizer profiles; an explicit value is never clamped (distcc).
- Dependency caches are content-keyed on the pins and restored by prefix: a bump
  would otherwise cold-download the full set in every concurrent job.
- Every build path installs system libraries through install-system-deps.sh.
- version-release resolves the release from MOXYGEN_REV, not moxygen's newest
  tag, which would strand the release as a draft.
- The release job stops compiling tests: MOQX_BUILD_TESTS defaults ON, so it
  built every test executable and never ran ctest.
- The releases API is read with a token; anonymous is 60/hour/IP, shared across
  runner egress.
- The build context no longer carries deps/, so .dockerignore drops the
  submodule excludes with it.
- The moxygen stage is keyed on the pin rather than on src/, so a source-only
  push reuses the prefix.
- It falls back to the superbuild when the pin publishes no prebuilt, the same
  ladder the build lanes use.
- One prefix feeds both targets, so relay and interop-client cannot carry
  different moxygen builds.
The PR and main lanes build moqx's own TUs under ASan against uninstrumented
prebuilt deps.
A real ASan/TSan run needs the whole stack instrumented, which only the
from-source superbuild produces, so it runs nightly rather than per push.
MOQBIN follows whatever moxygen the build resolved.
ctest passes it per test, and scripts outside ctest source the moqx-tools.env
the configure writes.
deps/moxygen is going away.
The source comes from --moxygen-dir, then CPM_moxygen_SOURCE, then what the
build already fetched, then a clone of the pinned rev.
Dead weight once CPM supplies both.
deps/moxygen/LICENSE is gone with the submodule; the fork's copy is the durable
target.
sync-relay.sh emits the same text it writes into the ported files, so the next
sync is a no-op.
- The superbuild and the moqx build share the CPM clones, so a from-source moqx
  compiles against the exact source its moxygen prefix was built from, and a raw
  `cmake --preset` build reuses them instead of re-fetching into its own _deps.
- The extracted prebuilt installs hang off the same root, so MOQX_DEPS_CACHE
  moves everything at once on a host with a small home partition.
- CI runs scripts/dev/format.sh; lint.sh is a manual entry point.
- The targets' tools/*.cc globs never matched anything.
The static libs are not consumable without installed headers or an
install(EXPORT), and they bloat the release tarballs.
- Nothing built or registered changes.
- The load test stays unregistered: it needs a live relay.
- The shell-test blocks stay explicit: their properties vary.
folly's static_assert on syscall addresses (NetOps.cpp) is not constant under
-fsanitize=function, which `,undefined` pulls in.
ASan's ABI still matches across the boundary, so moqx keeps both.
One SanitizerFlags.cmake now holds the sets the moqx build and the superbuild
share.
Only the env spelling was validated. -DBOOST_USE_STATIC_LIBS=auto, which
superbuild/README.md advertises as the knob, passed straight through to
Boost_USE_STATIC_LIBS where CMake reads it as truthy — forcing static Boost on
the distros that package none, the exact failure the probe exists to avoid.
An INTERFACE target linked PRIVATE, so the warning set reaches moqx's own
translation units — src/, test/ and benchmark/ — and propagates to no consumer.

-Wmissing-field-initializers is off: GCC fires it on C++20 designated
initializers that omit members carrying default member initializers, the idiom
throughout the tree and correct. Clang does not warn there.
Most of compile_commands.json is CPM-fetched dependency code.
Making .clang-tidy load surfaces thousands of warnings in it; that is
#518.
Two equal dependency modes, the pin table, and the scripts that drive them.
- README drops the submodule bootstrap and `build.sh setup` for the trilogy.
- ci-architecture documents the shared ~/.cache/moqx cache and how it is keyed.
- release replaces the `.moxygen-release` tag file with the MOXYGEN_REV pin, so a
  release/* branch must freeze on a v*-tagged rev.
- Repo paths are linked from the root so they resolve wherever they are rendered.
GitHub deprecated the Node 20 runtime: node20 actions still run, but on
Node 24, and warn on every job. Each action goes to its lowest major that
declares runs.using: node24, not to latest — the jumps are runtime-only,
so this stays a version bump rather than a behaviour change.

Two majors carry breaking notes that do not apply here: create-github-app-token
v3 drops custom proxy handling (no HTTP(S)_PROXY anywhere in .github/), and
setup-buildx-action v4 removes deprecated inputs (it is used with none).

upload-pages-artifact is composite and has no runtime of its own; v5 is the
first to pin a node24 upload-artifact internally.

All of these need Actions Runner >= 2.327.1. The self-hosted linode runner
reports 2.336.0.
clangd's default database search walks a source file's ancestors and a
literal build/ under each; it never descends into a named build/<profile>/.
Every translation unit therefore got an empty compilation database, with no
error surfaced anywhere — only `clangd --check` showed the cascade of
unresolved standard headers.

Symlinking from configure.sh and not build.sh makes "last configured profile
wins" fall out for free: build.sh runs on every incremental compile and would
flap the link for reasons unrelated to which profile you work against.
@michalhosna

Copy link
Copy Markdown
Member Author

Anecdote: I was doing all new work based of this branch to properly test/dog-feed. I rebased onto remote/main, did A clean build and it immediately failed.

Claude's output, no context, clean session just to rebase mh/multi-auth-token from this branch onto main.

CMake's post-build test-discovery step (which runs each test binary to enumerate its tests) crashes on every binary that links moqx_core/moqx_config_loader, not just ours:

ERROR: something wrong with flag 'flagfile' in file '.../gflags-2.2.2/src/gflags.cc'.
One possibility: file '...' is being linked both statically and dynamically into this executable.

....

This is arguably why the CPM migration branch exists — CPM vendors dependencies consistently, avoiding exactly this system-lib-vs-bundled-lib conflict.

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.

2 participants