博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[转] Linux下使用PyInstaller打包Python程序
阅读量:5931 次
发布时间:2019-06-19

本文共 11011 字,大约阅读时间需要 36 分钟。

hot3.png

1.Linux系统下安装pyinstaller

    # easy_install pyinstaller    Adding PyInstaller 2.1 to easy-install.pth file    Installing pyinstaller script to /usr/local/bin    Installing pyi-grab_version script to /usr/local/bin    Installing pyi-archive_viewer script to /usr/local/bin    Installing pyi-build script to /usr/local/bin    Installing pyi-make_comserver script to /usr/local/bin    Installing pyi-bindepend script to /usr/local/bin    Installing pyi-set_version script to /usr/local/bin    Installing pyi-makespec script to /usr/local/bin

2.pyinstaller工具安装在/usr/local/bin下

    # which pyinstaller    /usr/local/bin    # ls /usr/local/bin|grep pyi*    pyi-archive_viewer    pyi-bindepend    pyi-build    pyi-grab_version    pyi-make_comserver    pyi-makespec    pyinstaller    pyi-set_version

3.创建测试程序main.py

    print('hello world!')

4.使用pyinstaller创建可执行程序

    # pyinstaller main.py

执行后生成相关目录和文件

    # tree .    .    ├── build    │   └── main    │       ├── logdict2.7.8.final.0-1.log    │       ├── main    │       ├── out00-Analysis.toc    │       ├── out00-COLLECT.toc    │       ├── out00-EXE.toc    │       ├── out00-PKG.pkg    │       ├── out00-PKG.toc    │       ├── out00-PYZ.pyz    │       ├── out00-PYZ.toc    │       └── warnmain.txt    ├── dist    │   └── main    │       ├── audioop.so    │       ├── bz2.so    │       ├── _codecs_cn.so    │       ├── _codecs_hk.so    │       ├── _codecs_iso2022.so    │       ├── _codecs_jp.so    │       ├── _codecs_kr.so    │       ├── _codecs_tw.so    │       ├── _hashlib.so    │       ├── libbz2.so.1.0    │       ├── libcrypto.so.1.0.0    │       ├── libpython2.7.so.1.0    │       ├── libreadline.so.6    │       ├── libssl.so.1.0.0    │       ├── libtinfo.so.5    │       ├── libz.so.1    │       ├── main    │       ├── _multibytecodec.so    │       ├── readline.so    │       ├── _ssl.so    │       └── termios.so    ├── main.py    └── main.spec    4 directories, 33 files

生成了main.spec文件和build、dist两个文件夹,可执行程序位于目录./dist/main/,而该目录下所有程序均为运行时所需文件。

5.运行程序

    # ./dist/main/main    hello world!

6.生成单个文件

清除main.py以外文件

# rm -rf ./build ./dist main.spec
生成单一执行文件
# pyinstaller -F main.py
执行后生成相关目录和文件

    # tree .    .    ├── build    │   └── main    │       ├── logdict2.7.8.final.0-1.log    │       ├── out00-Analysis.toc    │       ├── out00-EXE.toc    │       ├── out00-PKG.pkg    │       ├── out00-PKG.toc    │       ├── out00-PYZ.pyz    │       ├── out00-PYZ.toc    │       └── warnmain.txt    ├── dist    │   └── main    ├── main.py    └── main.spec    3 directories, 11 files

7.运行程序

    # ./dist/main/main    hello world!

8.压缩程序

UPX使用一种叫做UCL的压缩算法,这是一个对有部分专有算法的NRV(Not Really Vanished)算法的一个开源实现。

debian下安装UPX方法如下:

    # apt-get install upx-ucl

确认upx路径

    # which upx    /usr/bin/upx

使用压缩方式打包单个可执行程序。由于–upx参数已废弃,需要使用–upx-dir参数

    # pyinstaller -F --upx-dir=/usr/bin main.py    # ll dist/main    -rwxr-xr-x 1 albert albert 4229368 Dec  5 20:54 dist/main

对比未压缩时的体积

    # pyinstaller -F --noupx main.py    # ll dist/main    -rwxr-xr-x 1 albert albert 4306465 Dec  5 21:03 dist/main

当然pyinstaller默认会搜索PATH路径的,所以apt安装upx的话,pyinstaller能识别出来,并自动启用压缩。

    24 INFO: UPX is available.

不想压缩可以使用–noupx参数。

9.资源文件(生成文件夹模式)

当使用图片或者外部数据时,就需要把这些资源文件一同打包到程序中。还记得main.py同目录下生成的main.spec文件吗?这个文件就是配置文件。现看下需要外部资源的python程序

    # cat main.py    with open('foo') as fi:        print(data)    # cat foo    Hello World!

我们为了添加资源需要使用pyi-makespec生成配置文件,然后使用pyi-build生成文件夹模式的可执行程序。

    # pyi-makespec main.py    wrote /home/albert/PycharmProjects/createonefile/main.spec    now run pyinstaller.py to build the executable

现在看下spec配置文件的内容,万幸是个python格式的文件。

    # cat main.spec    # -*- mode: python -*-    a = Analysis(['main.py'],                 pathex=['/home/albert/PycharmProjects/createonefile'],                 hiddenimports=[],                 hookspath=None,                 runtime_hooks=None)    pyz = PYZ(a.pure)    exe = EXE(pyz,              a.scripts,              exclude_binaries=True,              name='main',              debug=False,              strip=None,              upx=True,              console=True )    coll = COLLECT(exe,                   a.binaries,                   a.zipfiles,                   a.datas,                   strip=None,                   upx=True,                   name='main')

需要添加资源需要使用TOC(Table of Contents)格式(name,path,typecode)

name:打包到程序文件夹内的资源文件名
path:打包前资源文件的绝对路径(包括文件名)
typecode:有3种

    typecode    description name    path    'BINARY'    A shared library.   Run-time name.  Full path name in build.    'DATA'  Arbitrary files.    Run-time name.  Full path name in build.    'OPTION'    A Python run-time option.   Option code ignored.

那么需要添加spec的TOC便是

(‘foo’, ‘/home/albert/PycharmProjects/createonefile/foo’, ‘DATA’)
该TOC需要放到list中,然后作为参数传递给COLLECT函数,修改后的spec文件如下:

    # cat main.spec    # -*- mode: python -*-    a = Analysis(['main.py'],                 pathex=['/home/albert/PycharmProjects/createonefile'],                 hiddenimports=[],                 hookspath=None,                 runtime_hooks=None)    pyz = PYZ(a.pure)    exe = EXE(pyz,              a.scripts,              exclude_binaries=True,              name='main',              debug=False,              strip=None,              upx=True,              console=True )    coll = COLLECT(exe,                   a.binaries,                   a.zipfiles,                   a.datas,                   strip=None,                   upx=True,                   name='main')

最后一步就是执行pyi-build编译可执行文件

    # pyi-build main.spec    # tree .    .    ├── build    │   └── main    │       ├── logdict2.7.8.final.0-1.log    │       ├── main    │       ├── out00-Analysis.toc    │       ├── out00-COLLECT.toc    │       ├── out00-EXE.toc    │       ├── out00-PKG.pkg    │       ├── out00-PKG.toc    │       ├── out00-PYZ.pyz    │       ├── out00-PYZ.toc    │       └── warnmain.txt    ├── dist    │   └── main    │       ├── audioop.so    │       ├── bz2.so    │       ├── _codecs_cn.so    │       ├── _codecs_hk.so    │       ├── _codecs_iso2022.so    │       ├── _codecs_jp.so    │       ├── _codecs_kr.so    │       ├── _codecs_tw.so    │       ├── foo    │       ├── _hashlib.so    │       ├── libbz2.so.1.0    │       ├── libcrypto.so.1.0.0    │       ├── libpython2.7.so.1.0    │       ├── libreadline.so.6    │       ├── libssl.so.1.0.0    │       ├── libtinfo.so.5    │       ├── libz.so.1    │       ├── main    │       ├── _multibytecodec.so    │       ├── readline.so    │       ├── _ssl.so    │       └── termios.so    ├── foo    ├── main.py    └── main.spec    4 directories, 35 files

大家已经可以看到./dist/main/foo了吧。为了避免读取原文件,我们进入到./dist/main执行下main。

    # cd ./dist/main    # ./main    Hello World!

10.资源文件(生成单文件模式)

单文件模式包含外部资源就要麻烦许多了,运行程序的时候先将文件解压到sys._MEIPASS指向的目录下,所以调用资源文件就需要添加os.path.join(sys._MEIPASS,filename)。但打包前调试时sys又没有_MEIPASS属性,那就又要添加如下代码

    if getattr(sys, 'frozen', False):        # we are running in a |PyInstaller| bundle        basedir = sys._MEIPASS    else:        # we are running in a normal Python environment        basedir = os.path.dirname(__file__)

这样调用资源文件时就使用os.path.join(basedir,filename)就可以了。我们先来看看修改后的main.py

    # cat main.py    import sys    import os    if getattr(sys, 'frozen', False):        # we are running in a |PyInstaller| bundle        basedir = sys._MEIPASS    else:        # we are running in a normal Python environment        basedir = os.path.dirname(__file__)    with open(os.path.join(basedir,'foo')) as fi:        print(fi.read())

然后生成spec文件,onefile模式下生成的spec与onedic模式是不一样的。

    # pyi-makespec -F main.py    # -*- mode: python -*-    a = Analysis(['main.py'],                 pathex=['/home/albert/PycharmProjects/createonefile'],                 hiddenimports=[],                 hookspath=None,                 runtime_hooks=None)    pyz = PYZ(a.pure)    exe = EXE(pyz,              a.scripts,              a.binaries,              a.zipfiles,              a.datas,              name='main',              debug=False,              strip=None,              upx=True,              console=True )

需要把资源文件整理为TOC格式,添加到spec中,不同的是onedic模式是添加到COLLECT的参数中,onefile模式并不调用COLLECT函数,所以TOC是添加到EXE参数中。修改后的spec如下

    # cat main.spec    # -*- mode: python -*-    a = Analysis(['main.py'],                 pathex=['/home/albert/PycharmProjects/createonefile'],                 hiddenimports=[],                 hookspath=None,                 runtime_hooks=None)    pyz = PYZ(a.pure)    exe = EXE(pyz,              a.scripts,              a.binaries,              a.zipfiles,              a.datas,              [('foo', '/home/albert/PycharmProjects/createonefile/foo', 'DATA')],              name='main',              debug=False,              strip=None,              upx=True,              console=True )

然后编译测试

    # pyi-build main.spec    # cd dist    # ./main    Hello World!

11.多资源文件(生成单文件模式)

多资源文件时,可以把多个TOC文件放到同一个列表中

    [('foo1', '/home/albert/PycharmProjects/createonefile/resource/foo1', 'DATA'),     ('foo2', '/home/albert/PycharmProjects/createonefile/resource/foo2', 'DATA'),     ('foo3', '/home/albert/PycharmProjects/createonefile/resource/foo3', 'DATA')]

也可以TOC的升级版Tree。Tree可以把一个目录下的文件自动生成TCO列表。

Tree(root, prefix=run-time-folder, excludes=match)
root是需要打包的资源文件夹,可以是绝对路径也可以是相对路径
prefix是运行时的文件夹名。代码中就需要使用os.path.join(basedir,prefixdir,’foo1′)
excludes是个列表,可有可无,用于排除不需要的文件或子文件夹。既可以是文件名、文件夹名,也可以使用通配符如*.ext
上边的TOC列表就可以这样表达
Tree(‘/home/albert/PycharmProjects/createonefile/resource’,’resource’)
代码中可这样调用

    basedir=os.path.join(basedir,'resource')    foo1=open(os.path.join(basedir,'foo1'))    foo2=open(os.path.join(basedir,'foo2'))    foo3=open(os.path.join(basedir,'foo3'))

完整代码:

    # tree .    .    ├── main.py    └── resource        ├── foo1        ├── foo2        └── foo3    1 directory, 4 files    # cat main.py    import sys    import os    if getattr(sys, 'frozen', False):        # we are running in a |PyInstaller| bundle        basedir = sys._MEIPASS    else:        # we are running in a normal Python environment        basedir = os.path.dirname(__file__)    resourcedir=os.path.join(basedir,'resource')    for filename in ['foo1','foo2','foo3']:        with open(os.path.join(resourcedir,filename)) as fi:            print(fi.read())    # pyi-makespec -F main.py    # emacs -nw main.spec    ***add Tree Object to main.spec file***    # cat main.spec    # -*- mode: python -*-    a = Analysis(['main.py'],                 pathex=['/home/albert/PycharmProjects/createonefile'],                 hiddenimports=[],                 hookspath=None,                 runtime_hooks=None)    pyz = PYZ(a.pure)    exe = EXE(pyz,              a.scripts,              a.binaries,              a.zipfiles,              a.datas,              Tree('./resource','resource'),              name='main',              debug=False,              strip=None,              upx=True,              console=True )

运行生成的文件。

    # cd dist    # ./main    Hello foo1!    Hello foo2!    Hello foo3!

12.多资源文件(生成单文件夹模式)

真懒的写了,手动copy进去不就结了嘛。不嫌麻烦就参考9、10、11稍微想一想就有了。

13.参考

14.附件

转载于:https://my.oschina.net/goopand/blog/392195

你可能感兴趣的文章
C# GUID的使用
查看>>
浅谈Innodb Fast Index Creation
查看>>
AndroidManifest.xml文件详解(activity)(一)
查看>>
Ext4Yii 1.5 发布,Yii 的 ExtJS 扩展
查看>>
wim封装到iso
查看>>
发福利了!60本微软免费的电子书
查看>>
搜索引擎检查结果点击结果地址后 当前搜索引擎自动调整页面到指定地址
查看>>
SSMS错误代码大全
查看>>
7个步骤打造一份诱人的官网产品说明!
查看>>
站点系统压力測试Jmeter+Badboy
查看>>
dubbox开发rest+json指南【转】
查看>>
Sqlserver 中添加数据库登陆账号并授予数据库所有者权限
查看>>
JAVA实现二叉树(简易版--实现了二叉树的各种遍历)
查看>>
select option 不可以选
查看>>
Atitit. 软件设计 模式 变量 方法 命名最佳实践 vp820 attilax总结命名表大全...
查看>>
讲给普通人听的分布式数据存储(转载)
查看>>
【工具】今天有人问我可以直接离线一个完整的网站吗?有没有什么工具之类的?我推荐一款:Httrack (网站复制机)案例:离线你的博客园...
查看>>
Top N之MapReduce程序加强版Enhanced MapReduce for Top N items
查看>>
Beaglebone Black教程使用SSH通过USB和因特网连接Beaglebone Black
查看>>
MySQL 清除表空间碎片
查看>>