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