Python是解释型编程语言,代码可以不经过编译直接被解释器执行,如果直接开源发布py程序,对于某些不应公开的源码来说无疑是不可取的。那么如何安全地发布py程序而又不被反编译呢,经过对比pyd是最合适的发布方式。pyd是python源码经过Cython转换后再编译而来的Windows系统下Python可用的动态链接库,与dll动态链接库文件类似,区别在于pyd库可以被python直接引用,Linux系统下编译结果为so动态链接库。
Python是解释型动态编程语言,与JavaScript一样是边解释边执行。Python提供了高效的高级数据结构,还能简单有效地面向对象编程。Python语法和动态类型,以及解释型语言的特性,使它成为多数平台上写脚本和快速开发应用的编程语言,随着版本的不断更新和语言新功能的添加,逐渐被用于独立的、大型项目的开发。
Cython编程语言,它通过类似Python的语法来编写C扩展并可以被Python调用.既具备了Python快速开发的特点,又可以让代码运行起来像C一样快,同时还可以方便地调用C library。
MinGW,即 Minimalist GNU For Windows。它是一些头文件和端口库的集合,该集合允许在没有第三方动态链接库的情况下使用 GCC(GNU Compiler C)产生 Windows32 程序。MinGW 并不只是一个 C/C++ 编译器,而是一套 GNU 工具集合。除开 GCC (GNU 编译器集合) 以外,MinGW 还包含有一些其他的 GNU 程序开发工具 (比如 gawk bison 等等)。
他们的关系是python源码经过cython生成c文件,再通过MinGW编译成pyd动态链接库。pyd作为python的动态链接库,可以被py程序直接import使用。
Cython作为python模块,通过pip包管理器进行安装。
pip install cython
查看是否安装成功可以通过查看pip包列表
pip list
下载离线免安装版(https://sourceforge.net/projects/mingw-w64/files/mingw-w64/)(请下载最新的x86_64-posix-seh版本,win32版本不支持c++11的thread库),解压到非中文目录中(路径中不得包含中文),建议解压到D:\mingw64
。
在环境变量path
中添加路径D:\mingw64\bin
(建议用户变量和系统变量都添加上路径)。
在Python安装目录\Lib\distutils\
中添加distutils.cfg
文件,并写入以下内容。
[build]
compiler=mingw32
[build_ext]
compiler = mingw32
相关的软件编译器安装好后,就可以测试编译py文件为pyd,目的是通过报错信息来对应解决问题。
build.py
文件Python项目根目录下新建build.py
文件,用于执行编译。
# build.py
from distutils.core import setup
from Cython.Build import cythonize
# cythonize第一个参数为路径数组,路径数组可包含多个路径。
# libs/a/*.py表示libs/a/下所有py文件
setup(
name='My pyd',
ext_modules = cythonize(['libs/a/*.py','libs/b/*.py'], language_level=3),
)
python build.py build
(venv) C:\Users\Administrator\PycharmProjects\pythonPydTest>python build.py build
running build
running build_ext
building 'lib_a' extension
error: Unable to find vcvarsall.bat
解决:问题是没有正确安装vc++编译器,按上面步骤安装MinGW即可。
(venv) C:\Users\Administrator\PycharmProjects\pythonPydTest>python build.py build
running build
running build_ext
Traceback (most recent call last):
File "build.py", line 4, in <module>
setup(
File "D:\Program Files\Python38\lib\distutils\core.py", line 148, in setup
dist.run_commands()
File "D:\Program Files\Python38\lib\distutils\dist.py", line 966, in run_commands
self.run_command(cmd)
File "D:\Program Files\Python38\lib\distutils\dist.py", line 985, in run_command
cmd_obj.run()
File "D:\Program Files\Python38\lib\distutils\command\build.py", line 135, in run
self.run_command(cmd_name)
File "D:\Program Files\Python38\lib\distutils\cmd.py", line 313, in run_command
self.distribution.run_command(command)
File "D:\Program Files\Python38\lib\distutils\dist.py", line 985, in run_command
cmd_obj.run()
File "D:\Program Files\Python38\lib\distutils\command\build_ext.py", line 306, in run
self.compiler = new_compiler(compiler=self.compiler,
File "D:\Program Files\Python38\lib\distutils\ccompiler.py", line 1032, in new_compiler
return klass(None, dry_run, force)
File "D:\Program Files\Python38\lib\distutils\cygwinccompiler.py", line 282, in __init__
CygwinCCompiler.__init__ (self, verbose, dry_run, force)
File "D:\Program Files\Python38\lib\distutils\cygwinccompiler.py", line 157, in __init__
self.dll_libraries = get_msvcr()
File "D:\Program Files\Python38\lib\distutils\cygwinccompiler.py", line 86, in get_msvcr
raise ValueError("Unknown MS Compiler version %s " % msc_ver)
ValueError: Unknown MS Compiler version 1928
解决:按提示意思是找不到对应1928版本的编译器,我们需要在python中指定1928的编译器。
修改Python安装目录\Lib\distutils\cygwinccompiler.py
文件,在get_msvcr
函数中else
之前添加判断代码,判断msc_ver
为1928
,并指定编译器为vcruntime140
。
elif msc_ver == '1928':
# MinGW
return ['vcruntime140']
1928是版本号,根据错误提示进行修改
完整代码:
def get_msvcr():
"""Include the appropriate MSVC runtime library if Python was built
with MSVC 7.0 or later.
"""
msc_pos = sys.version.find('MSC v.')
if msc_pos != -1:
msc_ver = sys.version[msc_pos+6:msc_pos+10]
if msc_ver == '1300':
# MSVC 7.0
return ['msvcr70']
elif msc_ver == '1310':
# MSVC 7.1
return ['msvcr71']
elif msc_ver == '1400':
# VS2005 / MSVC 8.0
return ['msvcr80']
elif msc_ver == '1500':
# VS2008 / MSVC 9.0
return ['msvcr90']
elif msc_ver == '1600':
# VS2010 / MSVC 10.0
return ['msvcr100']
elif msc_ver == '1928':
# MinGW
return ['vcruntime140']
else:
raise ValueError("Unknown MS Compiler version %s " % msc_ver)
(venv) C:\Users\Administrator\PycharmProjects\pythonPydTest>python build.py build
running build
running build_ext
building 'lib_a' extension
creating build
creating build\temp.win-amd64-3.8
creating build\temp.win-amd64-3.8\Release
creating build\temp.win-amd64-3.8\Release\libs
D:\Program Files\mingw64\bin\gcc.exe -mdll -O -Wall -IC:\Users\Administrator\PycharmProjects\pythonPydTest\venv\include "-ID:\Program Files\Python38\include" "-ID:\Prog
ram Files\Python38\include" -c libs\lib_a.c -o build\temp.win-amd64-3.8\Release\libs\lib_a.o
libs\lib_a.c:215:41: warning: division by zero [-Wdiv-by-zero]
enum { __pyx_check_sizeof_voidp = 1 / (int)(SIZEOF_VOID_P == sizeof(void*)) };
^
libs\lib_a.c:215:12: error: enumerator value for '__pyx_check_sizeof_voidp' is not an integer constant
enum { __pyx_check_sizeof_voidp = 1 / (int)(SIZEOF_VOID_P == sizeof(void*)) };
^~~~~~~~~~~~~~~~~~~~~~~~
error: command 'D:\\Program Files\\mingw64\\bin\\gcc.exe' failed with exit status 1
解决:执行cmd编译命令是需加上-DMS_WIN64
参数
python build.py build_ext --inplace -DMS_WIN64