Files
leonpan-pc/build.py

224 lines
6.7 KiB
Python
Raw Permalink Normal View History

2025-11-02 22:06:42 +08:00
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
应用程序构建脚本
使用PyInstaller将Python应用打包为可执行文件
"""
import os
import sys
import subprocess
import shutil
from pathlib import Path
# 项目根目录
PROJECT_ROOT = Path(__file__).parent.absolute()
# 主程序入口
MAIN_SCRIPT = "main.py"
# 输出目录
OUTPUT_DIR = PROJECT_ROOT / "dist"
BUILD_DIR = PROJECT_ROOT / "build"
# 应用名称
APP_NAME = "LeonPan"
APP_NAME_CONSOLE = "LeonPan_Console"
# 图标文件
ICON_FILE = "logo.png"
# 需要包含的额外文件和目录
EXTRA_DATA = [
("_internal", "_internal"), # 编辑器和相关资源
("logo.png", "logo.png"), # 应用图标
("welcome_video.py", "welcome_video.py"), # 欢迎视频脚本
("app/resource", "app/resource") # 应用资源
]
def run_command(command, cwd=None):
"""执行命令并返回结果"""
print(f"执行命令: {' '.join(command)}")
try:
result = subprocess.run(
command,
cwd=cwd or PROJECT_ROOT,
check=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
print(f"命令执行成功: {result.stdout[:100]}..." if len(result.stdout) > 100 else f"命令执行成功: {result.stdout}")
return True
except subprocess.CalledProcessError as e:
print(f"命令执行失败: {e.stderr}")
return False
def check_dependencies():
"""检查构建依赖是否已安装"""
print("检查构建依赖...")
# 检查pyinstaller
try:
import PyInstaller
print(f"PyInstaller 已安装,版本: {PyInstaller.__version__}")
except ImportError:
print("PyInstaller 未安装,正在安装...")
if not run_command([sys.executable, "-m", "pip", "install", "pyinstaller"]):
print("安装PyInstaller失败请手动安装后重试")
return False
# 安装项目依赖
requirements_file = PROJECT_ROOT / "requirements.txt"
if requirements_file.exists():
print(f"安装项目依赖: {requirements_file}")
if not run_command([sys.executable, "-m", "pip", "install", "-r", str(requirements_file)]):
print("安装项目依赖失败请检查requirements.txt文件")
return False
return True
def clean_output():
"""清理之前的构建输出"""
print("清理之前的构建输出...")
# 删除dist目录
if OUTPUT_DIR.exists():
try:
shutil.rmtree(OUTPUT_DIR)
print(f"已删除目录: {OUTPUT_DIR}")
except Exception as e:
print(f"删除 {OUTPUT_DIR} 失败: {e}")
# 删除build目录
if BUILD_DIR.exists():
try:
shutil.rmtree(BUILD_DIR)
print(f"已删除目录: {BUILD_DIR}")
except Exception as e:
print(f"删除 {BUILD_DIR} 失败: {e}")
# 删除.spec文件
spec_file = PROJECT_ROOT / f"{APP_NAME}.spec"
if spec_file.exists():
try:
os.remove(spec_file)
print(f"已删除文件: {spec_file}")
except Exception as e:
print(f"删除 {spec_file} 失败: {e}")
def build_app():
"""使用PyInstaller构建应用"""
print("开始构建应用...")
# 构建两个版本:无控制台版本和有控制台版本
builds = [
{
"name": APP_NAME, # 无控制台版本
"console": False
},
{
"name": APP_NAME_CONSOLE, # 有控制台版本
"console": True
}
]
# 为每个版本执行构建
for build_config in builds:
build_name = build_config["name"]
use_console = build_config["console"]
print(f"\n构建版本: {build_name} (控制台: {'启用' if use_console else '禁用'})")
# 构建pyinstaller命令
cmd = [
sys.executable,
"-m",
"PyInstaller",
MAIN_SCRIPT,
"--name", build_name,
2025-11-03 22:28:58 +08:00
"--onedir", # 构建单个可执行文件
2025-11-02 22:06:42 +08:00
]
# 设置控制台选项
if use_console:
cmd.append("--console")
else:
cmd.extend(["--windowed", "--noconsole"])
# 添加图标
icon_path = PROJECT_ROOT / ICON_FILE
if icon_path.exists():
cmd.extend(["--icon", str(icon_path)])
print(f"使用图标: {icon_path}")
else:
print(f"警告: 图标文件 {icon_path} 不存在,将使用默认图标")
# 添加额外的数据文件
for src, dst in EXTRA_DATA:
src_path = PROJECT_ROOT / src
if src_path.exists():
cmd.extend(["--add-data", f"{src_path}{os.pathsep}{dst}"])
print(f"添加额外数据: {src} -> {dst}")
else:
print(f"警告: 额外数据 {src_path} 不存在,跳过")
# 执行构建命令
if not run_command(cmd):
print(f"{build_name} 构建失败!")
return False
print("所有版本构建成功!")
return True
def copy_additional_files():
"""复制额外的文件到输出目录"""
print("复制额外文件到输出目录...")
if not OUTPUT_DIR.exists():
print(f"错误: 输出目录 {OUTPUT_DIR} 不存在")
return False
# 复制_internal目录两个可执行文件共用
internal_src = PROJECT_ROOT / "_internal"
internal_dst = OUTPUT_DIR / "_internal"
if internal_src.exists():
try:
if internal_dst.exists():
shutil.rmtree(internal_dst)
shutil.copytree(internal_src, internal_dst)
print(f"已复制 _internal 目录到 {internal_dst}(两个可执行文件共用)")
except Exception as e:
print(f"复制 _internal 目录失败: {e}")
return True
def main():
"""主函数"""
print(f"开始构建 {APP_NAME} 应用...")
print(f"项目根目录: {PROJECT_ROOT}")
# 检查依赖
if not check_dependencies():
print("依赖检查失败,终止构建")
return 1
# 清理输出
clean_output()
# 构建应用
if not build_app():
print("应用构建失败,终止")
return 1
# 复制额外文件
copy_additional_files()
print("\n构建完成!")
print(f"无控制台版本可执行文件: {OUTPUT_DIR / APP_NAME}{'.exe' if sys.platform == 'win32' else ''}")
print(f"有控制台版本可执行文件: {OUTPUT_DIR / APP_NAME_CONSOLE}{'.exe' if sys.platform == 'win32' else ''}")
print(f"共用资源目录: {OUTPUT_DIR / '_internal'}")
return 0
if __name__ == "__main__":
sys.exit(main())