Skip to content
18 changes: 18 additions & 0 deletions .github/workflows/ci_linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,24 @@ jobs:
- name: Run all tests
run: bazelisk test --verbose_failures //...

# Python wheel: bazelisk build //... already produces dds3_wheel_dist
# as a side effect, but that alone never runs `pip install` on the
# packaged .whl, so a mistagged or incomplete archive would go
# unnoticed. 3.14 matches the cp314 tag dds3_wheel sets for its
# compiled _dds3 extension (see python/BUILD.bazel).
- name: Setup Python
uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
with:
python-version: "3.14"

- name: Build and smoke-test Python wheel
run: |
bazelisk build --verbose_failures //python:dds3_wheel_dist
wheel="$(ls bazel-bin/python/dist/*.whl)"
python3 -m venv "$RUNNER_TEMP/dds3_wheel_venv"
"$RUNNER_TEMP/dds3_wheel_venv/bin/pip" install --no-index --no-deps "$wheel"
"$RUNNER_TEMP/dds3_wheel_venv/bin/python" -c "import dds3; assert dds3.api_root() == 'dds.hpp'; print('dds3 wheel OK:', dds3.__file__)"

# --- Non-default-flag builds below. Each mutates the active Bazel
# config (bazel-bin, etc.), so they are kept together and last, after
# everything above that depends on the default configuration.
Expand Down
18 changes: 18 additions & 0 deletions .github/workflows/ci_macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,24 @@ jobs:
- name: Run all tests
run: bazelisk test --verbose_failures //...

# Python wheel: bazelisk build //... already produces dds3_wheel_dist
# as a side effect, but that alone never runs `pip install` on the
# packaged .whl, so a mistagged or incomplete archive would go
# unnoticed. 3.14 matches the cp314 tag dds3_wheel sets for its
# compiled _dds3 extension (see python/BUILD.bazel).
- name: Setup Python
uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
with:
python-version: "3.14"

- name: Build and smoke-test Python wheel
run: |
bazelisk build --verbose_failures //python:dds3_wheel_dist
wheel="$(ls bazel-bin/python/dist/*.whl)"
python3 -m venv "$RUNNER_TEMP/dds3_wheel_venv"
"$RUNNER_TEMP/dds3_wheel_venv/bin/pip" install --no-index --no-deps "$wheel"
"$RUNNER_TEMP/dds3_wheel_venv/bin/python" -c "import dds3; assert dds3.api_root() == 'dds.hpp'; print('dds3 wheel OK:', dds3.__file__)"

# .NET binding — this job is the only CI coverage of the AArch64 ABI, which
# is what the managed struct-layout tests exist to guard: a layout mismatch
# there corrupts results silently rather than throwing. Linux covers SysV
Expand Down
25 changes: 25 additions & 0 deletions .github/workflows/ci_windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,31 @@ jobs:

# .NET binding lives in CI – Windows .NET (ci_windows_dotnet.yml).

# Python wheel: the build above already produces dds3_wheel_dist as a
# side effect, but that alone never runs `pip install` on the packaged
# .whl, so a mistagged or incomplete archive would go unnoticed. 3.14
# matches the cp314 tag dds3_wheel sets for its compiled _dds3
# extension (see python/BUILD.bazel). --config=opt matches every other
# bazelisk build invocation on this job (avoids MSVC D9025).
- name: Setup Python
uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
with:
python-version: "3.14"

- name: Build and smoke-test Python wheel
shell: pwsh
run: |
bazelisk build --config=opt --verbose_failures //python:dds3_wheel_dist
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
$wheel = Get-ChildItem -Path "bazel-bin/python/dist" -Filter *.whl | Select-Object -First 1
if (-not $wheel) { Write-Error "no wheel found in bazel-bin/python/dist"; exit 1 }
python -m venv "$env:RUNNER_TEMP/dds3_wheel_venv"
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
& "$env:RUNNER_TEMP/dds3_wheel_venv/Scripts/pip.exe" install --no-index --no-deps $wheel.FullName
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
& "$env:RUNNER_TEMP/dds3_wheel_venv/Scripts/python.exe" -c "import dds3; assert dds3.api_root() == 'dds.hpp'; print('dds3 wheel OK:', dds3.__file__)"
exit $LASTEXITCODE

- name: Upload test logs - Windows
if: always()
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
Expand Down
24 changes: 24 additions & 0 deletions BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,18 @@ config_setting(
values = {"compilation_mode": "dbg"},
)

# More specific than build_macos (any CPU): needed where a select() arm must
# pick a CPU-qualified value (e.g. python/BUILD.bazel's macOS wheel platform
# tag) and a wrong-arch match would silently mislabel the output rather than
# failing closed like build_windows does for non-x86_64.
config_setting(
name = "build_macos_arm64",
constraint_values = [
"@platforms//os:osx",
"@platforms//cpu:arm64",
],
)

config_setting(
name = "build_linux",
constraint_values = [
Expand All @@ -28,6 +40,18 @@ config_setting(
values = {"compilation_mode": "dbg"},
)

# More specific than build_linux (any CPU): needed where a select() arm must
# pick a CPU-qualified value (e.g. python/BUILD.bazel's Linux wheel platform
# tag) and a wrong-arch match would silently mislabel the output rather than
# failing closed like build_windows does for non-x86_64.
config_setting(
name = "build_linux_x86_64",
constraint_values = [
"@platforms//os:linux",
"@platforms//cpu:x86_64",
],
)

config_setting(
name = "build_windows",
constraint_values = [
Expand Down
8 changes: 6 additions & 2 deletions docs/python_interface.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@ The DDS (Double Dummy Solver) library provides a Python interface for analyzing
## Building the Python Interface

### Prerequisites
- Python 3.10+ (tested with 3.10, 3.11, 3.12, 3.14)
- Bazel as pinned in [`.bazelversion`](../.bazelversion) (use
[bazelisk](https://github.com/bazelbuild/bazelisk), which picks it up
automatically)
automatically) — no host Python install is required to *build*: Bazel
downloads its own hermetic CPython via `rules_python`.
- C++ compiler (clang 15+ or GCC 11+)
- To *use* the built wheel, a CPython 3.14 interpreter: `MODULE.bazel` pins
exactly one toolchain version (3.14), `_dds3` links the full CPython API
(not the stable `abi3` ABI), and `dds3_wheel` is tagged `cp314`/`cp314`
accordingly — pip will refuse to install it on any other CPython version.

### Build Instructions

Expand Down
50 changes: 50 additions & 0 deletions python/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,26 @@ py_test(
],
)

# Guard: dds3_wheel must carry CPython/platform tags, not py_wheel's
# py3-none-any defaults, since _dds3 is a compiled extension.
py_test(
name = "ci_wheel_tags_test",
size = "small",
main = "tests/ci_wheel_tags_test.py",
srcs = ["tests/ci_wheel_tags_test.py"],
data = ["BUILD.bazel"],
)

# Guard: CI actually `pip install`s the packaged wheel (not just Bazel's own
# py_library layout) on all three host OSes.
py_test(
name = "ci_wheel_smoke_test",
size = "small",
main = "tests/ci_wheel_smoke_test.py",
srcs = ["tests/ci_wheel_smoke_test.py"],
data = ["//.github/workflows:all_workflows"],
)

# Guard: MemorySanitizer config + Linux CI job stay wired together.
py_test(
name = "ci_msan_test",
Expand Down Expand Up @@ -300,10 +320,40 @@ py_test(
],
)

# _dds3 links against the full CPython API (no Py_LIMITED_API/abi3) for the
# CPython version MODULE.bazel pins (python_version = "3.14"); the py_wheel
# defaults (python_tag="py3", abi="none", platform="any") would mislabel this
# compiled extension as a universal pure-Python wheel.
py_wheel(
name = "dds3_wheel",
distribution = "dds3",
version = "1.0.0",
python_tag = "cp314",
abi = "cp314",
platform = select({
Comment thread
zzcgumn marked this conversation as resolved.
Resolved
# CI builds on plain ubuntu-latest with this repo's own hermetic
# LLVM -- no manylinux sysroot or auditwheel step -- so a
# manylinux2014 tag would overclaim glibc-floor portability.
# linux_x86_64 is the honest, non-portable tag for how this is
# actually built. Keyed on the CPU-specific build_linux_x86_64, not
# build_linux (which matches any Linux CPU per
# specs/build-system.md): an aarch64 Linux build has no matching arm
# here and fails closed at analysis time, rather than silently
# mistagging an ARM extension as x86_64.
"//:build_linux_x86_64": "linux_x86_64",
# Keyed on the CPU-specific build_macos_arm64, not build_macos
# (which matches any macOS CPU per specs/build-system.md): an
# x86_64 mac build has no matching arm here and fails closed at
# analysis time, the same fail-closed behavior build_windows
# already has for non-x86_64, rather than silently mistagging.
# 11.0 is an assumed floor (arm64 Macs can't run below Big Sur
# anyway), not a verified -mmacosx-version-min: none is set anywhere
# in CPPVARIABLES.bzl/.bazelrc/BUILD.bazel, so the real minimum load
# command baked into the .so is whatever the runner's default Xcode
# SDK picks.
"//:build_macos_arm64": "macosx_11_0_arm64",
Comment thread
zzcgumn marked this conversation as resolved.
Resolved
"//:build_windows": "win_amd64",
}),
deps = [":dds3_lib", ":_dds3_in_package"],
strip_path_prefixes = ["python/"],
entry_points = {},
Expand Down
Loading
Loading