os: add support for windows - #2427
Draft
AmyJeanes wants to merge 7 commits into
Draft
Conversation
The default SCons tools pick MSVC there. The host-side libpanda test library is skipped on Windows: -nostdlib does not link as a DLL. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016AgqYZYWLpwE2T5vS3nVEt
The test library's own Environment picked MSVC on Windows; use the mingw tool with clang like the firmware build. SCons requires the .dll suffix there, so name the target and the cffi path per platform, and skip the import library nobody links against. CANPacket_t is memcpy'd onto the USB wire, so the DLL must use the packed GCC bitfield layout (-mno-ms-bitfields): mingw's MS layout puts the checksum at byte 8 instead of 5. cffi's Windows backend follows MSVC bitfield rules and cannot be switched, but MSVC packs same-typed bitfields contiguously, so declaring the three flag bits as unsigned int like addr gives the firmware layout under both rule sets (unchanged layout on Linux and macOS). tests/usbprotocol/test_comms.py passes on Windows (5/5). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016AgqYZYWLpwE2T5vS3nVEt
This was referenced Sep 7, 2026
The signing step ran `SETLEN=1 sign.py ...` as a shell command, which cmd.exe, the shell SCons uses on Windows, cannot run: it neither starts a script by its shebang nor understands a VAR=value prefix. The interpreter running SCons is the one with the dependencies, and SETLEN moves into the command's environment. Same command on Linux and macOS, spelled out. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016AgqYZYWLpwE2T5vS3nVEt
SCons' default construction environment on Windows carries only the system directories in PATH, so cmd.exe cannot find clang unless the mingw tool happens to locate a toolchain. The MSYS2 shell's PATH has it, like the firmware environment already inherits. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016AgqYZYWLpwE2T5vS3nVEt
Line endings only (git diff --ignore-cr-at-eol is empty), so a .gitattributes that keeps checkouts LF does not rewrite them on an unrelated edit later. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016AgqYZYWLpwE2T5vS3nVEt
The dev dependencies take the ARM toolchain and cppcheck from PyPI, like the
other comma-deps consumers: the release shim branches know only Linux and
macOS. Those wheels need Python 3.12, hence the markers. setup.sh activates
the venv from Scripts/ in an MSYS2 shell and pins uv to a native CPython
there (the toolchain's own python comes first on PATH and its wheels are
incompatible). test_misra.sh calls the venv's python: a uv venv on Windows
has no python3. The mutation test runs the script through the bash found on
PATH rather than the platform shell (cmd.exe on Windows, and a bare "bash"
would reach the WSL launcher in System32 first), and normalises its glob
paths, which Windows returns with backslashes that defeated the ignore list.
test.yaml gets a windows-latest entry running the same ./test.sh from a
CLANG64 shell. Only that entry names its shell; the others keep bash -e {0},
GitHub's implicit default. A .gitattributes keeps every checkout LF: Git for
Windows defaults to autocrlf, and bash cannot run a CRLF script.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016AgqYZYWLpwE2T5vS3nVEt
AmyJeanes
marked this pull request as draft
September 7, 2026 23:22
…ies#107 publishes --- TODO REMOVE AFTER DEPENDENCY PR MERGES (commaai/dependencies#107) --- This commit is dropped from the PR once the win_amd64 wheels are on PyPI. Until then it points uv at a GitHub release of the same wheels, built from that PR's branch on my fork, so the Windows CI entry runs here meanwhile: comma-deps-gcc-arm-none-eabi and comma-deps-cppcheck for sys_platform == 'win32'. Linux and macOS resolve from PyPI as before. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016AgqYZYWLpwE2T5vS3nVEt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
SCons' default tool picks MSVC on Windows; the firmware build and the libpanda test library use the mingw tool with clang there (MSYS2 CLANG64), like openpilot's own Windows build. SCons requires the
.dllsuffix, so the target and the cffi path are named per platform, and no import library is produced since nothing links against it. The libpanda build inherits the shell's environment so that cmd.exe finds clang on the MSYS2 PATH.CANPacket_tis memcpy'd onto the USB wire, so the DLL is compiled with-mno-ms-bitfields: mingw's default MS layout would put the checksum at byte 8 instead of 5. cffi's Windows backend follows MSVC bitfield rules and cannot be switched, but MSVC packs same-typed bitfields contiguously, so declaring the three flag bits asunsigned intlikeaddrgives the firmware layout under both rule sets; the layout on Linux and macOS is unchanged.The signing step runs
sign.pythrough the interpreter running SCons, withSETLENin the command's environment: cmd.exe, the shell SCons uses on Windows, runs neither a script by its shebang nor aVAR=valueprefix. It is the same command on Linux and macOS, spelled out.Same packet-layout fix as opendbc's libsafety.
panda's own
./test.shruns on Windows in CI too, which the rest of the change covers:comma-deps-gcc-arm-none-eabi,comma-deps-cppcheck), like the other comma-deps consumers: therelease-*shim branches know only Linux and macOS. Those wheels need Python 3.12, hence the markers; bumping panda's Python floor instead would be the other option.setup.shactivates the venv fromScripts/in an MSYS2 shell and pins uv to a native CPython there (the toolchain's own python comes first on PATH, and its wheels are incompatible).test_misra.shcalls the venv'spythoninstead ofpython3: setup.sh has activated the venv by then, and a uv venv on Windows has nopython3. The mutation test runs the script through thebashfound on PATH rather than the platform shell: cmd.exe cannot run it, and a barebashwould reach the WSL launcher in System32 first.test.yaml: awindows-latestentry in the./test.shmatrix, run from the MSYS2 CLANG64 shell that setup-msys2 provides (only clang is installed on top of the runner's MSYS2; the firmware toolchain comes from uv). Only that entry names its shell; the others keepbash -e {0}, GitHub's implicit default. The existing Windows pip package job stays: it checks the toolchain-free install path on a stock Python..gitattributeskeeps every checkout LF: Git for Windows defaults to autocrlf, and bash cannot run a CRLF script. The nine ST vendor files that were stored with CRLF are normalised to LF in their own commit (line endings only,git diff --ignore-cr-at-eolis empty), so nothing gets rewritten later on an unrelated edit.Note: the vast majority of this diff is the nine ST vendor files under
board/stm32h7going from CRLF to LF for the.gitattributeschange, in their own commit (9 files, 54,251 lines each way, line endings only:git diff --ignore-cr-at-eolis empty for it). The actual change is the other 10 files, +66/-16.Tested on Windows: the firmware and libpanda build and sign with the mingw tools from an MSYS2 CLANG64 shell, and panda's own
./test.shruns green there in 64 s (the firmware for all three boards and libpanda build and sign, ruff passes, and pytest passes all 9 tests, the three misra mutation cases included, about 50 s each in parallel);tests/usbprotocol/test_comms.pypasses against the DLL (5/5), which is the packet-layout check; this repo's workflow on this PR, including its existing windows-latest pip package job (the new Windows./test.shentry takes about 3.5 min of the job's 10); and openpilot's full unit test suite on its Windows CI job, which builds this branch: commaai/openpilot#38810. The Linux and macOS builds are unchanged, confirmed by the same workflow.Draft: waiting on commaai/dependencies#107. The Windows CI entry installs
comma-deps-gcc-arm-none-eabiandcomma-deps-cppcheckforwin_amd64from PyPI, which that PR publishes on merge. Until then the last commit, titledTEMPand marked--- TODO REMOVE AFTER DEPENDENCY PR MERGES ---, points uv at a GitHub release of the same wheels built from that PR's branch on my fork, so the entry runs green here meanwhile. That commit is dropped and this PR marked ready once the wheels are on PyPI; nothing else in it changes.This PR is part of a larger series of work bringing support for Windows to openpilot:
The dependencies PR goes first: the Windows CI entries here and in the other combined PRs install the wheels it publishes. Otherwise each PR merges independently; only the openpilot one depends on all of them.
The changes in this PR were generated by Claude Fable 5.1 but were human reviewed and fully tested end to end both locally and in CI.