|
|
#!/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() |