Skip to content

Commit 061314d

Browse files
committed
test(e2e),ci,docs: bare-Windows axis, spaced paths, MSVC build.mcpp
CI gains the Windows shapes it never had. windows-fresh becomes a matrix over windows-2022 and windows-2025 — GitHub publishes no Windows CLIENT image, so those Server builds are the closest stand-ins for the Win10 and Win11 kernel generations; genuinely client-only behaviour (UAC, Defender, long-path policy) still needs a self-hosted runner and is called out as uncovered. windows-nomsvc-fresh masks Visual Studio, because no VS-free runner exists. Masking risks a false green — miss one of msvc.cppm's three discovery strategies and mcpp still finds MSVC, takes the old path and passes while proving nothing — so e2e 182 opens by asserting that detection FAILS. It then checks the fallback, that both axes persist, that the exe runs with the toolchain off PATH, and that an explicit [toolchain] is refused rather than swapped. e2e 179 (spaced paths) asserts on the generated ninja file, not just on the build succeeding: a split include flag can still compile by luck. e2e 180 fills the empty `MSVC x build.mcpp` cell, including the link-lib translation and the module refusal. Docs: the winlibs route moves from a footnote to the stated default, and the three `[build] linkage` mentions are corrected — the parser only reads `linkage` under `[target.<triple>]`, so that key was silently ignored everywhere it was documented.
1 parent a8f9149 commit 061314d

13 files changed

Lines changed: 534 additions & 28 deletions

.github/workflows/ci-fresh-install.yml

Lines changed: 89 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -374,14 +374,26 @@ jobs:
374374
mcpp run
375375
376376
# ──────────────────────────────────────────────────────────────────
377-
# Windows: llvm@20.1.7 + MSVC STL
377+
# Windows WITH Visual Studio: llvm@20.1.7 + MSVC STL
378+
#
379+
# Two images, because the OS version is a real variable for a tool that
380+
# touches the UCRT, the Windows SDK and long paths. GitHub publishes no
381+
# Windows 10/11 CLIENT image, so these Server builds are the closest
382+
# available stand-ins: windows-2022 is the Win10 21H2 kernel generation,
383+
# windows-2025 the Win11 24H2 one. What they cannot cover is genuinely
384+
# client-only behaviour — UAC prompts, Defender real-time scanning, the
385+
# long-path policy default — which needs a self-hosted runner.
378386
# ──────────────────────────────────────────────────────────────────
379387
windows-fresh:
380388
needs: [wait-index]
381-
name: Windows fresh install
389+
name: Windows fresh install (${{ matrix.image }})
382390
if: ${{ github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' }}
383-
runs-on: windows-latest
391+
runs-on: ${{ matrix.image }}
384392
timeout-minutes: 30
393+
strategy:
394+
fail-fast: false
395+
matrix:
396+
image: [windows-2022, windows-2025]
385397
env:
386398
# The one derived value (see the header comment): every install job names
387399
# the SAME version the index guard waited for, so the two cannot disagree.
@@ -437,3 +449,77 @@ jobs:
437449
run: |
438450
mcpp clean
439451
mcpp run
452+
453+
# ──────────────────────────────────────────────────────────────────
454+
# Windows WITHOUT Visual Studio — the shape of an ordinary user's machine
455+
#
456+
# A stock Windows install ships the UCRT runtime DLLs and nothing else: the
457+
# MSVC STL and the Windows SDK arrive only with Visual Studio's "Desktop
458+
# development with C++" workload. mcpp's Windows default targeted the MSVC
459+
# ABI, so `mcpp new && mcpp build` failed on every such box — and no CI job
460+
# could see it, because every GitHub Windows image ships Visual Studio.
461+
#
462+
# There is no VS-free runner, so the image is masked instead. The risk with
463+
# masking is a false green: miss one of msvc.cppm's three discovery
464+
# strategies (vswhere, environment, well-known paths) and mcpp still finds
465+
# MSVC, takes the ordinary path, and the job passes while proving nothing.
466+
# e2e 182 opens by asserting `mcpp toolchain default msvc` FAILS, which
467+
# turns exactly that into a hard failure.
468+
# ──────────────────────────────────────────────────────────────────
469+
windows-nomsvc-fresh:
470+
needs: [wait-index]
471+
name: Windows fresh install (no Visual Studio)
472+
if: ${{ github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' }}
473+
runs-on: windows-2025
474+
timeout-minutes: 30
475+
env:
476+
MCPP_PIN: ${{ needs.wait-index.outputs.version }}
477+
steps:
478+
- uses: actions/checkout@v4
479+
480+
- name: Mask Visual Studio
481+
shell: pwsh
482+
run: |
483+
# All three discovery strategies at once. The runner is disposable,
484+
# so renaming in place is fine and is closer to "absent" than any
485+
# env-only trick would be.
486+
$vswhere = "C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"
487+
if (Test-Path $vswhere) { Rename-Item $vswhere "vswhere.exe.masked" }
488+
489+
Get-ChildItem "C:\Program Files*\Microsoft Visual Studio" -Directory `
490+
-ErrorAction SilentlyContinue | ForEach-Object {
491+
$masked = "$($_.FullName).masked"
492+
if (-not (Test-Path $masked)) { Rename-Item $_.FullName $masked }
493+
}
494+
495+
foreach ($v in @('VSINSTALLDIR','VCINSTALLDIR','VCToolsInstallDir',
496+
'VS170COMNTOOLS','VS160COMNTOOLS','VS150COMNTOOLS')) {
497+
"$v=" | Out-File -Append -FilePath $env:GITHUB_ENV -Encoding utf8
498+
}
499+
500+
- name: Install xlings
501+
shell: pwsh
502+
env:
503+
XLINGS_NON_INTERACTIVE: '1'
504+
run: |
505+
irm https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.ps1 | iex
506+
507+
$xlingsbin = "$env:USERPROFILE\.xlings\subos\current\bin"
508+
$env:PATH = "$xlingsbin;$env:PATH"
509+
$xlingsbin | Out-File -Append -FilePath $env:GITHUB_PATH -Encoding utf8
510+
511+
- name: Install mcpp and config mirror
512+
shell: pwsh
513+
run: |
514+
xlings update
515+
xlings install "mcpp@$env:MCPP_PIN" -y -g --verbose
516+
mcpp --version
517+
mcpp self config --mirror GLOBAL
518+
519+
# The self-check, the fallback, persistence, a self-contained exe, and
520+
# the refusal to overrule an explicit [toolchain] — all in e2e 182, so
521+
# the assertions live with the tests rather than in workflow YAML.
522+
- name: "No Visual Studio: fallback to winlibs GCC (e2e 182)"
523+
shell: bash
524+
run: |
525+
MCPP="$(command -v mcpp)" bash tests/e2e/182_windows_no_msvc_fallback.sh

.github/workflows/ci-windows.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,12 @@ jobs:
292292
cd "$GITHUB_WORKSPACE"
293293
MCPP="$MCPP_SELF" bash tests/e2e/99_msvc_native_build.sh
294294
295+
# build.mcpp under cl.exe. `MSVC x build.mcpp` was an empty cell in
296+
# this matrix and the feature was correspondingly at zero — the ten
297+
# build.mcpp e2e all run under clang, whose payload path has no
298+
# spaces in it either. Both gaps closed here.
299+
MCPP="$MCPP_SELF" bash tests/e2e/180_msvc_build_mcpp.sh
300+
295301
# restore the LLVM default for the remaining steps
296302
"$MCPP_SELF" toolchain default llvm@20.1.7
297303

README.md

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -294,8 +294,8 @@ the right toolchain payload is resolved and installed automatically.
294294
| `x86_64-linux-gnu` | gcc *(Linux default)* or llvm ||
295295
| `x86_64-linux-musl` | gcc 16, fully static ||
296296
| `aarch64-linux-musl` | gcc 16, fully static — cross from x86_64 (qemu-verified) or native ||
297-
| `x86_64-windows-gnu` | gcc 16 MinGW-w64 — native on Windows, cross from Linux (wine-verified) ||
298-
| `x86_64-windows-msvc` | `msvc@system` (detected VS/BuildTools) or llvm ¹ *(Windows default)* ||
297+
| `x86_64-windows-gnu` | gcc 16 MinGW-w64 — native on Windows, cross from Linux (wine-verified) *(Windows default without Visual Studio)* ||
298+
| `x86_64-windows-msvc` | `msvc@system` (detected VS/BuildTools) or llvm ¹ *(Windows default with Visual Studio)* ||
299299
| `aarch64-macos` | llvm *(macOS default)* ||
300300
| `riscv64-linux-musl` || 🔄 |
301301
| `aarch64-linux-gnu` || 🔄 |
@@ -308,10 +308,14 @@ the right toolchain payload is resolved and installed automatically.
308308
> `musl-gcc@…` — stay permanently accepted as aliases and normalize to the
309309
> canonical forms above.
310310
>
311-
> ¹ On Windows, llvm requires an existing **MSVC BuildTools or Visual Studio**
312-
> (UCRT, Windows SDK, MSVC STL). The MinGW route (`--target x86_64-windows-gnu`,
313-
> or `mcpp toolchain default gcc@16 --target x86_64-windows-gnu`) needs no
314-
> Visual Studio at all.
311+
> ¹ On Windows, llvm targets the MSVC ABI and therefore requires an existing
312+
> **MSVC BuildTools or Visual Studio** (UCRT, Windows SDK, MSVC STL). You do
313+
> not have to arrange this: on first run mcpp checks for a usable MSVC and,
314+
> finding none, defaults to `x86_64-windows-gnu` (winlibs MinGW-w64) — fully
315+
> self-contained, no Visual Studio, `import std` included. Nothing to install
316+
> or configure; `mcpp new && mcpp build` just works on a stock Windows box.
317+
> An explicit `[toolchain]` in `mcpp.toml` is always respected as written —
318+
> mcpp revises its own default, never yours.
315319
316320
## Documentation
317321

README.zh-CN.md

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -291,8 +291,8 @@ mcpp 的身份模型是两条正交轴:**工具链** = `family@version`(family
291291
| `x86_64-linux-gnu` | gcc(*Linux 默认*)或 llvm ||
292292
| `x86_64-linux-musl` | gcc 16,全静态 ||
293293
| `aarch64-linux-musl` | gcc 16,全静态——x86_64 交叉(qemu 实测)或原生 ||
294-
| `x86_64-windows-gnu` | gcc 16 MinGW-w64——Windows 原生,Linux 交叉(wine 实测) ||
295-
| `x86_64-windows-msvc` | `msvc@system`(探测 VS/BuildTools)或 llvm ¹(*Windows 默认*) ||
294+
| `x86_64-windows-gnu` | gcc 16 MinGW-w64——Windows 原生,Linux 交叉(wine 实测)(*无 Visual Studio 时的 Windows 默认*) ||
295+
| `x86_64-windows-msvc` | `msvc@system`(探测 VS/BuildTools)或 llvm ¹(*有 Visual Studio 时的 Windows 默认*) ||
296296
| `aarch64-macos` | llvm(*macOS 默认*) ||
297297
| `riscv64-linux-musl` || 🔄 |
298298
| `aarch64-linux-gnu` || 🔄 |
@@ -304,9 +304,12 @@ mcpp 的身份模型是两条正交轴:**工具链** = `family@version`(family
304304
> 旧拼写——`x86_64-w64-mingw32``gcc@16.1.0-musl``mingw-cross@…``musl-gcc@…`——
305305
> 作为别名**永久接受**,归一到上表的 canonical 形式。
306306
>
307-
> ¹ Windows 上 llvm 依赖已安装的 **MSVC BuildTools 或 Visual Studio**(UCRT、Windows
308-
> SDK、MSVC STL)。MinGW 路线(`--target x86_64-windows-gnu`,或
309-
> `mcpp toolchain default gcc@16 --target x86_64-windows-gnu`)完全不需要 Visual Studio。
307+
> ¹ Windows 上 llvm 打的是 MSVC ABI,因此依赖已安装的 **MSVC BuildTools 或
308+
> Visual Studio**(UCRT、Windows SDK、MSVC STL)。这件事你不需要自己安排:mcpp 首跑会
309+
> 探测是否有可用的 MSVC,探不到就默认走 `x86_64-windows-gnu`(winlibs MinGW-w64)——
310+
> 完全自包含、不需要 Visual Studio、`import std` 可用。无需安装或配置,裸 Windows 上
311+
> `mcpp new && mcpp build` 直接可用。而 `mcpp.toml` 里显式写的 `[toolchain]` 永远按你
312+
> 写的执行——mcpp 只修正自己选的默认值,不改你的。
310313
311314
## 文档
312315

docs/03-toolchains.md

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,21 @@ and `planned` targets that are registered but not yet shipped.
110110

111111
## Windows PE via MinGW-w64 (`x86_64-windows-gnu`, no Visual Studio required)
112112

113+
**This is the Windows default when no Visual Studio is present.** Windows ships
114+
the UCRT runtime DLLs but not the MSVC STL or the Windows SDK — those come with
115+
Visual Studio's "Desktop development with C++" workload. Since llvm on Windows
116+
targets the MSVC ABI and needs both, mcpp checks for a usable MSVC (STL **and**
117+
SDK — half of one is the trap) on first run and falls back here when it finds
118+
none, persisting the choice so later builds are silent. Nothing to install or
119+
configure.
120+
121+
The same check also repairs an existing setup: if a `[toolchain] default` mcpp
122+
chose earlier can no longer work on this machine, it is revised in place, with
123+
a line saying so. An explicit `[toolchain]` in `mcpp.toml` (or a
124+
`[target.X].toolchain`) is never overruled — a project that needs the MSVC ABI
125+
to link vcpkg-built `.lib` files gets an error naming the alternative, not a
126+
silent ABI swap.
127+
113128
"MinGW" in mcpp is a **target**, not a toolchain name: `x86_64-windows-gnu`
114129
— GCC producing Windows PE with the GNU CRT. The same identity works from
115130
both hosts; which self-contained payload serves it is resolved automatically
@@ -126,7 +141,14 @@ mcpp toolchain default gcc@16 --target x86_64-windows-gnu
126141
It uses the regular GCC module pipeline (`gcm.cache`, `import std` via
127142
libstdc++'s `bits/std.cc`). The target's default linkage is **static**
128143
the produced `.exe` is fully self-contained (no `libstdc++-6.dll` to ship,
129-
runs directly under wine); `[build] linkage = "dynamic"` opts out.
144+
runs directly under wine). To opt out, set it on the target section —
145+
`linkage` is exact-triple only (§2.7 of [mcpp.toml](05-mcpp-toml.md)), and a
146+
`[build] linkage` key does not exist and is silently ignored:
147+
148+
```toml
149+
[target.x86_64-windows-gnu]
150+
linkage = "dynamic"
151+
```
130152

131153
In a manifest:
132154

@@ -180,7 +202,9 @@ INCLUDE/LIB environment from the detected VC tools + Windows SDK (no
180202
`vcvarsall` involved), stages `std.ixx`/`std.compat.ixx` as `.ifc` BMIs,
181203
compiles `.cppm` module units via `/interface /TP /ifcOutput`, scans with
182204
`/scanDependencies`, and links with `link.exe`/`lib.exe` through response
183-
files. `[build] linkage = "static"` selects the `/MT` CRT. A missing Windows
205+
files. `[target.x86_64-windows-msvc] linkage = "static"` (or `mcpp build
206+
--static`) selects the `/MT` CRT — not `[build] linkage`, which is not a key.
207+
A missing Windows
184208
SDK fails the build with installation guidance (`mcpp self doctor` reports
185209
SDK status).
186210

docs/07-build-mcpp.md

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ interface and belongs in the declarative manifest/descriptor
6969
## Typed API: `import mcpp;` (recommended)
7070

7171
Instead of printing raw strings you can write `build.mcpp` **modules-first**
72-
`import mcpp;`, no `#include`, no `import std;`. The `mcpp` module is bundled in the
72+
`import mcpp;`, no `#include` needed. The `mcpp` module is bundled in the
7373
mcpp binary (so it always matches your mcpp's protocol) and is compiled on demand;
7474
its functions just emit the directives above:
7575

@@ -98,10 +98,39 @@ int main() {
9898
| `mcpp::include_dir(d)` / `mcpp::include_dir_after(d)` | `mcpp:include-dir=` / `mcpp:include-dir-after=` |
9999
| `mcpp::rerun_if_changed(p)` / `mcpp::rerun_if_env_changed(v)` | the matching `rerun-*` directives |
100100

101-
If your `build.mcpp` also needs to *write* a generated file, mix in a textual
102-
`#include <fstream>` — that's fine; only `import std;` is unnecessary. The raw
103-
stdout protocol above remains the low-level substrate; `import mcpp;` is the typed
104-
layer over it.
101+
The raw stdout protocol above remains the low-level substrate; `import mcpp;`
102+
is the typed layer over it.
103+
104+
### `import std;` (mcpp 2026.8.2.1+)
105+
106+
A `build.mcpp` may `import std;` (and `import std.compat;`), alone or together
107+
with `import mcpp;`:
108+
109+
```cpp
110+
// build.mcpp
111+
import std;
112+
import mcpp;
113+
114+
int main() {
115+
for (auto const& f : std::vector<std::string>{"FOO", "BAR"})
116+
mcpp::define(f.c_str());
117+
}
118+
```
119+
120+
mcpp stages the **same** std module its own build uses, keyed on
121+
(toolchain × standard × dialect) — so for an ordinary build this costs
122+
nothing, the artifact is already there. A cross build (`--target …`) pays for
123+
one extra std module, because `build.mcpp` compiles and runs on the *host*
124+
while the project targets something else.
125+
126+
`#include` still works and stays the right choice for a program that only
127+
needs `std::fopen`; there is no requirement to modularize a build script.
128+
129+
> **Not yet under MSVC.** Named modules with `cl.exe` go through `.ifc` +
130+
> `/reference`, a pipeline mcpp has not wired up. A `build.mcpp` using
131+
> `import mcpp;` or `import std;` under a native MSVC toolchain fails with an
132+
> explicit message telling you to use `#include` or a GCC/Clang toolchain —
133+
> `#include`-based programs are fully supported there.
105134
106135
## Environment contract (mcpp 0.0.95+)
107136

docs/zh/03-toolchains.md

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,19 @@ Available toolchains (run `mcpp toolchain install <family> <version>`):
115115

116116
## Windows PE 之 MinGW-w64(`x86_64-windows-gnu`,无需 Visual Studio)
117117

118+
**没装 Visual Studio 时,这就是 Windows 上的默认值。** Windows 自带的只有
119+
UCRT 运行时 DLL,MSVC STL 与 Windows SDK 都只随 Visual Studio 的
120+
"Desktop development with C++" 负载安装。而 llvm 在 Windows 上打的是 MSVC ABI,
121+
两者都需要,所以 mcpp 首跑时会探测机器上是否有可用的 MSVC(STL **** SDK
122+
两件齐——只有一半才是真正的坑),探不到就落到这里,并把选择持久化,之后的
123+
构建不再重复提示。无需任何安装或配置。
124+
125+
同一道检查也会修复既有配置:如果 mcpp 早先自己选定的 `[toolchain] default`
126+
在这台机器上已经不可用,它会被就地改写,并打印一行说明。但**用户在
127+
`mcpp.toml` 里显式写下的** `[toolchain]`(或 `[target.X].toolchain`)永远不会
128+
被推翻——一个需要 MSVC ABI 去链接 vcpkg 预编译 `.lib` 的工程,得到的是一条
129+
指明替代方案的错误,而不是被静默换掉 ABI。
130+
118131
mcpp 里 "MinGW" 是一个 **target**,不是工具链名:`x86_64-windows-gnu`
119132
——GCC 产出 Windows PE(GNU CRT)。两种宿主用同一个身份、同一条命令;
120133
由哪个自包含 payload 来承接是自动分流的(Windows 宿主 → winlibs UCRT
@@ -130,7 +143,13 @@ mcpp toolchain default gcc@16 --target x86_64-windows-gnu
130143
它走常规的 GCC 模块管线(`gcm.cache`、经 libstdc++ `bits/std.cc`
131144
`import std`)。该 target 默认 linkage 为 **static**——产出的 `.exe`
132145
完全自包含(无需随包分发 `libstdc++-6.dll`,可直接在 wine 下运行);
133-
`[build] linkage = "dynamic"` 可退出。
146+
要退出请写在 target 段上——`linkage` 只认精确 triple(见
147+
[mcpp.toml](05-mcpp-toml.md) §2.7),`[build] linkage` 这个键并不存在,写了会被静默忽略:
148+
149+
```toml
150+
[target.x86_64-windows-gnu]
151+
linkage = "dynamic"
152+
```
134153

135154
manifest 中:
136155

docs/zh/07-build-mcpp.md

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ manifest/描述符里(`[build] include_dirs`),而不是构建期程序里。
6262

6363
## 类型化 API:`import mcpp;`(推荐)
6464

65-
除了打印裸字符串,你还可以把 `build.mcpp` 写成**模块优先**——`import mcpp;`,
66-
`#include`、无 `import std;``mcpp` 模块**内置在 mcpp 二进制里**(因此永远和你这版 mcpp
65+
除了打印裸字符串,你还可以把 `build.mcpp` 写成**模块优先**——`import mcpp;`,不需要
66+
`#include``mcpp` 模块**内置在 mcpp 二进制里**(因此永远和你这版 mcpp
6767
的协议匹配),按需编译;它的函数只是 emit 上面那些指令:
6868

6969
```cpp
@@ -91,9 +91,36 @@ int main() {
9191
| `mcpp::include_dir(d)` / `mcpp::include_dir_after(d)` | `mcpp:include-dir=` / `mcpp:include-dir-after=` |
9292
| `mcpp::rerun_if_changed(p)` / `mcpp::rerun_if_env_changed(v)` | 对应的 `rerun-*` 指令 |
9393

94-
如果 `build.mcpp` 还需要**生成文件,混入一个文本 `#include <fstream>` 即可——这没问题,
95-
只有 `import std;` 是不必要的。上面的裸 stdout 协议仍是底层基底;`import mcpp;` 是其上的
96-
类型化层。
94+
上面的裸 stdout 协议仍是底层基底;`import mcpp;` 是其上的类型化层。
95+
96+
### `import std;`(mcpp 2026.8.2.1+)
97+
98+
`build.mcpp` 可以 `import std;`(以及 `import std.compat;`),单用或与
99+
`import mcpp;` 并用皆可:
100+
101+
```cpp
102+
// build.mcpp
103+
import std;
104+
import mcpp;
105+
106+
int main() {
107+
for (auto const& f : std::vector<std::string>{"FOO", "BAR"})
108+
mcpp::define(f.c_str());
109+
}
110+
```
111+
112+
mcpp 会把它自己构建时用的**同一份** std 模块暂存过来,缓存键是
113+
(工具链 × 标准 × 方言)——所以普通构建下这是零成本,产物本来就在。只有交叉构建
114+
(`--target …`)才会多编一份:`build.mcpp`**宿主**上编译并运行,而工程的目标
115+
是别的平台。
116+
117+
`#include` 依然有效,对只需要 `std::fopen` 的程序也依然是更合适的选择——构建脚本
118+
没有必须模块化的要求。
119+
120+
> **MSVC 下尚不支持。** `cl.exe` 的具名模块走 `.ifc` + `/reference`,这条管线 mcpp
121+
> 还没接。在原生 MSVC 工具链下使用 `import mcpp;``import std;``build.mcpp`
122+
> 会得到一条明确的报错,告诉你改用 `#include` 或换 GCC/Clang 工具链——基于
123+
> `#include` 的程序在那里是完全支持的。
97124
98125
## 环境契约(mcpp 0.0.95+)
99126

src/build/prepare.cppm

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1508,7 +1508,8 @@ prepare_build(bool print_fingerprint,
15081508
// dynamic-musl binaries depend on a system /lib/ld-musl-x86_64.so.1
15091509
// that most distros don't ship. Default linkage to "static" when
15101510
// the resolved toolchain is musl, unless the user has already opted
1511-
// out via [build].linkage / [target.<triple>].linkage.
1511+
// out via `--static` or [target.<triple>].linkage. (There is no
1512+
// [build].linkage — the parser only reads it under a target section.)
15121513
if (isMuslTc && m->buildConfig.linkage.empty()) {
15131514
m->buildConfig.linkage = "static";
15141515
}

0 commit comments

Comments
 (0)