在安装 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/
1.3 依次输入并运行./bootstrap.sh
和./configure
以及make
命令
1.4 输入并运行make install
命令,若出现文件拒绝访问的错误,可改用sudo make install
命令解决。
1.5 安装完成后,使用patchelf –version
命令验证安装是否成功。
本文使用 cx_Freeze 和 idna 进行打包,下载命令为 pip install cx_Freeze idna
2.1 在需打包的目录下,复制附录代码中的 setup.py 、slim.py 、one_step.py 粘贴到需打包程序入口文件的同级目录下,修改setup.py文件,将Executable函数内的路径改为程序的入口文件,将用到的包的名字以字符串列表的形式写入packages。需要特别注意的是,程序下自己写的包,包括下级及以上目录下的自己写的文件需要按照下图所示写入到packages中,粒度为文件,无需对文件内的函数进行标识
2.2 若需要移入文件,则需要修改setup.py文件中的列表include_files。列表的每一项可以为待移入文件的路径,也可以为由源地址与目的地址组成的二元组,且目的地址只可为相对路径
2.3 可以对setup.py中setup函数的name、version、description等等参数进行修改,修改后的name将作为程序名。也可以在Executable函数内添加icon参数,给程序增加图标
2.4 使用python3 setup.py build
命令对程序进行打包,打包后的文件可能会缺失部分pytorch的lib和bin文件,需要手动移入,目前尚未找到问题原因。同时可以修改并运行附件one_step.py来解决,详情见4.半自动化处理
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文件中的路径并运行,即可完成瘦身
本文提供了半自动化处理脚本one_step.py,读者可修改其中的代码以加快速度。由于未进行详细测试,因此不保证脚本的安全性和可用性,请谨慎使用。
在进行以下步骤前,请确保setup.py、slim.py已完成修改。
4.1 修改path_torch为torch的路径,修改path_dist_app,如’./build/exe.linux-x86_64-3.8’,linux表示目标程序的运行系统(不同的linux系统可能也会有区别),x86_64为处理器架构,3.8为python的版本,读者可根据具体情况进行修改。
4.2 修改app_name,使之成为运行生成程序的命令,如
./build/exe.linux-x86_64-3.8/api_server
4.3 运行one_step.py脚本,脚本会两次打开生成的程序。第一次为记录程序访问过的文件,因此需要在运行期间进行详细测试,测试完成后,按Ctrl+C终止测试;第二次打开程序为瘦身后验证程序的可执行性,按需进行验证即可。
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)
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
)
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))