# -*- encoding: utf-8 -*-
"""
@File : autobuild.py
@Time : 2022-10-26 16:39
@Author : 坐公交也用券
@Version : 1.0
@Contact : faith01238@hotmail.com
@Homepage : https://liumou.site
@Desc : 当前文件作用
"""
import platform
from os import path, system, mkdir, getcwd, remove, chdir
from shutil import rmtree, copy2
from subprocess import getstatusoutput, getoutput
from sys import exit
from time import sleep
from plbm import ColorLogger
class AutoBuild:
def __init__(self, file, model=None, upx=True, upx_path=None, clang=True, standalone=False):
"""
自动构建
:param file: 需要打包的.py文件
:param model: 需要绑定的依赖(自定义模块)
:param upx: 是否启用压缩
:param upx_path: Upx可执行文件路径
:param clang: 是否使用clang引擎
:param standalone: 是否打包成独立程序(不依赖Python,但是程序会非常大)
"""
self.logger = ColorLogger()
# 需要打包的文件
self.standalone = standalone
self.clang = clang
self.file = file
if not path.isfile(self.file):
self.logger.error('file does not exist: %s' % self.file)
exit(1)
# Upx执行文件路径
self.upx_path = upx_path
# 是否使用upx
self.upx = upx
# 自定义模块
self.model = model
# 当前系统架构
self.arch = platform.machine()
# 系统类型,默认使用当前系统类型,如果是Linux则使用具体发行版
self.os_type = platform.system()
# 截取打包文件文件名(去除后缀)
self.filename = str(file).split('.')[0]
# 当前路径
self.pwd = getcwd()
# 输出可执行文件路径
self.out = "out"
# ------------默认使用Windows环境信息
# 系统代号
code_name = platform.release()
self.client = "{0}_{1}{2}_{3}.exe".format(self.filename, self.os_type, code_name, self.arch)
self.linux = False
# 打包生成的文件名称
self.client_create = str("out/%s.exe" % self.filename)
# 判断是否属于Linux系统
if platform.system().lower() == 'linux'.lower():
# 获取Linux环境信息
code_name = getoutput('grep ^VERSION_CODENAME= /etc/os-release').split('=')[-1].replace('"', '')
self.os_type = getoutput("""grep ^ID /etc/os-release | sed 's/ID=//' | sed -n 1p | sed 's#\"##g'""")
if self.upx_path is None and self.upx:
self.upx_path = path.join(getcwd(), "upx/%s/upx" % self.arch)
self.client = str(self.filename) + "_{0}_{1}-{2}.bin".format(self.arch, self.os_type, code_name)
self.linux = True
self.client_create = str("out/%s.bin" % str(self.filename).split('/')[-1])
else:
# 生成最终执行文件
if self.upx_path is None and self.upx:
# 当没有传入Upx的时候, 使用预定义文件
self.upx_path = path.join(getcwd(), 'upx/Windows/upx.exe')
# 汇总
self.client_dir = path.join(self.pwd, 'client')
if path.isfile(self.client_dir):
remove(self.client_dir)
if not path.exists(self.client_dir):
mkdir(self.client_dir)
self.client = path.join(self.client_dir, self.client.split('/')[-1])
def check_exec(self, cmd):
"""
检查命令是否存在
:param cmd:
:return:
"""
# 是否需要安装
install = False
if self.linux:
res = system('which %s' % cmd)
if int(res) != 0:
install = True
else:
c = '%s -h' % cmd
if getstatusoutput(c)[0] != 0:
install = True
return install
def check_clang(self):
"""
检查本机是否已安装Clang
:return:
"""
# 是否已存在
install = self.check_exec(cmd='clang')
if install:
self.logger.info('clang is not installed')
return False
else:
self.logger.info('clang is installed')
return True
def check_nuitka(self):
"""
检查打包工具是否安装
:return:
"""
# 是否需要安装
if self.linux:
return self.check_exec(cmd='nuitka3')
else:
return self.check_exec(cmd='nuitka')
def show(self):
"""
显示信息
:return:
"""
self.logger.info('Build File: %s' % self.file)
self.logger.info('Generate files: %s' % self.client)
self.logger.info('Storage path: %s' % self.client_dir)
if self.model:
self.logger.info('Custom Catalog: %s' % self.model)
if self.upx:
self.logger.info('Upx compression turned on')
self.logger.info('Upx Path: %s' % self.upx_path)
def install_nuitka(self):
"""
安装打包工具
:return:
"""
cmd = "pip3 install nuitka -i https://pypi.tuna.tsinghua.edu.cn/simple"
if getstatusoutput(cmd)[0] == 0:
return False
return True
def delete(self):
"""
删除文件夹
:return:
"""
if path.exists(self.out):
try:
rmtree(path=self.out)
self.logger.info("Delete success : {0}".format(self.out))
except Exception as e:
self.logger.error(e)
exit(3)
def check_file(self):
"""
检查文件是否存在
:return:
"""
if path.isfile(self.file):
self.delete()
else:
self.logger.error("File is Not: ", self.file)
exit(2)
def mv(self):
"""
移动文件
:return:
"""
if path.isfile(self.client_create):
try:
copy2(src=self.client_create, dst=self.client)
self.logger.info("Copy Success")
return True
except Exception as e:
self.logger.error(e)
self.logger.error("Copy failed")
return False
else:
self.logger.error('cannot find file : %s' % self.client_create)
exit(2)
def run(self):
"""
开始构建
:return:
"""
nui = 'nuitka'
if self.linux:
nui = 'nuitka3'
if self.clang and self.check_clang():
nui = str(nui) + ' --clang'
cmd = "%s --show-progress --disable-ccache " % nui
if self.standalone:
cmd = str(cmd) + "--standalone "
if self.upx:
cmd = cmd + str(" --plugin-enable=upx ")
if self.upx_path:
cmd = cmd + str(" --upx-binary=%s " % self.upx_path)
cmd = cmd + str("--output-dir=%s " % self.out)
if self.model:
cmd = str(cmd) + " --include-package=%s %s" % (self.model, self.file)
else:
cmd = str(cmd) + "%s" % self.file
if self.linux and self.upx:
system("chmod 0777 %s" % self.upx_path)
self.logger.info('exec build: ', cmd)
sleep(3)
if int(system(cmd)) == 0:
self.logger.info("build success")
else:
self.logger.error("build failed")
exit(5)
def push(self):
system("git add %s" % self.client)
system("git commit -m 'build done'")
system('git push')
def check_upx(self):
"""
检查upx参数是否正确
:return:
"""
if self.upx:
if not path.isfile(self.upx_path):
self.logger.error('Upx compression is enabled, but no Upx program is found: %s' % self.upx_path)
exit(2)
def start(self):
"""
开始处理
:return:
"""
system("git pull")
self.check_upx()
if self.check_nuitka():
self.logger.warning("not installed nuitka3")
if self.install_nuitka():
self.logger.error("nuitka3 install failed")
exit(1)
else:
self.logger.info("nuitka3 is installed")
self.check_file()
self.delete()
self.run()
if not self.standalone:
self.mv()
self.delete()
chdir(self.client_dir)
cmd = "%s -h" % self.client
if self.linux:
cmd = "./%s -h" % self.client.split('/')[-1]
self.logger.debug(cmd)
if int(system(cmd)) == 0:
self.logger.info("run success")
self.push()
else:
self.logger.error("run failed")
f = path.getsize(self.client)
self.logger.info("{0} File Size: {1} MB".format(self.client, float(f / 1024 / 1024)))
chdir(self.pwd)
self.show()
if __name__ == "__main__":
b = AutoBuild(file="LiuMouTools.py", model='model,requests')
b.start()