ADR-2646: POSIX-only build parts stay off Windows, and preflight refuses an unguarded POSIX header¶
- Status: Accepted
- Date: 2026-10-08
- Deciders: lusoris
- Tags:
build,ci,windows,preflight
Context¶
MSVC ships no <unistd.h>, <dlfcn.h>, <poll.h>, <sys/socket.h> or the other POSIX-only headers. A C, C++ or CUDA file that includes one compiles on every Linux and macOS lane and fails only on the required Windows MSVC+CUDA and Windows MSVC+SYCL lanes. The local gates and the merge train build neither, so the break first shows up on a pull request: on 2026-10-08 the Vulkan frame import of the RC4 API stack added <unistd.h> to core/src/cuda/import_vulkan.c for dup() and close(), and the CUDA backend is built on Windows.
The msvcism stage of scripts/dev/preflight.sh (ADR-1234) checks MSVC rejection classes statically, without a compiler, and had no rule for this class. Run over the whole tree, a header check found 32 includes in 9 groups. Most sit in files the Windows build never compiles; three groups are compiled by an option a Windows configure accepts:
- the embedded MCP server (
enable_mcp,core/src/mcp/): Unix-domain and TCP sockets; - the libFuzzer harnesses (
fuzz,core/test/fuzz/):<unistd.h>; - the
vmaf_vpltool (built withenable_syclwhen VPL, VA-API and libva-drm are found): VA-API and<unistd.h>.
Two files are POSIX-only by design and no meson.build compiles them: the vendored MATLAB MEX source compat/python-vmaf/matlab/strred/matlabPyrTools/MEX/innerProd.c, and the LD_PRELOAD fault-injection shim ffmpeg-patches/test/fault_inject_libvmaf.c.
Decision¶
- The MCP server, the libFuzzer harnesses and
vmaf_vplare POSIX-only build parts. Their Meson blocks carryhost_machine.system() != 'windows'. - A Windows configure that asks for one of them fails with a message naming the POSIX dependency:
enable_mcp=trueincore/src/meson.build,fuzz=trueincore/test/meson.build. No option requestsvmaf_vpl; a Windows configure withenable_sycl=trueprints that the tool is disabled because it is Linux only, as it already prints a disabled tool on Linux. Nothing is skipped silently. - The
msvcismstage refuses a POSIX-only header in a changed source the Windows build compiles when the include sits outside every preprocessor conditional that names a platform.scripts/dev/find-posix-only-headers.pydecides which sources the Windows build compiles from themeson.buildfiles: a source named only inside a block kept off Windows (including thesubdir()that reads its directory, aforeachover a list filled only off Windows, and a list kept empty off Linux) is not built there; a header counts through the sources that include it; a source nomeson.buildnames counts as built. - The two design-POSIX files are named exceptions in
.config/lint-exceptions.d/msvcism-posix-headers.toml(rulemsvcism-posix-headers), each with a reason and an expiry (ADR-1142 exception form). No other file gets one.
Alternatives considered¶
| Option | Pros | Cons | Why not chosen |
|---|---|---|---|
| Gate the three build parts off Windows, fail configure on an explicit request, except the two design-POSIX files (chosen) | The check reads the build the way the Windows lanes do; an explicit Windows request fails at configure with a reason; two exceptions, both outside the build | The scanner models a subset of Meson's control flow | Chosen |
| One named exception per finding file | No build change | About 20 exceptions that each restate "not built on Windows"; a new POSIX file under core/src/mcp/ needs one more; a Windows enable_mcp=true still fails in the compiler | An unbounded exception list is the tier ADR-1142 retired |
Scope the check to core/src/ and gate the rest | Small scanner | Leaves core/tools/, core/test/ and the CUDA sources outside the check; the 2026-10-08 break class reappears there | Partial coverage of a class that only the Windows lanes see |
Port the MCP server, the harnesses and vmaf_vpl to Windows | Feature parity | Winsock and named-pipe transports, a VA-API replacement, Windows libFuzzer; no user asked for any of it | Out of RC scope; the gate keeps the door open for a later port |
Consequences¶
- Positive: an unguarded POSIX header in a source the Windows build compiles fails locally, before a pull request reaches the MSVC lanes. A Windows configure that asks for a POSIX-only part fails at once with a reason instead of in the compiler.
- Negative:
find-posix-only-headers.pyunderstandsif/elif/else,foreach,subdir()and the empty-list ternary; an unusual way to keep a source off Windows can produce a false finding, fixed in the scanner or with a named exception. - Neutral / follow-ups:
scripts/ci/tests/test-preflight-msvcism.sh, run bymake lint-sh, plants an unguarded<unistd.h>in a source the Windows build compiles and checks that the stage refuses it, that a guarded include and a source kept off Windows pass, and that a scan that cannot run fails the stage. Master's stage before this change passed the planted include.
References¶
- ADR-1234: the local preflight gate and its
msvcismstage. - ADR-1142: every standard binds every file; exceptions name file, rule, reason and expiry.
- Source:
Q-266(maintainer, 2026-10-08): "Yes, with a planted negative case run against the old stage". - Source:
Q-269(maintainer, 2026-10-08): "Option (b): mcp, fuzz and vpl meson blocks get a non-Windows gate that fails configure with a clear message when requested on Windows; only the MATLAB MEX file and the LD_PRELOAD shim get named, expiring exceptions".