将 py 文件编译为 pyd(原创)

将 py 文件编译为 pyd

将 py 文件编译为 pyd 可以起到保护源代码的作用,使用 cython 和 nuitka 都可以实现,cython 编译出来的 pyd 体积比较小,但是需要写 setup.py 配置文件,编译时遇到的问题也比较多;nuitka 编译出来的体积比较大,但是不用写配置文件,编译过程相对比较顺利。

使用 Cython 编译 pyd

,网上一般配合 VisualStudio 来编译,不过 VS 的体积过于臃肿,下面将使用 MingW64 编译的过程记录如下(win7 + python 3.8 32位):

1、安装 MingW64

下载 w64devkit-x86-2.2.0.7z,需要注意的是:MingW64 的版本必须与本地的 python 环境一致,32位 python 要配合 32 位的 MingW64,解压缩后将 MingW64 的 bin 路径加入到 PATH 环境变量中
打开命令行窗口,执行 gcc -v,能够正确显示 GCC 的版本号,表示已经正确安装 MingW64

2、安装 cython

pip install cython

3、编写宿主程序

假设需要保护的代码为 example.py,需要将其中代码封装到一个函数中,将 example.py编译为 pyd 文件后,需要在宿主程序中调用该函数。
原 example.py*:

print("Hello World!")

新 example.py

# example.py
def say_hello():
    print("Hello World!")

宿主程序 main.py

# main.py
from example import *

say_hello()

4、编写编译设置文件 setup.py

在 example.py、main.py 相同目录创建 setup.py

# setup.py
from distutils.core import setup
from Cython.Build import cythonize
 
setup(
    # 下面的 example.py 根据需要保护的脚本修改
    ext_modules = cythonize("example.py", compiler_directives={'language_level' : "3"}),
    include_dirs=[],
    libraries=[],
    library_dirs=[],
    extra_compile_args=['-std=c++11'],  # 如果你需要C++特性的话
)

下面是一个改良版的配置文件,可以稍加改动,执行自动将宿主程序复制到输出目录,pyd文件更名等操作

import setuptools
from distutils.core import setup
from Cython.Build import cythonize
import os


'''
该文件的执行需要的在Terminal中输入   python setup.py build_ext --inplace
使用Cpython 编译python文件,关键函数编译成pyd文件(相当于dll)
'''

# 针对多文件情况设置,单文件就只写一个就行
key_funs = ["example.py"]

 
setup(
    name="testapp",
    ext_modules=cythonize(key_funs),
)

 
'''
1、将编译后的pyd文件的命名更改成与原py文件一致
2、删除编译后得到的c文件和原py文件
'''

files = os.listdir(os.getcwd())
print(files)

for fi in files:
    if fi.__contains__(".pyd"):
        re_name = fi.split(".")[0] + ".pyd"
        print(re_name)
        print(fi, re_name)
        os.rename(fi, re_name)
    elif fi.__contains__(".c") or fi in key_funs:
        os.remove(fi)
        print(fi)

# 运行方式 在原目录的命令行下执行
# python setup.py build_ext --inplace
# 用 GCC 编译使用以下命令
# python setup.py build_ext --compiler=mingw32

5、编译

执行以下命令

python setup.py build_ext --compiler=mingw32

会在当前文件夹下生成 build 文件夹,其中 lib.win32-cpython-38 文件夹下包含了编译的 pyd 文件 example.cp38-win32.pyd,配合 main.py 即可脱离 cython 运行(将其更名为 example.pyd 不影响正常运行)。

补充:python_64bit + MingW64_64bit 下遇到的坑

1、Unknown MS Compiler version 1928

修改 Python 安装目录 \Lib\distutils\cygwinccompiler.py 文件,在 get_msvcr 函数中 else 之前添加判断代码,判断 msc_ver为1928,并指定编译器为 vcruntime140。

elif msc_ver == '1928':
	# MinGW
	return ['vcruntime140']

2、error: enumerator value for '__pyx_check_sizeof_voidp'

解决:执行编译命令时需加上-DMS_WIN64参数

python setup.py build_ext --compiler=mingw32 -DMS_WIN64

如果使用的是脚本,可以做如下修改

setup(ext_modules=cythonize(filePath), script_args=["build_ext", "-b", absPath, "-t", build_tmp_dir])
setup(ext_modules=cythonize(filePath, language_level=3), script_args=["build_ext", "-b", absPath, "-t", build_tmp_dir, "--inplace", "-DMS_WIN64"])

3、ld.exe: cannot find -lvcruntime140错误。

复制 C:\Program Files\Python\vcruntime140.dll 到一个文件夹中,执行以下命令:

gendef vcruntime140.dll
dlltool -D vcruntime140.dll -d vcruntime140.def -l libvcruntime140.a

将生成的 libvcruntime140.a 复制到 C:\ProgramFiles\Python\libs 目录即可。

附注:以下可供参考,不过本次实践都没用到
cython: 使用mingw编译器
https://blog.csdn.net/tianxifeng/article/details/103160925
https://www.cnblogs.com/cokefentas/p/16787325.html
https://blog.csdn.net/qq_30386941/article/details/126620215

使用 nuitka 编译

将 py 文件编译成 pyd

nuitka --module test.py

将 py 项目编译成 exe

# 假设主程序文件为 main.py
nuitka --onefile main.py

nuitka 无法指定 MWing64 编译器的位置,编译时默认情况下会自动下载编译器(MWing64)和依赖项(depends),也可以手工下载后解压到指定位置,再次运行 nuitka 就不会再重复下载了。
Ming64 保存位置
C:\Users<用户名>\AppData\Local\Nuitka\Nuitka\Cache\downloads\gcc\x86\14.2.0posix-19.1.1-12.0.0-msvcrt-r2
depends 保存位置
C:\Users<用户名>\AppData\Local\Nuitka\Nuitka\Cache\downloads\depends\x86

posted @ 2025-05-07 11:35  汉学  阅读(370)  评论(0)    收藏  举报