Skip to content

Windows/MSVC support: CI harness + portability fixes (Phase 0 + 1a) - #5

Merged
hugary1995 merged 4 commits into
mainfrom
feat/windows-msvc-support
Jul 9, 2026
Merged

Windows/MSVC support: CI harness + portability fixes (Phase 0 + 1a)#5
hugary1995 merged 4 commits into
mainfrom
feat/windows-msvc-support

Conversation

@hugary1995

Copy link
Copy Markdown
Contributor

Goal

First step toward native Windows/MSVC support, driven by neml2 issue #414. nmhit is the critical path — it fails to build with MSVC today, and that blocks every NEML2 route on Windows, including pure Python (the factory parses HIT via the nmhit bindings).

Strategy

  • nmhit → fully supported on Windows: MSVC-clean source, Windows wheels, Windows CI.
  • neml2 → best-effort source build on Windows (separate effort, no wheels/CI).

This PR (Phase 0 + 1a)

Phase 0 — Windows dev harness

  • Non-blocking windows-latest cell added to the python and cpp jobs (continue-on-error while iterating; the required Linux/macOS cells still gate merges). The generated parser/lexer MSVC errors can only be surfaced by compiling on Windows, so this cell is the iteration loop for Phase 1b.
  • cpp job now runs under bash on every OS and passes an explicit config (Windows defaults to the multi-config Visual Studio generator).

Phase 1a — free/known MSVC fixes

  • Guard -Wall -Wextra -Wno-unused-parameter behind the GCC/Clang compiler id; MSVC gets /W3. (Passing the GCC/Clang flags to cl.exe was the originally reported blocker.)
  • abi_compat.cpp: move #include <cxxabi.h> (absent on MSVC) inside the existing __GLIBCXX__ guard → the shim compiles to nothing on MSVC/libc++.
  • Define YY_NO_UNISTD_H target-wide on Windows (flex-generated Lexer.h otherwise pulls in POSIX <unistd.h>); define _CRT_SECURE_NO_WARNINGS (C4996 on std::getenv).

Next (not in this PR)

Phase 1b — clear the residual generated-code MSVC errors (C2664/C2208/C2062/C2513 in Lexer.cpp/Parser.cpp), iterated on the Windows CI cell; likely a regen with a newer MSVC-clean Flex/Bison or targeted shims.
Phase 2 — Windows wheels (release.yml + cibuildwheel AMD64), then flip the Windows CI cells to required.

Linux/macOS build + tests verified locally.

🤖 Generated with Claude Code

hugary1995 and others added 4 commits July 9, 2026 08:21
First step toward native Windows/MSVC support (neml2 issue #414). The
critical path is nmhit: it fails to build with MSVC today, which blocks
every neml2 route on Windows (including pure Python, since the factory
parses HIT via the nmhit bindings).

Phase 0 -- Windows dev harness:
  - Add a non-blocking `windows-latest` cell to the `python` and `cpp` CI
    jobs (continue-on-error while we iterate the build to green; the
    required Linux/macOS cells still gate merges). The generated
    parser/lexer MSVC errors can only be surfaced by compiling on Windows,
    so this job IS the iteration loop for the remaining fixes.
  - `cpp` job runs under bash on every OS and passes an explicit config so
    the multi-config Visual Studio generator (CMake's Windows default)
    builds/tests correctly.

Phase 1a -- free/known MSVC fixes:
  - Guard the `-Wall -Wextra -Wno-unused-parameter` warning flags behind
    the GCC/Clang compiler id; give MSVC `/W3`. Passing the GCC/Clang
    flags to cl.exe was the originally reported blocker.
  - abi_compat.cpp: move `#include <cxxabi.h>` (nonexistent on MSVC) inside
    the existing `__GLIBCXX__` guard, so the whole shim compiles to nothing
    on MSVC/libc++.
  - Define YY_NO_UNISTD_H target-wide on Windows so the flex-generated
    Lexer.h does not pull in the POSIX <unistd.h>; define
    _CRT_SECURE_NO_WARNINGS to silence C4996 on std::getenv.

Remaining generated-code MSVC errors (C2664/C2208/C2062/C2513 in
Lexer.cpp/Parser.cpp) are Phase 1b -- iterated on the Windows CI cell.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Surfaced by the Windows CI cells. The Flex/Bison-generated parser/lexer
compiled cleanly under MSVC; all failures were in hand-written code and
the build config:

  - Node.h/Node.cpp: rename the `_inline` member to `_is_inline`. `_inline`
    is a reserved keyword in the MSVC dialect (an alias for `inline`), so a
    member of that name parsed as `inline` and produced the reported
    C2062/C2513/C2208/C2059/C2222/C3536 cascade.

  - Node.cpp wrap_in_sections(): take the filename as std::filesystem::path
    instead of const std::string&. Callers hold a path (_fname) and forward
    it straight to _set_location (which also takes a path); path->string is
    implicit only where path::string_type is std::string (POSIX), but is
    std::wstring on Windows, so the string parameter failed to convert
    (C2664). path end-to-end removes both conversions.

  - CMakeLists: make the parser-regeneration gate multi-config-aware. The
    old `NOT CMAKE_BUILD_TYPE` branch pulled in FLEX/BISON on the Windows
    wheel build, because scikit-build-core drives the multi-config Visual
    Studio generator where CMAKE_BUILD_TYPE is empty by design. Guard on
    GENERATOR_IS_MULTI_CONFIG so multi-config generators always use the
    committed generated/ sources (no flex/bison), same as Release.

Linux Release + Debug builds and tests unaffected (verified locally).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…(Phase 1b, round 2)

The Windows cells reduced to a single error: Parser.h(260) referenced the
deleted copy constructor of the move-only semantic value
std::unique_ptr<nmhit::Node> (C2280). Root cause: MSVC reports
__cplusplus == 199711L even under /std:c++17 unless /Zc:__cplusplus is
passed, so the Bison-generated header took its pre-C++11 branch, which
copies the semantic value instead of moving it.

Add /Zc:__cplusplus to the MSVC compile options so __cplusplus reports the
true standard and Bison selects the C++11 (move) path. Compiler-scoped
genexpr, so GCC/Clang builds are unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… test env (Phase 1b, round 3)

The nmhit library now compiles under MSVC (round 2). Remaining failures:

  - _nmhit binding (C2280, deleted unique_ptr copy): the binding forced
    Node's copy/move traits to false via an ILLEGAL std::is_move_constructible
    specialization (C5285), which newer MSVC ignores -- so nanobind
    instantiated Node's copy ctor and tried to copy the move-only
    vector<unique_ptr<Node>> _children. Fix at the root: delete Node's copy
    constructor and copy assignment (they were a vestigial protected
    `= default` that nothing uses -- every clone() rebuilds from fields).
    With the copy ops deleted, std::is_copy_constructible /
    is_move_constructible report false on every compiler, so both the
    nanobind::detail::is_copy_constructible AND the illegal
    std::is_move_constructible specializations are removed. Verified the
    GCC miscomputation that motivated them is gone (Linux build + 27 pytest
    + C++ tests all pass without the workarounds).

  - test_hit.cpp used POSIX setenv(): add a _putenv_s/setenv portable shim.

  - Move /Zc:__cplusplus from the nmhit target to a project-wide MSVC flag
    so the library, the Python module, and the tests all report the true
    __cplusplus.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@hugary1995
hugary1995 merged commit 9a6966a into main Jul 9, 2026
9 checks passed
@hugary1995
hugary1995 deleted the feat/windows-msvc-support branch July 9, 2026 13:58
hugary1995 added a commit that referenced this pull request Jul 9, 2026
Now that nmhit builds and tests clean on Windows/MSVC (PR #5), make it a
first-class published platform:

  - release.yml: add windows-latest to the wheel matrix (CIBW_ARCHS_WINDOWS
    = AMD64), so cp39-cp314 Windows wheels are built and published to PyPI
    alongside Linux/macOS.
  - ci.yml: drop continue-on-error from the Windows python/cpp cells -- they
    now gate merges like every other cell. The `package` job also builds +
    tests a Windows wheel, pinned to the same cibuildwheel as release.yml so
    the packaging check exercises the exact published-wheel build path.
  - pyproject.toml: add [tool.cibuildwheel.windows] archs = AMD64.
  - Bump 0.3.4 -> 0.3.5 (pyproject + CMakeLists) for the release.
  - README: correct the published-platform list (Linux x86_64 -- the
    workflow never built aarch64 -- macOS x86_64/arm64, and now Windows
    AMD64).

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.

1 participant