Skip to content

Commit f19911f

Browse files
committed
Keep __CYGWIN__/__CYGWIN32__ defined under the posix realisation
Design revision from the openkal-musl spike. The Cygwin-flavoured Windows realisation no longer undefines __CYGWIN__/__CYGWIN32__ -- the -U tokens are dropped, everything else (the --target= substitution, LP64, no _WIN32, -fno-short-wchar for wchar=32) is unchanged. The reason, stated once and correctly (an earlier private draft of this change mis-attributed the cause to libunwind's own branch selection; that was wrong on inspection of the vendored source and is not repeated here): third-party portable code that needs to know the OBJECT FORMAT -- distinct from the C environment and from the platform API -- has no other name for "PE format with a POSIX-presenting C environment" than __CYGWIN__, and such code cannot be patched the way this ecosystem's own packages can. This is a trade-off for the 30-member measurement to settle: a library reaching for __CYGWIN__ may also reach for a real Cygwin interface that does not exist here, and if defining it produces more new failures than it fixes, the answer flips. Updated: the realisation table and its verification expectations (src/toolchain/cenv.cppm), the unit test and e2e assertions (now checking __CYGWIN__ stays DEFINED, the opposite of the first version), and the English + zh docs/CHANGELOG prose.
1 parent 2a33e7e commit f19911f

6 files changed

Lines changed: 95 additions & 23 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,18 @@
2222
(`modules/manifest/src/{targetside_model,toml,types}.cppm`,单测 `test_manifest.cpp`)
2323
- **实现(realisation)**:新模块 `mcpp.toolchain.cenv` 保存「请求 → 三元组与开关」的映射
2424
——通用知识,不含包名。Windows 上 `presents = "posix", data-model = "arch-default"`
25-
采用 Cygwin 式语义,仅在编译行把 `--target=` 换成 `x86_64-pc-cygwin` 并去掉
26-
`__CYGWIN__`/`__CYGWIN32__`;链接行保持图解析出的三元组不变,因为两个三元组生成的机器码
27-
实测完全一致(PE、Win64 调用约定、SEH)。无法满足的请求明确拒绝,点名目标、请求与缺什么。
28-
`[package] c-environment = "platform"` 让一个包(如 openkal-windows)的自身单元退出这项
29-
实现,继续按三元组自身的默认环境编译。(`src/toolchain/cenv.cppm`,单测 `test_cenv.cpp`)
25+
采用 Cygwin 式语义,仅在编译行把 `--target=` 换成 `x86_64-pc-cygwin`;链接行保持图解析出
26+
的三元组不变,因为两个三元组生成的机器码实测完全一致(PE、Win64 调用约定、SEH)。
27+
无法满足的请求明确拒绝,点名目标、请求与缺什么。`[package] c-environment = "platform"`
28+
让一个包(如 openkal-windows)的自身单元退出这项实现,继续按三元组自身的默认环境编译。
29+
(`src/toolchain/cenv.cppm`,单测 `test_cenv.cpp`)
30+
- **`__CYGWIN__`/`__CYGWIN32__` 保持定义,经 openkal-musl 尖峰实验修订。** 最初的实现
31+
取消定义它们(理由是图里没有真正的 Cygwin 用户态)。第三方可移植代码里需要知道**目标文件
32+
格式**——不是 C 环境,也不是平台 API——的那部分,没有别的名字能指代「PE 格式 + 呈现
33+
POSIX 的 C 环境」这一组合,只有 `__CYGWIN__`;这样的代码不像本生态自己的包那样可以打
34+
补丁。这是一项留给 30 个成员那轮实测去判定的权衡,不是已经定论的事实:一个库伸手去够
35+
`__CYGWIN__`,也可能伸手去够一个这里并不存在的真正 Cygwin 接口——如果定义它带来的新
36+
失败比修好的还多,结论就会翻过来。(`src/toolchain/cenv.cppm`)
3037
- **声明被校验,不被信任**:新模块 `mcpp.toolchain.cenv_probe` 用最终参数编译一次纯预处理
3138
探针(`-E -dM`,不执行、不需要目标可在本机运行),核对 `__SIZEOF_LONG__`、
3239
`__SIZEOF_WCHAR_T__` 与环境身份宏是否与声明相符,不符即失败并同时打印声明值与实测值;

‎docs/22-target-side.md‎

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ flags, generic knowledge that names no C library:
351351
|---|---|---|
352352
| Linux | `posix` / `arch-default` | the default triple already satisfies it |
353353
| macOS | `posix` / `arch-default` | the default triple already satisfies it |
354-
| Windows | `posix` / `arch-default` | Cygwin-flavoured: `--target=x86_64-pc-cygwin` on the compile line only, `-U__CYGWIN__ -U__CYGWIN32__` (those interfaces are not in this graph); `data-model` becomes LP64 as a consequence of the triple, not a separate flag |
354+
| Windows | `posix` / `arch-default` | Cygwin-flavoured: `--target=x86_64-pc-cygwin` on the compile line only; `__CYGWIN__`/`__CYGWIN32__` are left defined (see the note below); `data-model` becomes LP64 as a consequence of the triple, not a separate flag |
355355
| any | `builtins = "iso"` | turns off code-generation idioms that assume a platform C library — `-fno-builtin-memset_pattern16` on Apple targets is the one this survey measured; see `src/toolchain/cenv.cppm` for what else was checked and found not to apply |
356356
| anything else | | refused, naming the target, the request and what is missing — never a silent downgrade |
357357

@@ -362,6 +362,21 @@ preprocessor sees and how wide `long` is. Realisation therefore touches only
362362
the **compile** line; the **link** line keeps the triple the graph resolved,
363363
because nothing about the object format changed.
364364

365+
**`__CYGWIN__`/`__CYGWIN32__` are left defined — a revision from the
366+
openkal-musl spike, not the design's original claim.** Undefining them was
367+
tried first, on the reasoning that a real Cygwin userland is not in the
368+
graph. Portable third-party code that needs to know the **object format** —
369+
not the C environment, not the platform API — has no name for "PE format
370+
with a POSIX-presenting C environment" other than `__CYGWIN__`, and such code
371+
cannot be patched the way this ecosystem's own packages can. `presents =
372+
"posix"` answers one question, which environment-identity macros source
373+
sees; it does not get to answer a different one, what object format this is,
374+
by deleting the only macro that names it. This is a **trade-off for the
375+
30-member measurement to settle, not a settled fact**: a library reaching for
376+
`__CYGWIN__` may also reach for a real Cygwin interface (`sys/cygwin.h`,
377+
`cygwin_conv_path`) that does not exist here, and if defining it produces
378+
more new failures than it fixes, the answer flips.
379+
365380
**A package's own units can opt out.** A package that provides
366381
`mcpp:kernel-abi=openkal` (openkal-windows, say) has to see the platform's
367382
own environment — it includes platform declarations and `_WIN32` must be

‎docs/zh/22-target-side.md‎

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,14 +292,24 @@ builtins = "iso" # iso | platform(默认 platform)
292292
|---|---|---|
293293
| Linux | `posix` / `arch-default` | 默认三元组已经满足 |
294294
| macOS | `posix` / `arch-default` | 默认三元组已经满足 |
295-
| Windows | `posix` / `arch-default` | 采用 Cygwin 式语义:仅在编译行加 `--target=x86_64-pc-cygwin`,加 `-U__CYGWIN__ -U__CYGWIN32__`(这些接口不在本图里);`data-model` 变为 LP64 是三元组切换的结果,不是另一个开关 |
295+
| Windows | `posix` / `arch-default` | 采用 Cygwin 式语义:仅在编译行加 `--target=x86_64-pc-cygwin`;`__CYGWIN__`/`__CYGWIN32__` 保持定义(见下方说明);`data-model` 变为 LP64 是三元组切换的结果,不是另一个开关 |
296296
| 任意目标 | `builtins = "iso"` | 关闭代码生成阶段假定平台 C 库在场的惯用法识别——本轮实测到的唯一一例是 Apple 目标上的 `-fno-builtin-memset_pattern16`;`src/toolchain/cenv.cppm` 记录了还核实过哪些、结论是不适用 |
297297
| 其余情况 | | 明确拒绝,点名目标、请求与缺什么——不静默降级 |
298298

299299
Windows 一行是旗舰情形:`x86_64-w64-windows-gnu` 与 `x86_64-pc-cygwin` 生成的机器码完全一致——
300300
同样的 PE 格式、同样的 Win64 调用约定、同样的 SEH——差别只在预处理器看到什么、`long` 有多宽。
301301
因此实现只触及**编译**行;**链接**行保持图解析出的三元组,因为目标文件格式没有变化。
302302

303+
**`__CYGWIN__`/`__CYGWIN32__` 保持定义——这是 openkal-musl 尖峰实验带来的修订,不是设计
304+
最初的陈述。** 最初试过取消定义它们,理由是图里没有真正的 Cygwin 用户态。第三方可移植
305+
代码里,需要知道**目标文件格式**——不是 C 环境,也不是平台 API——的那部分,没有别的名字
306+
能指代「PE 格式加呈现 POSIX 的 C 环境」这个组合,只有 `__CYGWIN__`;这样的代码不像本生态
307+
自己的包那样可以打补丁。`presents = "posix"` 回答的是一个问题——源码看到哪些环境身份宏;
308+
它不能顺带删掉唯一能回答另一个问题——这是什么目标文件格式——的宏。这是一项**留给 30 个
309+
成员那轮实测去判定的权衡,不是已经定论的事实**:一个库伸手去够 `__CYGWIN__`,也可能伸手
310+
去够一个这里并不存在的真正 Cygwin 接口(`sys/cygwin.h`、`cygwin_conv_path`)——如果定义它
311+
带来的新失败比修好的还多,结论就会翻过来。
312+
303313
**包可以让自己的单元退出。** 提供 `mcpp:kernel-abi=openkal` 的包(比如 openkal-windows)必须
304314
看到平台自身的环境——它要 include 平台声明,`_WIN32` 对它必须为真。这样的包,或者一个平台
305315
shim,声明:

‎src/toolchain/cenv.cppm‎

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@
1919
// Linux posix / arch-default the default triple already satisfies it
2020
// macOS posix / arch-default the default triple already satisfies it
2121
// Windows posix / arch-default Cygwin-flavoured: `--target=x86_64-pc-cygwin`,
22-
// `-U__CYGWIN__ -U__CYGWIN32__` (those interfaces
23-
// are not in the graph)
22+
// `__CYGWIN__`/`__CYGWIN32__` STAY DEFINED (see
23+
// the note below the table — this is a design
24+
// revision, not the original §3.3 text)
2425
// * builtins = iso turn off the platform-C-library idioms the
2526
// code generator assumes (§3.2.1) — Apple's
2627
// `memset_pattern16` is the one measured case
@@ -43,6 +44,25 @@
4344
// same Win64 argument placement — so an object compiled under the Cygwin
4445
// identity links exactly like one compiled under the MinGW one. Only the
4546
// preprocessor saw a different environment; the linker never has to know.
47+
//
48+
// `__CYGWIN__`/`__CYGWIN32__` ARE NOT REMOVED, AND THE FIRST VERSION OF THIS
49+
// MODULE GOT THAT WRONG. §3.3's original text called for `-U__CYGWIN__
50+
// -U__CYGWIN32__` on the reasoning that a real Cygwin userland is not in the
51+
// graph. A first reading of the openkal-musl spike's libunwind build failure
52+
// blamed a missing `__CYGWIN__` branch in libunwind itself; reading the
53+
// vendored source shows that is wrong — upstream libunwind has no such
54+
// branch, so defining it there would have changed nothing (the actual break
55+
// was a downstream package selecting on `_WIN32` and is being fixed there).
56+
// The reason to keep them defined is narrower and still real: third-party
57+
// portable code that has to know the OBJECT FORMAT — as opposed to which C
58+
// environment or which platform API — has no name for "PE format with a
59+
// POSIX-presenting C environment" other than `__CYGWIN__`, and such code
60+
// cannot be patched the way this ecosystem's own packages can. The cost is
61+
// symmetric: a library that reaches for `__CYGWIN__` may also reach for a
62+
// real Cygwin interface (`sys/cygwin.h`, `cygwin_conv_path`) that does not
63+
// exist here. This is a TRADE-OFF for the 30-member measurement to settle —
64+
// if defining it produces more new failures than it fixes, the answer flips
65+
// — not a fact this module is asserting as closed.
4666
export module mcpp.toolchain.cenv;
4767

4868
import std;
@@ -203,18 +223,16 @@ inline std::expected<Realisation, std::string> realise(
203223
"on x86_64 only; this arch has no verified "
204224
"substitute triple");
205225
// `--target=x86_64-pc-cygwin`, on the COMPILE line only (module
206-
// header above). `-U__CYGWIN__`/`-U__CYGWIN32__`: the Cygwin
207-
// triple predefines them, and they are not withheld by the
208-
// identity switch itself — they name a real Cygwin userland
209-
// (`sys/cygwin.h`, `cygwin_conv_path`) that is not in this
210-
// graph, so a library probing for them would be steered toward
211-
// an interface openkal-musl does not implement (design §3.3).
226+
// header above). `__CYGWIN__`/`__CYGWIN32__` are LEFT AS THE
227+
// TRIPLE SUBSTITUTION DEFINES THEM — not undefined (see the
228+
// module header's note: portable third-party code that needs to
229+
// know the object format has no other name for "PE format,
230+
// POSIX-presenting environment", and this is a trade-off for the
231+
// 30-member measurement, not a settled fact).
212232
r.tokens.push_back("--target=x86_64-pc-cygwin");
213-
r.tokens.push_back("-U__CYGWIN__");
214-
r.tokens.push_back("-U__CYGWIN32__");
215233
r.expectDefined.push_back("__unix__");
234+
r.expectDefined.push_back("__CYGWIN__");
216235
r.expectUndefined.push_back("_WIN32");
217-
r.expectUndefined.push_back("__CYGWIN__");
218236
cygwinIdentity = true;
219237
} else if (decl.presents == CAbiPresents::Windows) {
220238
// Already the base triple's own identity — nothing to add.

‎tests/e2e/741_c_abi_environment_declared_by_the_c_library.sh‎

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,13 +128,25 @@ def joined(argv_iter):
128128
129129
# The ordinary consumer (main.cpp, package "cabi-probe") is target-side and
130130
# does NOT declare c-environment: it must carry the Cygwin-flavoured tokens.
131+
#
132+
# `__CYGWIN__`/`__CYGWIN32__` are NOT undefined (design revision from the
133+
# openkal-musl spike: third-party portable code that needs to know the
134+
# object format -- not the C environment, not the platform API -- has no
135+
# other name for "PE format, POSIX-presenting environment", and such code
136+
# cannot be patched the way this ecosystem's own packages can; a trade-off
137+
# for the 30-member measurement to settle, not a settled fact) -- so this
138+
# asserts `-U__CYGWIN__` is ABSENT from the command line, the opposite of an
139+
# earlier version of this test.
131140
consumer = joined(args_for("main.cpp"))
132-
missing = [tok for tok in ("--target=x86_64-pc-cygwin", "-U__CYGWIN__", "-U__CYGWIN32__",
133-
"-fno-short-wchar")
141+
missing = [tok for tok in ("--target=x86_64-pc-cygwin", "-fno-short-wchar")
134142
if tok not in consumer]
135143
if missing:
136144
print(f"FAIL: ordinary package is missing realised tokens {missing}\n args: {consumer}")
137145
sys.exit(1)
146+
present = [tok for tok in ("-U__CYGWIN__", "-U__CYGWIN32__") if tok in consumer]
147+
if present:
148+
print(f"FAIL: __CYGWIN__/__CYGWIN32__ must stay defined, but found {present}\n args: {consumer}")
149+
sys.exit(1)
138150
139151
# fakemusl's OWN units get them too -- the environment applies to the C
140152
# library itself, not only to its consumers.

‎tests/unit/test_cenv.cpp‎

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,20 +53,30 @@ TEST(CEnv, MacosPosixArchDefaultIsANoOp) {
5353
}
5454

5555
// The flagship case: Cygwin-flavoured Windows.
56+
//
57+
// `__CYGWIN__`/`__CYGWIN32__` STAY DEFINED — a design revision from the
58+
// openkal-musl spike, not the original §3.3 text. Third-party portable code
59+
// that needs to know the OBJECT FORMAT (as opposed to the C environment or
60+
// the platform API) has no name for "PE format, POSIX-presenting
61+
// environment" other than `__CYGWIN__`, and such code cannot be patched the
62+
// way this ecosystem's own packages can. This is a trade-off for the
63+
// 30-member measurement to settle, not a settled fact: the cost is that a
64+
// library reaching for `__CYGWIN__` may also reach for a real Cygwin
65+
// interface that does not exist here.
5666
TEST(CEnv, WindowsPosixArchDefaultSubstitutesTheCygwinTriple) {
5767
auto d = decl(ts::CAbiPresents::Posix, ts::CAbiDataModel::ArchDefault, 32);
5868
auto r = cenv::realise(d, "windows", "x86_64", false);
5969
ASSERT_TRUE(r.has_value()) << r.error();
6070
EXPECT_TRUE(has(r->tokens, "--target=x86_64-pc-cygwin"));
61-
EXPECT_TRUE(has(r->tokens, "-U__CYGWIN__"));
62-
EXPECT_TRUE(has(r->tokens, "-U__CYGWIN32__"));
71+
EXPECT_FALSE(has(r->tokens, "-U__CYGWIN__"));
72+
EXPECT_FALSE(has(r->tokens, "-U__CYGWIN32__"));
6373
// wchar 32 differs from Cygwin's own default (16) — the flag is added.
6474
EXPECT_TRUE(has(r->tokens, "-fno-short-wchar"));
6575
EXPECT_EQ(r->expectLongBytes, 8);
6676
EXPECT_EQ(r->expectWcharBits, 32);
6777
ASSERT_TRUE(has(r->expectDefined, "__unix__"));
78+
ASSERT_TRUE(has(r->expectDefined, "__CYGWIN__"));
6879
ASSERT_TRUE(has(r->expectUndefined, "_WIN32"));
69-
ASSERT_TRUE(has(r->expectUndefined, "__CYGWIN__"));
7080
}
7181

7282
// wchar = 16 on the Cygwin substitution matches Cygwin's own default, so no

0 commit comments

Comments
 (0)