提问人:geoff22873 提问时间:11/12/2023 最后编辑:geoff22873 更新时间:11/22/2023 访问量:102
在 Windows 上编译用 Zig 编写的 CPython 扩展
compile CPython extension written in Zig on Windows
问:
Zig 能够导入 C 库,因此可用于编写 CPython 扩展并对其进行编译。这对我来说可能真的很有用。
这是我的 simple.zig Python 扩展
const py = @cImport({
@cDefine("PY_SSIZE_T_CLEAN", {});
@cInclude("Python.h");
});
const std = @import("std");
const print = std.debug.print;
const PyObject = py.PyObject;
const PyMethodDef = py.PyMethodDef;
const PyModuleDef = py.PyModuleDef;
const PyModuleDef_Base = py.PyModuleDef_Base;
const Py_BuildValue = py.Py_BuildValue;
const PyModule_Create = py.PyModule_Create;
const METH_NOARGS = py.METH_NOARGS;
fn hello(self: [*c]PyObject, args: [*c]PyObject) callconv(.C) [*]PyObject {
_ = self;
_ = args;
print("welcom to ziglang\n", .{});
return Py_BuildValue("");
}
var Methods = [_]PyMethodDef{
PyMethodDef{
.ml_name = "hello",
.ml_meth = hello,
.ml_flags = METH_NOARGS,
.ml_doc = null,
},
PyMethodDef{
.ml_name = null,
.ml_meth = null,
.ml_flags = 0,
.ml_doc = null,
},
};
var module = PyModuleDef{
.m_base = PyModuleDef_Base{
.ob_base = PyObject{
.ob_refcnt = 1,
.ob_type = null,
},
.m_init = null,
.m_index = 0,
.m_copy = null,
},
.m_name = "simple",
.m_doc = null,
.m_size = -1,
.m_methods = &Methods,
.m_slots = null,
.m_traverse = null,
.m_clear = null,
.m_free = null,
};
pub export fn PyInit_simple() [*]PyObject {
return PyModule_Create(&module);
}
当我使用 Zig 编译器编译用 Zig 编写的 CPython 扩展时,Python 无法导入 DLL。我使用的是 Windows 10。
zig build-lib -lc -dynamic -target x86_64-windows-msvc -I"C:\Users\me\Anaconda3\include" -L"C:\Users\me\Anaconda3\libs" -l"python39" simple.zig
当我尝试导入simple.dll时,Python说没有找到模块。我正在使用 os 模块将包含我的扩展 DLL 的目录添加到扩展搜索位置,因为我相信自 Python 3.8 以来在 Windows 上是必需的。
import os
os.add_dll_directory(r"C:\Users\me\my_zig_project")
import simple
ModuleNotFoundError: No module named 'simple'
我怀疑问题是 Windows 上的 CPython 是使用 MSVC 编译器编译的(MSVC 的版本取决于 Python 版本),并且即使我将目标环境指定为 Windows MSVC,Zig 编译器也不知何故不兼容。
有没有办法编译用 Zig 编写的 CPython 扩展以在 Windows 上运行?
如果可能的话,一个最小的例子将不胜感激。
答:
我无法使用典型的方法使用 Zig 创建 Python 模块,但我已经能够使用 wheel
模块创建模块,这些模块链接使用 Zig 编译的 DLL 文件,然后可以使用 Pip 安装这些文件。这是一个最小的例子。setuptools
如果您的系统上尚未安装 setuptools
和 wheel
模块,则需要先安装它们。我从这个内容丰富的 SO 答案中获得了灵感。
创建如下所示的目录结构:
hello_zig_mod/
|
|-- setup.py
|
|-- hello/
|
|-- __init__.py
|
|-- hello.zig
该文件是将编译为 DLL 的 Zig 代码:hello.zig
const std = @import("std");
pub export fn message(msg: [*c]const u8) void {
std.debug.print("Hello, {s}!\n", .{std.mem.span(msg)});
}
该文件是用于初始化模块的 Python 文件。它链接到 DLL 并将其中的函数包装在 Python 函数中:__init__.py
message
from ctypes import *
import os
lib_path = os.path.join(os.path.dirname(__file__), 'hello.dll')
lib = CDLL(lib_path)
def message(msg):
lib.message(bytes(msg, 'utf-8'))
该文件用于构建可由 Pip 安装的轮子。您可以在我上面链接的答案中阅读有关此文件详细信息的更多信息。setup.py
from setuptools import setup, Distribution
class BinaryDistribution(Distribution):
def has_ext_modules(foo):
return True
setup(
name="hello",
packages=['hello'],
package_data={
'hello':['hello.dll'],
},
distclass=BinaryDistribution
)
进入目录并使用 Zig 编译器构建一个动态库。hello/
> zig build-lib .\hello.zig -dynamic
这将生成 DLL 以及其他一些项目。现在回到目录并构建轮子:hello_zig_mod
> python setup.py bdist_wheel
这会将轮子与其他工件一起构建为文件。您现在终于可以使用 Pip 安装轮子了:dist\
.whl
> pip install .\dist\hello-0.0.0-cp312-cp312-win_amd64.whl
签入 Python REPL:
>>> import hello
>>> hello.message("Zig Module")
Hello, Zig Module!
评论
看过这个视频 https://www.youtube.com/watch?v=9q-LHP7cMfg 我意识到我所需要的只是将我的 simple.dll重命名为 simple.pyd
这是一个最小的工作示例。创建 simple.zig,如下所示
const py = @cImport({
@cDefine("PY_SSIZE_T_CLEAN", {});
@cInclude("Python.h");
});
const std = @import("std");
const print = std.debug.print;
const PyObject = py.PyObject;
const PyMethodDef = py.PyMethodDef;
const PyModuleDef = py.PyModuleDef;
const PyModuleDef_Base = py.PyModuleDef_Base;
const Py_BuildValue = py.Py_BuildValue;
const PyModule_Create = py.PyModule_Create;
const METH_NOARGS = py.METH_NOARGS;
fn hello(self: [*c]PyObject, args: [*c]PyObject) callconv(.C) [*]PyObject {
_ = self;
_ = args;
print("welcome to ziglang\n", .{});
return Py_BuildValue("");
}
var Methods = [_]PyMethodDef{
PyMethodDef{
.ml_name = "hello",
.ml_meth = hello,
.ml_flags = METH_NOARGS,
.ml_doc = null,
},
PyMethodDef{
.ml_name = null,
.ml_meth = null,
.ml_flags = 0,
.ml_doc = null,
},
};
var module = PyModuleDef{
.m_base = PyModuleDef_Base{
.ob_base = PyObject{
.ob_refcnt = 1,
.ob_type = null,
},
.m_init = null,
.m_index = 0,
.m_copy = null,
},
.m_name = "simple",
.m_doc = null,
.m_size = -1,
.m_methods = &Methods,
.m_slots = null,
.m_traverse = null,
.m_clear = null,
.m_free = null,
};
pub export fn PyInit_simple() [*]PyObject {
return PyModule_Create(&module);
}
构建 simple.zig 以获取 simple.dll
zig build-lib -lc -dynamic -I"<directory containing Python.h>" -L"<directory containing python3.lib>" -l"python3" simple.zig
将 simple.dll 重命名为 simple.pyd
在包含 simple.pyd 的目录中运行 python.exe(python.exe 的 3.X 版本必须与您链接的 python3.lib 匹配)
>>> import simple
>>> simple.hello()
welcome to ziglang
评论