Skip to content

Commit acb9ee0

Browse files
committed
Meson build, replacing setuptools
Limitation: The sdist (source distribution for PyPI) now contains the whole of mmCoreAndDevices and more (33 MiB). While technically functional, we should fix this before merging this change. Update mmCoreAndDevices to latest (which has meson.build for MMCore and MMDevice). Add Meson build file. Use meson-python so that `python -m build` just works via pyproject.toml. Move the single source of truth for the version number from _version.py (now generated) to meson.build. The Meson build has several advantages: - Build details of MMDevice and MMCore come from their own build files, rather than being duplicated here in setup.py - Editable installs truly work (even if C++ files are edited) (Caveat: beware of importing pymmcore from the source root) - Since we are using a true C++ build system, controlling build options is much easier and cleaner than it was with setuptools The main disadvantage is that MANIFEST.in can no longer be used to control what gets included in the sdist (meson-python uses `meson dist` to produce the sdist, which includes all version-controlled files). However, once we are ready to use MMDevice and MMCore from independent repositories, this will no longer be an issue (and ends up being simpler than the error-prone MANIFEST.in).
1 parent f6d417e commit acb9ee0

File tree

10 files changed

+201
-196
lines changed

10 files changed

+201
-196
lines changed

.github/workflows/ci.yml

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,6 @@ concurrency:
1414
cancel-in-progress: true
1515

1616
jobs:
17-
# check that sdist contains all files and that extra files
18-
# are explicitly ignored in manifest or pyproject
19-
check-manifest:
20-
runs-on: ubuntu-latest
21-
steps:
22-
- uses: actions/checkout@v4
23-
with:
24-
fetch-depth: 0
25-
submodules: "recursive"
26-
- name: Check manifest
27-
run: pipx run check-manifest
28-
2917
test:
3018
name: Test ${{ matrix.os }} py${{ matrix.python-version }} np${{ matrix.numpy }}
3119
runs-on: ${{ matrix.os }}
@@ -53,6 +41,8 @@ jobs:
5341
with:
5442
submodules: "recursive"
5543

44+
- uses: ilammy/msvc-dev-cmd@v1
45+
5646
- name: Set up Python ${{ matrix.python-version }}
5747
uses: actions/setup-python@v2
5848
with:
@@ -86,14 +76,12 @@ jobs:
8676
with:
8777
submodules: "recursive"
8878

79+
- uses: ilammy/msvc-dev-cmd@v1
80+
8981
- name: Build wheels
9082
uses: pypa/[email protected]
9183
env:
9284
CIBW_ARCHS_MACOS: "${{ matrix.macos_arch }}"
93-
# Python on Linux is usually configured to add debug information,
94-
# which increases binary size by ~11-fold. Remove for the builds we
95-
# distribute.
96-
CIBW_ENVIRONMENT_LINUX: "LDFLAGS=-Wl,--strip-debug"
9785

9886
- uses: actions/upload-artifact@v3
9987
with:
@@ -110,8 +98,7 @@ jobs:
11098

11199
- name: Build sdist
112100
run: |
113-
pip install -U pip build check-manifest
114-
check-manifest
101+
pip install -U pip build
115102
python -m build --sdist
116103
117104
- uses: actions/upload-artifact@v3

.gitignore

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,7 @@ venv/
55

66
build/
77
dist/
8-
*.pdb
9-
*.py[cod]
10-
118
wheelhouse/
129

13-
src/pymmcore/pymmcore_swig_wrap.h
14-
src/pymmcore/pymmcore_swig_wrap.cpp
15-
src/pymmcore/_pymmcore_swig.*
16-
src/pymmcore/pymmcore_swig.py
17-
pymmcore.egg-info
1810
.mypy_cache/
1911

MANIFEST.in

Lines changed: 0 additions & 12 deletions
This file was deleted.

maintainer-notes.md

Lines changed: 17 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,10 @@ maintaining separate branches; this can ease transition when the device
2828
interface version changes. Such branches should be named `mmcore-x.y.z.w`.
2929

3030
When upgrading the MMCore version (by bumping the mmCoreAndDevices submodule
31-
commit), the pymmcore version in `_version.py` should be updated in synchrony.
32-
The versioning for the python package is taken dynamically from that file
33-
in the `[tool.setuptools.dynamic]` table in `pyproject.toml`.
31+
commit), the pymmcore version in `meson.build` should be updated in synchrony.
32+
The versioning for the python package is taken dynamically from that file, via
33+
the generated `_version.py` and the `project.dynamic` field in
34+
`pyproject.toml`.
3435

3536
## Building Binary Wheels and Source Distributions
3637

@@ -63,12 +64,14 @@ The package can be built in a few ways:
6364
This will build an sdist and wheel for the current platform and Python
6465
version, and place them in the `dist` directory.
6566

66-
3. Use `pip install -e .`
67+
3. Use `pip install --no-build-isolation -e .`
6768
This will build the extension module in-place and allow you to run tests,
68-
but will not build a wheel or sdist. Note that if you do this, you will
69-
need to rerun it each time you change the extension module.
70-
71-
69+
but will not build a wheel or sdist. `meson-python` (the build backend) will
70+
arrange to automatically rebuild the extension module each time it is
71+
imported. This method requires that you first manually install all of the
72+
build requirements listed in `pyproject.toml`. See the
73+
[meson-python docs](https://meson-python.readthedocs.io/en/latest/how-to-guides/editable-installs.html)
74+
for more information.
7275

7376
## Release procedure
7477

@@ -79,11 +82,11 @@ prefixed to the version:
7982
```bash
8083
git checkout main
8184
82-
vim src/pymmcore/_version.py # Remove .dev0
85+
vim meson.build # Remove .dev0
8386
git commit -a -m 'Version 1.2.3.42.4'
8487
git tag -a v1.2.3.42.4 -m Release
8588
86-
vim src/pymmcore/_version.py # Set version to 1.2.3.42.5.dev0
89+
vim meson.build # Set version to 1.2.3.42.5.dev0
8790
git commit -a -m 'Version back to dev'
8891
8992
git push upstream --follow-tags
@@ -101,8 +104,9 @@ and the binary wheels attached.
101104

102105
- The minimum version of python supported is declared in `pypyproject.toml`,
103106
in the `[project.requires-python]` section.
104-
- SWIG 4.x is required and automatically fetched via `pyproject.toml` under
105-
`[build-system.requires]`.
107+
- Meson (via `meson-python`), Ninja, and SWIG 4.x are required and
108+
automatically fetched via `pyproject.toml` under `[build-system.requires]`.
109+
- A C++ toolchain is required and must be available on your system.
106110
- The build-time versions of numpy are in `pyproject.toml`, in the
107111
`[build-system.requires]` section.
108112
- The run-time numpy dependency is declared in `pyproject.toml`, in the
@@ -111,7 +115,7 @@ and the binary wheels attached.
111115
determined by the settings in the `[tool.cibuildwheel]` section of
112116
`pyproject.toml`.
113117
- _We_ should provide wheels for all Python versions we claim to support,
114-
built agains the oldest NumPy version that we claim to support. Thus, any
118+
built against the oldest NumPy version that we claim to support. Thus, any
115119
issue with the build or our CI will limit the lowest supported versions.
116120

117121
## ABI Compatibility
@@ -125,18 +129,6 @@ and the binary wheels attached.
125129
[`oldest-supported-numpy`](https://github.com/scipy/oldest-supported-numpy)
126130
in our build requires.
127131

128-
## Building with debug symbols on Windows
129-
130-
Since there is no easy way to pass compile and linker options to `build_clib`,
131-
the easiest hack is to edit the local `setuptools` installation's
132-
`_distutils/_msvccompiler.py` to add the compiler flag `/Zi` and linker flag
133-
`/DEBUG:FULL` (see the method `initialize`). This produces `vc140.pdb`.
134-
135-
(The "normal" method would be to run `setup.py build_clib` and `setup.py
136-
build_ext` with the `--debug` option, and run with `python_d.exe`. But then we
137-
would need a debug build of NumPy, which is hard to build on Windows.)
138-
139-
140132
### Legacy Build Notes
141133

142134
Many of these notes are probably obviated by the use of cibuildwheel... but

meson.build

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# Copyright 2020-2024 Board of Regents of the University of Wisconsin System
2+
#
3+
# This library is free software; you can redistribute it and/or modify it under
4+
# the terms of the GNU Lesser General Public License, version 2.1, as published
5+
# by the Free Software Foundation.
6+
#
7+
# This library is distributed in the hope that it will be useful, but WITHOUT
8+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
9+
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
10+
# details.
11+
#
12+
# You should have received a copy of the GNU Lesser General Public License
13+
# along with this library; if not, write to the Free Software Foundation, Inc.,
14+
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15+
#
16+
# Author: Mark A. Tsuchida
17+
18+
project(
19+
'pymmcore',
20+
'cpp',
21+
version: '11.1.1.71.1.dev0',
22+
meson_version: '>=1.3.0',
23+
default_options: [
24+
'cpp_std=c++14',
25+
'warning_level=3',
26+
],
27+
# Until MMDevice and MMCore are available individually, we need to use them
28+
# from the same git submodule, so this is a bit of a hack:
29+
subproject_dir: 'mmCoreAndDevices',
30+
)
31+
32+
cxx = meson.get_compiler('cpp')
33+
if cxx.get_id() in ['gcc', 'clang']
34+
add_project_arguments('-Wno-deprecated', language: 'cpp') # throw()
35+
# Disable warnings triggered by SWIG-generated code:
36+
add_project_arguments('-Wno-unused-parameter', language: 'cpp')
37+
add_project_arguments('-Wno-unused-variable', language: 'cpp')
38+
endif
39+
if cxx.get_id() in ['msvc', 'clang-cl']
40+
add_project_arguments('-DNOMINMAX', language: 'cpp')
41+
add_project_arguments('-D_CRT_SECURE_NO_WARNINGS', language: 'cpp')
42+
# Disable warnings triggered by SWIG-generated code:
43+
add_project_arguments('/wd4100', language: 'cpp')
44+
add_project_arguments('/wd4101', language: 'cpp')
45+
add_project_arguments('/wd4127', language: 'cpp')
46+
add_project_arguments('/wd4456', language: 'cpp')
47+
add_project_arguments('/wd4706', language: 'cpp')
48+
endif
49+
50+
fs = import('fs')
51+
52+
python = import('python').find_installation(pure: false)
53+
54+
threads_dep = dependency('threads')
55+
56+
numpy_abs_incdir = run_command(
57+
python, '-c', 'import numpy; print(numpy.get_include())',
58+
check: true,
59+
).stdout().strip()
60+
# The "correct" way would be to "detect" NumPy as a dependency. Since we are
61+
# cutting corners, we need to use a relative path as if the NumPy headers are
62+
# part of this project.
63+
numpy_incdirs = include_directories(fs.relative_to(numpy_abs_incdir, '.'))
64+
65+
swig = find_program('swig', native: true)
66+
67+
# For now, use MMCore as a subproject. This may be changed to using as a
68+
# proper dependency via a wrap, but that will likely require better SWIG
69+
# support by Meson in order to get the SWIG include directories from the
70+
# dependency object.
71+
mmcore_proj = subproject(
72+
'MMCore',
73+
default_options: {
74+
'default_library': 'static',
75+
'tests': 'disabled', # Avoid Catch2 subproject in sdist
76+
},
77+
)
78+
mmcore_dep = mmcore_proj.get_variable('mmcore')
79+
80+
swig_include_dirs = mmcore_proj.get_variable('swig_include_dirs')
81+
82+
subdir('src/pymmcore')

pyproject.toml

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
# https://peps.python.org/pep-0517/
22
[build-system]
33
requires = [
4-
"setuptools >=61.0.0",
4+
"meson-python",
5+
"ninja",
56
"swig >=4.1",
67
# https://github.com/scipy/oldest-supported-numpy/blob/main/setup.cfg
78
"numpy==1.19.3; python_version=='3.8' and platform_machine=='aarch64' and platform_python_implementation != 'PyPy'",
@@ -13,7 +14,7 @@ requires = [
1314
# https://numpy.org/devdocs/dev/depending_on_numpy.html#adding-a-dependency-on-numpy
1415
"numpy>=2.0.0b1; python_version>='3.9'",
1516
]
16-
build-backend = "setuptools.build_meta"
17+
build-backend = "mesonpy"
1718

1819
# https://peps.python.org/pep-0621/
1920
[project]
@@ -46,15 +47,10 @@ test = ["pytest"]
4647
homepage = "https://micro-manager.org"
4748
repository = "https://github.com/micro-manager/pymmcore"
4849

49-
[tool.setuptools.dynamic]
50-
version = { attr = "pymmcore._version.__version__" }
51-
52-
[tool.setuptools.package-dir]
53-
"" = "src"
54-
55-
[tool.setuptools.package-data]
56-
"*" = ["py.typed", ".pyi"]
57-
50+
[tool.meson-python.args]
51+
setup = ['-Dstrip=true']
52+
install = ['--tags=python-runtime,runtime']
53+
dist = ['--include-subprojects']
5854

5955
[tool.cibuildwheel]
6056
# Skip 32-bit builds, musllinux, and PyPy wheels on all platforms

0 commit comments

Comments
 (0)