Skip to content

build: pin rust 1.98.1 so macOS 27 loads our stripped dylibs - #3904

Merged
kixelated merged 1 commit into
mainfrom
claude/obs-macos-compilation-87f1a0
Sep 22, 2026
Merged

kixelated merged 1 commit into
mainfrom
claude/obs-macos-compilation-87f1a0

Conversation

@kixelated

@kixelated kixelated commented Sep 22, 2026 •

Copy link
Copy Markdown
Collaborator

The bug

A contributor could not build the OBS plugin on macOS 27: the build failed with mis-aligned LINKEDIT string pool. Their workaround was a [profile.release.build-override] block, which works but only covers build scripts and proc macros.

The root cause is upstream. Cargo passes -C strip=debuginfo by default in the release profile, and below 1.98 rustc strips Mach-O with an llvm-objcopy that writes the symbol string table straight after the 4-byte-per-entry indirect symbol table with no padding. Whenever that table has an odd entry count, LC_SYMTAB.stroff lands 4-byte aligned, and macOS 27's dyld refuses the image. Reproduced on a stock proc-macro crate:

rustc 1.95 -> stroff=345252  stroff%8=4  nindirectsyms=115   (rejected)
rustc 1.98 -> stroff=328880  stroff%8=0  nindirectsyms=115

That is rust-lang/rust#157750, fixed in 1.98.0 by rust-lang/rust#158410 (an LLVM bump).

Why the pin moves, rather than a profile hack

Every release-profile proc macro is a coin flip, which is what breaks the macOS plugin build: rustc cannot dlopen a derive macro, so the crate using it fails to compile. But so is every release cdylib we ship there, which build-override does not touch:

  • rs/moq-ffi's cdylib, loaded at runtime by the Python, Kotlin, and Dart bindings
  • rs/moq-gst's plugin, dlopen'd by GStreamer

rs/libmoq is a staticlib, so the OBS plugin itself links fine once the build gets that far.

The pin therefore sits above the relay's 1.95 MSRV for a reason of its own. Crate rust-version floors are unchanged: libraries stay at 1.91, moq-relay and moq-cli at 1.95. The rust-overlay in flake.lock already serves 1.98.1, so no flake update is needed.

Lint attributes

Two 1.98 lints have no fix, so they carry an #[expect]. They could not ride along with #3899 because on 1.95 manual_noop_waker is an unknown lint and result_large_err does not fire, so both attributes would warn on the old pin.

  • kio's park_retires_a_waiter_for_another_task builds two no-op wakers that must be distinct. Clippy suggests Waker::noop(), which is a singleton, so taking the suggestion would make both wakers name the same task and the assertion would pass without testing anything.
  • The three relay handlers return axum::response::Result, whose Err is axum's own ErrorResponse. There is nothing on our side to box.

The as_chunks rewrites the same lint pass required landed separately in #3899.

Public API and wire impact

None. No exported signature, catalog field, or wire format moves. The only consumer-visible change is the toolchain a source build needs, which rust-toolchain.toml pins for them.

Testing

On 1.98.1: cargo clippy --locked --all-targets -- -D warnings, cargo fmt --all --check, and RUSTDOCFLAGS="-D warnings" cargo doc are all clean, and nix develop resolves 1.98.1 without a lock change. Linux CI Check passed on the previous push.

Note for reviewers: this PR touches the root manifest, so the diff-scoped just test selects the whole workspace rather than the usual handful of crates, and the run above is 4848 tests rather than the couple of thousand a typical PR selects.

On an earlier push that selection surfaced a failure in moq_gst::a_subscription_resolving_after_cancellation_creates_no_pad, which passed on the clean re-run here. It looks like a latent race in the test rather than anything this PR does: the test clears the catalog to cancel a pump and then immediately accepts the request, with nothing ordering the cancellation first, and its 500ms sleep falls after the accept. It also passes 30+ consecutive local runs on 1.98 including under parallelism. Worth tightening on its own, since a moq-gst suite is rarely selected by PR CI and the race can sit unexercised.

(Written by claude-opus-5)

🤖 Generated with Claude Code

@kixelated
kixelated force-pushed the claude/as-chunks-lints branch from 82e8d23 to fc2b259 Compare September 22, 2026 16:39
@kixelated
kixelated force-pushed the claude/obs-macos-compilation-87f1a0 branch from 8f3fca8 to 4c880b1 Compare September 22, 2026 16:40
@kixelated
kixelated marked this pull request as ready for review September 22, 2026 17:05
Base automatically changed from claude/as-chunks-lints to main September 22, 2026 17:09
@moq-bot

moq-bot Bot commented Sep 22, 2026

Copy link
Copy Markdown
Contributor

No issues found.
Verdict: approve
(Written by Muse Spark)

New%20session%20-%202026-09-22T17%3A05%3A45.855Z
opencode session  |  github run

@coderabbitai

coderabbitai Bot commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 117c29fc-5425-4646-a583-18fc85d5cf60

📥 Commits

Reviewing files that changed from the base of the PR and between 4c880b1 and 63f3893.

📒 Files selected for processing (1)
  • Cargo.toml
🚧 Files skipped from review as they are similar to previous changes (1)
  • Cargo.toml

Included review availability: Your plan provides up to 4 included reviews per hour; 0 remain after this review.


Walkthrough

The build toolchain is updated from Rust 1.95.0 to 1.98.1 in rust-toolchain.toml and Nix configuration. Comments now document the macOS 27 Mach-O debuginfo requirement and fixed-version policy. Cache documentation uses a generic toolchain probe. The waiter test creates distinct no-op wakers. Relay handlers add targeted expectations for Axum's large ErrorResponse type.

Priority: ➖ Normal

Severity of issue fixed: Medium

Merge Risk: ⚪ Minimal · up to 63f38

The pinned toolchain and targeted lint/test updates do not show an actionable merge-blocking risk.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 90.91% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 33 functions across 20 files. (1 skipped: 1…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: pinning Rust 1.98.1 so macOS 27 can load stripped dylibs.
Description check ✅ Passed The description directly explains the macOS 27 compatibility issue, the Rust toolchain pin, lint updates, unchanged MSRV floors, and validation results.
✨ Finishing Touches
✨ Simplify code
  • Commit to this branch
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Cargo passes `-C strip=debuginfo` by default in the release profile, and
below 1.98 rustc strips Mach-O with an llvm-objcopy that writes the symbol
string table straight after the 4-byte-per-entry indirect symbol table with
no padding. Whenever that table has an odd entry count the LINKEDIT string
pool lands 4-byte aligned, and macOS 27's dyld refuses to load the image:

    rustc 1.95 -> stroff=345252  stroff%8=4  (rejected)
    rustc 1.98 -> stroff=328880  stroff%8=0

Every release-profile proc macro is a coin flip, which is what breaks the
macOS OBS plugin build (rustc can't dlopen a derive macro), and so is every
cdylib we ship there: moq-ffi behind the Python/Kotlin/Dart bindings, and
the moq-gst plugin.

Fixed upstream in 1.98.0 by rust-lang/rust#158410, so the build toolchain
pin moves above the relay's 1.95 MSRV. The crates keep their own floors.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@kixelated
kixelated force-pushed the claude/obs-macos-compilation-87f1a0 branch from 320fe74 to 63f3893 Compare September 22, 2026 17:27
@moq-bot

moq-bot Bot commented Sep 22, 2026

Copy link
Copy Markdown
Contributor

No issues found.
Verdict: approve
(Written by Muse Spark)

New%20session%20-%202026-09-22T17%3A24%3A20.003Z
opencode session  |  github run

@moq-bot

moq-bot Bot commented Sep 22, 2026

Copy link
Copy Markdown
Contributor

No issues found.
Verdict: approve
(Written by Muse Spark)

New%20session%20-%202026-09-22T17%3A28%3A51.885Z
opencode session  |  github run

@kixelated

Copy link
Copy Markdown
Collaborator Author

Merged current origin/main into the branch to unstick CI (head 320fe74f8).

Why: the branch predated #3898 (merged), which fixed the moq-gst flake a_subscription_resolving_after_cancellation_creates_no_pad -> a_rendition_delisted_while_subscribing_takes_no_pad (now waits on Request::poll_unused instead of sleeping). It was also stacked on #3899, now merged.

Result: the diff against origin/main is back to the intended 8 files (+32/-18). The 1.98.1 pin (rust-toolchain.toml, flake.nix, nix/overlay.nix, rust-cache comment, Cargo.toml comment) and the three #[expect] attributes (kio manual_noop_waker, relay result_large_err x3) are intact, and crate rust-version floors are unchanged. No moq-gst workaround.

CI on 320fe74f8 is green: Check, Test, Android, OBS, WASM, review all pass. Meta-review: no issues found, approve. No inline feedback to address.

(Written by DeepSeek V4.1 Flash)

@kixelated
kixelated merged commit 236b219 into main Sep 22, 2026
7 checks passed
@kixelated
kixelated deleted the claude/obs-macos-compilation-87f1a0 branch September 22, 2026 17:48
@moq-bot moq-bot Bot mentioned this pull request Sep 23, 2026
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.

1 participant