Skip to content

fix(build): stop disabling libc++ availability annotations - #180

Merged
16bit-ykiko merged 3 commits into
mainfrom
fix/libcxx-availability
Jul 25, 2026
Merged

fix(build): stop disabling libc++ availability annotations#180
16bit-ykiko merged 3 commits into
mainfrom
fix/libcxx-availability

Conversation

@16bit-ykiko

@16bit-ykiko 16bit-ykiko commented Jul 25, 2026

Copy link
Copy Markdown
Member

Summary

Removes the -D_LIBCPP_DISABLE_AVAILABILITY macro from both build systems
(CMake and xmake). This macro made libc++ headers bypass the vendor
availability annotations.

Why this is wrong

With availability annotations disabled, libc++ headers emit extern
references to dylib-only symbols (for example std::__1::__hash_memory,
introduced in libc++ 21) instead of taking the safe inline fallback path.

The consequences:

  • When linking against the target system's libc++ (or its SDK .tbd
    stubs), those symbols are undefined and the link fails.
  • Alternatively, the produced binary is forced to depend at runtime on the
    exact libc++ dylib present in the build environment.

Downstream consumers hit this concretely when cross-linking on macOS x64.

The correct approach

If a future API is genuinely gated behind an availability annotation, the
right fix is to raise CMAKE_OSX_DEPLOYMENT_TARGET (and the equivalent
xmake deployment target), not to globally switch the annotations off.

macOS toolchain bump to clang 22.1.8

Removing the macro exposed a separate issue in the macOS CI toolchain:
conda-forge's libc++ 20 headers contain availability attributes (e.g. in
__charconv/from_chars_floating_point.h) written in a form that clang 20
cannot parse once annotations are active — every TU that includes
<charconv> fails with error: expected ')'. The attribute uses an
unreachable sentinel version, so no deployment target setting can avoid it.

The clang 22.1.8 + libc++ 22.1.8 combination parses these headers
correctly and takes the inline fallback path with annotations enabled, and
has been verified green on downstream macOS CI. This PR therefore bumps
the macos-clang pixi environment from 20.1.8 to 22.1.8 (clang, clangxx,
lld, llvm-tools, compiler-rt, libcxx) and regenerates the lockfile. The
Linux and Windows toolchains are unchanged.

pixi bump and lockfile churn

The lockfile was regenerated with pixi v0.71.1, which writes the v7 lockfile
format — the v0.61.0 previously pinned in CI cannot read it, so the three
workflows move to v0.71.1 as well.

The v7 format adds per-platform virtual-packages records and reorders
existing entries, which is what most of the pixi.lock diff consists of.
The only dependency change in it is the macOS toolchain bump described
above; Linux and Windows resolve to the same packages as before.

@16bit-ykiko
16bit-ykiko force-pushed the fix/libcxx-availability branch from 9ed4d1a to 0bbd370 Compare July 25, 2026 05:32
@clice-io clice-io deleted a comment from coderabbitai Bot Jul 25, 2026
@16bit-ykiko
16bit-ykiko merged commit febb730 into main Jul 25, 2026
33 checks passed
@16bit-ykiko
16bit-ykiko deleted the fix/libcxx-availability branch July 25, 2026 06:25
16bit-ykiko added a commit to clice-io/clice that referenced this pull request Jul 25, 2026
)

## Summary

Build cleanup: drop a duplicated dependency, pick up an upstream fix,
and add a
release check for the bug class that motivated that fix.

## Changes

- **flatbuffers now comes from kotatsu.** kotatsu already fetches it for
its own
codec, so clice was fetching and building a second copy of the same
library.
`flatc` comes from pixi, since kotatsu does not build the schema
compiler.
- **gcc 15.2.0** in the build environment.
- **kotatsu pin bumped, availability workaround deleted.** kotatsu
defined
`-D_LIBCPP_DISABLE_AVAILABILITY` globally, which made libc++ headers
reference
dylib-only symbols that a target system's libc++ may not have. clice
worked
around it by stripping the flag off every kotatsu target. The flag is
gone
  upstream (clice-io/kotatsu#180), so the workaround goes with it.
- **New check on packaged binaries.** `scripts/check_artifact_deps.py`
reads a
packaged binary's dynamic dependencies and fails if any of them resolve
into a
conda/pixi environment instead of the OS. It runs between packaging and
  upload, so a violation blocks the upload.

## Why the check exists

A macOS artifact once linked conda's `@rpath/libc++.1.dylib`. It ran
fine on the
build machine, where that environment existed, and died everywhere else
— and
every test job passed, because they all ran inside that same
environment. This
turns that class of failure into a packaging-time error instead of a
post-release
surprise.

## Verification

The kotatsu bump was checked against the fetched source rather than the
declared
pin: a stale `_deps` checkout still carries the old flag and would make
removing
the workaround look safe when it is not.

The check was run against a binary carrying `@rpath/libc++.1.dylib`,
against a
conda-linked `libstdc++.so.6`, and against a clean packaged binary —
plus empty
and malformed tool output, to confirm it fails rather than passing
silently when
it cannot parse what it is given.

Linux: 1060 unit, 304 integration, 3 smoke.

## Recorded, not fixed here

Auditing the shipped artifacts turned up two portability limits. Both
need the
prebuilt LLVM rebuilt to fix, so both are recorded as
`TODO(prebuilt-respin)` in
`cmake/toolchain.cmake` and left for the next toolchain bump to carry,
rather
than triggering a rebuild on their own.

**Windows binaries need the Visual C++ redistributable.** They import
`MSVCP140.dll` and `VCRUNTIME140.dll` (x64 also `VCRUNTIME140_1.dll`),
which are
not part of Windows. This follows from building with `/MD`; `/MT` would
make the
exe self-contained. The flag cannot be flipped alone — every object
embeds
`/DEFAULTLIB` and `/FAILIFMISMATCH` directives naming its CRT, so
linking a `/MD`
prebuilt LLVM into a `/MT` clice is a hard link error.

**macOS binaries require macOS 15 or newer.** No
`CMAKE_OSX_DEPLOYMENT_TARGET`
is set, so the target follows the build machine and the artifact is
stamped
`minos 15.0`. Lowering it needs the prebuilt built against the same
target.

Neither is covered by the new check: it reads ELF and Mach-O
dependencies, not
PE imports and not deployment targets. Extending it is noted in the
script.

For contrast, the Linux `RUNPATH` pointing into the build machine's pixi
environment is *not* a problem: all six `NEEDED` entries are glibc
sonames and
that environment ships none of them, so the loader finds nothing there
and falls
through to the system — `ldd` on the build machine itself resolves every
entry
from `/lib/x86_64-linux-gnu`. The script prints it as a note so it stays
visible
without failing builds over a path that changes no resolution.
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