Python中编译与打包的方法

有时候用Python进行开发的时候,为了防止源码泄漏,会在发布前进行封装与打包。比较常见的操作方法是使用PyInstaller对python程序进行打包。但是这样打包是可以进行解包与反编译的,代码很容易泄漏。

因此我们可以先用Cython对除了入口函数外其他所有模块进行编译,然后再用PyInstaller对入口函数进行打包。由于Cython会将python程序转换成C代码,然后调用gcc对C程序编译成动态库so或者pyd,此时编译好的二进制文件是无法进行逆向工程的。

在linux下可以对所有的py文件进行Cython封装,只需要暴露主入口。但是在windows下,尝试编译__init__.py文件的时候会报错,LNK2001无法解析的外部符号。因此在windows下还需要暴露__init__.py文件。可以将逻辑处理移到常规py文件中,而__init__.py只用来进行import等操作。

在使用PyInstaller打包时,常使用

# 以目录形式打包,动态库与可执行文件分开,
# 看起来较乱,不容易找到入口
PyInstaller -D main.py

或者

# 以单文件形式打包,动态库封在了可执行文件内部,
# 看起来整洁,但是运行前需要在缓存中释放动态库,速度相比会慢一点
PyInstaller -F main.py

但是如果直接这样打包,主程序是找不到已经封装成so或者pyd的动态库的。

这个时候我们可以找到主程序同目录下的spec文件,在hiddenimports里边加上各种需要import的包名。比如

hiddenimports=['Module1', 'Module2.func1', 'numpy'],

然后再运行

PyInstaller -D main.spec

或者

PyInstaller -F main.spec

这样就可以顺利打包完整的可执行程序了。

写下这篇博客主要是今天在两个地方踩坑了。

(1)在windows下Cython封装__init__.py报错

(2)PyInstaller打包的hiddenimports忘记加上了,每次都不记得这么操作,总是不长记性,所以这次写个博客当作笔记。

Yannx

2021年4月12日

Leave a Comment