Skip to content

Commit 518bd92

Browse files
committed
feat(macos): built-in 14.0 deployment floor + static libc++ by default
A fresh user's `mcpp new hello && mcpp run` on macOS 14 died at dyld (`__ZNSt3__119__is_posix_terminalEP7__sFILE` missing from the system libc++): with no declared floor the binary compiled against libc++-20 headers but dynamically linked the host system libc++. Caught by the macos-14 fresh-install CI lane. The resolver (platform::macos::deployment_target) now falls back to a built-in 14.0 floor (rustc-style baseline; the official LLVM static archive floor), so the declared-floor-implies-static semantic becomes portable-by-default: every macOS build gets minos=14.0 + static LLVM libc++ out of the box. Opt-out: [build] static_stdlib = false. Both blockers that deferred this default are resolved: split-brain SIGSEGV fixed by -Wl,-load_hidden (#117 forensics, shipped in 0.0.50), fingerprint/stdmod drift fixed by the single resolver (#119) — which is why the default lives in the resolver, not at any single consumer.
1 parent 70c89cc commit 518bd92

4 files changed

Lines changed: 46 additions & 35 deletions

File tree

docs/05-mcpp-toml.md

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -98,15 +98,17 @@ macos_deployment_target = "14.0" # macOS 产物的最低支持系统版本(仅
9898
(`LC_BUILD_VERSION minos`),即二进制能运行的最老 macOS。优先级与各生态
9999
惯例一致:环境变量 `MACOSX_DEPLOYMENT_TARGET`(单次调用的显式覆盖,
100100
cargo/rustc、cc 等同样尊重该变量)> 本字段(项目默认,类似 SwiftPM 的
101-
`platforms:`)> 工具链/SDK 默认。该值会进入 BMI 指纹——切换 target 会
102-
自动重建模块缓存。
103-
104-
**声明 floor 即静态运行时**:显式设置了 deployment target(env 或本
105-
字段)且 `static_stdlib = true`(默认)时,macOS 链接会静态链入 LLVM
106-
自带的 libc++/libc++abi —— 系统 libc++ 会把实际可运行版本钉死在构建机
107-
的 OS(老系统缺新符号,如 `std::print` 的支撑符号),静态化才能真正
108-
兑现声明的 floor。注意 LLVM 官方静态库自身的下限是 **14.0**。未声明
109-
floor 时保持动态系统 libc++(产物只保证在构建机同版本及以上运行)。
101+
`platforms:`)> **内建默认 `14.0`**(rustc 风格——每个 target 都有基线,
102+
14.0 即 LLVM 官方静态库自身的下限)。该值会进入 BMI 指纹——切换 target
103+
会自动重建模块缓存。
104+
105+
**默认即静态运行时(portable by default)**:`static_stdlib = true`
106+
(默认)时,macOS 链接会静态链入 LLVM 自带的 libc++/libc++abi ——
107+
系统 libc++ 会把实际可运行版本钉死在构建机的 OS(老系统缺新符号,
108+
`std::print` 的支撑符号),静态化才能真正兑现 floor。因此默认构建的
109+
产物在任何 macOS ≥ 14 上开箱即用。设 `static_stdlib = false` 退回动态
110+
系统 libc++(产物只保证在构建机同版本及以上运行)。更低 floor(11–13)
111+
需自建 libc++ 归档(已验证可行,数据级切换,按需提供)。
110112

111113
C++ 标准不要通过 `build.cxxflags = ["-std=..."]` 配置。请使用:
112114

src/build/flags.cppm

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -369,23 +369,22 @@ CompileFlags compute_flags(const BuildPlan& plan) {
369369
// lld ships with the exact toolchain doing the compile.
370370
f.ldStdlibDefault = " -lc++";
371371
f.ldStdlibTest = " -lc++";
372-
// Static libc++ is tied to an EXPLICIT deployment floor: when the
373-
// user (or the release pipeline) declares a minimum macOS via the
374-
// env var or [build] macos_deployment_target, the static LLVM
375-
// libc++ is what makes that floor real (the system libc++ caps it
376-
// at the build host's OS). With no declared floor, keep the
377-
// 0.0.49 behavior — dynamic system libc++, host-coupled.
378-
//
379-
// TODO(macos-static-default): flip static to the unconditional
380-
// default (rust-style "portable by default") once two tracked
381-
// issues are fixed — (1) mixed C/C++ static binaries SIGSEGV at
382-
// runtime (e2e 36_llvm_toolchain: answer.c + std::cout main.cpp,
383-
// exit 139; root cause not yet isolated), (2) the std-module
384-
// staging/fingerprint boundary (see canonical_compile_flags).
372+
// Static libc++ + the deployment floor are the DEFAULT (rust-style
373+
// "portable by default"): the resolver always yields a floor on
374+
// macOS (built-in 14.0 unless env/manifest override), and the
375+
// static LLVM libc++ is what makes that floor real — the system
376+
// libc++ caps binaries at the build host's OS (a fresh user's
377+
// std::println hello on macOS 14 died at dyld against the system
378+
// libc++ before this). Opt-out: [build] static_stdlib = false
379+
// (host-coupled dynamic libc++, the pre-0.0.52 no-declaration
380+
// behavior). The two blockers that deferred this default are
381+
// resolved: (1) mixed C/C++ split-brain SIGSEGV — fixed by
382+
// -load_hidden (PR #117 forensics), (2) std-module staging /
383+
// fingerprint drift — fixed by the single resolver (PR #119).
385384
// TODO(macos-floor-11): the official LLVM archives are built for
386385
// macOS 14; supporting 11-13 needs a custom libc++ build shipped
387386
// via xlings-res (data-only change — swap the archive source).
388-
// Both tracked in xlings
387+
// Tracked in xlings
389388
// .agents/docs/2026-06-05-macos-min-version-support.md §5.
390389
if (f.staticStdlib && !macosDeploymentTarget.empty()
391390
&& !llvmRootForStdlib.empty()) {

src/cli.cppm

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -594,13 +594,11 @@ std::string canonical_compile_flags(const mcpp::manifest::Manifest& m) {
594594
// into the fingerprint so switching targets rebuilds the BMI cache
595595
// instead of dying with a module config mismatch.
596596
//
597-
// TODO(macos-default-floor): a built-in default floor (rustc-style,
598-
// see the 0.0.50 revert) cannot land until the std-module prebuild /
599-
// staging pipeline consumes the SAME resolved value as this rule and
600-
// flags.cppm — injecting a default here alone left the test build's
597+
// The built-in default floor (rustc-style) lives in the single
598+
// resolver (platform::macos::deployment_target), so this rule, the
599+
// flags and the std-module prebuild always agree — the 0.0.50-era
600+
// attempt to inject a default here alone left the test build's
601601
// std.pcm unstaged (import std failed wholesale on macos CI).
602-
// Centralize the resolution in one helper, then re-land.
603-
// See xlings .agents/docs/2026-06-05-macos-min-version-support.md §5.
604602
if constexpr (mcpp::platform::is_macos) {
605603
auto dtv = mcpp::platform::macos::deployment_target(
606604
m.buildConfig.macosDeploymentTarget);

src/platform/macos.cppm

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,22 +32,34 @@ bool has_xcode_clt();
3232
// Returns the SDK path if found, or nullopt.
3333
std::optional<std::filesystem::path> sdk_path();
3434

35+
// Built-in default deployment floor (rustc-style: every target has a
36+
// baseline). 14.0 = the floor of the official LLVM static libc++
37+
// archives; with the default-static stdlib this makes `mcpp run`
38+
// binaries portable to any macOS ≥ 14 out of the box (no declaration
39+
// needed — a fresh user's std::println hello on macOS 14 used to die
40+
// at dyld against the system libc++). Lower floors need a custom
41+
// libc++ build (tracked; data-only swap via xlings-res).
42+
inline constexpr std::string_view default_deployment_target = "14.0";
43+
3544
// Resolve the effective macOS deployment target: the
3645
// MACOSX_DEPLOYMENT_TARGET env var (explicit per-invocation override,
3746
// the convention cargo/rustc/cc honor) wins over `manifestValue` (the
38-
// [build] macos_deployment_target project default); empty means
39-
// toolchain/SDK default. THE single source of truth — flags.cppm, the
40-
// BMI fingerprint rule and the std-module prebuild must all consume
41-
// this same resolution, or cached std.pcm modules drift from the TUs
42-
// (config-mismatch / unstaged-module failures observed on macos CI).
47+
// [build] macos_deployment_target project default), which wins over
48+
// the built-in default floor — the result is never empty on macOS.
49+
// THE single source of truth — flags.cppm, the BMI fingerprint rule
50+
// and the std-module prebuild must all consume this same resolution,
51+
// or cached std.pcm modules drift from the TUs (config-mismatch /
52+
// unstaged-module failures observed on macos CI).
4353
std::string deployment_target(std::string_view manifestValue);
4454

4555
// Return macOS-specific runtime library directories for LLVM toolchains.
4656
std::string deployment_target(std::string_view manifestValue) {
4757
#if defined(__APPLE__)
4858
if (const char* dt = std::getenv("MACOSX_DEPLOYMENT_TARGET"); dt && *dt)
4959
return dt;
50-
return std::string(manifestValue);
60+
if (!manifestValue.empty())
61+
return std::string(manifestValue);
62+
return std::string(default_deployment_target);
5163
#else
5264
(void)manifestValue;
5365
return {};

0 commit comments

Comments
 (0)