Skip to content

Commit f9ca398

Browse files
committed
fix(pack,prepare): the neutral link form only where it says the same thing
e2e 257 caught a regression the previous commit introduced, and the reason is worth keeping: a leg's link line is not always just a library reference. A PE/MinGW shared leg links with `-L… -Wl,-Bdynamic -lmathkit`, and `-Wl,-Bdynamic` only works IMMEDIATELY BEFORE the `-l` it enables — mcpp gives PE executables `-static`, which otherwise leaves ld in static-only mode where it refuses an import library and says `have you installed the static version of the mathkit library?`, naming neither the DLL nor `-static`. The first attempt cleared the leg's ldflags wholesale and lost the flag. The second kept it but rendered the library reference through the neutral channel, which puts it in a different slot on the command line — so the flag and its argument were separated and the same failure came back. Both are the same mistake: treating a hand-tuned link line as if it were a two-field record. So the neutral form is emitted only for legs where it is EQUIVALENT, and the PE/MinGW shared leg keeps the spelling that works. It costs nothing — a PE/GNU leg is not an MSVC-ABI leg, and cl.exe, the reason the neutral form exists, never reads it. On the consuming side only the library references are replaced, never the whole list.
1 parent a0d9874 commit f9ca398

4 files changed

Lines changed: 57 additions & 16 deletions

File tree

docs/12-binary-distribution.md

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -329,11 +329,19 @@ target, so a `cl.exe` consumer links the package. They are not new vocabulary
329329
`[runtime]` has had both keys at the top level all along; this makes them
330330
per-target.
331331

332-
**Both spellings are emitted, and a newer mcpp ignores the `ldflags` rather than
333-
adding to them.** An older mcpp reads only the `ldflags` and silently ignores the
334-
`runtime` block, so dropping the `ldflags` would leave every older client with no
335-
link line at all; adding both would put `-L` back on the `cl` command line, which
336-
is the thing being avoided.
332+
**Both spellings are emitted, and a newer mcpp drops the leg's library
333+
references rather than adding to them.** An older mcpp reads only the `ldflags`
334+
and silently ignores the `runtime` block, so dropping the `ldflags` would leave
335+
every older client with no link line at all; adding both would put `-L` back on
336+
the `cl` command line, which is the thing being avoided.
337+
338+
One leg is deliberately left out of this: a **PE/MinGW shared** leg links with
339+
`-L… -Wl,-Bdynamic -lmathkit`, and `-Wl,-Bdynamic` only works immediately before
340+
the `-l` it enables — mcpp gives PE executables `-static`, which otherwise leaves
341+
the linker in static-only mode where it refuses an import library. The neutral
342+
form cannot say "switch link mode first", so that leg keeps the spelling that
343+
works. It costs nothing: a PE/MinGW leg is not an MSVC-ABI leg, and `cl.exe`
344+
never reads it.
337345

338346
Naming the file by path instead (`lib/<triple>/mathkit.lib`) is the spelling
339347
every driver takes, and it does not work either: ninja runs link commands with

docs/zh/12-binary-distribution.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,11 +295,17 @@ mcpp 会按 target 把它们渲染成 `/LIBPATH:` + `<name>.lib` 或 `-L` + `-l<
295295
于是 `cl.exe` 的消费者也能链上。这两个键不是新词表 —— `[runtime]` 顶层一直就有,
296296
这里只是让它们可以按 target 给。
297297

298-
**两种拼写都会写出来,而新版 mcpp 读到中立形式时会忽略同一条腿的 `ldflags`,
298+
**两种拼写都会写出来,而新版 mcpp 读到中立形式时会丢掉同一条腿的库引用,
299299
而不是叠加。** 旧版 mcpp 只读 `ldflags` 并静默忽略 `runtime` 段,所以去掉
300300
`ldflags` 会让所有旧客户端一个链接 flag 都拿不到;而两者都应用又会把 `-L` 送回
301301
`cl` 的命令行 —— 那正是要避免的事。
302302

303+
有一条腿被刻意排除在外:**PE/MinGW 的动态库腿**链接行是
304+
`-L… -Wl,-Bdynamic -lmathkit`,而 `-Wl,-Bdynamic` 只有**紧邻它所启用的那个 `-l`**
305+
时才有效 —— mcpp 给 PE 可执行文件加 `-static`,否则链接器停在纯静态模式并拒绝
306+
导入库。中立形式没法表达「先切换链接模式」,所以那条腿保留能用的拼写。
307+
这不付出任何代价:PE/MinGW 的腿不是 MSVC ABI 的腿,`cl.exe` 永远读不到它。
308+
303309
**改成直接写文件路径也不行**(`lib/<triple>/mathkit.lib` 才是每个 driver 都吃的
304310
拼写):ninja 执行链接命令时 cwd 是**输出目录**,而只有 include 家族前缀
305311
(`-I``-L` …)会被 `normalize_include_flags` 相对包根绝对化 —— 没有前缀的 token

src/build/prepare.cppm

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,8 +232,22 @@ void merge_conditional_config(mcpp::manifest::Manifest& m,
232232
// a broader unconditional one under GNU last-wins — which is what
233233
// makes an off-OS REMOVAL expressible (`-U` after the base `-D`).
234234
if (neutralWins) {
235+
// ⚠️ Drop the LIBRARY REFERENCES, not the whole ldflags list.
236+
//
237+
// Clearing it outright was a measured regression: a PE/MinGW shared
238+
// leg's ldflags also carry `-Wl,-Bdynamic`, without which `-static`
239+
// leaves ld in static-only mode and it refuses the import library
240+
// with `have you installed the static version of the mathkit
241+
// library?`. e2e 257 caught it.
242+
//
243+
// The neutral form replaces exactly what it can express — a library
244+
// and where to find it. Anything else in that block says something
245+
// it cannot say, and must survive.
235246
auto inputs = cc.inputs;
236-
inputs.ldflags.clear();
247+
std::erase_if(inputs.ldflags, [](std::string_view f) {
248+
return f.starts_with("-L") || f.starts_with("-l")
249+
|| f.starts_with("/LIBPATH:");
250+
});
237251
mcpp::manifest::append(m.buildConfig, inputs);
238252
} else {
239253
mcpp::manifest::append(m.buildConfig, cc.inputs);

src/pack/manifest_emit.cppm

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -243,19 +243,32 @@ std::string emit_package_manifest(const PackageDoc& doc) {
243243
peGnuShared ? "\"-Wl,-Bdynamic\", " : "",
244244
leg.linkName);
245245

246-
// …and the same statement without a dialect. mcpp renders these as
247-
// `/LIBPATH:` + `<n>.lib` or `-L` + `-l<n>` from the target, which is
248-
// what lets a consumer driven by native `cl.exe` link this package at
249-
// all — cl rejects `-L`.
246+
// …and, where it says the SAME thing, the dialect-neutral form. mcpp
247+
// renders these as `/LIBPATH:` + `<n>.lib` or `-L` + `-l<n>` from the
248+
// target, which is what lets a consumer driven by native `cl.exe` link
249+
// this package at all — cl rejects `-L`.
250250
//
251251
// BOTH are emitted, deliberately. An older mcpp reads only the ldflags
252252
// above and silently ignores this block (measured), so dropping the
253253
// ldflags would leave every older client with no link line at all. A
254-
// newer mcpp seeing this block ignores that leg's ldflags rather than
255-
// adding to them — see merge_conditional_config.
256-
o += std::format("[target.'{}'.runtime]\n", cfg_predicate_for(leg.triple));
257-
o += std::format("link_library_dirs = [\"lib/{}\"]\n", leg.triple);
258-
o += std::format("libraries = [\"{}\"]\n\n", leg.linkName);
254+
// newer mcpp seeing this block drops that leg's library references and
255+
// uses these instead — see merge_conditional_config.
256+
//
257+
// ⚠️ NOT FOR A PE/MinGW SHARED LEG, and this was measured rather than
258+
// reasoned. That leg's line is `-L… -Wl,-Bdynamic -lmathkit`, and
259+
// `-Wl,-Bdynamic` only works IMMEDIATELY BEFORE the `-l` it enables:
260+
// mcpp gives PE executables `-static`, which leaves ld in static-only
261+
// mode where it refuses an import library. The neutral form has no way
262+
// to say "and switch link mode first", and rendering the two halves
263+
// through different slots separates the flag from its argument — e2e
264+
// 257 fails with `have you installed the static version of the mathkit
265+
// library?`. So that one leg keeps the spelling that works, and cl.exe
266+
// never sees it: a PE/GNU leg is not an MSVC-ABI leg.
267+
if (!peGnuShared) {
268+
o += std::format("[target.'{}'.runtime]\n", cfg_predicate_for(leg.triple));
269+
o += std::format("link_library_dirs = [\"lib/{}\"]\n", leg.triple);
270+
o += std::format("libraries = [\"{}\"]\n\n", leg.linkName);
271+
}
259272
}
260273
// A shared library has to be FOUND at run time as well as linked, and the
261274
// two are different search paths — `link_library_dirs` is not rpath.

0 commit comments

Comments
 (0)