Skip to content

Commit bfbfaea

Browse files
committed
fix: fold [build].defines into cflags/cxxflags before P1689 scan
[build].defines was parsed but never folded into the compile flags used by the P1689 module scanner. When imports were guarded by a macro from [build].defines, the scanner saw no imports while the planner (via scan_overrides) expected them, causing module-graph divergence. - Add defines to BuildConfig - Parse [build].defines in TOML loader - Fold defines into -D<x> on cflags/cxxflags before snapshot/fingerprint - Add unit test and E2E regression test - Update English and Chinese docs Closes #296
1 parent 96f6e2e commit bfbfaea

7 files changed

Lines changed: 120 additions & 0 deletions

File tree

docs/05-mcpp-toml.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ c_standard = "c11" # Standard for C source files (default c11)
141141
cflags = ["-DFOO=1"] # Extra C compile flags
142142
cxxflags = ["-DBAR=2"] # Extra C++ compile flags (do not put -std=... here)
143143
ldflags = ["-lfoo"] # Extra link flags
144+
defines = ["BIZ=1", "QUX"] # Preprocessor macros for every TU (desugars to -D; reaches module scans)
144145
static_stdlib = true # Statically link libstdc++ (default true)
145146
target = "x86_64-linux-musl" # Default build target when no --target is passed
146147
# (≙ cargo build.target; e.g. "ship fully-static")
@@ -182,6 +183,8 @@ then only guaranteed to run on the build machine's version and above). A lower
182183
floor (11–13) requires a self-built libc++ archive (already verified to work, a
183184
data-level switch, available on request).
184185

186+
`defines` desugars each entry to `-D<x>` on both the C and C++ compile channels and reaches every TU in the package — including module interface units — so it affects the P1689 module scan. For macros that should only affect a single binary's entry source, use `[targets.<name>].defines` instead.
187+
185188
Do not configure the C++ standard via `build.cxxflags = ["-std=..."]`. Instead use:
186189

187190
```toml

docs/zh/05-mcpp-toml.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ c_standard = "c11" # C 源文件的标准(默认 c11)
136136
cflags = ["-DFOO=1"] # 额外 C 编译参数
137137
cxxflags = ["-DBAR=2"] # 额外 C++ 编译参数(不要放 -std=...)
138138
ldflags = ["-lfoo"] # 额外链接参数
139+
defines = ["BIZ=1", "QUX"] # 作用于每个 TU 的预处理宏(脱糖为 -D;会进入模块扫描)
139140
static_stdlib = true # 静态链接 libstdc++(默认 true)
140141
macos_deployment_target = "14.0" # macOS 产物的最低支持系统版本(仅 macOS 生效)
141142
```
@@ -166,6 +167,8 @@ cargo/rustc、cc 等同样尊重该变量)> 本字段(项目默认,类似 SwiftP
166167
系统 libc++(产物只保证在构建机同版本及以上运行)。更低 floor(11–13)
167168
需自建 libc++ 归档(已验证可行,数据级切换,按需提供)。
168169

170+
`defines` 把每个条目脱糖为 `-D<x>`,同时作用于 C 和 C++ 编译通道,并覆盖包内每个 TU(包括模块接口单元),因此会影响 P1689 模块扫描。若只想让某个二进制入口源读到宏,请改用 `[targets.<name>].defines`
171+
169172
C++ 标准不要通过 `build.cxxflags = ["-std=..."]` 配置。请使用:
170173

171174
```toml

src/build/prepare.cppm

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,19 @@ void merge_conditional_build_inputs(mcpp::manifest::Manifest& m,
442442
}
443443
}
444444

445+
// Desugar `[build].defines` into `-D<x>` on both C and C++ flag channels.
446+
// This must run AFTER conditional `[target.'cfg(...)'.build]` sections are
447+
// merged (so conditional defines land too) and BEFORE the manifest is
448+
// snapshotted into packages[] / fingerprinted. Idempotent: clearing the
449+
// `defines` vector after folding makes repeated calls harmless.
450+
void fold_build_defines_into_flags(mcpp::manifest::BuildConfig& bc) {
451+
for (auto const& d : bc.defines) {
452+
bc.cflags.push_back("-D" + d);
453+
bc.cxxflags.push_back("-D" + d);
454+
}
455+
bc.defines.clear();
456+
}
457+
445458
// Feature-activation closure — THE single implementation (build.mcpp env
446459
// contract, Stage 2a feature-deps, and the main feature pass all call this):
447460
// seed = [features].default ∪ requested, expanded transitively over implies;
@@ -943,6 +956,10 @@ prepare_build(bool print_fingerprint,
943956
m->buildDependencies.insert(cc.buildDependencies.begin(), cc.buildDependencies.end());
944957
}
945958
}
959+
// `[build].defines` must reach the scanner (P1689) and the compile edge,
960+
// and must participate in the fingerprint. Fold before dependency
961+
// resolution / fingerprinting.
962+
fold_build_defines_into_flags(m->buildConfig);
946963

947964
// msvc@system: a *system* toolchain — located on the machine, never
948965
// resolved through xim packages. mcpp does not install MSVC.
@@ -2073,6 +2090,7 @@ prepare_build(bool print_fingerprint,
20732090
cfgpred::context_for(overrides.target_triple),
20742091
overrides.target_triple);
20752092
}
2093+
fold_build_defines_into_flags(manifest->buildConfig);
20762094

20772095
return std::pair{effRoot, std::move(*manifest)};
20782096
};
@@ -2964,6 +2982,7 @@ prepare_build(bool print_fingerprint,
29642982
cfgpred::context_for(overrides.target_triple),
29652983
overrides.target_triple);
29662984
}
2985+
fold_build_defines_into_flags(dep_manifest->buildConfig);
29672986
} else {
29682987
auto loaded = loadVersionDep(name, key.ns, key.shortName, spec.version);
29692988
if (!loaded) return std::unexpected(loaded.error());

src/manifest/toml.cppm

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -841,6 +841,7 @@ std::expected<Manifest, ManifestError> parse_string(std::string_view content,
841841
if (auto v = doc->get_bool("build.allow_host_libs")) m.buildConfig.allowHostLibs = *v;
842842
if (auto v = doc->get_string_array("build.cflags")) m.buildConfig.cflags = *v;
843843
if (auto v = doc->get_string_array("build.cxxflags")) m.buildConfig.cxxflags = *v;
844+
if (auto v = doc->get_string_array("build.defines")) m.buildConfig.defines = *v;
844845
// Module-graph-global dialect flags (issue #210) — see types.cppm
845846
// dialect_flags(); this key is the explicit escape hatch for flags the
846847
// known-list doesn't recognize yet.

src/manifest/types.cppm

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,13 @@ inline void append(BuildInputs& dst, const BuildInputs& src) {
203203
// is read in ~150 places, and a BuildConfig genuinely IS a set of build
204204
// inputs plus the selection axis and resolved policy scalars.
205205
struct BuildConfig : BuildInputs {
206+
// Package-level preprocessor defines. Unlike per-target `defines` (which
207+
// only affect the binary's entry TU), these reach every TU in this
208+
// package — including module interface units — so they participate in
209+
// the P1689 module scan. Desugared to `-D<x>` on both C and C++ compiles
210+
// before the manifest is snapshot into BuildPlan / fingerprint.
211+
std::vector<std::string> defines;
212+
206213
// feature name → extra source globs gated by that feature. A glob listed
207214
// here is EXCLUDED from the default build and only compiled/linked when the
208215
// feature is active for this package (resolved in prepare_build). Lets a
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
#!/usr/bin/env bash
2+
# requires:
3+
# [build].defines must reach the P1689 module scan AND the compile command
4+
# for every TU (including module interfaces). Previously it was parsed but
5+
# silently dropped, causing plan-vs-scan divergence for conditional imports.
6+
set -e
7+
8+
TMP=$(mktemp -d)
9+
trap "rm -rf $TMP" EXIT
10+
11+
cd "$TMP"
12+
mkdir -p src
13+
14+
cat > src/lib.cppm <<'EOF'
15+
#ifndef TEST_USE_MODULES
16+
module;
17+
#include <print>
18+
#endif
19+
20+
export module lib;
21+
22+
#ifdef TEST_USE_MODULES
23+
import std;
24+
#endif
25+
26+
export void printHello(){
27+
std::println("hello from lib");
28+
}
29+
EOF
30+
31+
cat > src/main.cpp <<'EOF'
32+
#ifdef TEST_USE_MODULES
33+
import std;
34+
import lib;
35+
#else
36+
#include <print>
37+
#endif
38+
39+
int main() {
40+
std::println("hello from main");
41+
printHello();
42+
return 0;
43+
}
44+
EOF
45+
46+
cat > mcpp.toml <<'EOF'
47+
[package]
48+
name = "hello"
49+
version = "0.1.0"
50+
description = "A modular C++23 package"
51+
license = "Apache-2.0"
52+
53+
[build]
54+
defines = ["TEST_USE_MODULES"]
55+
56+
[scan_overrides."src/lib.cppm"]
57+
provides = ["lib"]
58+
imports = ["std"]
59+
60+
[scan_overrides."src/main.cpp"]
61+
imports = ["std", "lib"]
62+
EOF
63+
64+
"$MCPP" build > build.log 2>&1 || { cat build.log; echo "build failed"; exit 1; }
65+
out=$("$MCPP" run 2>&1 | tail -2 | tr '\n' ' ')
66+
[[ "$out" == *"hello from main"*"hello from lib"* ]] || {
67+
echo "unexpected output: $out"; exit 1; }
68+
69+
echo "OK"

tests/unit/test_manifest.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,24 @@ kind = "lib"
341341
EXPECT_EQ(m->buildConfig.cStandard, "c11");
342342
}
343343

344+
// [build].defines is sugar for `-D<x>` on both C and C++ channels; it must
345+
// parse into buildConfig.defines so prepare_build can fold it into flags
346+
// before the P1689 scan and fingerprint.
347+
TEST(Manifest, BuildDefinesParsesIntoSeparateVector) {
348+
constexpr auto src = R"(
349+
[package]
350+
name = "x"
351+
version = "0.1.0"
352+
[build]
353+
defines = ["TEST_USE_MODULES", "VALUE=42"]
354+
)";
355+
auto m = mcpp::manifest::parse_string(src);
356+
ASSERT_TRUE(m.has_value()) << m.error().format();
357+
ASSERT_EQ(m->buildConfig.defines.size(), 2u);
358+
EXPECT_EQ(m->buildConfig.defines[0], "TEST_USE_MODULES");
359+
EXPECT_EQ(m->buildConfig.defines[1], "VALUE=42");
360+
}
361+
344362
// #249: `[build] include_dirs_after` parses into buildConfig.includeDirsAfter
345363
// — the -idirafter channel (searched AFTER the toolchain's system dirs), so
346364
// an extracted-tarball root containing a file named like a standard header

0 commit comments

Comments
 (0)