Skip to content

Commit 7eb0057

Browse files
Fix Windows build on non-UTF8 system code pages by compiling MSVC targets with /utf-8 (#2)
1 parent 7a487cf commit 7eb0057

29 files changed

Lines changed: 913 additions & 123 deletions

.github/workflows/ci.yml

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,81 @@ jobs:
7979
- uses: subosito/flutter-action@v2
8080
with:
8181
channel: stable
82-
- run: cd example && flutter build windows
82+
- uses: ilammy/msvc-dev-cmd@v1
83+
84+
- name: Verify /utf-8 is set for MSVC in windows/CMakeLists.txt
85+
shell: bash
86+
run: |
87+
grep -q '/utf-8' windows/CMakeLists.txt \
88+
|| { echo "::error::windows/CMakeLists.txt is missing /utf-8 — see chinese-pc-compat.md"; exit 1; }
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+
# Note: MSVC refuses `/source-charset:.936` together with `/utf-8`
128+
# (error D8016: options are incompatible). On a real Chinese Windows
129+
# host, CP936 is NOT a flag — it's an implicit default from GetACP().
130+
# `/utf-8` overrides that implicit default. We prove the fix in two
131+
# independent invocations: the previous step shows CP936 is hostile
132+
# to our bytes; this step shows `/utf-8` makes MSVC read them as UTF-8
133+
# and compile cleanly with /WX. Together they imply that on a real
134+
# CP936 host, adding `/utf-8` switches MSVC from CP936-mode (fail)
135+
# to UTF-8-mode (pass).
136+
- name: Compile with /utf-8 must succeed (proves /utf-8 resolves our chars)
137+
shell: cmd
138+
run: cl /c /WX /utf-8 /nologo ci\cp936_repro.cpp
139+
140+
- name: Build the example (generates the plugin vcxproj)
141+
run: cd example && flutter build windows
142+
143+
- name: Verify /utf-8 is threaded into the generated plugin vcxproj
144+
shell: bash
145+
run: |
146+
vcxproj=$(find example/build/windows -name 'camera_desktop_plugin.vcxproj' | head -n1)
147+
if [ -z "$vcxproj" ]; then
148+
echo "::error::camera_desktop_plugin.vcxproj not found under example/build/windows"
149+
find example/build/windows -name '*.vcxproj' || true
150+
exit 1
151+
fi
152+
echo "Inspecting: $vcxproj"
153+
if ! grep -q '/utf-8' "$vcxproj"; then
154+
echo "::error::/utf-8 missing from $vcxproj — CMake did not thread the flag through"
155+
echo "--- vcxproj contents ---"
156+
cat "$vcxproj"
157+
exit 1
158+
fi
159+
echo "OK: /utf-8 present in $vcxproj"

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1+
## 1.1.3
2+
3+
* Fix Windows build failure (C4819 / C2220) on hosts with a non-UTF-8 system code page (e.g. CP936 on Simplified Chinese Windows) by compiling the plugin with `/utf-8` under MSVC (#2)
4+
5+
## 1.1.2
6+
7+
* Fix macOS build failure on Xcode 26+ by removing unavailable `AVCaptureSessionInterruptionReasonKey` (re-introduced in 1.1.1)
8+
* Fix Windows build failure caused by implicit `wchar_t` to `char` conversion in debug logging
9+
10+
## 1.1.1
11+
12+
* Add comprehensive diagnostic logging across all platforms (Linux, macOS, Windows)
13+
* Log device enumeration, backend selection, pipeline construction, resolution selection, recording lifecycle, and error paths
14+
115
## 1.1.0
216

317
* Add PipeWire camera portal support for Flatpak sandbox compatibility on Linux

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ Add `camera_desktop` alongside `camera` in your `pubspec.yaml`:
3030
```yaml
3131
dependencies:
3232
camera: ^0.11.0
33-
camera_desktop: ^1.1.0
33+
camera_desktop: ^1.1.2
3434
```
3535
3636
That's it. All three desktop platforms are covered, no additional packages needed.

ci/check_unicode_inventory.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
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+
# Windows runners default stdout to CP1252, which cannot encode characters
20+
# like → or ↔. Force UTF-8 so the diagnostic prints below never raise.
21+
try:
22+
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
23+
except Exception:
24+
pass
25+
26+
27+
def non_ascii_chars(path: pathlib.Path) -> set[str]:
28+
return {c for c in path.read_text(encoding="utf-8") if ord(c) > 0x7F}
29+
30+
31+
def main() -> int:
32+
windows_sources = sorted(
33+
list(pathlib.Path("windows").glob("*.cpp"))
34+
+ list(pathlib.Path("windows").glob("*.h"))
35+
)
36+
if not windows_sources:
37+
print("::error::No windows/*.cpp|*.h files found — run from repo root.")
38+
return 1
39+
40+
real: set[str] = set()
41+
per_file: dict[str, set[str]] = {}
42+
for f in windows_sources:
43+
chars = non_ascii_chars(f)
44+
if chars:
45+
per_file[str(f)] = chars
46+
real |= chars
47+
48+
synthetic_path = pathlib.Path("ci/cp936_repro.cpp")
49+
if not synthetic_path.exists():
50+
print(f"::error::{synthetic_path} missing.")
51+
return 1
52+
53+
synthetic = non_ascii_chars(synthetic_path)
54+
55+
missing = real - synthetic
56+
if missing:
57+
print("::error::ci/cp936_repro.cpp is missing characters used in windows/ sources.")
58+
print("Missing:")
59+
for c in sorted(missing):
60+
sources = [f for f, cs in per_file.items() if c in cs]
61+
print(f" U+{ord(c):04X} {c!r} (in: {', '.join(sources)})")
62+
print()
63+
print("Fix: add these characters to ci/cp936_repro.cpp so the CP936 CI")
64+
print("simulation stays representative of the real sources.")
65+
return 1
66+
67+
print(f"OK: all {len(real)} non-ASCII chars in windows/ are covered by {synthetic_path}.")
68+
for c in sorted(real):
69+
print(f" U+{ord(c):04X} {c}")
70+
return 0
71+
72+
73+
if __name__ == "__main__":
74+
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.

example/pubspec.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ packages:
6363
path: ".."
6464
relative: true
6565
source: path
66-
version: "1.1.0"
66+
version: "1.1.3"
6767
camera_platform_interface:
6868
dependency: "direct main"
6969
description:

example/windows/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ function(APPLY_STANDARD_SETTINGS TARGET)
4141
target_compile_features(${TARGET} PUBLIC cxx_std_17)
4242
target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100")
4343
target_compile_options(${TARGET} PRIVATE /EHsc)
44+
target_compile_options(${TARGET} PRIVATE /utf-8)
4445
target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0")
4546
target_compile_definitions(${TARGET} PRIVATE "$<$<CONFIG:Debug>:_DEBUG>")
4647
endfunction()

ios/camera_desktop.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Pod::Spec.new do |s|
22
s.name = 'camera_desktop'
3-
s.version = '1.1.0'
3+
s.version = '1.1.2'
44
s.summary = 'Flutter camera plugin (iOS stub).'
55
s.description = <<-DESC
66
Flutter camera plugin for desktop platforms. iOS stub for platform declaration.

0 commit comments

Comments
 (0)