You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Curriculum_Design/build_macos_app.py

309 lines
9.3 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
MagicWord macOS M系列芯片打包脚本
用于构建macOS Apple Silicon原生应用程序
"""
import os
import sys
import subprocess
import platform
import shutil
import plistlib
from datetime import datetime
def run_command(command, shell=False, cwd=None):
"""运行命令并返回结果"""
try:
result = subprocess.run(command, shell=shell, capture_output=True, text=True, encoding='utf-8', cwd=cwd)
return result.returncode, result.stdout, result.stderr
except Exception as e:
return -1, "", str(e)
def check_system():
"""检查系统是否为macOS Apple Silicon"""
if platform.system() != "Darwin":
print("错误: 此脚本仅支持macOS系统")
return False
# 检查是否为Apple Silicon
machine = platform.machine()
if machine not in ['arm64', 'aarch64']:
print(f"警告: 当前为 {machine} 架构建议Apple Silicon (arm64) 以获得最佳性能")
print(f"系统信息: macOS {platform.mac_ver()[0]}, {machine}")
return True
def clean_build_dirs():
"""清理构建目录"""
print("清理构建目录...")
dirs_to_clean = ['build', 'dist', '__pycache__', '*.egg-info']
for dir_name in dirs_to_clean:
if '*' in dir_name:
import glob
for path in glob.glob(dir_name):
if os.path.isdir(path):
shutil.rmtree(path, ignore_errors=True)
elif os.path.exists(dir_name):
if os.path.isdir(dir_name):
shutil.rmtree(dir_name, ignore_errors=True)
else:
os.remove(dir_name)
# 清理src目录下的__pycache__
for root, dirs, files in os.walk('src'):
for dir_name in dirs:
if dir_name == '__pycache__':
cache_path = os.path.join(root, dir_name)
shutil.rmtree(cache_path, ignore_errors=True)
print(f"清理缓存: {cache_path}")
def install_dependencies():
"""安装依赖"""
print("安装项目依赖...")
# 首先确保pip是最新的
run_command([sys.executable, "-m", "pip", "install", "--upgrade", "pip"])
# 安装requirements.txt中的依赖
code, stdout, stderr = run_command([sys.executable, "-m", "pip", "install", "-r", "requirements.txt"])
if code != 0:
print(f"依赖安装失败: {stderr}")
return False
# 安装PyInstaller
print("安装PyInstaller...")
code, stdout, stderr = run_command([sys.executable, "-m", "pip", "install", "pyinstaller"])
if code != 0:
print(f"PyInstaller安装失败: {stderr}")
return False
print("依赖安装成功")
return True
def build_macos_app():
"""构建macOS应用包"""
print("构建macOS应用包...")
# PyInstaller命令 - 针对macOS优化
pyinstaller_cmd = [
"pyinstaller",
"--name", "MagicWord",
"--version", "0.3.0",
"--distpath", "dist",
"--workpath", "build",
"--specpath", ".",
# macOS特定的数据文件路径格式
"--add-data", "resources:resources",
"--add-data", "src:src",
# 隐藏导入模块
"--hidden-import", "PyQt5",
"--hidden-import", "PyQt5.QtCore",
"--hidden-import", "PyQt5.QtGui",
"--hidden-import", "PyQt5.QtWidgets",
"--hidden-import", "requests",
"--hidden-import", "beautifulsoup4",
"--hidden-import", "python-docx",
"--hidden-import", "PyPDF2",
"--hidden-import", "ebooklib",
"--hidden-import", "chardet",
"--hidden-import", "PIL",
# macOS应用包选项
"--windowed", # 无控制台窗口
"--osx-bundle-identifier", "com.magicword.app",
"--target-architecture", "arm64", # Apple Silicon
"--noconfirm",
"src/main.py"
]
print("运行PyInstaller...")
code, stdout, stderr = run_command(pyinstaller_cmd)
if code != 0:
print(f"构建失败: {stderr}")
print("尝试通用架构...")
# 尝试通用架构
pyinstaller_cmd[-2] = "--target-architecture"
pyinstaller_cmd[-1] = "universal2"
code, stdout, stderr = run_command(pyinstaller_cmd)
if code != 0:
print(f"通用架构构建也失败: {stderr}")
return False
print("macOS应用包构建成功")
return True
def create_app_bundle():
"""创建macOS应用束"""
print("创建macOS应用束...")
app_path = "dist/MagicWord.app"
if not os.path.exists(app_path):
print(f"错误: 找不到应用包 {app_path}")
return False
# 创建Info.plist文件
info_plist = {
'CFBundleName': 'MagicWord',
'CFBundleDisplayName': 'MagicWord - 隐私学习软件',
'CFBundleIdentifier': 'com.magicword.app',
'CFBundleVersion': '0.3.0',
'CFBundleShortVersionString': '0.3.0',
'CFBundleExecutable': 'MagicWord',
'CFBundlePackageType': 'APPL',
'CFBundleSignature': '????',
'LSMinimumSystemVersion': '11.0', # macOS Big Sur及更高版本
'NSHighResolutionCapable': True,
'NSHumanReadableCopyright': 'Copyright © 2024 MagicWord Team. All rights reserved.',
'CFBundleDocumentTypes': [
{
'CFBundleTypeName': 'Text Document',
'CFBundleTypeExtensions': ['txt', 'docx', 'pdf'],
'CFBundleTypeRole': 'Editor'
}
]
}
plist_path = os.path.join(app_path, "Contents", "Info.plist")
with open(plist_path, 'wb') as f:
plistlib.dump(info_plist, f)
# 复制图标文件
icon_files = [
'resources/icons/app_icon_128X128.png',
'resources/icons/app_icon_256X256.png',
'resources/icons/app_icon_32X32.png',
'resources/icons/app_icon_64X64.png'
]
resources_dir = os.path.join(app_path, "Contents", "Resources")
os.makedirs(resources_dir, exist_ok=True)
for icon_file in icon_files:
if os.path.exists(icon_file):
shutil.copy2(icon_file, resources_dir)
print(f"复制图标: {icon_file}")
print("macOS应用束创建完成")
return True
def create_dmg():
"""创建DMG安装包"""
print("创建DMG安装包...")
app_path = "dist/MagicWord.app"
if not os.path.exists(app_path):
print(f"错误: 找不到应用包 {app_path}")
return False
# 创建发布目录
release_dir = "macos_release"
if os.path.exists(release_dir):
shutil.rmtree(release_dir)
os.makedirs(release_dir)
# 复制应用到发布目录
release_app_path = os.path.join(release_dir, "MagicWord.app")
shutil.copytree(app_path, release_app_path)
# 创建应用程序链接
applications_link = os.path.join(release_dir, "Applications")
os.symlink("/Applications", applications_link)
# 创建README文件
readme_content = f"""# MagicWord 0.3.0 for macOS
## 安装说明
1. 将 MagicWord.app 拖拽到 Applications 文件夹
2. 首次运行时,如果出现安全提示,请前往 系统设置 > 隐私与安全性 允许应用运行
3. 或者右键点击应用选择"打开"
## 系统要求
- macOS Big Sur (11.0) 或更高版本
- Apple Silicon (M1/M2/M3) 或 Intel 处理器
## 功能特性
- 隐私学习:通过打字练习来学习文档内容
- 支持多种文档格式TXT, DOCX, PDF
- 智能打字模式
- 美观的Word风格界面
## 版本信息
构建时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
平台: {platform.system()} {platform.machine()}
Python版本: {platform.python_version()}
## 技术支持
如有问题,请查看项目文档或联系开发团队。
"""
with open(os.path.join(release_dir, "README.txt"), "w") as f:
f.write(readme_content)
# 创建DMG文件如果系统支持
dmg_name = f"MagicWord-0.3.0-macOS-{platform.machine()}.dmg"
dmg_path = os.path.join("dist", dmg_name)
# 使用hdiutil创建DMG
create_dmg_cmd = [
"hdiutil", "create",
"-volname", "MagicWord",
"-srcfolder", release_dir,
"-ov",
"-format", "UDZO",
dmg_path
]
code, stdout, stderr = run_command(create_dmg_cmd)
if code == 0:
print(f"DMG创建成功: {dmg_path}")
return True
else:
print(f"DMG创建失败: {stderr}")
print("已创建应用包可手动打包DMG")
return False
def main():
"""主函数"""
print("=== MagicWord macOS打包脚本 ===")
print(f"构建时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
# 检查系统
if not check_system():
return False
# 清理构建目录
clean_build_dirs()
# 安装依赖
if not install_dependencies():
print("依赖安装失败")
return False
# 构建应用
if not build_macos_app():
print("应用构建失败")
return False
# 创建应用束
if not create_app_bundle():
print("应用束创建失败")
return False
# 创建DMG
create_dmg()
print("\n=== 构建完成 ===")
print("应用位置: dist/MagicWord.app")
print("如需安装请将应用拖拽到Applications文件夹")
print("首次运行时可能需要允许未知来源的应用")
if __name__ == "__main__":
main()