Skip to content

Commit 6d7de48

Browse files
committed
TST: add test package with internal shared libraries, installed in site-packages
1 parent 35348fc commit 6d7de48

File tree

12 files changed

+232
-0
lines changed

12 files changed

+232
-0
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# SPDX-FileCopyrightText: 2022 The meson-python developers
2+
#
3+
# SPDX-License-Identifier: MIT
4+
5+
project('sharedlib-in-package', 'c', version: '1.0.0')
6+
7+
py = import('python').find_installation(pure: false)
8+
9+
subdir('mypkg')
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# SPDX-FileCopyrightText: 2024 The meson-python developers
2+
#
3+
# SPDX-License-Identifier: MIT
4+
5+
import os
6+
import sys
7+
8+
9+
def _enable_sharedlib_loading():
10+
"""
11+
Ensure the `examplelib` and `examplelib2` shared libraries can be loaded on
12+
Windows.
13+
14+
One shared library is installed alongside this __init__.py file. Windows can
15+
load it because it searches for DLLs in the directory a .pyd (Python extension
16+
module) is located in. Cygwin does not. For a shared library in another
17+
directory inside the package, Windows also needs a hint.
18+
19+
This function is Windows-specific due to lack of RPATH support on Windows.
20+
It cannot find shared libraries installed within wheels unless we either
21+
amend the DLL search path or pre-load the DLL.
22+
23+
Note that `delvewheel` inserts a similar snippet into the main
24+
`__init__.py` of a package when it vendors external shared libraries.
25+
26+
.. note::
27+
28+
`os.add_dll_directory` is only available for Python >=3.8, and with
29+
the Conda `python` packages only works as advertised for >=3.10.
30+
If you require support for older versions, pre-loading the DLL
31+
with `ctypes.WinDLL` may be preferred (the SciPy code base has an
32+
example of this).
33+
"""
34+
basedir = os.path.dirname(__file__)
35+
subdir = os.path.join(basedir, 'sub')
36+
if os.name == "nt":
37+
os.add_dll_directory(subdir)
38+
elif sys.platform == "cygwin":
39+
os.environ["PATH"] = f"{os.environ['PATH']}:{basedir}:{subdir}"
40+
41+
42+
_enable_sharedlib_loading()
43+
44+
45+
from ._example import example_prod, example_sum
46+
47+
48+
__all__ = ['example_prod', 'example_sum']
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// SPDX-FileCopyrightText: 2022 The meson-python developers
2+
//
3+
// SPDX-License-Identifier: MIT
4+
5+
#include <Python.h>
6+
7+
#include "examplelib.h"
8+
#include "examplelib2.h"
9+
10+
static PyObject* example_sum(PyObject* self, PyObject *args)
11+
{
12+
int a, b;
13+
if (!PyArg_ParseTuple(args, "ii", &a, &b)) {
14+
return NULL;
15+
}
16+
17+
long result = sum(a, b);
18+
19+
return PyLong_FromLong(result);
20+
}
21+
22+
static PyObject* example_prod(PyObject* self, PyObject *args)
23+
{
24+
int a, b;
25+
if (!PyArg_ParseTuple(args, "ii", &a, &b)) {
26+
return NULL;
27+
}
28+
29+
long result = prod(a, b);
30+
31+
return PyLong_FromLong(result);
32+
}
33+
34+
static PyMethodDef methods[] = {
35+
{"example_prod", (PyCFunction)example_prod, METH_VARARGS, NULL},
36+
{"example_sum", (PyCFunction)example_sum, METH_VARARGS, NULL},
37+
{NULL, NULL, 0, NULL},
38+
};
39+
40+
static struct PyModuleDef module = {
41+
PyModuleDef_HEAD_INIT,
42+
"_example",
43+
NULL,
44+
-1,
45+
methods,
46+
};
47+
48+
PyMODINIT_FUNC PyInit__example(void)
49+
{
50+
return PyModule_Create(&module);
51+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// SPDX-FileCopyrightText: 2022 The meson-python developers
2+
//
3+
// SPDX-License-Identifier: MIT
4+
5+
#include "sub/mypkg_dll.h"
6+
7+
MYPKG_DLL int sum(int a, int b) {
8+
return a + b;
9+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// SPDX-FileCopyrightText: 2022 The meson-python developers
2+
//
3+
// SPDX-License-Identifier: MIT
4+
5+
#include "sub/mypkg_dll.h"
6+
7+
MYPKG_DLL int sum(int a, int b);
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# SPDX-FileCopyrightText: 2022 The meson-python developers
2+
#
3+
# SPDX-License-Identifier: MIT
4+
5+
if meson.get_compiler('c').get_id() in ['msvc', 'clang-cl', 'intel-cl']
6+
export_dll_args = ['-DMYPKG_DLL_EXPORTS']
7+
import_dll_args = ['-DMYPKG_DLL_IMPORTS']
8+
else
9+
export_dll_args = []
10+
import_dll_args = []
11+
endif
12+
13+
example_lib = shared_library(
14+
'examplelib',
15+
'examplelib.c',
16+
c_args: export_dll_args,
17+
install: true,
18+
install_dir: py.get_install_dir() / 'mypkg',
19+
)
20+
21+
example_lib_dep = declare_dependency(
22+
compile_args: import_dll_args,
23+
link_with: example_lib,
24+
)
25+
26+
subdir('sub')
27+
28+
py.extension_module(
29+
'_example',
30+
'_examplemod.c',
31+
dependencies: [example_lib_dep, example_lib2_dep],
32+
include_directories: 'sub',
33+
install: true,
34+
subdir: 'mypkg',
35+
install_rpath: '$ORIGIN',
36+
)
37+
38+
py.install_sources(
39+
['__init__.py'],
40+
subdir: 'mypkg',
41+
)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// SPDX-FileCopyrightText: 2022 The meson-python developers
2+
//
3+
// SPDX-License-Identifier: MIT
4+
5+
#include "mypkg_dll.h"
6+
7+
MYPKG_DLL int prod(int a, int b) {
8+
return a * b;
9+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// SPDX-FileCopyrightText: 2022 The meson-python developers
2+
//
3+
// SPDX-License-Identifier: MIT
4+
5+
#include "mypkg_dll.h"
6+
7+
MYPKG_DLL int prod(int a, int b);
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# SPDX-FileCopyrightText: 2022 The meson-python developers
2+
#
3+
# SPDX-License-Identifier: MIT
4+
5+
example_lib2 = shared_library(
6+
'examplelib2',
7+
'examplelib2.c',
8+
c_args: export_dll_args,
9+
install: true,
10+
install_dir: py.get_install_dir() / 'mypkg/sub',
11+
)
12+
13+
example_lib2_dep = declare_dependency(
14+
compile_args: import_dll_args,
15+
link_with: example_lib2,
16+
)
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#pragma once
2+
3+
// MYPKG_DLL
4+
// inspired by https://github.com/abseil/abseil-cpp/blob/20240116.2/absl/base/config.h#L736-L753
5+
// and https://github.com/scipy/scipy/blob/9ded83b51099eee745418ccbb30196db96c81f3f/scipy/_build_utils/src/scipy_dll.h
6+
//
7+
// When building the `examplelib` DLL, this macro expands to `__declspec(dllexport)`
8+
// so we can annotate symbols appropriately as being exported. When used in
9+
// headers consuming a DLL, this macro expands to `__declspec(dllimport)` so
10+
// that consumers know the symbol is defined inside the DLL. In all other cases,
11+
// the macro expands to nothing.
12+
// Note: MYPKG_DLL_{EX,IM}PORTS are set in mypkg/meson.build
13+
#if defined(MYPKG_DLL_EXPORTS)
14+
#define MYPKG_DLL __declspec(dllexport)
15+
#elif defined(MYPKG_DLL_IMPORTS)
16+
#define MYPKG_DLL __declspec(dllimport)
17+
#else
18+
#define MYPKG_DLL
19+
#endif

0 commit comments

Comments
 (0)