Skip to content

os: add support for windows - #38810

Draft
AmyJeanes wants to merge 14 commits into
commaai:masterfrom
AmyJeanes:windows
Draft

os: add support for windows#38810
AmyJeanes wants to merge 14 commits into
commaai:masterfrom
AmyJeanes:windows

Conversation

@AmyJeanes

@AmyJeanes AmyJeanes commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Native Windows development build of openpilot on an MSYS2 CLANG64 toolchain (clang/lld/libc++) with a uv-managed CPython. Covers the build and the PC dev tools: the UI, cabana, replay, PlotJuggler, msgq/params, plus panda firmware build and flash. No on-road support, and the manager/daemon stack is out of scope (as on macOS); those pieces are kept as possible follow-ups.

Prefers cross-platform code (std::filesystem, std::chrono, portable Python) over Windows-only branches, so most of the diff changes existing lines rather than adding platform forks. Linux and macOS are unchanged. Line endings were split into a separate change that comma has already merged.

The two TEMP commits at the tip (both marked TODO REMOVE) drop before this is marked ready: one points uv at a pre-release wheel index until commaai/dependencies#107 publishes the win_amd64 wheels, the other points the submodules at my fork until the four submodule PRs merge.

Part of a six-repo series: commaai/msgq#709, commaai/panda#2427, commaai/opendbc#3724, commaai/rednose#61, commaai/dependencies#107. dependencies merges first; the others merge independently; only openpilot depends on all of them.

Generated with Claude, human-reviewed and tested end to end, locally and in CI (comma's suite on Linux plus a Windows build-and-tool-test job).

@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Process replay diff report

Replays driving segments through this PR and compares the behavior to master.
Please review any changes carefully to ensure they are expected.

✅ 0 changed, 66 passed, 0 errors

@AmyJeanes
AmyJeanes force-pushed the windows branch 4 times, most recently from 235758d to 7054a07 Compare September 9, 2026 02:01
adeebshihadeh pushed a commit that referenced this pull request Sep 9, 2026
AmyJeanes and others added 10 commits September 9, 2026 23:16
Development builds only: the daemons, modeld, pandad, v4l and socketcan
stay Linux. SCons runs build commands through MSYS2 bash so the
SConscripts' POSIX shell syntax keeps working. Portable forms replace
dirent, readlink, socketpair, aligned_alloc and the rm -rf shell-outs on
every platform; what the CRT still spells differently is shimmed in
common/util.h. replace_file retries the rename a Windows reader blocks.
teleoprtc has no Windows wheels yet, so it is marked off there.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016AgqYZYWLpwE2T5vS3nVEt
url_file: os.register_at_fork does not exist on Windows.
ui: the Windows CRT has no vasprintf for the log callback; a missing
AF_UNIX family degrades WifiManager like a missing system D-Bus, and
the init worker is skipped then instead of failing on the None router.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016AgqYZYWLpwE2T5vS3nVEt
currentSeconds() subtracted two uint64 timestamps that start 1 ns apart
in the wrong order; the wrapped result made ctime() return NULL on the
UCRT. winpthreads' clock_nanosleep rejects CLOCK_MONOTONIC, which the
sleep loop took as "done sleeping": precise_nano_sleep now sleeps in
slices and polls the interrupt flag on every platform, which also
replaces the SIGUSR1 wake-up.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016AgqYZYWLpwE2T5vS3nVEt
is_termresized() stays true until resize_term() acknowledges it, so
every frame rebuilt the screen; chgat() takes a pair number and never
changes the character, so the markers were pair 255 blanks; 16-colour
terminals reject the 256-colour indices; wbkgd() paints an attribute-only
background just on the cells it clears itself. The bookmark and alert
markers now draw '_' like the legend, on ncurses too.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016AgqYZYWLpwE2T5vS3nVEt
/tmp/comma_download_cache resolved to <drive>:\tmp on Windows. The cache
now lives under Path::tmp_dir() / hw.TMP_DIR: $TEMP, /tmp when unset.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016AgqYZYWLpwE2T5vS3nVEt
time.monotonic() on Windows Python 3.12 ticks every 15.6 ms, so SubMaster
measured dt=0 and its frequency checks divided by zero. perf_counter is
the same CLOCK_MONOTONIC on Linux and macOS, and on Windows the
QueryPerformanceCounter clock 3.13 moved monotonic to. The tests that
time SubMaster and logMonoTime read the same clock.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016AgqYZYWLpwE2T5vS3nVEt
SwaglogState's destructor runs from DllMain during ExitProcess, after
the zmq I/O thread is gone, and zmq_ctx_destroy() then waits forever.
The process is going away, so leak the context.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016AgqYZYWLpwE2T5vS3nVEt
Windows spawns instead of forking, so child targets live at module
level; NamedTemporaryFile handles close before another process opens
the file; binaries are launched by absolute path (CreateProcess
resolves ./name against the parent's cwd) and test_native adds the
.exe suffix; the hardcoded /tmp becomes TMP_DIR; manager.helpers
imports fcntl lazily, since common/test.py imports the manager; a
sleep measured with the 15.6 ms monotonic tick can read short, so
recv_one_retry's timing uses perf_counter.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016AgqYZYWLpwE2T5vS3nVEt
The MSYS2 CLANG64 shell is the build environment: pacman installs the
toolchain, uv and MSYS2's native git (the venv's git-lfs mis-resolves
the POSIX paths the Cygwin-style git package reports). The venv lives
in Scripts/ and gets a python3.exe for the build's shebangs. pycapnp's
wheel links the Visual C++ runtime, so the setup checks for it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016AgqYZYWLpwE2T5vS3nVEt
A build_windows job next to build_mac: op.sh setup and build in the
MSYS2 CLANG64 shell the README describes, then the tests that cover
what Windows is for (common, messaging, cabana, jotpluggler, tools/lib,
the UI library).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016AgqYZYWLpwE2T5vS3nVEt
@dzid26

dzid26 commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Initially I had linking errors, but updating msys packages (pacman -Syu) did the trick.

After the op build completes most tools just work. (plotjuggler doesn't atm, but jotplugger does and it's fast!).
image
This is already useful for logs reading to avoid slow WSL.

USB Panda finally works without much hassle as well.

Quote paths so an install directory containing spaces works. op.sh and
setup.sh quote $OPENPILOT_ROOT; the cabana and jotpluggler string defines
use escaped double quotes rather than '"..."', whose outer single quotes
SCons' argument quoting drops and splits the path; the acados and modeld
build commands quote their embedded paths. Cross-platform: the same paths
break on Linux, spaces are just far more common on Windows.

The PC tools (cabana, replay, jotpluggler, the UI) build and run from a
spaced path; on Linux a full build, modeld and acados codegen included,
does too.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016AgqYZYWLpwE2T5vS3nVEt
@AmyJeanes
AmyJeanes force-pushed the windows branch 2 times, most recently from 4a67bb1 to 4899422 Compare September 10, 2026 14:16
AmyJeanes and others added 3 commits September 10, 2026 15:34
Three things broke op lint on Windows, all latent on Linux too:

- lint.sh passed the whole openpilot/ file list (~48k of paths) as one argv,
  over Windows' ~32k command-line limit, so the file-list checks failed with
  "Argument list too long". Batch the arguments through xargs.
- check_shebang_format flagged the LFS-tracked updater zipapp, whose real
  "#!/usr/bin/env python3" line sits over a ZIP payload, as a bad shebang
  because grep reported the binary as a match. Skip binaries with grep -I.
- A spaced install path broke `cd $ROOT` and the check invocations. Quote
  $ROOT and $DIR.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016AgqYZYWLpwE2T5vS3nVEt
…ies#107 publishes

--- TODO REMOVE AFTER DEPENDENCY PR MERGES (commaai/dependencies#107) ---

Until the win_amd64 wheels are on PyPI, uv resolves the comma-deps
packages openpilot names for sys_platform == 'win32' from a GitHub
release of the same wheels on my fork. 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
--- TODO REMOVE AFTER THE SUBMODULE PRS MERGE (commaai/msgq#709, commaai/panda#2427, commaai/rednose#61, commaai/opendbc#3724) ---

Until the four submodule PRs merge, the pointers move to the windows-tidy
branches on my fork and .gitmodules fetches them from there: panda and
opendbc just below their own TEMP index commits (their uv sources would
leak into this lockfile), rednose at its TEMP commit (its source supplies
comma-deps-eigen for Windows), msgq at its tip. The lock follows the
submodules' metadata.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016AgqYZYWLpwE2T5vS3nVEt
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.

2 participants