Skip to content

Commit 38625e8

Browse files
Add CP936 CI verification for Windows /utf-8 fix
1 parent 97ea086 commit 38625e8

3 files changed

Lines changed: 158 additions & 3 deletions

File tree

.github/workflows/ci.yml

Lines changed: 66 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: CI
22

33
on:
44
push:
5-
branches: [main]
5+
branches: [main, cp936-ci-verification]
66
pull_request:
77
branches: [main]
88

@@ -79,9 +79,72 @@ jobs:
7979
- uses: subosito/flutter-action@v2
8080
with:
8181
channel: stable
82-
- name: Verify /utf-8 is set for MSVC
82+
- uses: ilammy/msvc-dev-cmd@v1
83+
84+
- name: Verify /utf-8 is set for MSVC in windows/CMakeLists.txt
8385
shell: bash
8486
run: |
8587
grep -q '/utf-8' windows/CMakeLists.txt \
8688
|| { echo "::error::windows/CMakeLists.txt is missing /utf-8 — see chinese-pc-compat.md"; exit 1; }
87-
- run: cd example && flutter build windows
89+
90+
- name: Verify ci/cp936_repro.cpp covers every non-ASCII char in windows/
91+
shell: bash
92+
run: python ci/check_unicode_inventory.py
93+
94+
- name: Verify ci/cp936_repro.cpp has no UTF-8 BOM
95+
shell: bash
96+
run: |
97+
bom=$(head -c 3 ci/cp936_repro.cpp | od -An -tx1 | tr -d ' \n')
98+
if [ "$bom" = "efbbbf" ]; then
99+
echo "::error::ci/cp936_repro.cpp must not have a BOM (MSVC would auto-detect UTF-8 and bypass /source-charset:.936)"
100+
exit 1
101+
fi
102+
echo "OK: no BOM"
103+
104+
- name: CP936 simulation without /utf-8 must fail with C4819/C2220
105+
shell: cmd
106+
run: |
107+
cl /c /WX /source-charset:.936 /execution-charset:.936 /nologo ci\cp936_repro.cpp > cl.log 2>&1
108+
set CL_EXIT=%errorlevel%
109+
type cl.log
110+
if %CL_EXIT% equ 0 (
111+
echo ::error::Expected C4819/C2220 but compile succeeded — CP936 simulation is not triggering the bug
112+
exit /b 1
113+
)
114+
findstr /c:"C4819" cl.log >nul
115+
if errorlevel 1 (
116+
echo ::error::cl.exe failed but did not emit C4819 — test is not reproducing the real bug
117+
exit /b 1
118+
)
119+
findstr /c:"C2220" cl.log >nul
120+
if errorlevel 1 (
121+
echo ::error::cl.exe failed but did not emit C2220 — /WX promotion is not working as expected
122+
exit /b 1
123+
)
124+
echo OK: CP936 simulation reproduced C4819/C2220 as expected
125+
exit /b 0
126+
127+
- name: CP936 simulation with /utf-8 must succeed (proves the fix)
128+
shell: cmd
129+
run: cl /c /WX /source-charset:.936 /execution-charset:.936 /utf-8 /nologo ci\cp936_repro.cpp
130+
131+
- name: Build the example (generates the plugin vcxproj)
132+
run: cd example && flutter build windows
133+
134+
- name: Verify /utf-8 is threaded into the generated plugin vcxproj
135+
shell: bash
136+
run: |
137+
vcxproj=$(find example/build/windows -name 'camera_desktop_plugin.vcxproj' | head -n1)
138+
if [ -z "$vcxproj" ]; then
139+
echo "::error::camera_desktop_plugin.vcxproj not found under example/build/windows"
140+
find example/build/windows -name '*.vcxproj' || true
141+
exit 1
142+
fi
143+
echo "Inspecting: $vcxproj"
144+
if ! grep -q '/utf-8' "$vcxproj"; then
145+
echo "::error::/utf-8 missing from $vcxproj — CMake did not thread the flag through"
146+
echo "--- vcxproj contents ---"
147+
cat "$vcxproj"
148+
exit 1
149+
fi
150+
echo "OK: /utf-8 present in $vcxproj"

ci/check_unicode_inventory.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
#!/usr/bin/env python3
2+
"""Assert that every non-ASCII character appearing in windows/*.cpp|*.h also
3+
appears in ci/cp936_repro.cpp.
4+
5+
Purpose: the CP936 simulation step in CI compiles cp936_repro.cpp to prove that
6+
/utf-8 resolves C4819 for the exact character set we use. If a new file adds a
7+
new Unicode character (e.g. a µ in a comment) without updating the synthetic
8+
repro, CI would silently keep passing while real Simplified-Chinese Windows
9+
hosts would start failing again.
10+
11+
Run from the repo root. Exits non-zero with a clear message if drift is found.
12+
"""
13+
14+
from __future__ import annotations
15+
16+
import pathlib
17+
import sys
18+
19+
20+
def non_ascii_chars(path: pathlib.Path) -> set[str]:
21+
return {c for c in path.read_text(encoding="utf-8") if ord(c) > 0x7F}
22+
23+
24+
def main() -> int:
25+
windows_sources = sorted(
26+
list(pathlib.Path("windows").glob("*.cpp"))
27+
+ list(pathlib.Path("windows").glob("*.h"))
28+
)
29+
if not windows_sources:
30+
print("::error::No windows/*.cpp|*.h files found — run from repo root.")
31+
return 1
32+
33+
real: set[str] = set()
34+
per_file: dict[str, set[str]] = {}
35+
for f in windows_sources:
36+
chars = non_ascii_chars(f)
37+
if chars:
38+
per_file[str(f)] = chars
39+
real |= chars
40+
41+
synthetic_path = pathlib.Path("ci/cp936_repro.cpp")
42+
if not synthetic_path.exists():
43+
print(f"::error::{synthetic_path} missing.")
44+
return 1
45+
46+
synthetic = non_ascii_chars(synthetic_path)
47+
48+
missing = real - synthetic
49+
if missing:
50+
print("::error::ci/cp936_repro.cpp is missing characters used in windows/ sources.")
51+
print("Missing:")
52+
for c in sorted(missing):
53+
sources = [f for f, cs in per_file.items() if c in cs]
54+
print(f" U+{ord(c):04X} {c!r} (in: {', '.join(sources)})")
55+
print()
56+
print("Fix: add these characters to ci/cp936_repro.cpp so the CP936 CI")
57+
print("simulation stays representative of the real sources.")
58+
return 1
59+
60+
print(f"OK: all {len(real)} non-ASCII chars in windows/ are covered by {synthetic_path}.")
61+
for c in sorted(real):
62+
print(f" U+{ord(c):04X} {c}")
63+
return 0
64+
65+
66+
if __name__ == "__main__":
67+
sys.exit(main())

ci/cp936_repro.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Synthetic CP936-reproduction file for CI.
2+
//
3+
// This file deliberately contains every non-ASCII character that appears in
4+
// windows/*.cpp and windows/*.h, so that compiling it with
5+
// cl /c /WX /source-charset:.936 /execution-charset:.936
6+
// reproduces the exact C4819 / C2220 failure that users see on Simplified
7+
// Chinese Windows hosts (where GetACP() == 936 / GBK).
8+
//
9+
// DO NOT add a BOM to this file. With a BOM, MSVC auto-detects UTF-8 and
10+
// ignores /source-charset:.936, which would defeat the test.
11+
//
12+
// If you add a new non-ASCII character anywhere under windows/, the CI step
13+
// `ci/check_unicode_inventory.py` will fail until you add that character
14+
// here. Keep the inventory below in sync.
15+
//
16+
// Covered characters (also listed explicitly so a byte-level grep for the
17+
// UTF-8 sequences finds them here):
18+
// U+2026 HORIZONTAL ELLIPSIS …
19+
// U+2192 RIGHTWARDS ARROW →
20+
// U+2194 LEFT RIGHT ARROW ↔
21+
// U+2264 LESS-THAN OR EQUAL TO ≤
22+
// U+2500 BOX DRAWINGS LIGHT HORIZONTAL ─
23+
//
24+
// No code needed — /c (compile only) is sufficient to trigger C4819 on the
25+
// comment bytes above.

0 commit comments

Comments
 (0)