Tags: adobe-type-tools/pyots
Tags
Build Windows wheels (#89) Fixes #46 * Build Windows wheels Add Windows (x64/AMD64) support so wheels can be built and published for Windows. - build.py: force the zlib meson subproject fallback on Windows (no system zlib to link against) - setup.py: resolve static libs by globbing instead of hardcoding Unix '.a' names so MSVC '.lib' output is found; statically link zlib and use MSVC compile flags (/std:c++14, no -fPIC/-lz) on Windows - release.yml: add windows-latest to the build and publish matrices, set CIBW_ARCHS_WINDOWS, and set up the MSVC environment via msvc-dev-cmd - run_tests.yml: add a Windows build-and-test job - pyproject.toml / README.md: add Windows classifier, badge, and docs * Defer extra_objects resolution to build_ext The refactored _get_extra_objects() verifies the static libs exist on disk, but the Extension was built at module-load time -- so even the `setup.py download` step crashed before any libs were compiled, breaking both the Linux and Windows test jobs. Resolve extra_objects in a custom build_ext command instead, which runs after build.py has produced the static libs. * Fix tests on Windows Two test bugs surfaced once the extension built and ran on Windows: - test_ots_fuzzing: EXPECT_FAIL uses '/'-separated paths but str(Path.relative_to()) yields '\' on Windows, so no expected-failure font matched and all 42 were flagged as unexpected. Compare via .as_posix() so the separator is normalized on every platform. - test_compare_*: the pyots wheel bundles a top-level 'ots' package, so `import ots` succeeds even without opentype-sanitizer installed, leaving have_ots=True and ots.sanitize missing. Guard on hasattr(ots, "sanitize") so the comparison tests skip unless the real package is present. * Gate PyPI publish to tag releases - Only run `publish_release` on tag pushes, so manual `workflow_dispatch` runs build and upload artifacts for verification without attempting to publish (which would fail on the setuptools_scm dev version). - Add `skip-existing` to the PyPI publish step so an already-uploaded filename is skipped instead of failing the release. * Pin cibuildwheel build frontend to pip cibuildwheel v3 defaults to the 'python -m build' frontend, which -- run from the project root -- imports our top-level build.py instead of the pypa/build package and fails on build's --wheel/--outdir args. Set CIBW_BUILD_FRONTEND=pip to restore the frontend used by all prior releases (built under cibuildwheel v2). * Drop distutils from setup.py distutils was removed from the stdlib in Python 3.12 (PEP 632), so `python setup.py sdist` under the runner's Python 3.14 failed with ModuleNotFoundError. Replace distutils.log with the stdlib logging module and distutils.errors.DistutilsSetupError with setuptools.errors.SetupError (setuptools>=61 is already required). * Disable `fail-fast` * Install setuptools before building sdist Python 3.12+ no longer bundles setuptools, so `python setup.py sdist` run against the bare runner Python failed with ModuleNotFoundError. Install setuptools and setuptools_scm before the sdist build. * Rename build.py to build_ots.py to drop release.yml workarounds The top-level build.py shadowed the pypa/build package: run from the project root, `python -m build` imported our argparse script instead of the real build backend. That forced two workarounds in release.yml: - CIBW_BUILD_FRONTEND: pip, to avoid cibuildwheel v3's default `python -m build` wheel frontend - a manual `pip install setuptools setuptools_scm` before the legacy `python setup.py sdist` (the fallback once `python -m build` was unusable; setuptools is unbundled on Python 3.12+) Renaming the script removes the name collision, so both workarounds are dropped: wheels build through cibuildwheel's default frontend and the sdist is built with an isolated `python -m build --sdist`. * Drop ilammy/msvc-dev-cmd in favor of meson --vsenv ilammy/msvc-dev-cmd is unmaintained (last release Jan 2024) and still runs on Node.js 20, which GitHub Actions deprecates on June 16 2026. Its only job was to put cl.exe on PATH so the Windows build uses MSVC. That action is unnecessary: setuptools finds MSVC natively for the extension, and meson can do the same for the static libs via its built-in vswhere/vcvars detection. Pass meson --vsenv on Windows to force MSVC activation (otherwise meson would prefer the gcc/clang the runner ships on PATH, causing an ABI mismatch with the MSVC-built extension), and remove the msvc-dev-cmd step from both workflows. * Build via 'meson compile' on Windows so MSVC env reaches ninja --vsenv activates the MSVC environment inside meson's configure process, but build_ots.py then ran bare ninja as a separate subprocess that didn't inherit it, so cl.exe was missing at compile time ("Could not find compiler cl in PATH"). Drive the Windows build through 'meson compile', which re-activates the stored vsenv setting before invoking ninja. The Unix path keeps calling ninja directly and is unchanged. * Use 'meson compile' on all platforms Now that the Windows build drives ninja through 'meson compile', use it everywhere instead of branching on platform. 'meson compile' autodetects the ninja backend and just runs ninja on Linux/macOS, so it's a drop-in for the previous bare-ninja call while keeping the MSVC env activation on Windows. Removes the per-platform condition in make(). * Re-enable `fail-fast`
PreviousNext