aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/markdown/Builtin-options.md3
-rw-r--r--docs/markdown/Python-module.md9
-rw-r--r--man/meson.1441
-rw-r--r--mesonbuild/backend/ninjabackend.py34
-rw-r--r--mesonbuild/dependencies/python.py135
-rw-r--r--mesonbuild/modules/python.py14
-rw-r--r--test cases/python/9 extmodule limited api/limited.c14
-rw-r--r--test cases/python/9 extmodule limited api/meson.build7
-rw-r--r--test cases/python/9 extmodule limited api/test_limited.py6
-rw-r--r--unittests/pythontests.py31
10 files changed, 606 insertions, 88 deletions
diff --git a/docs/markdown/Builtin-options.md b/docs/markdown/Builtin-options.md
index b4039d6..6adc421 100644
--- a/docs/markdown/Builtin-options.md
+++ b/docs/markdown/Builtin-options.md
@@ -408,7 +408,8 @@ interpreter directly, even if it is a venv. Setting to `venv` will instead use
the paths for the virtualenv the python found installation comes from (or fail
if it is not a virtualenv). Setting to `auto` will check if the found
installation is a virtualenv, and use `venv` or `system` as appropriate (but
-never `prefix`). This option is mutually exclusive with the `platlibdir`/`purelibdir`.
+never `prefix`). Note that Conda environments are treated as `system`.
+This option is mutually exclusive with the `platlibdir`/`purelibdir`.
For backwards compatibility purposes, the default `install_env` is `prefix`.
diff --git a/docs/markdown/Python-module.md b/docs/markdown/Python-module.md
index 05ae57d..c02eed9 100644
--- a/docs/markdown/Python-module.md
+++ b/docs/markdown/Python-module.md
@@ -12,6 +12,15 @@ authors:
This module provides support for finding and building extensions against
python installations, be they python 2 or 3.
+If you want to build and package Python extension modules using tools
+compatible with [PEP-517](https://peps.python.org/pep-0517/), check out
+[meson-python](https://mesonbuild.com/meson-python/index.html).
+
+If you are building Python extension modules against a Python interpreter
+located in a venv or Conda environment, you probably want to set
+`python.install_venv=auto`;
+see [Python module options](Builtin-options.md#python-module) for details.
+
*Added 0.46.0*
## Functions
diff --git a/man/meson.1 b/man/meson.1
index 0221faa..10a5eb9 100644
--- a/man/meson.1
+++ b/man/meson.1
@@ -83,6 +83,93 @@ To set values, use the \-D command line argument like this.
.B meson configure \-Dopt1=value1 \-Dopt2=value2
+.SH The dist command
+
+.B meson dist
+generates a release archive.
+
+.B meson dist [
+.I options
+.B ]
+
+.SS "options:"
+.TP
+\fB\-h, \-\-help\fR
+show this help message and exit
+
+.TP
+\fB\-C WD\fR
+directory to cd into before running
+
+.TP
+\fB\-\-allow-dirty\fR
+Allow even when repository contains uncommitted changes.
+
+.TP
+\fB\-\-formats FORMATS\fR
+Comma separated list of archive types to create. Supports xztar
+(default), gztar, and zip.
+
+.TP
+\fB\-\-include\-subprojects\fR
+Include source code of subprojects that have been used for the build.
+
+.TP
+\fB\-\-no\-tests\fR
+Do not build and test generated packages.
+
+.SH The install command
+
+.B meson install
+installs the project.
+
+.B meson install [
+.I options
+.B ]
+
+.SS "options:"
+
+.TP
+\fB\-h, \-\-help\fR
+show this help message and exit
+
+.TP
+\fB\-C WD\fR
+directory to cd into before running
+
+.TP
+\fB\-\-no-rebuild\fR
+Do not rebuild before installing.
+
+.TP
+\fB\-\-only\-changed\fR
+Only overwrite files that are older than the copied file.
+
+.TP
+\fB\-\-quiet\fR
+Do not print every file that was installed.
+
+.TP
+\fB\-\-destdir DESTDIR\fR
+Sets or overrides DESTDIR environment. (Since 0.57.0)
+
+.TP
+\fB\-\-dry\-run, \-n\fR
+Doesn't actually install, but print logs. (Since 0.57.0)
+
+.TP
+\fB\-\-skip\-subprojects [SKIP_SUBPROJECTS]\fR
+Do not install files from given subprojects. (Since 0.58.0)
+
+.TP
+\fB\-\-tags TAGS\fR
+Install only targets having one of the given tags. (Since 0.60.0)
+
+.TP
+\fB\-\-strip\fR
+Strip targets even if strip option was not set during
+configure. (Since 0.62.0)
+
.SH The introspect command
Meson introspect is a command designed to make it simple to integrate with
@@ -113,6 +200,68 @@ print all unit tests
\fB\-\-help\fR
print command line help
+.SH The init command
+
+.B meson init
+creates a new project
+
+.B meson init [
+.I options
+.B ] [
+.I sourcefile...
+.B ]
+
+.SS "positional arguments:"
+.TP
+sourcefile...
+source files. default: all recognized files in current directory
+
+.SS "options:"
+.TP
+\fB\-h, \-\-help\fR
+show this help message and exit
+
+.TP
+\fB\-C WD\fR
+directory to cd into before running
+
+.TP
+\fB\-n NAME, \-\-name NAME\fR
+project name. default: name of current directory
+
+.TP
+\fB\-e EXECUTABLE, \-\-executable EXECUTABLE\fR
+executable name. default: project name
+
+.TP
+\fB\-d DEPS, \-\-deps DEPS\fR
+dependencies, comma-separated
+
+.TP
+\fB\-l {c,cpp,cs,cuda,d,fortran,java,objc,objcpp,rust,vala}, \
+\-\-language {c,cpp,cs,cuda,d,fortran,java,objc,objcpp,rust,vala}\fR
+project language. default: autodetected based on source files
+
+.TP
+\fB\-b, \-\-build
+build after generation
+
+.TP
+\fB\-\-builddir BUILDDIR\fR
+directory for build
+
+.TP
+\fB\-f, \-\-force\fR
+force overwrite of existing files and directories.
+
+.TP
+\fB\-\-type {executable,library}\fR
+project type. default: executable based project
+
+.TP
+\fB\-\-version VERSION\fR
+project version. default: 0.1
+
.SH The test command
.B meson test
@@ -214,6 +363,298 @@ show available versions of the specified project
\fBstatus\fR
show installed and available versions of currently used subprojects
+.SH The subprojects command
+
+.B meson subprojects
+is used to manage subprojects.
+
+.B meson subprojects [
+.I options
+.B ] [
+.I command
+.B ]
+
+.SS "options:"
+.TP
+\fB\-h, \-\-help\fR
+show this help message and exit
+
+.SS "commands:"
+.TP
+\fBupdate\fR
+Update all subprojects from wrap files
+
+.TP
+\fBcheckout\fR
+Checkout a branch (git only)
+
+.TP
+\fBdownload\fR
+Ensure subprojects are fetched, even if not in use. Already downloaded
+subprojects are not modified. This can be used to pre-fetch all
+subprojects and avoid downloads during configure.
+
+.TP
+\fBforeach\fR
+Execute a command in each subproject directory.
+
+.TP
+\fBpurge\fR
+Remove all wrap-based subproject artifacts
+
+.TP
+\fBpackagefiles\fR
+Manage the packagefiles overlay
+
+.SH The rewrite command
+
+.B meson rewrite
+modifies the project definition.
+
+.B meson rewrite [
+.I options
+.B ] [
+.I command
+.B ]
+
+.SS "options:"
+
+.TP
+\fB\-h, \-\-help\fR
+show this help message and exit
+
+.TP
+\fB\-s SRCDIR, \-\-sourcedir SRCDIR\fR
+Path to source directory.
+
+.TP
+\fB\-V, \-\-verbose\fR
+Enable verbose output
+
+.TP
+\fB\-S, \-\-skip\-errors\fR
+Skip errors instead of aborting
+
+.SS "commands:"
+
+.TP
+\fBtarget (tgt)\fR
+Modify a target
+
+.TP
+\fBkwargs\fR
+Modify keyword arguments
+
+.TP
+\fBdefault-options (def)\fR
+Modify the project default options
+
+.TP
+\fBcommand (cmd)\fR
+Execute a JSON array of commands
+
+.SH The compile command
+
+.B meson compile
+builds the project.
+
+.B meson compile [
+.I options
+.B ] [
+.I TARGET...
+.B ]
+
+.SS "positional arguments:"
+.TP
+\fBTARGET\fR
+Targets to build. Target has the following format:
+[PATH_TO_TARGET/]TARGET_NAME.TARGET_SUFFIX[:TARGET_TYPE].
+
+.SS "options:"
+
+.TP
+\fB\-h, \-\-help\fR
+show this help message and exit
+
+.TP
+\fB\-\-clean\fR
+Clean the build directory.
+
+.TP
+\fB\-C WD\fR
+directory to cd into before running
+
+.TP
+\fB\-j JOBS, \-\-jobs JOBS\fR
+The number of worker jobs to run (if supported). If the value is less
+than 1 the build program will guess.
+
+.TP
+\fB\-l LOAD_AVERAGE, \-\-load-average LOAD_AVERAGE\fR
+The system load average to try to maintain (if supported).
+
+.TP
+\fB\-v, \-\-verbose\fR
+Show more verbose output.
+
+.TP
+\fB\-\-ninja\-args NINJA_ARGS\fR
+Arguments to pass to `ninja` (applied only on `ninja` backend).
+
+.TP
+\fB\-\-vs\-args VS_ARGS\fR
+Arguments to pass to `msbuild` (applied only on `vs` backend).
+
+.TP
+\fB\-\-xcode\-args XCODE_ARGS\fR
+Arguments to pass to `xcodebuild` (applied only on `xcode` backend).
+
+.SH The devenv command
+
+.B meson devenv
+runs commands in the developer environment.
+
+.B meson devenv [
+.I options
+.B ] [
+.I command
+.B ]
+
+.SS "positional arguments:"
+
+.TP
+\fBcommand\fR
+Command to run in developer environment (default: interactive shell)
+
+.SS "options:"
+
+.TP
+\fB\-h, \-\-help\fR
+show this help message and exit
+
+.TP
+\fB\-C BUILDDIR\fR
+Path to build directory
+
+.TP
+\fB\-\-workdir WORKDIR, \-w WORKDIR\fR
+Directory to cd into before running (default: builddir, Since 1.0.0)
+
+.TP
+\fB\-\-dump [DUMP]\fR
+Only print required environment (Since 0.62.0) Takes an optional file
+path (Since 1.1.0)
+
+.TP
+\fB\-\-dump-format {sh,export,vscode}\fR
+Format used with --dump (Since 1.1.0)
+
+.SH The env2mfile command
+
+.B meson env2mfile
+converts the current environment to a cross or native file.
+
+.B meson env2mfile [
+.I options
+.B ]
+
+.SS "options:"
+
+.TP
+\fB\-h, \-\-help\fR
+show this help message and exit
+
+.TP
+\fB\-\-debarch DEBARCH\fR
+The dpkg architecture to generate.
+
+.TP
+\fB\-\-gccsuffix GCCSUFFIX\fR
+A particular gcc version suffix if necessary.
+
+.TP
+\fB\-o OUTFILE\fR
+The output file.
+
+.TP
+\fB\-\-cross\fR
+Generate a cross compilation file.
+
+.TP
+\fB\-\-native\fR
+Generate a native compilation file.
+
+.TP
+\fB\-\-system SYSTEM\fR
+Define system for cross compilation.
+
+.TP
+\fB\-\-subsystem SUBSYSTEM\fR
+Define subsystem for cross compilation.
+
+.TP
+\fB\-\-kernel KERNEL\fR
+Define kernel for cross compilation.
+
+.TP
+\fB\-\-cpu CPU\fR
+Define cpu for cross compilation.
+
+.TP
+\fB\-\-cpu-family CPU_FAMILY\fR
+Define cpu family for cross compilation.
+
+.TP
+\fB\-\-endian {big,little}\fR
+Define endianness for cross compilation.
+
+.SH The format command
+
+.B meson format
+formats a meson source file.
+
+.B meson format [
+.I options
+.B ] [
+.I sources...
+.B ]
+
+.SS "positional arguments:"
+
+.TP
+\fBsources...\fR
+meson source files
+
+.SS "options:"
+
+.TP
+\fB-h, --help\fR
+show this help message and exit
+
+.TP
+\fB-q, --check-only\fR
+exit with 1 if files would be modified by meson format
+
+.TP
+\fB-i, --inplace\fR
+format files in-place
+
+.TP
+\fB-r, --recursive\fR
+recurse subdirs (requires --check-only or --inplace option)
+
+.TP
+\fB-c meson.format, --configuration meson.format\fR
+read configuration from meson.format
+
+.TP
+\fB-e, --editor-config\fR
+try to read configuration from .editorconfig
+
+.TP
+\fB-o OUTPUT, --output OUTPUT\fR
+output file (implies having exactly one input)
+
.SH EXIT STATUS
.TP
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
index 759ae9a..3052967 100644
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -1412,7 +1412,6 @@ class NinjaBackend(backends.Backend):
outname_rel = os.path.join(self.get_target_dir(target), fname)
src_list = target.get_sources()
resources = target.get_java_resources()
- class_list = []
compiler = target.compilers['java']
c = 'c'
m = 'm'
@@ -1430,10 +1429,8 @@ class NinjaBackend(backends.Backend):
if rel_src.endswith('.java'):
gen_src_list.append(raw_src)
- compile_args = self.determine_single_java_compile_args(target, compiler)
- for src in src_list + gen_src_list:
- plain_class_path = self.generate_single_java_compile(src, target, compiler, compile_args)
- class_list.append(plain_class_path)
+ compile_args = self.determine_java_compile_args(target, compiler)
+ class_list = self.generate_java_compile(src_list + gen_src_list, target, compiler, compile_args)
class_dep_list = [os.path.join(self.get_target_private_dir(target), i) for i in class_list]
manifest_path = os.path.join(self.get_target_private_dir(target), 'META-INF', 'MANIFEST.MF')
manifest_fullpath = os.path.join(self.environment.get_build_dir(), manifest_path)
@@ -1530,7 +1527,8 @@ class NinjaBackend(backends.Backend):
self.generate_generator_list_rules(target)
self.create_target_source_introspection(target, compiler, commands, rel_srcs, generated_rel_srcs)
- def determine_single_java_compile_args(self, target, compiler):
+ def determine_java_compile_args(self, target, compiler):
+ args = []
args = self.generate_basic_compiler_args(target, compiler)
args += target.get_java_args()
args += compiler.get_output_args(self.get_target_private_dir(target))
@@ -1544,20 +1542,30 @@ class NinjaBackend(backends.Backend):
args += ['-sourcepath', sourcepath]
return args
- def generate_single_java_compile(self, src, target, compiler, args):
+ def generate_java_compile(self, srcs, target, compiler, args):
deps = [os.path.join(self.get_target_dir(l), l.get_filename()) for l in target.link_targets]
generated_sources = self.get_target_generated_sources(target)
for rel_src in generated_sources.keys():
if rel_src.endswith('.java'):
deps.append(rel_src)
- rel_src = src.rel_to_builddir(self.build_to_src)
- plain_class_path = src.fname[:-4] + 'class'
- rel_obj = os.path.join(self.get_target_private_dir(target), plain_class_path)
- element = NinjaBuildElement(self.all_outputs, rel_obj, self.compiler_to_rule_name(compiler), rel_src)
+
+ rel_srcs = []
+ plain_class_paths = []
+ rel_objs = []
+ for src in srcs:
+ rel_src = src.rel_to_builddir(self.build_to_src)
+ rel_srcs.append(rel_src)
+
+ plain_class_path = src.fname[:-4] + 'class'
+ plain_class_paths.append(plain_class_path)
+ rel_obj = os.path.join(self.get_target_private_dir(target), plain_class_path)
+ rel_objs.append(rel_obj)
+ element = NinjaBuildElement(self.all_outputs, rel_objs, self.compiler_to_rule_name(compiler), rel_srcs)
element.add_dep(deps)
element.add_item('ARGS', args)
+ element.add_item('FOR_JAR', self.get_target_filename(target))
self.add_build(element)
- return plain_class_path
+ return plain_class_paths
def generate_java_link(self):
rule = 'java_LINKER'
@@ -2376,7 +2384,7 @@ class NinjaBackend(backends.Backend):
def generate_java_compile_rule(self, compiler):
rule = self.compiler_to_rule_name(compiler)
command = compiler.get_exelist() + ['$ARGS', '$in']
- description = 'Compiling Java object $in'
+ description = 'Compiling Java sources for $FOR_JAR'
self.add_rule(NinjaRule(rule, command, [], description))
def generate_cs_compile_rule(self, compiler: 'CsCompiler') -> None:
diff --git a/mesonbuild/dependencies/python.py b/mesonbuild/dependencies/python.py
index 27340eb..17c807c 100644
--- a/mesonbuild/dependencies/python.py
+++ b/mesonbuild/dependencies/python.py
@@ -161,69 +161,6 @@ class _PythonDependencyBase(_Base):
else:
self.major_version = 2
-
-class PythonPkgConfigDependency(PkgConfigDependency, _PythonDependencyBase):
-
- def __init__(self, name: str, environment: 'Environment',
- kwargs: T.Dict[str, T.Any], installation: 'BasicPythonExternalProgram',
- libpc: bool = False):
- if libpc:
- mlog.debug(f'Searching for {name!r} via pkgconfig lookup in LIBPC')
- else:
- mlog.debug(f'Searching for {name!r} via fallback pkgconfig lookup in default paths')
-
- PkgConfigDependency.__init__(self, name, environment, kwargs)
- _PythonDependencyBase.__init__(self, installation, kwargs.get('embed', False))
-
- if libpc and not self.is_found:
- mlog.debug(f'"python-{self.version}" could not be found in LIBPC, this is likely due to a relocated python installation')
-
- # pkg-config files are usually accurate starting with python 3.8
- if not self.link_libpython and mesonlib.version_compare(self.version, '< 3.8'):
- self.link_args = []
-
-
-class PythonFrameworkDependency(ExtraFrameworkDependency, _PythonDependencyBase):
-
- def __init__(self, name: str, environment: 'Environment',
- kwargs: T.Dict[str, T.Any], installation: 'BasicPythonExternalProgram'):
- ExtraFrameworkDependency.__init__(self, name, environment, kwargs)
- _PythonDependencyBase.__init__(self, installation, kwargs.get('embed', False))
-
-
-class PythonSystemDependency(SystemDependency, _PythonDependencyBase):
-
- def __init__(self, name: str, environment: 'Environment',
- kwargs: T.Dict[str, T.Any], installation: 'BasicPythonExternalProgram'):
- SystemDependency.__init__(self, name, environment, kwargs)
- _PythonDependencyBase.__init__(self, installation, kwargs.get('embed', False))
-
- # match pkg-config behavior
- if self.link_libpython:
- # link args
- if mesonlib.is_windows():
- self.find_libpy_windows(environment, limited_api=False)
- else:
- self.find_libpy(environment)
- else:
- self.is_found = True
-
- # compile args
- inc_paths = mesonlib.OrderedSet([
- self.variables.get('INCLUDEPY'),
- self.paths.get('include'),
- self.paths.get('platinclude')])
-
- self.compile_args += ['-I' + path for path in inc_paths if path]
-
- # https://sourceforge.net/p/mingw-w64/mailman/message/30504611/
- # https://github.com/python/cpython/pull/100137
- if mesonlib.is_windows() and self.get_windows_python_arch().endswith('64') and mesonlib.version_compare(self.version, '<3.12'):
- self.compile_args += ['-DMS_WIN64=']
-
- if not self.clib_compiler.has_header('Python.h', '', environment, extra_args=self.compile_args)[0]:
- self.is_found = False
-
def find_libpy(self, environment: 'Environment') -> None:
if self.is_pypy:
if self.major_version == 3:
@@ -311,9 +248,15 @@ class PythonSystemDependency(SystemDependency, _PythonDependencyBase):
lib = Path(self.variables.get('base_prefix')) / libpath
elif self.platform.startswith('mingw'):
if self.static:
- libname = self.variables.get('LIBRARY')
+ if limited_api:
+ libname = self.variables.get('ABI3DLLLIBRARY')
+ else:
+ libname = self.variables.get('LIBRARY')
else:
- libname = self.variables.get('LDLIBRARY')
+ if limited_api:
+ libname = self.variables.get('ABI3LDLIBRARY')
+ else:
+ libname = self.variables.get('LDLIBRARY')
lib = Path(self.variables.get('LIBDIR')) / libname
else:
raise mesonlib.MesonBugException(
@@ -347,6 +290,68 @@ class PythonSystemDependency(SystemDependency, _PythonDependencyBase):
self.link_args = largs
self.is_found = True
+class PythonPkgConfigDependency(PkgConfigDependency, _PythonDependencyBase):
+
+ def __init__(self, name: str, environment: 'Environment',
+ kwargs: T.Dict[str, T.Any], installation: 'BasicPythonExternalProgram',
+ libpc: bool = False):
+ if libpc:
+ mlog.debug(f'Searching for {name!r} via pkgconfig lookup in LIBPC')
+ else:
+ mlog.debug(f'Searching for {name!r} via fallback pkgconfig lookup in default paths')
+
+ PkgConfigDependency.__init__(self, name, environment, kwargs)
+ _PythonDependencyBase.__init__(self, installation, kwargs.get('embed', False))
+
+ if libpc and not self.is_found:
+ mlog.debug(f'"python-{self.version}" could not be found in LIBPC, this is likely due to a relocated python installation')
+
+ # pkg-config files are usually accurate starting with python 3.8
+ if not self.link_libpython and mesonlib.version_compare(self.version, '< 3.8'):
+ self.link_args = []
+
+
+class PythonFrameworkDependency(ExtraFrameworkDependency, _PythonDependencyBase):
+
+ def __init__(self, name: str, environment: 'Environment',
+ kwargs: T.Dict[str, T.Any], installation: 'BasicPythonExternalProgram'):
+ ExtraFrameworkDependency.__init__(self, name, environment, kwargs)
+ _PythonDependencyBase.__init__(self, installation, kwargs.get('embed', False))
+
+
+class PythonSystemDependency(SystemDependency, _PythonDependencyBase):
+
+ def __init__(self, name: str, environment: 'Environment',
+ kwargs: T.Dict[str, T.Any], installation: 'BasicPythonExternalProgram'):
+ SystemDependency.__init__(self, name, environment, kwargs)
+ _PythonDependencyBase.__init__(self, installation, kwargs.get('embed', False))
+
+ # match pkg-config behavior
+ if self.link_libpython:
+ # link args
+ if mesonlib.is_windows():
+ self.find_libpy_windows(environment, limited_api=False)
+ else:
+ self.find_libpy(environment)
+ else:
+ self.is_found = True
+
+ # compile args
+ inc_paths = mesonlib.OrderedSet([
+ self.variables.get('INCLUDEPY'),
+ self.paths.get('include'),
+ self.paths.get('platinclude')])
+
+ self.compile_args += ['-I' + path for path in inc_paths if path]
+
+ # https://sourceforge.net/p/mingw-w64/mailman/message/30504611/
+ # https://github.com/python/cpython/pull/100137
+ if mesonlib.is_windows() and self.get_windows_python_arch().endswith('64') and mesonlib.version_compare(self.version, '<3.12'):
+ self.compile_args += ['-DMS_WIN64=']
+
+ if not self.clib_compiler.has_header('Python.h', '', environment, extra_args=self.compile_args)[0]:
+ self.is_found = False
+
@staticmethod
def log_tried() -> str:
return 'sysconfig'
diff --git a/mesonbuild/modules/python.py b/mesonbuild/modules/python.py
index b599a5e..013723d 100644
--- a/mesonbuild/modules/python.py
+++ b/mesonbuild/modules/python.py
@@ -184,13 +184,9 @@ class PythonInstallation(_ExternalProgramHolder['PythonExternalProgram']):
new_cpp_args.append(limited_api_definition)
kwargs['cpp_args'] = new_cpp_args
- # When compiled under MSVC, Python's PC/pyconfig.h forcibly inserts pythonMAJOR.MINOR.lib
- # into the linker path when not running in debug mode via a series #pragma comment(lib, "")
- # directives. We manually override these here as this interferes with the intended
- # use of the 'limited_api' kwarg
+ # On Windows, the limited API DLL is python3.dll, not python3X.dll.
for_machine = kwargs['native']
- compilers = self.interpreter.environment.coredata.compilers[for_machine]
- if any(compiler.get_id() == 'msvc' for compiler in compilers.values()):
+ if self.interpreter.environment.machines[for_machine].is_windows():
pydep_copy = copy.copy(pydep)
pydep_copy.find_libpy_windows(self.env, limited_api=True)
if not pydep_copy.found():
@@ -199,6 +195,12 @@ class PythonInstallation(_ExternalProgramHolder['PythonExternalProgram']):
new_deps.remove(pydep)
new_deps.append(pydep_copy)
+ # When compiled under MSVC, Python's PC/pyconfig.h forcibly inserts pythonMAJOR.MINOR.lib
+ # into the linker path when not running in debug mode via a series #pragma comment(lib, "")
+ # directives. We manually override these here as this interferes with the intended
+ # use of the 'limited_api' kwarg
+ compilers = self.interpreter.environment.coredata.compilers[for_machine]
+ if any(compiler.get_id() == 'msvc' for compiler in compilers.values()):
pyver = pydep.version.replace('.', '')
python_windows_debug_link_exception = f'/NODEFAULTLIB:python{pyver}_d.lib'
python_windows_release_link_exception = f'/NODEFAULTLIB:python{pyver}.lib'
diff --git a/test cases/python/9 extmodule limited api/limited.c b/test cases/python/9 extmodule limited api/limited.c
index 0d1c718..b977419 100644
--- a/test cases/python/9 extmodule limited api/limited.c
+++ b/test cases/python/9 extmodule limited api/limited.c
@@ -6,12 +6,22 @@
#error Wrong value for Py_LIMITED_API
#endif
+static PyObject *
+hello(PyObject * Py_UNUSED(self), PyObject * Py_UNUSED(args)) {
+ return PyUnicode_FromString("hello world");
+}
+
+static struct PyMethodDef methods[] = {
+ { "hello", hello, METH_NOARGS, NULL },
+ { NULL, NULL, 0, NULL },
+};
+
static struct PyModuleDef limited_module = {
PyModuleDef_HEAD_INIT,
- "limited_api_test",
+ "limited",
NULL,
-1,
- NULL
+ methods
};
PyMODINIT_FUNC PyInit_limited(void) {
diff --git a/test cases/python/9 extmodule limited api/meson.build b/test cases/python/9 extmodule limited api/meson.build
index 68afc96..bdf1b7b 100644
--- a/test cases/python/9 extmodule limited api/meson.build
+++ b/test cases/python/9 extmodule limited api/meson.build
@@ -14,3 +14,10 @@ ext_mod = py.extension_module('not_limited',
'not_limited.c',
install: true,
)
+
+test('load-test',
+ py,
+ args: [files('test_limited.py')],
+ env: { 'PYTHONPATH': meson.current_build_dir() },
+ workdir: meson.current_source_dir()
+)
diff --git a/test cases/python/9 extmodule limited api/test_limited.py b/test cases/python/9 extmodule limited api/test_limited.py
new file mode 100644
index 0000000..fcbf67b
--- /dev/null
+++ b/test cases/python/9 extmodule limited api/test_limited.py
@@ -0,0 +1,6 @@
+from limited import hello
+
+def test_hello():
+ assert hello() == "hello world"
+
+test_hello()
diff --git a/unittests/pythontests.py b/unittests/pythontests.py
index 6079bd5..aaea906 100644
--- a/unittests/pythontests.py
+++ b/unittests/pythontests.py
@@ -11,7 +11,7 @@ from .allplatformstests import git_init
from .baseplatformtests import BasePlatformTests
from .helpers import *
-from mesonbuild.mesonlib import MachineChoice, TemporaryDirectoryWinProof
+from mesonbuild.mesonlib import MachineChoice, TemporaryDirectoryWinProof, is_windows
from mesonbuild.modules.python import PythonModule
class PythonTests(BasePlatformTests):
@@ -86,3 +86,32 @@ python = pymod.find_installation('python3', required: true)
if shutil.which('python2') or PythonModule._get_win_pythonpath('python2'):
raise self.skipTest('python2 installed, already tested')
self._test_bytecompile()
+
+ def test_limited_api_linked_correct_lib(self):
+ if not is_windows():
+ return self.skipTest('Test only run on Windows.')
+
+ testdir = os.path.join(self.src_root, 'test cases', 'python', '9 extmodule limited api')
+
+ self.init(testdir)
+ self.build()
+
+ from importlib.machinery import EXTENSION_SUFFIXES
+ limited_suffix = EXTENSION_SUFFIXES[1]
+
+ limited_library_path = os.path.join(self.builddir, f'limited{limited_suffix}')
+ self.assertPathExists(limited_library_path)
+
+ limited_dep_name = 'python3.dll'
+ if shutil.which('dumpbin'):
+ # MSVC
+ output = subprocess.check_output(['dumpbin', '/DEPENDENTS', limited_library_path],
+ stderr=subprocess.STDOUT)
+ self.assertIn(limited_dep_name, output.decode())
+ elif shutil.which('objdump'):
+ # mingw
+ output = subprocess.check_output(['objdump', '-p', limited_library_path],
+ stderr=subprocess.STDOUT)
+ self.assertIn(limited_dep_name, output.decode())
+ else:
+ raise self.skipTest('Test needs either dumpbin(MSVC) or objdump(mingw).')