Skip to content

StreamTxDemo: fix mingw/Windows-GCC build and binary stdin#100

Merged
josephnef merged 2 commits into
OpenIPC:masterfrom
AlaEddine-Zoghlami:mingw-streamtxdemo-binary-stdin
Jun 24, 2026
Merged

StreamTxDemo: fix mingw/Windows-GCC build and binary stdin#100
josephnef merged 2 commits into
OpenIPC:masterfrom
AlaEddine-Zoghlami:mingw-streamtxdemo-binary-stdin

Conversation

@AlaEddine-Zoghlami

Copy link
Copy Markdown
Contributor

Problem

Building StreamTxDemo (the stream-link TX demo) with mingw-w64 (GCC on Windows) is broken in two independent ways. Both are mingw-only and do not affect the MSVC/vcpkg build.

1. Binary stdin not set on mingw → first frame truncated

txdemo/stream_tx_demo/main.cpp gates both the Windows includes (io.h/fcntl.h) and the _setmode(_fileno(stdin), _O_BINARY) call on #if defined(_MSC_VER). mingw/GCC defines _WIN32 but not _MSC_VER, so it falls into the POSIX #else branch and leaves stdin in text mode. A 0x1A (or CRLF) byte in the binary <u32_le len><PSDU> stream then triggers:

stream_tx_demo: short read on stdin (76/269); record truncated mid-PSDU

and the demo exits before transmitting a single frame.

2. CMake aborts under mingw (no vcpkg)

CMakeLists.txt unconditionally sets CMAKE_TOOLCHAIN_FILE to "$ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" on WIN32. With VCPKG_ROOT unset (the normal mingw / pkg-config case) this expands to the bogus /scripts/buildsystems/vcpkg.cmake and configuration fails with "Could not find toolchain file".

Fix

  • Add a __MINGW32__ || __MINGW64__ include branch (io.h + fcntl.h + unistd.h + libusb) and gate _setmode on _WIN32 — identical behavior on MSVC, now also correct on mingw.
  • Only set the vcpkg toolchain file when VCPKG_ROOT is non-empty.

Verification

mingw-w64, libusb 1.0.28 via pkg-config, real RTL8812AU. After the fix StreamTxDemo.exe reads the full binary stream and radiates every frame:

<stream-tx>TX #1 ok=1 psdu=269 total=306
...
<stream-tx>done; sent 512 PSDUs

🤖 Generated with Claude Code

Building the stream-link TX demo with mingw (GCC on Windows) was broken two ways:

1. The Windows include block (io.h/fcntl.h) and the
   `_setmode(_fileno(stdin), _O_BINARY)` call were gated on `_MSC_VER`, but
   mingw/GCC defines `_WIN32`, not `_MSC_VER`. So mingw fell into the POSIX
   `#else` branch, left stdin in TEXT mode, and a 0x1A or CRLF byte in the
   binary length-prefixed PSDU stream truncated the first record
   ("short read on stdin (76/269); record truncated mid-PSDU") before a single
   frame was transmitted. Add a `__MINGW32__/__MINGW64__` include branch
   (io.h + fcntl.h + unistd.h + libusb) and gate `_setmode` on `_WIN32`
   (identical on MSVC, now also correct on mingw).

2. CMakeLists unconditionally set CMAKE_TOOLCHAIN_FILE to
   "$ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" on WIN32; with
   VCPKG_ROOT unset (the mingw/pkg-config case) it expands to the bogus
   "/scripts/buildsystems/vcpkg.cmake" and configuration aborts. Only set it
   when VCPKG_ROOT is non-empty.

Verified on mingw-w64 (libusb 1.0.28 via pkg-config): StreamTxDemo.exe now
reads the full binary stream and radiates every frame
("TX #N ok=1 ... done; sent 512 PSDUs").

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@josephnef josephnef left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the diff against the source and CI. Both fixes are correct and minimal:

  • _setmode regate _MSC_VER_WIN32 + the __MINGW32__/__MINGW64__ include branch — correct. mingw needs io.h+fcntl.h for _setmode/_O_BINARY and unistd.h for sleep() (used at L214); pid_t and <windows.h> aren't referenced in this file, so their absence from the new branch is harmless. MSVC is unchanged — it defines both _MSC_VER and _WIN32, so it still takes the first include branch and still calls _setmode.
  • CMake vcpkg guard — correct, and correctly placed before project() (required for CMAKE_TOOLCHAIN_FILE to take effect). CI's Windows job always sets VCPKG_ROOT, so there's no CI behavior change; the only effect is the unset-VCPKG_ROOT (mingw) case, which is the intent.

One gap before merge: txdemo/stream_duplex_demo/main.cpp has the byte-for-byte identical pattern — include block gated on _MSC_VER (L38) and _setmode gated on _MSC_VER (L202–204) — and it reads the same <u32_le len><PSDU> binary stream via read_exact. So it hits the exact first-frame text-mode truncation this PR fixes for StreamTxDemo. Worse: this PR's CMake change is what makes the duplex demo buildable under mingw in the first place, so as written this PR effectively ships a buildable-but-broken StreamDuplexDemo (mingw isn't in CI, so nothing catches it).

Please mirror the same _WIN32 gate + mingw include branch into stream_duplex_demo/main.cpp so the two stdin-driven stream demos stay in lockstep.

For completeness: precoder_demo/main.cpp and txdemo/main.cpp also gate includes on _MSC_VER, but neither reads length-prefixed stdin nor calls _setmode, so they're unaffected — no change needed there.

Per review: stream_duplex_demo/main.cpp had the byte-identical _MSC_VER-gated
include block + _setmode and reads the same <u32_le len><PSDU> binary stream,
so it hit the same first-frame text-mode truncation — and this PR's CMake guard
is what makes it buildable under mingw in the first place. Mirror the
StreamTxDemo fix (__MINGW32__/__MINGW64__ include branch + _setmode gated on
_WIN32) so the two stdin-driven stream demos stay in lockstep.

Verified on mingw-w64: StreamDuplexDemo now reads all 512 length-prefixed FEC
PSDUs from stdin without truncation ("tx OpenIPC#1..#500 ok=1 ... tx EOF after 512").

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@AlaEddine-Zoghlami

Copy link
Copy Markdown
Contributor Author

Good catch — fixed in 2880c0d.

stream_duplex_demo/main.cpp had the identical _MSC_VER-gated include block + _setmode, and since this PR's CMake guard is exactly what makes the duplex demo buildable under mingw, you're right that as-was it would ship buildable-but-broken. Mirrored the same change: __MINGW32__/__MINGW64__ include branch + _setmode gated on _WIN32.

Verified on mingw-w64 (libusb 1.0.28) with the real RTL8812AU — StreamDuplexDemo now reads the full binary stream end to end:

<stream-duplex>tx #1 ok=1 psdu=269
...
<stream-duplex>tx #500 ok=1 psdu=269
<stream-duplex>tx EOF after 512 PSDUs

(pre-fix it died at short read on stdin (76/269) on frame 1).

Agreed on precoder_demo/main.cpp and txdemo/main.cpp — they gate includes on _MSC_VER too but neither reads length-prefixed stdin nor calls _setmode, so they're unaffected; left them as-is. The two stdin-driven stream demos are now in lockstep.

@josephnef josephnef left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving — the duplex-demo gap is resolved in 2880c0d (exact mirror of the StreamTxDemo fix; verified on hardware), and all 5 required CI checks are green. The two stdin-driven stream demos are now in lockstep.

@josephnef josephnef merged commit b865ba9 into OpenIPC:master Jun 24, 2026
5 checks passed
josephnef added a commit that referenced this pull request Jun 24, 2026
## Problem

CI builds the Windows target with MSVC + vcpkg only — mingw-w64 (GCC on
Windows) is never exercised. That's the toolchain the recent stream-demo
binary-stdin fix (#100) targets: mingw defines `_WIN32` but not
`_MSC_VER`, and gets libusb from pkg-config rather than vcpkg. Two gaps
follow:

- Nothing catches a mingw build/link break (e.g. the CMake `VCPKG_ROOT`
guard, or a missing include branch).
- Worse, the actual runtime bug class is invisible to a build-only
check: reverting the `_setmode` gate from `_WIN32` back to `_MSC_VER`
compiles clean on mingw and only truncates the first PSDU at runtime.

## Change

**1. `build-mingw` CI job** — MSYS2 / MINGW64, libusb via `pkgconf`,
`VCPKG_ROOT` unset (so CMake takes the pkg-config path). Builds all
targets and runs `ctest`.

**2. Headless stdin-binary regression test**
- `txdemo/stream_stdin.h` — single source of truth for the
`_WIN32`-gated `set_stdin_binary()` plus a tri-state `read_exact()`.
Both stream demos now use it (removes two duplicate `read_exact` copies;
behavior preserved — `StreamTxDemo` aborts on a truncated record,
`StreamDuplexDemo` stops its TX thread and keeps RX running).
- `StreamStdinSelftest` + `tests/stream_stdin_test.cmake` — pipes a
canonical `<u32_le len><PSDU>` stream containing `0x1A`/CRLF through the
same gate the demos use and asserts the round-trip. No libusb, no radio.
Registered as the `stream_stdin_binary` ctest.

Because the demos and the self-test share the gate, a regression fails
`ctest` on the Windows/mingw jobs instead of only surfacing on hardware.
`ctest` now runs in every job, so a total gate removal is also caught on
the MSVC cell.

## Verification

Local (macOS): all targets build, `ctest` passes (`records=5 bytes=20
OK`), and a truncated-at-`0x1A` stream correctly fails the test. The
mingw job and Windows text-mode behavior validate in CI.

## Notes

- `build-mingw` is intentionally **not** a required status check yet —
suggest landing it non-required and promoting once it's proven stable.
- The now-redundant `io.h`/`fcntl.h` in the demos'
`_MSC_VER`/`__MINGW32__` include branches were left in place to keep
this diff off the just-merged #100 include structure; trivial follow-up
to tidy.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants