#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ BootRepairTool 打包脚本 使用 Python 3.9 + PyInstaller 打包 """ import sys import os import subprocess import shutil # 需要的 Python 版本 REQUIRED_PYTHON_VERSION = (3, 9) def check_python_version(): """检查 Python 版本是否为 3.9""" current = sys.version_info[:2] if current != REQUIRED_PYTHON_VERSION: print(f"错误: 需要使用 Python {REQUIRED_PYTHON_VERSION[0]}.{REQUIRED_PYTHON_VERSION[1]}") print(f"当前版本: Python {current[0]}.{current[1]}") print("\n请使用以下命令运行:") print(f" python3.9 {sys.argv[0]}") return False print(f"✓ Python 版本检查通过: {sys.version.split()[0]}") return True def install_dependencies(): """安装打包所需的依赖""" print("\n[1/5] 安装依赖...") deps = ["pyinstaller"] for dep in deps: print(f" 安装 {dep}...") try: subprocess.check_call([sys.executable, "-m", "pip", "install", "-q", dep]) print(f" ✓ {dep} 安装成功") except subprocess.CalledProcessError as e: print(f" ✗ {dep} 安装失败: {e}") return False return True def clean_build(): """清理之前的构建文件""" print("\n[2/5] 清理构建文件...") dirs_to_remove = ["build", "dist"] files_to_remove = ["*.pyc", "*.spec"] for d in dirs_to_remove: if os.path.exists(d): shutil.rmtree(d) print(f" 删除 {d}/") # 清理 __pycache__ for root, dirs, files in os.walk("."): if "__pycache__" in dirs: pycache_path = os.path.join(root, "__pycache__") shutil.rmtree(pycache_path) print(f" 删除 {pycache_path}/") print(" ✓ 清理完成") def build_executable(): """使用 PyInstaller 打包""" print("\n[3/5] 开始打包...") # 打包命令 - 输出为 .bin 文件,wrapper 脚本将调用它 cmd = [ sys.executable, "-m", "PyInstaller", "--name=LinuxGrubRepair.bin", "--onefile", "--console", "--clean", "--noconfirm", "frontend.py" ] print(f" 执行: {' '.join(cmd)}") try: subprocess.check_call(cmd) print(" ✓ 打包成功") return True except subprocess.CalledProcessError as e: print(f" ✗ 打包失败: {e}") return False def create_launcher(): """创建启动器脚本,解决 X11 字体渲染问题""" print("\n[4/5] 创建启动器脚本...") launcher_content = '''#!/bin/bash # ============================================================================== # BootRepairTool 启动脚本 # 解决 Arch Linux 等系统上的 X11 字体渲染问题 # ============================================================================== # 获取脚本实际所在目录(处理软链接情况) SCRIPT_SOURCE="${BASH_SOURCE[0]}" while [ -L "$SCRIPT_SOURCE" ]; do SCRIPT_DIR="$(cd "$(dirname "$SCRIPT_SOURCE")" && pwd)" SCRIPT_SOURCE="$(readlink "$SCRIPT_SOURCE")" [[ $SCRIPT_SOURCE != /* ]] && SCRIPT_SOURCE="$SCRIPT_DIR/$SCRIPT_SOURCE" done SCRIPT_DIR="$(cd "$(dirname "$SCRIPT_SOURCE")" && pwd)" # 如果是从 /bin 软链接启动,则使用实际安装目录 if [ "$SCRIPT_DIR" = "/bin" ] || [ "$SCRIPT_DIR" = "/usr/bin" ]; then SCRIPT_DIR="/opt/boot-repair-tool" fi # 设置字体渲染环境变量,解决 "BadLength (poly request too large)" 错误 export XLIB_SKIP_ARGB_VISUALS=1 # 禁用某些 X11 RENDER 扩展功能 export GDK_NATIVE_WINDOWS=1 # 设置 tkinter 使用基本字体渲染 export TCL_DONT_USE_RENDER_EXTENSION=1 # 如果使用 Wayland,强制使用 XWayland if [ "$XDG_SESSION_TYPE" = "wayland" ]; then export GDK_BACKEND=x11 fi # 执行实际程序 exec "${SCRIPT_DIR}/LinuxGrubRepair.bin" "$@" ''' dist_path = os.path.join(os.getcwd(), "dist") launcher_path = os.path.join(dist_path, "LinuxGrubRepair") with open(launcher_path, 'w') as f: f.write(launcher_content) # 设置可执行权限 os.chmod(launcher_path, 0o755) print(f" ✓ 启动器创建完成: {launcher_path}") return True def show_result(): """显示打包结果""" print("\n[5/5] 打包完成!") dist_path = os.path.join(os.getcwd(), "dist") launcher_name = "LinuxGrubRepair" bin_name = "LinuxGrubRepair.bin" launcher_path = os.path.join(dist_path, launcher_name) bin_path = os.path.join(dist_path, bin_name) if os.path.exists(launcher_path) and os.path.exists(bin_path): bin_size = os.path.getsize(bin_path) bin_size_mb = bin_size / (1024 * 1024) print(f"\n 启动器: {launcher_path}") print(f" 程序文件: {bin_path}") print(f" 文件大小: {bin_size_mb:.2f} MB") print("\n 使用方式:") print(f" sudo ./{launcher_name}") print("\n 注意: 启动器脚本已配置 X11 环境变量,可解决字体渲染问题") else: print(" 未找到生成的文件") def main(): """主函数""" print("=" * 50) print("BootRepairTool 打包脚本") print("=" * 50) # 检查 Python 版本 if not check_python_version(): sys.exit(1) # 安装依赖 if not install_dependencies(): print("依赖安装失败") sys.exit(1) # 清理旧构建 clean_build() # 打包 if not build_executable(): print("打包失败") sys.exit(1) # 创建启动器 if not create_launcher(): print("创建启动器失败") sys.exit(1) # 显示结果 show_result() if __name__ == "__main__": main()