Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ENH: adjust default build options #383

Merged
merged 9 commits into from
Apr 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,44 @@ jobs:
env_vars: PYTHON
name: ${{ matrix.python }}

msvc:
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
python:
- '3.11'
meson:
-

steps:
- name: Checkout
uses: actions/checkout@v3

- name: Set up target Python
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python }}

- name: Install Ninja
run: python -m pip --disable-pip-version-check install ninja

- name: Setup MSVC
uses: bus1/cabuild/action/msdevshell@e22aba57d6e74891d059d66501b6b5aed8123c4d # v1
with:
architecture: x64

- name: Install Meson
run: python -m pip --disable-pip-version-check install "meson==${{ matrix.meson }}"
if: ${{ matrix.meson }}

- name: Install
run: python -m pip --disable-pip-version-check install .[test]

- name: Run tests
run: >-
python -m pytest --showlocals -vv

cygwin:
runs-on: windows-latest
strategy:
Expand Down
115 changes: 80 additions & 35 deletions docs/explanations/default-options.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,42 +8,87 @@
Default build options
*********************

Meson offers many `built-in options <https://mesonbuild.com/Builtin-options.html>`__,
and in the vast majority of cases those have good defaults. There are a couple
of cases however where ``meson-python`` either needs to or chooses to override
those with its own defaults. To view what those are for the version of
``meson-python`` you have installed, look at the *User defined options* section
of the output during the configure stage of the build (e.g., by running
``python -m build --wheel``). This will look something like:
Meson offers many `built-in options`__ to control how the project is
built and installed. In the vast majority of cases those have good
defaults. There are however a few options that ``meson-python``
overrides with its own defaults to adjust the build process to the
task of building Python wheels.

The default options specified by ``meson-python`` are overridden by
package specific options specified in ``pyproject.toml`` and by
options provided by the user at build time via the Python build
front-end. Refer to the :ref:`how-to-guides-meson-args` guide for
details.

The options used to build the project are summarized in the *User
defined options* section of the output of the ``meson setup`` stage of
the build, for example when running ``python -m build -w``. This will
look something like:

__ https://mesonbuild.com/Builtin-options.html

.. code-block:: text

User defined options
Native files : /home/username/code/project/.mesonpy-native-file.ini
debug : false
optimization : 2
prefix : /home/username/mambaforge/envs/project-dev
python.platlibdir: /home/username/mambaforge/envs/project-dev/lib/python3.10/site-packages
python.purelibdir: /home/username/mambaforge/envs/project-dev/lib/python3.10/site-packages
b_ndebug : if-release

Let's go through each option and why they are used:

- meson-python uses a native file, written to the build dir and named
``mesonpy-native-file.ini``, in order to point Meson at the correct
``python`` interpreter to use (the same one for which ``meson-python`` was
installed). This is necessary, because Meson may otherwise look for the first
Python interpreter on the PATH (usually the same one, but not always the
case). Users may use ``--native-file`` to pass a second native file to Meson;
Meson will merge contents of both native file, so as long as the
user-provided file does not try to pass a different path for the ``python``
binary, this will work without a conflict.
- The ``prefix`` and ``platlibdir``/``purelibdir`` options also point Meson at
that same interpreter and the environment in which it is installed.
- The ``debug``, ``optimization`` and ``b_ndebug`` options are overridden,
because Meson defaults to values that are appropriate for development, while
the main purpose of meson-python is to build release artifacts.

It is possible to override these defaults, either permanently in your
``pyproject.toml`` or at build time via the build frontend CLI.
See the :ref:`how-to-guides-meson-args` page for examples of both methods.
Native files: $builddir/meson-python-native-file.ini
buildtype : release
b_ndebug : if-release
b_vscrt : md

where the path to the build directory has been replaced with
``$builddir`` for clarity.

The options that ``meson-python`` specifies by default are:

.. option:: native-file=$builddir/meson-python-native-file.ini

``meson-python`` uses a native file to point Meson at the
``python`` interpreter that the build must target. This is the
Python interpreter that is used to run the Python build
front-end. Meson would otherwise look for the first Python
interpreter on the ``$PATH``, which may not be the same.

Additional ``--native-file`` options can be passed to ``meson
setup`` if further adjustments to the native environment need to be
made. Meson will merge the contents of all machine files. The
user-provided machine files must not override the path for the
``python`` binary.

.. option:: buildtype=release

The Meson default is to produce a debug build with binaries
compiled with debug symbols and, when compiling with MSVC, linking
to the Visual Studio debug runtime, see below. The main purpose of
``meson-python`` is to build release artifacts, therefore a more
appropriate `build type`__ is selected. A release build is compiled
without debug symbols and with compiler optimizations. Refer to the
`Meson documentation`__ for more details.

__ https://mesonbuild.com/Builtin-options.html#details-for-buildtype
__ https://mesonbuild.com/Builtin-options.html#core-options

.. option:: b_ndebug=if-release

For reasons related to backward compatibility, Meson does not
disable assertions for release builds. For most users this is a
surprising and undesired behavior. This option instructs Meson to
pass the ``-DNDEBUG`` option to the compilers, unless the build
type is set to something else than release.

.. option:: b_vscrt=md

With the default options, when compiling a debug build, Meson
instructs the MSVC compiler to use the debug version of the Visual
Studio runtime library. This causes the MSVC linker to look for the
debug build of all the linked DLLs. The Python distribution for
Windows does not contain a debug version of the Python DLL and
linking fails. These linking failures are surprising and hard to
diagnose. To avoid this issue when users explicitly asks for a
debug build, ``meson-python`` sets this options to instruct Meson
to compile with the release version of the Visual Studio
runtime. For more details, refer to the `Meson documentation`__ and
to the `Visual Studio documentation`__ . This option is ignored
when other compilers are used.

__ https://mesonbuild.com/Builtin-options.html#base-options
__ https://learn.microsoft.com/en-us/cpp/build/reference/md-mt-ld-use-run-time-library?view=msvc-170
6 changes: 3 additions & 3 deletions docs/how-to-guides/meson-args.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ precedence over, and can be used to override, the ones specified in

``meson-python`` overrides some of the default Meson options with
:ref:`settings <explanations-default-options>` more appropriate for
building Python wheel. User options specified via ``pyproject.toml``
or via Python build front-end config settings can be used to override
the ``meson-python`` defaults..
building a Python wheel. User options specified via ``pyproject.toml``
or via Python build front-end config settings override the
``meson-python`` defaults.


Examples
Expand Down
1 change: 0 additions & 1 deletion meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ py.install_sources(
'mesonpy/_dylib.py',
'mesonpy/_editable.py',
'mesonpy/_elf.py',
'mesonpy/_introspection.py',
'mesonpy/_tags.py',
'mesonpy/_util.py',
'mesonpy/_wheelfile.py',
Expand Down
69 changes: 23 additions & 46 deletions mesonpy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@
import mesonpy._compat
import mesonpy._dylib
import mesonpy._elf
import mesonpy._introspection
import mesonpy._tags
import mesonpy._util
import mesonpy._wheelfile
Expand Down Expand Up @@ -128,7 +127,8 @@ def _init_colors() -> Dict[str, str]:
_STYLES = _init_colors() # holds the color values, should be _COLORS or _NO_COLORS


_EXTENSION_SUFFIXES = importlib.machinery.EXTENSION_SUFFIXES.copy()
_SUFFIXES = importlib.machinery.all_suffixes()
_EXTENSION_SUFFIXES = importlib.machinery.EXTENSION_SUFFIXES
_EXTENSION_SUFFIX_REGEX = re.compile(r'^\.(?:(?P<abi>[^.]+)\.)?(?:so|pyd|dll)$')
assert all(re.match(_EXTENSION_SUFFIX_REGEX, x) for x in _EXTENSION_SUFFIXES)

Expand Down Expand Up @@ -378,25 +378,15 @@ def top_level_modules(self) -> Collection[str]:
modules = set()
for type_ in self._wheel_files:
for path, _ in self._wheel_files[type_]:
top_part = path.parts[0]
# file module
if top_part.endswith('.py'):
modules.add(top_part[:-3])
name, dot, ext = path.parts[0].partition('.')
if dot:
# module
suffix = dot + ext
if suffix in _SUFFIXES:
modules.add(name)
else:
# native module
for extension in _EXTENSION_SUFFIXES:
if top_part.endswith(extension):
modules.add(top_part[:-len(extension)])
# XXX: We assume the order in _EXTENSION_SUFFIXES
# goes from more specific to last, so we go
# with the first match we find.
break
else: # nobreak
# skip Windows import libraries
if top_part.endswith('.a'):
continue
# package module
modules.add(top_part)
# package
modules.add(name)
return modules

def _is_native(self, file: Union[str, pathlib.Path]) -> bool:
Expand All @@ -422,7 +412,7 @@ def _is_native(self, file: Union[str, pathlib.Path]) -> bool:
return True
return False

def _install_path(
def _install_path( # noqa: C901
self,
wheel_file: mesonpy._wheelfile.WheelFile,
counter: mesonpy._util.CLICounter,
Expand Down Expand Up @@ -477,7 +467,12 @@ def _install_path(
raise NotImplementedError("Bundling libraries in wheel is not supported on platform '{}'"
.format(platform.system()))

wheel_file.write(origin, location)
try:
wheel_file.write(origin, location)
except FileNotFoundError:
# work around for Meson bug, see https://github.com/mesonbuild/meson/pull/11655
if not os.fspath(origin).endswith('.pdb'):
raise

def _wheel_write_metadata(self, whl: mesonpy._wheelfile.WheelFile) -> None:
# add metadata
Expand Down Expand Up @@ -742,26 +737,15 @@ def _run(self, cmd: Sequence[str]) -> None:

def _configure(self, reconfigure: bool = False) -> None:
"""Configure Meson project."""
sys_paths = mesonpy._introspection.SYSCONFIG_PATHS
setup_args = [
f'--prefix={sys.base_prefix}',
os.fspath(self._source_dir),
os.fspath(self._build_dir),
f'--native-file={os.fspath(self._meson_native_file)}',
# TODO: Allow configuring these arguments
'-Ddebug=false',
# default build options
'-Dbuildtype=release',
'-Db_ndebug=if-release',
'-Doptimization=2',

# XXX: This should not be needed, but Meson is using the wrong paths
# in some scenarios, like on macOS.
# https://github.com/mesonbuild/meson-python/pull/87#discussion_r1047041306
'--python.purelibdir',
sys_paths['purelib'],
'--python.platlibdir',
sys_paths['platlib'],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll note that the change to --destdir and these options no longer being needed was discussed in #338 (comment). Looks like we agreed on that, and it has a good rationale.


# user args
'-Db_vscrt=md',
# user build options
*self._meson_args['setup'],
]
if reconfigure:
Expand Down Expand Up @@ -800,18 +784,11 @@ def _wheel_builder(self) -> _WheelBuilder:
self._install_plan,
)

def build_commands(self, install_dir: Optional[pathlib.Path] = None) -> Sequence[Sequence[str]]:
def build_commands(self) -> Sequence[Sequence[str]]:
assert self._ninja is not None # help mypy out
return (
(self._ninja, *self._meson_args['compile'],),
(
'meson',
'install',
'--only-changed',
'--destdir',
os.fspath(install_dir or self._install_dir),
*self._meson_args['install'],
),
('meson', 'install', '--no-rebuild', '--destdir', os.fspath(self._install_dir), *self._meson_args['install']),
)

@functools.lru_cache(maxsize=None)
Expand Down
27 changes: 0 additions & 27 deletions mesonpy/_introspection.py

This file was deleted.

4 changes: 3 additions & 1 deletion tests/test_wheel.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import platform
import re
import shutil
import stat
import subprocess
import sys
Expand All @@ -28,7 +29,8 @@
from distutils.sysconfig import get_config_var
EXT_SUFFIX = get_config_var('EXT_SUFFIX')

EXT_IMP_SUFFIX = re.sub(r'.pyd$', '.dll', EXT_SUFFIX) + '.a'
if sys.platform in {'win32', 'cygwin'}:
EXT_IMP_SUFFIX = re.sub(r'.(pyd|dll)$', '.lib' if shutil.which('cl.exe') else '.dll.a', EXT_SUFFIX)

# Test against the wheel tag generated by packaging module.
tag = next(packaging.tags.sys_tags())
Expand Down