构建
This commit is contained in:
308
build.py
Normal file
308
build.py
Normal file
@@ -0,0 +1,308 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Linux 存储管理器 - PyInstaller 打包脚本
|
||||
自动安装依赖并打包应用程序
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
import os
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def run_command(cmd, description, check=True):
|
||||
"""运行命令并输出结果"""
|
||||
print(f"\n{'='*60}")
|
||||
print(f" {description}")
|
||||
print(f" 命令: {' '.join(cmd)}")
|
||||
print(f"{'='*60}")
|
||||
|
||||
try:
|
||||
result = subprocess.run(cmd, check=check, capture_output=False, text=True)
|
||||
print(f"✓ {description} 完成")
|
||||
return result.returncode == 0
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"✗ {description} 失败")
|
||||
print(f" 错误: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def check_python():
|
||||
"""检查 Python 版本"""
|
||||
print(f"\n{'='*60}")
|
||||
print(" 检查 Python 版本")
|
||||
print(f"{'='*60}")
|
||||
|
||||
version = sys.version_info
|
||||
print(f" Python 版本: {version.major}.{version.minor}.{version.micro}")
|
||||
|
||||
if version.major < 3 or (version.major == 3 and version.minor < 6):
|
||||
print("✗ Python 版本过低,需要 Python 3.6+")
|
||||
return False
|
||||
|
||||
print("✓ Python 版本符合要求")
|
||||
return True
|
||||
|
||||
|
||||
def is_externally_managed():
|
||||
"""检测是否为 PEP 668 外部管理环境"""
|
||||
# 检查是否在虚拟环境中
|
||||
if hasattr(sys, 'real_prefix') or (hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix):
|
||||
return False
|
||||
if os.environ.get('VIRTUAL_ENV'):
|
||||
return False
|
||||
|
||||
# 检查 EXTERNALLY-MANAGED 文件
|
||||
try:
|
||||
import sysconfig
|
||||
stdlib_path = sysconfig.get_path('stdlib')
|
||||
if os.path.exists(os.path.join(stdlib_path, 'EXTERNALLY-MANAGED')):
|
||||
return True
|
||||
except:
|
||||
pass
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def install_dependencies():
|
||||
"""安装依赖包"""
|
||||
print(f"\n{'='*60}")
|
||||
print(" 安装依赖包")
|
||||
print(f"{'='*60}")
|
||||
|
||||
dependencies = [
|
||||
"pyinstaller",
|
||||
"pexpect",
|
||||
]
|
||||
|
||||
# 尝试使用 pip3 或 pip
|
||||
pip_cmd = None
|
||||
for pip in ["pip3", "pip"]:
|
||||
if shutil.which(pip):
|
||||
pip_cmd = pip
|
||||
break
|
||||
|
||||
if not pip_cmd:
|
||||
print("✗ 未找到 pip,请手动安装 pip")
|
||||
return False
|
||||
|
||||
# 检测外部管理环境
|
||||
is_managed = is_externally_managed()
|
||||
if is_managed:
|
||||
print(" 检测到外部管理环境 (PEP 668),使用 --break-system-packages 选项")
|
||||
|
||||
print(f" 使用 {pip_cmd} 安装依赖...")
|
||||
|
||||
# 构建安装参数
|
||||
extra_args = ["--break-system-packages"] if is_managed else []
|
||||
|
||||
# 升级 pip 本身
|
||||
run_command([pip_cmd, "install", "--upgrade", "pip"] + extra_args, "升级 pip", check=False)
|
||||
|
||||
# 安装依赖
|
||||
all_success = True
|
||||
for dep in dependencies:
|
||||
if not run_command([pip_cmd, "install", dep] + extra_args, f"安装 {dep}"):
|
||||
print(f" 警告: {dep} 安装失败,尝试使用 sudo...")
|
||||
if not run_command(["sudo", pip_cmd, "install", dep] + extra_args, f"使用 sudo 安装 {dep}", check=False):
|
||||
print(f" ✗ {dep} 安装失败")
|
||||
all_success = False
|
||||
|
||||
if not all_success:
|
||||
print(f"\n{'='*60}")
|
||||
print(" 依赖安装失败")
|
||||
print(f"{'='*60}")
|
||||
print(" 您可以选择以下方式安装:")
|
||||
print("")
|
||||
print(" 方式 1 - 创建虚拟环境 (推荐):")
|
||||
print(" python3 -m venv venv")
|
||||
print(" source venv/bin/activate")
|
||||
print(" pip install pyinstaller pexpect")
|
||||
print(" python3 build.py")
|
||||
print("")
|
||||
print(" 方式 2 - 使用系统包管理器 (Arch):")
|
||||
print(" sudo pacman -S python-pyinstaller python-pexpect")
|
||||
print(" python3 build.py")
|
||||
print("")
|
||||
print(" 方式 3 - 强制安装 (有风险):")
|
||||
print(" pip3 install pyinstaller pexpect --break-system-packages")
|
||||
print(" python3 build.py")
|
||||
print("")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def clean_build():
|
||||
"""清理之前的构建文件"""
|
||||
print(f"\n{'='*60}")
|
||||
print(" 清理构建文件")
|
||||
print(f"{'='*60}")
|
||||
|
||||
dirs_to_remove = ["build", "dist", "__pycache__"]
|
||||
files_to_remove = ["*.spec"]
|
||||
|
||||
removed = []
|
||||
for d in dirs_to_remove:
|
||||
if os.path.exists(d):
|
||||
try:
|
||||
shutil.rmtree(d)
|
||||
removed.append(d)
|
||||
except Exception as e:
|
||||
print(f" 警告: 无法删除 {d}: {e}")
|
||||
|
||||
# 保留用户自定义的 spec 文件,只删除自动生成的
|
||||
for f in os.listdir("."):
|
||||
if f.startswith("mainwindow") and f.endswith(".spec") and f != "disk-manager.spec":
|
||||
try:
|
||||
os.remove(f)
|
||||
removed.append(f)
|
||||
except Exception as e:
|
||||
print(f" 警告: 无法删除 {f}: {e}")
|
||||
|
||||
if removed:
|
||||
print(f" 已清理: {', '.join(removed)}")
|
||||
else:
|
||||
print(" 没有需要清理的文件")
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def build_app():
|
||||
"""使用 PyInstaller 打包应用程序"""
|
||||
print(f"\n{'='*60}")
|
||||
print(" 开始打包应用程序")
|
||||
print(f"{'='*60}")
|
||||
|
||||
# 确定要打包的主文件
|
||||
# 默认使用 mainwindow_tkinter.py (Tkinter 版本)
|
||||
main_script = "mainwindow_tkinter.py"
|
||||
|
||||
if not os.path.exists(main_script):
|
||||
print(f"✗ 未找到 {main_script}")
|
||||
main_script = "mainwindow.py"
|
||||
if not os.path.exists(main_script):
|
||||
print(f"✗ 也未找到 {main_script}")
|
||||
return False
|
||||
print(f" 使用替代文件: {main_script}")
|
||||
|
||||
# 构建命令
|
||||
output_name = "linux-storage-manager"
|
||||
|
||||
cmd = [
|
||||
sys.executable, "-m", "PyInstaller",
|
||||
"--onefile", # 单文件模式
|
||||
"--name", output_name, # 输出文件名
|
||||
"--clean", # 清理临时文件
|
||||
"--noconfirm", # 不询问确认
|
||||
"--console", # 显示控制台 (用于调试)
|
||||
main_script
|
||||
]
|
||||
|
||||
if not run_command(cmd, "PyInstaller 打包"):
|
||||
return False
|
||||
|
||||
# 检查输出
|
||||
output_path = Path("dist") / output_name
|
||||
if output_path.exists():
|
||||
size = output_path.stat().st_size / (1024 * 1024) # MB
|
||||
print(f"\n{'='*60}")
|
||||
print(" 打包成功!")
|
||||
print(f"{'='*60}")
|
||||
print(f" 输出文件: {output_path}")
|
||||
print(f" 文件大小: {size:.2f} MB")
|
||||
print(f"\n 使用方法:")
|
||||
print(f" sudo ./{output_path}")
|
||||
return True
|
||||
else:
|
||||
print(f"✗ 未找到输出文件: {output_path}")
|
||||
return False
|
||||
|
||||
|
||||
def create_desktop_entry():
|
||||
"""创建桌面快捷方式文件"""
|
||||
print(f"\n{'='*60}")
|
||||
print(" 创建桌面快捷方式文件")
|
||||
print(f"{'='*60}")
|
||||
|
||||
desktop_content = """[Desktop Entry]
|
||||
Name=Linux 存储管理器
|
||||
Comment=Linux 存储管理工具
|
||||
Exec=/usr/local/bin/linux-storage-manager
|
||||
Icon=drive-harddisk
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Categories=System;Utility;
|
||||
Keywords=storage;disk;partition;raid;lvm;
|
||||
"""
|
||||
|
||||
desktop_file = "linux-storage-manager.desktop"
|
||||
try:
|
||||
with open(desktop_file, "w") as f:
|
||||
f.write(desktop_content)
|
||||
print(f" 已创建: {desktop_file}")
|
||||
print(f" 安装方法: sudo cp {desktop_file} /usr/share/applications/")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f" 警告: 无法创建桌面文件: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
print("""
|
||||
╔══════════════════════════════════════════════════════════════╗
|
||||
║ Linux 存储管理器 - PyInstaller 打包工具 ║
|
||||
╚══════════════════════════════════════════════════════════════╝
|
||||
""")
|
||||
|
||||
# 检查是否在正确的目录
|
||||
if not os.path.exists("mainwindow_tkinter.py") and not os.path.exists("mainwindow.py"):
|
||||
print("✗ 错误: 未找到主程序文件")
|
||||
print(" 请确保在包含 mainwindow_tkinter.py 或 mainwindow.py 的目录中运行此脚本")
|
||||
sys.exit(1)
|
||||
|
||||
# 执行步骤
|
||||
steps = [
|
||||
("检查 Python 版本", check_python),
|
||||
("安装依赖包", install_dependencies),
|
||||
("清理构建文件", clean_build),
|
||||
("打包应用程序", build_app),
|
||||
("创建桌面文件", create_desktop_entry),
|
||||
]
|
||||
|
||||
results = []
|
||||
for name, func in steps:
|
||||
try:
|
||||
result = func()
|
||||
results.append((name, result))
|
||||
except Exception as e:
|
||||
print(f"\n✗ {name} 出错: {e}")
|
||||
results.append((name, False))
|
||||
|
||||
# 打印总结
|
||||
print(f"\n{'='*60}")
|
||||
print(" 打包总结")
|
||||
print(f"{'='*60}")
|
||||
for name, result in results:
|
||||
status = "✓ 成功" if result else "✗ 失败"
|
||||
print(f" {status} - {name}")
|
||||
|
||||
# 检查最终结果
|
||||
if all(r[1] for r in results):
|
||||
print(f"\n{'='*60}")
|
||||
print(" 所有步骤完成!")
|
||||
print(f"{'='*60}")
|
||||
print(" 可执行文件位置: dist/linux-storage-manager")
|
||||
print(" 运行命令: sudo dist/linux-storage-manager")
|
||||
sys.exit(0)
|
||||
else:
|
||||
print(f"\n{'='*60}")
|
||||
print(" 打包过程中有步骤失败,请检查错误信息")
|
||||
print(f"{'='*60}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user