Skip to content

Commit cf975ca

Browse files
committed
feat: [build] macos_deployment_target manifest field
First-class manifest support for the macOS minimum supported OS version of produced binaries (LC_BUILD_VERSION minos), following ecosystem conventions: the MACOSX_DEPLOYMENT_TARGET env var (which cargo/rustc and the cc crate honor) stays as the explicit per-invocation override, the new [build] macos_deployment_target field provides the project default (SwiftPM platforms: style), and the toolchain/SDK default applies when neither is set. The resolved value feeds the same paths the env var already did: explicit -mmacosx-version-min on compile+link command lines and the BMI fingerprint (switching targets rebuilds the module cache). No effect off macOS. Unit tests + docs/05-mcpp-toml.md.
1 parent e5b877a commit cf975ca

5 files changed

Lines changed: 76 additions & 14 deletions

File tree

docs/05-mcpp-toml.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,16 @@ cflags = ["-DFOO=1"] # 额外 C 编译参数
9191
cxxflags = ["-DBAR=2"] # 额外 C++ 编译参数(不要放 -std=...)
9292
ldflags = ["-lfoo"] # 额外链接参数
9393
static_stdlib = true # 静态链接 libstdc++(默认 true)
94+
macos_deployment_target = "11.0" # macOS 产物的最低支持系统版本(仅 macOS 生效)
9495
```
9596

97+
`macos_deployment_target` 设定产物 Mach-O 头里的最低系统版本
98+
(`LC_BUILD_VERSION minos`),即二进制能运行的最老 macOS。优先级与各生态
99+
惯例一致:环境变量 `MACOSX_DEPLOYMENT_TARGET`(单次调用的显式覆盖,
100+
cargo/rustc、cc 等同样尊重该变量)> 本字段(项目默认,类似 SwiftPM 的
101+
`platforms:`)> 工具链/SDK 默认。该值会进入 BMI 指纹——切换 target 会
102+
自动重建模块缓存。
103+
96104
C++ 标准不要通过 `build.cxxflags = ["-std=..."]` 配置。请使用:
97105

98106
```toml

src/build/flags.cppm

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,18 @@ CompileFlags compute_flags(const BuildPlan& plan) {
8989
// any new branching added to this function.
9090
auto caps = mcpp::toolchain::capabilities_for(plan.toolchain);
9191

92+
// macOS minimum supported OS version for produced binaries.
93+
// Precedence: MACOSX_DEPLOYMENT_TARGET env (explicit per-invocation
94+
// override, the convention cargo/rustc/cc honor) > the manifest's
95+
// [build] macos_deployment_target (project default, SwiftPM-style) >
96+
// empty (toolchain/SDK default).
97+
std::string macosDeploymentTarget;
98+
if (const char* dt = std::getenv("MACOSX_DEPLOYMENT_TARGET"); dt && *dt) {
99+
macosDeploymentTarget = dt;
100+
} else {
101+
macosDeploymentTarget = plan.manifest.buildConfig.macosDeploymentTarget;
102+
}
103+
92104
f.cxxBinary = plan.toolchain.binaryPath;
93105
f.ccBinary = mcpp::toolchain::derive_c_compiler(plan.toolchain);
94106

@@ -137,20 +149,17 @@ CompileFlags compute_flags(const BuildPlan& plan) {
137149
auto llvmRoot = plan.toolchain.binaryPath.parent_path().parent_path();
138150
auto libcxxInclude = llvmRoot / "include" / "c++" / "v1";
139151
compile_toolchain_flags = " --no-default-config -nostdinc++";
140-
// macOS deployment target: make MACOSX_DEPLOYMENT_TARGET explicit
141-
// on the command line so (a) the ninja commands don't depend on
142-
// env propagation and (b) the value participates in the BMI
152+
// macOS deployment target: make the resolved value explicit on
153+
// the command line so (a) the ninja commands don't depend on env
154+
// propagation and (b) the value participates in the BMI
143155
// fingerprint via canonical flags — mixing targets in one sandbox
144156
// otherwise reuses a std.pcm built for a different
145157
// arm64-apple-macosxNN triple and dies with a config mismatch
146158
// (observed on macos CI). The link side is added to f.ld below
147159
// (the macOS link path doesn't consume link_toolchain_flags).
148-
if (mcpp::platform::is_macos) {
149-
if (const char* dt = std::getenv("MACOSX_DEPLOYMENT_TARGET");
150-
dt && *dt) {
151-
compile_toolchain_flags +=
152-
std::string(" -mmacosx-version-min=") + dt;
153-
}
160+
if (mcpp::platform::is_macos && !macosDeploymentTarget.empty()) {
161+
compile_toolchain_flags +=
162+
" -mmacosx-version-min=" + macosDeploymentTarget;
154163
}
155164
llvmRootForStdlib = llvmRoot;
156165
// libc++ headers
@@ -363,8 +372,8 @@ CompileFlags compute_flags(const BuildPlan& plan) {
363372
}
364373
}
365374
std::string version_min;
366-
if (const char* dt = std::getenv("MACOSX_DEPLOYMENT_TARGET"); dt && *dt) {
367-
version_min = std::string(" -mmacosx-version-min=") + dt;
375+
if (!macosDeploymentTarget.empty()) {
376+
version_min = " -mmacosx-version-min=" + macosDeploymentTarget;
368377
}
369378
f.ld = std::format("{}{}{} -fuse-ld=lld{}{}{}{}", full_static, static_stdlib,
370379
b_flag, version_min, stdlib_link, user_ldflags, link_extra);

src/cli.cppm

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -589,13 +589,20 @@ std::string canonical_compile_flags(const mcpp::manifest::Manifest& m) {
589589
s += " -fmodules";
590590
// macOS deployment target changes the effective compile triple
591591
// (arm64-apple-macosxNN) — a std.pcm built for one target cannot be
592-
// loaded by a TU compiled for another. Fold it into the fingerprint
593-
// so switching MACOSX_DEPLOYMENT_TARGET rebuilds the BMI cache
592+
// loaded by a TU compiled for another. Fold the resolved value
593+
// (env override > [build] macos_deployment_target manifest default)
594+
// into the fingerprint so switching targets rebuilds the BMI cache
594595
// instead of dying with a module config mismatch.
595596
if constexpr (mcpp::platform::is_macos) {
597+
std::string dtv;
596598
if (const char* dt = std::getenv("MACOSX_DEPLOYMENT_TARGET"); dt && *dt) {
599+
dtv = dt;
600+
} else {
601+
dtv = m.buildConfig.macosDeploymentTarget;
602+
}
603+
if (!dtv.empty()) {
597604
s += " macos_deployment_target=";
598-
s += dt;
605+
s += dtv;
599606
}
600607
}
601608
if (!m.buildConfig.cStandard.empty()) {

src/manifest.cppm

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,14 @@ struct BuildConfig {
104104
std::vector<std::string> cxxflags;
105105
std::vector<std::string> ldflags;
106106
std::string cStandard;
107+
// macOS minimum supported OS version for produced binaries
108+
// (LC_BUILD_VERSION minos), e.g. "11.0". Mirrors the ecosystem
109+
// conventions around deployment targets (the MACOSX_DEPLOYMENT_TARGET
110+
// env var that cargo/rustc/cc honor; SwiftPM's `platforms:` manifest
111+
// field; CMAKE_OSX_DEPLOYMENT_TARGET). Precedence: the env var (an
112+
// explicit per-invocation override) wins over this manifest default;
113+
// empty + no env = toolchain/SDK default. No effect off macOS.
114+
std::string macosDeploymentTarget;
107115
// Resolved build-profile knobs (from [profile.<name>] + built-in defaults).
108116
std::string optLevel = "2"; // -O level
109117
bool debug = false; // -g
@@ -888,6 +896,8 @@ std::expected<Manifest, ManifestError> parse_string(std::string_view content,
888896
if (auto v = doc->get_string_array("build.cxxflags")) m.buildConfig.cxxflags = *v;
889897
if (auto v = doc->get_string_array("build.ldflags")) m.buildConfig.ldflags = *v;
890898
if (auto v = doc->get_string("build.c_standard")) m.buildConfig.cStandard = *v;
899+
if (auto v = doc->get_string("build.macos_deployment_target"))
900+
m.buildConfig.macosDeploymentTarget = *v;
891901
for (auto const& flag : m.buildConfig.cxxflags) {
892902
if (starts_with_std_flag(flag)) {
893903
return std::unexpected(error(origin,

tests/unit/test_manifest.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,34 @@ kind = "lib"
295295
EXPECT_EQ(m->buildConfig.cStandard, "c11");
296296
}
297297

298+
TEST(Manifest, BuildMacosDeploymentTarget) {
299+
constexpr auto src = R"(
300+
[package]
301+
name = "x"
302+
version = "0.1.0"
303+
[build]
304+
macos_deployment_target = "11.0"
305+
[targets.x]
306+
kind = "lib"
307+
)";
308+
auto m = mcpp::manifest::parse_string(src);
309+
ASSERT_TRUE(m.has_value()) << m.error().format();
310+
EXPECT_EQ(m->buildConfig.macosDeploymentTarget, "11.0");
311+
}
312+
313+
TEST(Manifest, BuildMacosDeploymentTargetDefaultsEmpty) {
314+
constexpr auto src = R"(
315+
[package]
316+
name = "x"
317+
version = "0.1.0"
318+
[targets.x]
319+
kind = "lib"
320+
)";
321+
auto m = mcpp::manifest::parse_string(src);
322+
ASSERT_TRUE(m.has_value()) << m.error().format();
323+
EXPECT_TRUE(m->buildConfig.macosDeploymentTarget.empty());
324+
}
325+
298326
TEST(Manifest, RuntimeConfig) {
299327
constexpr auto src = R"(
300328
[package]

0 commit comments

Comments
 (0)