利用cx_Freeze在linux下打包python程序

Linux 下打包方法总结

1. 安装 patchelf

  在安装 patchelf 前,须保证系统安装 wget、autoconf、automake、libtool 等工具,安命令如下:

sudo apt install wget autoconf automake libtool -y

  1.1 使用以下命令下载patchelf:

wget https://github.com/NixOS/patchelf/archive/refs/tags/0.12.tar.gz

  1.2 使用以下命令解压并进入该文件夹

tar -axf  0.12.tar.gz
cd patchelf-0.12/

利用cx_Freeze在linux下打包python程序_第1张图片
  1.3 依次输入并运行./bootstrap.sh./configure以及make命令
  1.4 输入并运行make install命令,若出现文件拒绝访问的错误,可改用sudo make install命令解决。
利用cx_Freeze在linux下打包python程序_第2张图片
  1.5 安装完成后,使用patchelf –version命令验证安装是否成功。
在这里插入图片描述

2. 使用cx_Freeze进行打包

  本文使用 cx_Freezeidna 进行打包,下载命令为 pip install cx_Freeze idna
  2.1 在需打包的目录下,复制附录代码中的 setup.pyslim.pyone_step.py 粘贴到需打包程序入口文件的同级目录下,修改setup.py文件,将Executable函数内的路径改为程序的入口文件,将用到的包的名字以字符串列表的形式写入packages。需要特别注意的是,程序下自己写的包,包括下级及以上目录下的自己写的文件需要按照下图所示写入到packages中,粒度为文件,无需对文件内的函数进行标识
利用cx_Freeze在linux下打包python程序_第3张图片
  2.2 若需要移入文件,则需要修改setup.py文件中的列表include_files。列表的每一项可以为待移入文件的路径,也可以为由源地址与目的地址组成的二元组,且目的地址只可为相对路径
  2.3 可以对setup.pysetup函数的nameversiondescription等等参数进行修改,修改后的name将作为程序名。也可以在Executable函数内添加icon参数,给程序增加图标
  2.4 使用python3 setup.py build命令对程序进行打包,打包后的文件可能会缺失部分pytorch的lib和bin文件,需要手动移入,目前尚未找到问题原因。同时可以修改并运行附件one_step.py来解决,详情见4.半自动化处理

3. 追踪程序

  3.1 使用以下命令对程序进行追踪,如strace -f -F -o ./dcop-strace.txt ./build/exe.linux-x86_64-3.8/api_server。此命令将运行./build/exe.linux-x86_64-3.8/api_server程序,追踪程序对文件操作的系统调用,结果将保存在dcop-strace.txt

strace -f -F -o ./dcop-strace.txt app_name

  3.2 保持3.1中的命令的运行,对程序进行详细的测试后,按Ctrl+C终止strace命令
  3.3 修改slim.py文件中的路径并运行,即可完成瘦身

4.半自动化处理

  本文提供了半自动化处理脚本one_step.py,读者可修改其中的代码以加快速度。由于未进行详细测试,因此不保证脚本的安全性和可用性,请谨慎使用。
  在进行以下步骤前,请确保setup.pyslim.py已完成修改。
  4.1 修改path_torchtorch的路径,修改path_dist_app,如’./build/exe.linux-x86_64-3.8’,linux表示目标程序的运行系统(不同的linux系统可能也会有区别),x86_64为处理器架构,3.8python的版本,读者可根据具体情况进行修改。
  4.2 修改app_name,使之成为运行生成程序的命令,如

./build/exe.linux-x86_64-3.8/api_server

4.3 运行one_step.py脚本,脚本会两次打开生成的程序。第一次为记录程序访问过的文件,因此需要在运行期间进行详细测试,测试完成后,按Ctrl+C终止测试;第二次打开程序为瘦身后验证程序的可执行性,按需进行验证即可。

代码附录

one_step.py

import os 
import shutil


#troch存在的目录
path_torch='/home/lfyu/.local/lib/python3.8/site-packages/torch' 

#生成程序的目录,需要根据说明文档进行修改
path_dist_app=r'./build/exe.linux-x86_64-3.8'

#生成程序的程序名,需和setup.py内的name保持一致
app_name=path_dist_app+'/api_server'

#使用cx_Freeze进行打包,在运行脚本前需要根据说明文档修改setup.py
os.system('python3 setup.py build')


print('Build app success,starting to copy lack file...')

#复制可能会缺少的库文件
def copydirs(from_file, to_file):
    if not os.path.exists(to_file):
        os.makedirs(to_file)
    files = os.listdir(from_file)
    for f in files:
        if os.path.isdir(from_file + '/' + f): 
            copydirs(from_file + '/' + f, to_file + '/' + f)
        else:
            shutil.copy(from_file + '/' + f, to_file + '/' + f)


copydirs(path_torch+'/lib',path_dist_app+'/lib/torch/lib')
copydirs(path_torch+'/bin',path_dist_app+'/lib/torch/bin')

print('Complete the copy of the file,please starting to test your program!')

#追踪程序的系统调用,需要进行详细的测试
os.system('strace -f -F -o ./dcop-strace.txt {:s}'.format(app_name))

print('Complete the test and start the program to slim down!')

#运行瘦身程序
os.system('python3 slim.py')

print('Slimming is complete, the dist program opens again to help you test availability!')

#验证程序的可行性
os.system(app_name)

steup.py

from cx_Freeze import setup, Executable

base = None    

executables = [Executable("api_server.py", base=base)]

packages = ['idna','cv2','numpy','traceback',
'onnxruntime','flask','openpyxl','torch',
'dominate','yaml','requests','tqdm','base64','time','json',
'utils.torch_utils','utils.activations','utils.autoanchor','utils.datasets','utils.general','utils.loss','utils.google_utils','utils.metrics']

include_files=[('./models/best.onnx','./models/best.onnx')]
options = {
    'build_exe': {    
        'packages':packages,
        'include_files':include_files
    },    
}

setup(
    name = "api_server",
    options = options,
    version = "0.0.1",
    description = '',
    executables = executables
)

slim.py

import glob 
import os 

path_txt=r'./dcop-strace.txt'
path_lib=r'./build/exe.linux-x86_64-3.8/lib'
fp=open(path_txt)
file=[os.path.abspath(line.split('"')[1]) for line in fp.readlines() if '"' in line]

size1=0
size2=0

def find_file(path):
    global size1,size2
    for p in glob.glob(path+'/*'):
        p=os.path.abspath(p) 
        if os.path.isfile(p) :
            size1=size1+os.path.getsize(p)
            if p not in file:
                size2=size2+os.path.getsize(p)
                print('removing file: %s',p)
                os.remove(p)
        else:
            find_file(p)


cnt=0

def delete_gap_dir(dir):
    global cnt
    if os.path.isdir(dir):
        for d in os.listdir(dir):
            delete_gap_dir(os.path.join(dir, d))
        if not os.listdir(dir):
            os.rmdir(dir)
            cnt=cnt+1

find_file(path_lib)
delete_gap_dir(path_lib)
print('Success,reduced {}MB files and {} file folders'.format((size2)/(1024**2),cnt))

你可能感兴趣的:(python,linux)