Files
leonpan-pc/build.py
2025-11-03 22:28:58 +08:00

224 lines
6.7 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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,
"--onedir", # 构建单个可执行文件
]
# 设置控制台选项
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())