使用cython+pyinstaller打包python项目

使用cython+pyinstaller打包python项目

第一步使用cython将python文件编译成so文件,

cython 官网:

参考项目: https://github.com/cckuailong/py2sec 开箱即用

第二步安装pyinstaller,将cyhton编译后的项目和依赖进行打包

官网:https://www.pyinstaller.org/documentation.html

pyinstall安装: pip install pyinstaller

pyinstaller -h 查看帮助

最简单的使用方式:

`pyinstaller main.py``

main.py为项目的入口文件,根据具体情况修改

默认打包将所有的依赖打包到一个文件夹,如果需要修改,通过以下指令修改

-D, --onedir          打包到一个目录 (default)
-F, --onefile       打包到一个可执行文件 

对于简单的文件来说这已经就够了,pyinstaller会自动搜索所有依赖,并打包依赖。

但如果你的项目里面有使用 __import__或者使用 importlib.import_module()

运行时导入的依赖,那会pyinstaller则可能不能完全导入,这里就需要帮助pyinstller找到依赖。如

--hidden-import 指令。

对于一些资源文件,如html. js等, 则可以使用—add-data添加

对于一此可执行文件,如so. dll等,则可以使用 --add-binary 来添加,注意这两个指令的使用方式,如 --add-binary="源路径:目标路径" 使用;或者:分隔,取决于你在win还在linux上使用。

其它的一些指令如下

What to bundle, where to search:
  --add-data <SRC;DEST or SRC:DEST>
                        Additional non-binary files or folders to be added to the executable. The path separator is platform specific, ``os.pathsep`` (which is ``;`` on Windows and ``:`` on most unix
                        systems) is used. This option can be used multiple times.
  --add-binary <SRC;DEST or SRC:DEST>
                        Additional binary files to be added to the executable. See the ``--add-data`` option for more details. This option can be used multiple times.
  -p DIR, --paths DIR   A path to search for imports (like using PYTHONPATH). Multiple paths are allowed, separated by ``':'``, or use this option multiple times. Equivalent to supplying the ``pathex``
                        argument in the spec file.
  --hidden-import MODULENAME, --hiddenimport MODULENAME
                        
  --collect-submodules MODULENAME
                        Collect all submodules from the specified package or module. This option can be used multiple times.
  --collect-data MODULENAME, --collect-datas MODULENAME
                        Collect all data from the specified package or module. This option can be used multiple times.
  --collect-binaries MODULENAME
                        Collect all binaries from the specified package or module. This option can be used multiple times.
  --collect-all MODULENAME
                        Collect all submodules, data files, and binaries from the specified package or module. This option can be used multiple times.
  --copy-metadata PACKAGENAME
                        Copy metadata for the specified package. This option can be used multiple times.
  --recursive-copy-metadata PACKAGENAME
                        Copy metadata for the specified package and all its dependencies. This option can be used multiple times.
  --additional-hooks-dir HOOKSPATH
                        An additional path to search for hooks. This option can be used multiple times.
  --runtime-hook RUNTIME_HOOKS
                        Path to a custom runtime hook file. A runtime hook is code that is bundled with the executable and is executed before any other code or module to set up special features of the
                        runtime environment. This option can be used multiple times.
  --exclude-module EXCLUDES
                        Optional module or package (the Python name, not the path name) that will be ignored (as though it was not found). This option can be used multiple times.
												py3.9以后会把pip. 等打包进去,可以通过这个指令来排除

  --key KEY             aes加密密钥,用于加密字节码.
  --splash IMAGE_FILE   (EXPERIMENTAL) 添加启动画面

配置文件

对于配置文件打包,需要理解两个路径,一个路径是程序所在的路径,以下叫它为当前路径,另外一个是打包的程序要运行,必须先解压,这个解压的路径,以下叫它为运行时路径。linux默认在/tmp目录下面。

判断当前程序的运行方式

可以通过以下代码来检测当前运行的程序是以打包的方式运行还是py脚本的方式运行

# ref: https://pyinstaller.readthedocs.io/en/stable/runtime-information.html
import sys
if getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS'):
    print('running in a PyInstaller bundle')
else:
    print('running in a normal Python process')

路径检测

__file__

对于py脚本的运行方式, 这个变量指向的是py脚本的当前路径,而对于pyinstaller打包后的程序来说。这个变量指向的是运行时路径。

sys.executable

对于py脚本的运行方式, 这个变量指向的是python解释器,而对于pyinstaller打包后的程序来说。这个变量指向的是当前路径。

我们可以通过以下代码片段得到这两个路径。

    weAreFrozen = True if getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS') else False
    _ = sys.executable if weAreFrozen else __file__
    rootPath = os.path.dirname(os.path.realpath(_)) #当前路径
    runtimePath = os.path.dirname(os.path.realpath(__file__))  #运行时路径

对于配置文件来说,那我们就需要通过 os.path.join(rootPath, "myConfig")的路径方式来访问。

upx

可以使用upx对目标程序进行压缩 。

先下载upx: https://github.com/upx/upx/releases

然后将upx复制到path路径中。pyinstaller检测到path中有upx的可执行程序时会自动调用。

如果看到以下提示,则说明正常

245 INFO: UPX is available.

示例:


pyinstaller --noconfirm  -F main.py  --key="aes密钥" --clean  --collect-data="celpy" --add-data="./plugins/:.plugins/"   --hidden-import="xxx" --hidden-import="xxxx" 

pyi-makespec

如果程序中依赖不会变动,则可以使用pyi-makespec命令生成一个spec文件,下次则可以直接使用pyinstaller spec文件的方式来进行打包,避免每次输入过长的命令,使用方式跟以上完全一样,就不多写了。

posted @ 2021-09-19 11:57  Hslim  阅读(1945)  评论(0编辑  收藏  举报