-
Notifications
You must be signed in to change notification settings - Fork 308
Expand file tree
/
Copy pathbuild_macos.sh
More file actions
executable file
·281 lines (231 loc) · 9.99 KB
/
Copy pathbuild_macos.sh
File metadata and controls
executable file
·281 lines (231 loc) · 9.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
#!/usr/bin/env bash
# build_macos.sh
# PasteMD macOS Nuitka 打包脚本(带签名 / 固定 Bundle ID / 固定安装路径)
# 目的:
# 1) 用“固定的证书身份”签名,减少每次构建都被 macOS 当成“新应用”的概率(权限更容易沿用)
# 2) 确保 CFBundleIdentifier(Bundle ID)稳定
# 3) 将 .app 复制到固定目录(/Applications),进一步提升权限复用稳定性
set -euo pipefail # 遇到错误退出;未定义变量当错误;管道错误会传递
echo "开始构建 PasteMD (macOS)..."
############################
# 你需要修改/确认的配置项 #
############################
# 应用显示名称(.app 名称)
APP_NAME="PasteMD"
# 你的 Bundle ID(建议长期固定不变)
BUNDLE_ID="com.richqaq.pastemd"
# 你的代码签名证书身份(security find-identity -v -p codesigning 输出里的那条)
# 例:Apple Development: xxx@163.com (xxxxxxxx)
CERT_IDENTITY="${CERT_IDENTITY_PASTEMD}"
# Nuitka 输出目录
OUT_DIR="nuitka/macos"
# 入口脚本
ENTRY="PasteMD.py"
# 你希望把 app 安装到哪里(开发期建议固定路径,权限更稳)
INSTALL_DIR="${INSTALL_DIR:-/Applications}"
# 是否静默(默认 0:不静默,便于排错;设置 QUIET=1 ./build_macos.sh 可静默)
QUIET="${QUIET:-0}"
# Python 可执行文件(默认用当前环境 python;你也可 PYTHON_BIN=/path/to/python ./build_macos.sh)
PYTHON_BIN="${PYTHON_BIN:-python}"
####################
# 基础环境检查 #
####################
echo "检查 Bundle ID:$BUNDLE_ID"
if [[ -z "$BUNDLE_ID" ]]; then
echo "错误:BUNDLE_ID 不能为空。"
exit 1
fi
if [[ "$BUNDLE_ID" != *.* ]] || [[ ! "$BUNDLE_ID" =~ ^[A-Za-z0-9][A-Za-z0-9.-]*[A-Za-z0-9]$ ]]; then
echo "错误:BUNDLE_ID 格式不合法,建议使用反向域名格式(例如 com.example.app)。"
exit 1
fi
command -v "$PYTHON_BIN" >/dev/null 2>&1 || { echo "错误:找不到 python:$PYTHON_BIN"; exit 1; }
command -v codesign >/dev/null 2>&1 || { echo "错误:找不到 codesign(Xcode Command Line Tools 是否安装?)"; exit 1; }
test -x /usr/libexec/PlistBuddy || { echo "错误:找不到 /usr/libexec/PlistBuddy"; exit 1; }
# 检查 Nuitka 是否可用
"$PYTHON_BIN" -m nuitka --version >/dev/null 2>&1 || {
echo "错误:当前 python 环境中没有 Nuitka。请先安装:pip install nuitka"
exit 1
}
echo "使用签名身份:${CERT_IDENTITY:-<未设置>}"
if [[ -z "${CERT_IDENTITY:-}" ]]; then
echo "警告:CERT_IDENTITY_PASTEMD 未设置,将导致 codesign 失败。"
fi
# 检查证书身份是否存在(只是提示,不强制退出)
if ! security find-identity -v -p codesigning | grep -Fq "$CERT_IDENTITY"; then
echo "警告:在钥匙串中没找到该签名身份:$CERT_IDENTITY"
echo " 你可以运行:security find-identity -v -p codesigning 复制正确的名称"
fi
####################
# 获取版本号 #
####################
VERSION="$("$PYTHON_BIN" -c "import sys; sys.path.insert(0, '.'); from pastemd import __version__; print(__version__)")"
echo "构建版本:$VERSION"
####################
# 清理旧构建 #
####################
echo "清理旧构建目录:$OUT_DIR"
rm -rf "$OUT_DIR"
############################
# 判断 Nuitka 是否支持某参数 #
############################
has_nuitka_opt() {
# 用 --help 判断参数是否存在;不同 Nuitka 版本参数会变
"$PYTHON_BIN" -m nuitka --help 2>/dev/null | grep -q -- "$1"
}
require_nuitka_opt() {
if ! has_nuitka_opt "$1"; then
echo "错误:当前 Nuitka 不支持 $1,请升级 Nuitka。"
exit 1
fi
}
####################
# 组装 Nuitka 命令 #
####################
echo "开始运行 Nuitka..."
APPLE_EVENTS_DESC="PasteMD needs permission to identify the frontmost app window (Word, WPS, etc.) so it can insert content into the correct target."
SCREEN_CAPTURE_DESC="PasteMD needs screen recording access to read window titles (macOS treats window title access as Screen Recording)."
INPUT_MONITORING_DESC="PasteMD needs permission for global hotkey listening."
require_nuitka_opt "--macos-signed-app-name"
# 用数组拼命令,避免空格/引号问题
NUITKA_CMD=(
"$PYTHON_BIN" -m nuitka "$ENTRY"
--standalone
--macos-create-app-bundle
--macos-app-name="$APP_NAME"
--macos-app-icon=assets/icons/logo.icns
--enable-plugin=tk-inter
--output-dir="$OUT_DIR"
--output-filename="$APP_NAME"
# 资源打包
--include-data-dir=assets/icons=assets/icons
--include-data-dir=pastemd/lua=lua
--include-data-files=pastemd/i18n/locales/*.json=i18n/locales/
--include-data-dir=third_party/pandoc/macos=pandoc
# 显式包含的包/模块(按你当前项目需要)
--include-package=pync
--include-package-data=pync
--include-package=plyer
--include-package=plyer.platforms.macosx
--include-module=plyer.platforms.macosx.notification
# --include-package=pynput
# --include-package=pynput.keyboard
# --include-package=pynput._util
# --include-package=pynput._util.darwin
# --include-package=Quartz
# --include-package=AppKit
# --include-package=Foundation
# --include-package=objc
# --include-package=Cocoa
# --include-package=PIL
# --include-package=tkinter
# --include-package=pystray
# 排除测试相关依赖
--nofollow-import-to=pytest
--nofollow-import-to=test
--nofollow-import-to=tests
)
# 可选:设置版本号(若当前 Nuitka 支持)
if has_nuitka_opt "--macos-app-version"; then
NUITKA_CMD+=( --macos-app-version="$VERSION" )
fi
# 可选:设置签名相关的“应用标识名”(不少版本用它来生成/写入 bundle id 或签名名)
# 注意:不同版本对这个参数含义略有差异,但通常我们希望它稳定为反向域名
if has_nuitka_opt "--macos-signed-app-name"; then
NUITKA_CMD+=( --macos-signed-app-name="$BUNDLE_ID" )
fi
# 可选:让 Nuitka 自己签名(若当前 Nuitka 支持)
# 你已经确认有 Apple Development 证书身份,这里可以直接用
# if has_nuitka_opt "--macos-sign-identity"; then
# NUITKA_CMD+=( --macos-sign-identity="$CERT_IDENTITY" )
# fi
# if has_nuitka_opt "--macos-sign-notarization" ; then
# NUITKA_CMD+=( --macos-sign-notarization )
# fi
# 是否静默
if [[ "$QUIET" == "1" ]]; then
NUITKA_CMD+=( --quiet )
fi
# 执行构建
"${NUITKA_CMD[@]}"
####################
# 构建产物路径 #
####################
APP_PATH="$OUT_DIR/$APP_NAME.app"
PLIST_PATH="$APP_PATH/Contents/Info.plist"
if [[ ! -d "$APP_PATH" ]]; then
echo "错误:构建完成但未找到 app:$APP_PATH"
exit 1
fi
echo "构建完成:$APP_PATH"
#############################################
# 写入 Info.plist 权限描述(Usage Descriptions)
#############################################
echo "写入 Info.plist 权限描述..."
/usr/libexec/PlistBuddy -c "Set :NSAppleEventsUsageDescription $APPLE_EVENTS_DESC" "$PLIST_PATH" 2>/dev/null \
|| /usr/libexec/PlistBuddy -c "Add :NSAppleEventsUsageDescription string $APPLE_EVENTS_DESC" "$PLIST_PATH"
/usr/libexec/PlistBuddy -c "Set :NSScreenCaptureUsageDescription $SCREEN_CAPTURE_DESC" "$PLIST_PATH" 2>/dev/null \
|| /usr/libexec/PlistBuddy -c "Add :NSScreenCaptureUsageDescription string $SCREEN_CAPTURE_DESC" "$PLIST_PATH"
/usr/libexec/PlistBuddy -c "Set :NSInputMonitoringUsageDescription $INPUT_MONITORING_DESC" "$PLIST_PATH" 2>/dev/null \
|| /usr/libexec/PlistBuddy -c "Add :NSInputMonitoringUsageDescription string $INPUT_MONITORING_DESC" "$PLIST_PATH"
#############################################
# 复制 InfoPlist.strings 本地化文件 #
#############################################
INFO_PLIST_LPROJ_DIR="assets/macos/InfoPlist.strings"
if [[ -d "$INFO_PLIST_LPROJ_DIR" ]]; then
echo "复制 InfoPlist.strings 本地化文件..."
mkdir -p "$APP_PATH/Contents/Resources"
for lproj in "$INFO_PLIST_LPROJ_DIR"/*.lproj; do
[[ -d "$lproj" ]] || continue
cp -R "$lproj" "$APP_PATH/Contents/Resources/"
done
else
echo "警告:未找到 InfoPlist.strings 目录:$INFO_PLIST_LPROJ_DIR"
fi
echo "InfoPlist.strings 本地化文件已复制。"
#############################################
# 检查 Bundle ID 是否符合预期
#############################################
echo "检查 Bundle ID..."
ACTUAL_BUNDLE_ID="$(/usr/libexec/PlistBuddy -c "Print :CFBundleIdentifier" "$PLIST_PATH" 2>/dev/null || true)"
if [[ -z "$ACTUAL_BUNDLE_ID" ]]; then
echo "错误:未在 Info.plist 中找到 CFBundleIdentifier"
exit 1
fi
if [[ "$ACTUAL_BUNDLE_ID" != "$BUNDLE_ID" ]]; then
echo "错误:Bundle ID 不一致"
echo " 期望:$BUNDLE_ID"
echo " 实际:$ACTUAL_BUNDLE_ID"
exit 1
fi
echo "Bundle ID 检查通过:$ACTUAL_BUNDLE_ID"
####################
# 重新签名(稳) #
####################
# 即使 Nuitka 已签名,修 plist 后也必须重签。
# --deep:对内部嵌套框架/二进制一起签
# --force:覆盖旧签名
echo "对 app 进行 codesign 签名:$CERT_IDENTITY"
codesign --force --deep --options runtime --timestamp --sign "$CERT_IDENTITY" "$APP_PATH"
####################
# 验证签名信息 #
####################
echo "验证签名(若这里报错,说明签名不完整或有未签文件)..."
codesign --verify --deep --strict --verbose=2 "$APP_PATH" || true
echo "打印关键签名信息(Identifier/TeamIdentifier/Authority)..."
codesign -dv --verbose=4 "$APP_PATH" 2>&1 | egrep "Identifier|TeamIdentifier|Authority" || true
echo "确认 Info.plist 的 Bundle ID:"
defaults read "$PLIST_PATH" CFBundleIdentifier || true
#################################
# 开发期:复制到固定路径再运行 #
#################################
# 原因:部分权限对“路径”也敏感。开发期反复构建如果每次路径/包结构变化太大,
# 系统更容易当成新应用。把 app 固定放到 ~/Applications 往往更省心。
echo "复制到固定安装目录:$INSTALL_DIR"
mkdir -p "$INSTALL_DIR"
rm -rf "$INSTALL_DIR/$APP_NAME.app"
cp -R "$APP_PATH" "$INSTALL_DIR/$APP_NAME.app"
echo "最终 App 位置:$INSTALL_DIR/$APP_NAME.app"
echo "启动应用..."
open "$INSTALL_DIR/$APP_NAME.app"
echo "全部完成 ✅"