Skip to content

fix: Use Xcode's libclang on macOS#3557

Merged
larseggert merged 15 commits intomozilla:mainfrom
larseggert:fix-macos-llvm-homebrew
Apr 14, 2026
Merged

fix: Use Xcode's libclang on macOS#3557
larseggert merged 15 commits intomozilla:mainfrom
larseggert:fix-macos-llvm-homebrew

Conversation

@larseggert
Copy link
Copy Markdown
Collaborator

Instead of Homebrew's, for example.

Copilot AI review requested due to automatic review settings April 13, 2026 06:03
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 13, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.47%. Comparing base (375d2f7) to head (4de5a05).
⚠️ Report is 3 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3557      +/-   ##
==========================================
- Coverage   94.57%   94.47%   -0.11%     
==========================================
  Files         128      132       +4     
  Lines       39736    40066     +330     
  Branches    39736    40066     +330     
==========================================
+ Hits        37581    37853     +272     
- Misses       1317     1365      +48     
- Partials      838      848      +10     
Flag Coverage Δ
freebsd 93.52% <ø> (-0.09%) ⬇️
linux 94.52% <ø> (-0.02%) ⬇️
macos 94.49% <ø> (+0.02%) ⬆️
windows 94.51% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Components Coverage Δ
neqo-common 98.61% <ø> (ø)
neqo-crypto 87.08% <ø> (ø)
neqo-http3 93.92% <ø> (ø)
neqo-qpack 95.14% <ø> (ø)
neqo-transport 95.64% <ø> (ø)
neqo-udp 84.90% <ø> (ø)
mtu 86.61% <ø> (ø)

Comment thread neqo-crypto/build.rs Outdated
Comment thread neqo-crypto/build.rs
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Good fix. The core change — moving setup_clang() from setup_standalone() to main() — is correct: build_bindings() always invokes bindgen regardless of the setup path (gecko, standalone, or pkg-config), so ensuring libclang is discoverable should be unconditional. The macOS-specific logic to pin Xcode's libclang is well-motivated by clang-sys's version-sorting behavior that can pick up a Homebrew LLVM lacking the macOS SDK include paths.

Two minor notes posted inline (both non-blocking).

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR updates neqo-crypto’s build script to make bindgen/libclang discovery more reliable on macOS by preferring the libclang shipped with Xcode (avoiding Homebrew LLVM mismatches), and centralizes clang setup so it runs for all build modes.

Changes:

  • Add macOS-specific logic to set LIBCLANG_PATH based on xcode-select --print-path.
  • Move setup_clang() invocation to main() so it runs for all configuration paths (gecko, NSS_DIR, pkg-config).
  • Narrow the Gecko/mozbuild-derived libclang setup to Windows-only logic.

Comment thread neqo-crypto/build.rs Outdated
Comment thread neqo-crypto/build.rs Outdated
Comment thread neqo-crypto/build.rs
Comment thread neqo-crypto/build.rs Outdated
Comment thread neqo-crypto/build.rs Outdated
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Good change — the core fix (detecting and preferring Xcode's libclang over Homebrew's on macOS) addresses a real build breakage, and the restructuring into per-platform branches is clearer than the old code.

Two architectural observations:

  1. setup_clang() now runs for all build paths (gecko, standalone, pkg-config) instead of only standalone. This is correct — the old function was a no-op on non-Windows anyway (early return for OS != "windows"), and now it actually provides value on macOS regardless of which NSS discovery path is used.

  2. Several pre-existing Cargo directive bugs survive in the Windows branch: missing cargo: prefixes on rerun-if-env-changed (line 103) and warning (line 126) mean those directives are silently ignored by Cargo. Since you're restructuring this function, it would be worth fixing them in the same pass — see inline comments. Copilot's earlier review also noted the MOZBUILD_STATE_PATH one.

As Copilot noted, xcode-select --print-path may return the Command Line Tools path (/Library/Developer/CommandLineTools) rather than a full Xcode installation, in which case the Toolchains/XcodeDefault.xctoolchain/usr/lib subpath won't exist. A fallback to <path>/usr/lib would improve robustness for CLT-only setups.

Comment thread neqo-crypto/build.rs Outdated
Comment thread neqo-crypto/build.rs Outdated
Comment thread neqo-crypto/build.rs Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated 1 comment.

Comment thread neqo-crypto/build.rs
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Good change. Moving setup_clang() from setup_standalone() to main() is the right call — bindgen runs for all three setup paths (gecko, standalone, pkg-config), so libclang discovery should be unconditional. The macOS logic to prefer Xcode's libclang over Homebrew's is well-motivated by clang-sys's version-sorting behavior, and the CLT fallback (usr/lib in addition to the Xcode toolchain path) handles both full Xcode and Command Line Tools installs.

The pre-existing Cargo directive bugs (cargo: prefix, rustc-env syntax) have been fixed in subsequent commits. One minor new observation posted inline: the cargo:rustc-env=LIBCLANG_PATH directive is now syntactically correct (was broken before), which means it now actively leaks a build-time-only variable into the compiled crate where it's unused.

Comment thread neqo-crypto/build.rs
Comment thread neqo-crypto/build.rs Outdated
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Clean, well-scoped fix. Moving setup_clang() from setup_standalone() to main() is the right call — build_bindings() runs unconditionally regardless of the NSS setup path (gecko, standalone, pkg-config), so libclang discovery should be unconditional too. Running it for gecko builds is benign since LIBCLANG_PATH is typically already set by mach (early return on line 75).

The macOS logic correctly addresses clang-sys's version-sorting behavior that can prefer a Homebrew LLVM lacking SDK include paths. The cargo: prefix fixes and rustc-env removal are good cleanups done along the way.

One inline comment posted (CLT fallback, also noted by Copilot).

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Clean, well-scoped fix. Moving setup_clang() from setup_standalone() to main() is correct — build_bindings() invokes bindgen unconditionally regardless of which NSS setup path is taken, so libclang discovery should be unconditional too. The macOS logic to prefer Xcode's libclang over Homebrew's addresses a real clang-sys version-sorting pitfall, and the candidate array with CLT fallback handles both full Xcode and Command Line Tools installs.

Prior review feedback (missing cargo: prefixes, rustc-env removal, CLT fallback, xcode-select error diagnostics) has been well-incorporated across the follow-up commits. One minor remaining item posted inline: the new macOS warning messages should use println!("cargo:warning=...") instead of eprintln! to be reliably surfaced by Cargo.

Comment thread neqo-crypto/build.rs Outdated
@larseggert
Copy link
Copy Markdown
Collaborator Author

Should be rebased once #3558 has landed.

@larseggert
Copy link
Copy Markdown
Collaborator Author

@Not-Nik this will need to move to nss-rs

larseggert and others added 9 commits April 14, 2026 17:12
Instead of Homebrew's, for example.
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Signed-off-by: Lars Eggert <lars@eggert.org>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Lars Eggert <lars@eggert.org>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Signed-off-by: Lars Eggert <lars@eggert.org>
@larseggert larseggert force-pushed the fix-macos-llvm-homebrew branch from a263357 to 8af26c2 Compare April 14, 2026 14:13
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Good, well-scoped fix. Moving setup_clang() from setup_standalone() to main() is correct — build_bindings() invokes bindgen unconditionally, so libclang discovery should be too. The per-platform branching is cleaner than the old inverted OS != "windows" guard, and the candidates array handles both full Xcode and Command Line Tools installs.

Prior review feedback (cargo: prefixes, rustc-env removal, CLT fallback, error diagnostics) has been well-incorporated. One remaining issue: the macOS warning paths use eprintln! while the Windows branch uses println!("cargo:warning=...") — see inline comments.

Comment thread neqo-crypto/build.rs Outdated
Comment thread neqo-crypto/build.rs Outdated
Comment thread neqo-crypto/build.rs Outdated
larseggert and others added 4 commits April 14, 2026 17:17
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Signed-off-by: Lars Eggert <lars@eggert.org>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Signed-off-by: Lars Eggert <lars@eggert.org>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Signed-off-by: Lars Eggert <lars@eggert.org>
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Good, well-scoped fix. The core change — lifting setup_clang() from setup_standalone() to main() — is correct: build_bindings() invokes bindgen unconditionally regardless of the NSS discovery path, so libclang setup should be unconditional too. Running it for gecko builds is benign since LIBCLANG_PATH is typically already set by mach (early return on line 75).

The macOS logic to prefer Xcode's libclang over Homebrew's is well-motivated by clang-sys's version-sorting behavior, and the candidates array with CLT fallback (usr/lib in addition to the Xcode toolchain path) handles both full Xcode and Command Line Tools installs. The removal of the pre-existing broken cargo:rustc-env=LIBCLANG_PATH directive is a good cleanup.

One inline comment posted about the CI detection guard.

Comment thread neqo-crypto/build.rs Outdated
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Clean, well-motivated fix. Moving setup_clang() from setup_standalone() to main() is correct — build_bindings() invokes bindgen unconditionally regardless of the NSS discovery path, so libclang setup should be too. The per-platform branching with macOS Xcode detection and CLT fallback via the candidates array is a clear improvement over the old inverted OS != "windows" guard.

All prior review feedback has been well-incorporated across the follow-up commits: correct cargo: prefixes, println!("cargo:warning=...") instead of eprintln!, CLT fallback, env::var("CI") for CI detection, and removal of the unused cargo:rustc-env directive.

One minor inline comment about a missing cargo:rerun-if-env-changed for the CI variable.

Comment thread neqo-crypto/build.rs
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Signed-off-by: Lars Eggert <lars@eggert.org>
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Good, well-scoped fix. The changes look correct and all prior review feedback has been incorporated.

Architecture: Moving setup_clang() from setup_standalone() to main() is the right call — build_bindings() invokes bindgen unconditionally regardless of the NSS discovery path (gecko, standalone, pkg-config), so libclang setup should be unconditional too. For gecko builds this is benign since LIBCLANG_PATH is already set by mach (early return at line 76).

macOS logic: The Xcode detection via xcode-select --print-path with the two-entry candidates array correctly handles both full Xcode installs (Toolchains/XcodeDefault.xctoolchain/usr/lib) and Command Line Tools-only setups (usr/lib). This addresses the clang-sys version-sorting behavior that can pick up a Homebrew LLVM lacking macOS SDK include paths.

Cleanups: Switching from GITHUB_WORKFLOW == "CI" to env::var("CI") for CI detection is more robust (standard env var across all CI systems). The cargo:rerun-if-env-changed directives are correctly registered for all consulted env vars. The removal of the broken cargo:rustc-env=LIBCLANG_PATH directive is a good cleanup — that variable is only needed during build script execution, not at crate compile time.

@larseggert larseggert merged commit eb3b800 into mozilla:main Apr 14, 2026
182 of 183 checks passed
@larseggert larseggert deleted the fix-macos-llvm-homebrew branch April 14, 2026 14:52
@github-actions
Copy link
Copy Markdown
Contributor

Benchmark results

No significant performance differences relative to f5eab61.

All results
transfer/1-conn/1-100mb-resp (aka. Download)/mtu-1504: No change in performance detected.
       time:   [204.26 ms 204.69 ms 205.22 ms]
       thrpt:  [487.29 MiB/s 488.56 MiB/s 489.58 MiB/s]
change:
       time:   [-0.5729% -0.2266% +0.1111] (p = 0.21 > 0.05)
       thrpt:  [-0.1110% +0.2271% +0.5762]
       No change in performance detected.
Found 4 outliers among 100 measurements (4.00%)
1 (1.00%) low mild
3 (3.00%) high severe
transfer/1-conn/10_000-parallel-1b-resp (aka. RPS)/mtu-1504: No change in performance detected.
       time:   [285.66 ms 287.63 ms 289.64 ms]
       thrpt:  [34.526 Kelem/s 34.767 Kelem/s 35.007 Kelem/s]
change:
       time:   [-1.4470% -0.5229% +0.4273] (p = 0.29 > 0.05)
       thrpt:  [-0.4255% +0.5256% +1.4682]
       No change in performance detected.
transfer/1-conn/1-1b-resp (aka. HPS)/mtu-1504: No change in performance detected.
       time:   [38.570 ms 38.699 ms 38.849 ms]
       thrpt:  [25.740   B/s 25.840   B/s 25.927   B/s]
change:
       time:   [-1.3363% -0.6654% -0.0089] (p = 0.05 > 0.05)
       thrpt:  [+0.0089% +0.6699% +1.3544]
       No change in performance detected.
Found 7 outliers among 100 measurements (7.00%)
3 (3.00%) high mild
4 (4.00%) high severe
transfer/1-conn/1-100mb-req (aka. Upload)/mtu-1504: No change in performance detected.
       time:   [203.89 ms 204.34 ms 204.89 ms]
       thrpt:  [488.06 MiB/s 489.38 MiB/s 490.47 MiB/s]
change:
       time:   [-0.1651% +0.1218% +0.4334] (p = 0.45 > 0.05)
       thrpt:  [-0.4315% -0.1216% +0.1654]
       No change in performance detected.
Found 4 outliers among 100 measurements (4.00%)
2 (2.00%) high mild
2 (2.00%) high severe
streams/walltime/1-streams/each-1000-bytes: Change within noise threshold.
       time:   [585.96 µs 588.19 µs 590.74 µs]
       change: [-1.2273% -0.6451% -0.0677] (p = 0.03 < 0.05)
       Change within noise threshold.
Found 11 outliers among 100 measurements (11.00%)
1 (1.00%) high mild
10 (10.00%) high severe
streams/walltime/1000-streams/each-1-bytes: Change within noise threshold.
       time:   [12.429 ms 12.456 ms 12.491 ms]
       change: [+0.6200% +0.8988% +1.2246] (p = 0.00 < 0.05)
       Change within noise threshold.
Found 2 outliers among 100 measurements (2.00%)
1 (1.00%) high mild
1 (1.00%) high severe
streams/walltime/1000-streams/each-1000-bytes: Change within noise threshold.
       time:   [44.657 ms 44.706 ms 44.759 ms]
       change: [+0.3528% +0.5194% +0.6934] (p = 0.00 < 0.05)
       Change within noise threshold.
Found 3 outliers among 100 measurements (3.00%)
2 (2.00%) high mild
1 (1.00%) high severe
transfer/walltime/pacing-false/varying-seeds: Change within noise threshold.
       time:   [82.135 ms 82.181 ms 82.228 ms]
       change: [+1.2826% +1.4014% +1.5072] (p = 0.00 < 0.05)
       Change within noise threshold.
transfer/walltime/pacing-true/varying-seeds: Change within noise threshold.
       time:   [82.036 ms 82.094 ms 82.157 ms]
       change: [-0.4906% -0.3875% -0.2836] (p = 0.00 < 0.05)
       Change within noise threshold.
Found 4 outliers among 100 measurements (4.00%)
2 (2.00%) high mild
2 (2.00%) high severe
transfer/walltime/pacing-false/same-seed: Change within noise threshold.
       time:   [80.941 ms 81.023 ms 81.117 ms]
       change: [+0.3123% +0.4347% +0.5759] (p = 0.00 < 0.05)
       Change within noise threshold.
Found 6 outliers among 100 measurements (6.00%)
2 (2.00%) high mild
4 (4.00%) high severe
transfer/walltime/pacing-true/same-seed: No change in performance detected.
       time:   [82.503 ms 82.606 ms 82.760 ms]
       change: [-0.1844% -0.0291% +0.1659] (p = 0.78 > 0.05)
       No change in performance detected.
Found 3 outliers among 100 measurements (3.00%)
2 (2.00%) high mild
1 (1.00%) high severe

Download data for profiler.firefox.com or download performance comparison data.

@github-actions
Copy link
Copy Markdown
Contributor

Failed Interop Tests

QUIC Interop Runner, client vs. server, differences relative to main at f5eab61.

neqo-pr as clientneqo-pr as server
neqo-pr vs. go-x-net: BP BA
neqo-pr vs. haproxy: BP BA
neqo-pr vs. kwik: S L1 C1 BP BA
neqo-pr vs. lsquic: L1 C1
neqo-pr vs. msquic: A L1 C1
neqo-pr vs. mvfst: A BP BA
neqo-pr vs. neqo: Z A
neqo-pr vs. nginx: BP BA
neqo-pr vs. ngtcp2: CM
neqo-pr vs. picoquic: A
neqo-pr vs. quic-go: A
neqo-pr vs. quiche: BP BA
neqo-pr vs. s2n-quic: CM
neqo-pr vs. tquic: S BP BA
neqo-pr vs. xquic: A 🚀C1 ⚠️L1
aioquic vs. neqo-pr: Z 🚀L1 CM
go-x-net vs. neqo-pr: CM
kwik vs. neqo-pr: Z BP BA CM
lsquic vs. neqo-pr: Z ⚠️L1
msquic vs. neqo-pr: Z ⚠️C1 CM
mvfst vs. neqo-pr: Z A L1 C1 CM
neqo vs. neqo-pr: Z A
openssl vs. neqo-pr: LR M A CM
picoquic vs. neqo-pr: Z
quic-go vs. neqo-pr: 🚀Z CM
quic-zig vs. neqo-pr: ⚠️3
quiche vs. neqo-pr: Z ⚠️BP CM
quinn vs. neqo-pr: Z L1 ⚠️C1 V2 CM
s2n-quic vs. neqo-pr: CM
tquic vs. neqo-pr: Z CM
xquic vs. neqo-pr: M CM
All results

Succeeded Interop Tests

QUIC Interop Runner, client vs. server

neqo-pr as client

neqo-pr as server

Unsupported Interop Tests

QUIC Interop Runner, client vs. server

neqo-pr as client

neqo-pr as server

@github-actions
Copy link
Copy Markdown
Contributor

Client/server transfer results

Performance differences relative to f5eab61.

No significant performance differences.

Table above only shows statistically significant changes. See all results below.

All results

Transfer of 33554432 bytes over loopback, min. 100 runs. All unit-less numbers are in milliseconds.

Client vs. server (params) Mean ± σ Min Max MiB/s ± σ Δ baseline Δ baseline
google-google-nopacing 451.4 ± 4.2 444.0 461.2 70.9 ± 7.6
google-neqo-cubic 275.0 ± 4.2 266.4 289.0 116.3 ± 7.6 0.8 0.3%
msquic-msquic-nopacing 139.0 ± 37.1 112.3 328.6 230.3 ± 0.9
msquic-neqo-cubic 157.9 ± 20.7 125.0 251.4 202.7 ± 1.5 -0.2 -0.2%
neqo-google-cubic 748.4 ± 4.3 740.7 763.8 42.8 ± 7.4 -0.4 -0.1%
neqo-msquic-cubic 153.6 ± 4.3 147.6 162.4 208.4 ± 7.4 0.7 0.5%
neqo-neqo-cubic 96.3 ± 4.0 88.4 103.5 332.1 ± 8.0 -0.6 -0.6%
neqo-neqo-cubic-nopacing 95.9 ± 4.8 84.4 118.6 333.7 ± 6.7 0.1 0.1%
neqo-neqo-newreno 95.7 ± 3.9 89.5 102.9 334.4 ± 8.2 0.5 0.5%
neqo-neqo-newreno-nopacing 95.1 ± 4.1 87.5 105.2 336.6 ± 7.8 -0.6 -0.6%
neqo-quiche-cubic 189.6 ± 3.5 185.6 199.6 168.8 ± 9.1 -0.7 -0.4%
neqo-s2n-cubic 220.4 ± 4.3 213.4 232.5 145.2 ± 7.4 -0.5 -0.2%
quiche-neqo-cubic 183.2 ± 6.8 169.8 208.5 174.7 ± 4.7 0.3 0.2%
quiche-quiche-nopacing 140.3 ± 4.6 133.4 152.5 228.1 ± 7.0
s2n-neqo-cubic 221.5 ± 6.8 211.3 273.5 144.4 ± 4.7 0.4 0.2%
s2n-s2n-nopacing 294.3 ± 23.9 279.2 394.1 108.7 ± 1.3

Download data for profiler.firefox.com or download performance comparison data.

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