-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMakefile
More file actions
331 lines (287 loc) · 12.5 KB
/
Copy pathMakefile
File metadata and controls
331 lines (287 loc) · 12.5 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
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
# Hell RCMenu - macOS 右键菜单管理工具
# Usage:
# make - 编译主应用 + Finder Extension
# make install - 编译 + 签名 + 安装到 /Applications/Hell/
# make dev - 编译 + 运行
# make notarize - 公证 release(需要 Makefile.local 配置 Apple 凭证)
# make clean - 清除构建产物
# make help - 显示帮助
#
# 私密配置(Apple ID / 专用密码 / Team ID / Developer ID 证书名)放在 Makefile.local,
# 该文件已加入 .gitignore。模板见 Makefile.local.example。
# 加载本地私密配置(如存在);使用 - 前缀使其缺失时不报错
-include Makefile.local
# --- 配置 ---
APP_NAME := HellRCMenu
DISPLAY_NAME := Hell RCMenu
EXT_NAME := HellRCMenuFinder
BUNDLE_ID := com.hell.tools.rcmenu
EXT_BUNDLE_ID := com.hell.tools.rcmenu.finder-extension
MIN_MACOS := 13.0
ARCH := arm64
# --- 路径 ---
BUILD_DIR := build
APP_BUNDLE := $(BUILD_DIR)/$(APP_NAME).app
CONTENTS := $(APP_BUNDLE)/Contents
MACOS_DIR := $(CONTENTS)/MacOS
RES_DIR := $(CONTENTS)/Resources
PLUGINS_DIR := $(CONTENTS)/PlugIns
EXT_BUNDLE := $(PLUGINS_DIR)/$(EXT_NAME).appex
EXT_CONTENTS := $(EXT_BUNDLE)/Contents
EXT_MACOS := $(EXT_CONTENTS)/MacOS
INSTALL_DIR := /Applications/Hell
# --- 第三方二进制(7-Zip 用于解压 rar/zip/7z 等) ---
VENDOR_DIR := Vendor/7zz
SEVENZIP_BIN := $(VENDOR_DIR)/7zz
SEVENZIP_VERSION := 26.00
SEVENZIP_URL := https://github.com/ip7z/7zip/releases/download/$(SEVENZIP_VERSION)/7z2600-mac.tar.xz
# --- 签名身份 ---
# 自动检测: 优先 Hell Dev(本地开发自签证书) → Developer ID Application → Apple Development → ad-hoc(-)
# 可通过 `SIGN_IDENTITY="xxx" make install` 显式覆盖
# 注意: 嵌入的 7zz、Extension、主 App 必须使用同一身份,否则沙盒中启动 7zz 会被拦截
SIGN_IDENTITY ?= $(shell \
IDS=$$(security find-identity -v -p codesigning 2>/dev/null); \
if echo "$$IDS" | grep -q '"Hell Dev"'; then \
echo "Hell Dev"; \
elif echo "$$IDS" | grep -q '"Developer ID Application'; then \
echo "$$IDS" | grep -oE '"Developer ID Application[^"]*"' | head -1 | tr -d '"'; \
elif echo "$$IDS" | grep -q '"Apple Development'; then \
echo "$$IDS" | grep -oE '"Apple Development[^"]*"' | head -1 | tr -d '"'; \
else \
echo "-"; \
fi)
# --- 源文件 ---
# 主应用 = Sources/ + Shared/ 所有 Swift
APP_SRCS := $(wildcard Sources/*.swift) $(wildcard Shared/*.swift)
# Extension = Extension/ + Shared/ 中不含 ConfigManager 的文件
SHARED_SRCS := $(filter-out Shared/ConfigManager.swift,$(wildcard Shared/*.swift))
EXT_SRCS := $(wildcard Extension/*.swift) $(SHARED_SRCS)
# --- 工具链 ---
SWIFTC := swiftc
SDK_PATH := $(shell xcrun --show-sdk-path 2>/dev/null || echo "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk")
SWIFT_BASE := -sdk $(SDK_PATH) -target $(ARCH)-apple-macosx$(MIN_MACOS) -O
APP_FLAGS := $(SWIFT_BASE) -module-name $(APP_NAME)
APP_FRAMEWORKS := -framework Cocoa -framework SwiftUI -framework UniformTypeIdentifiers -framework ServiceManagement -framework IOKit
EXT_FLAGS := $(SWIFT_BASE) -module-name $(EXT_NAME) -application-extension -Xlinker -e -Xlinker _NSExtensionMain
EXT_FRAMEWORKS := -framework Cocoa -framework FinderSync
# --- 可执行文件 ---
APP_EXEC := $(MACOS_DIR)/$(APP_NAME)
EXT_EXEC := $(EXT_MACOS)/$(EXT_NAME)
.PHONY: all build install dev clean uninstall help sign kill vendor-7zz preflight
all: build
# --- 预检: 工具链与依赖 ---
# 缺 Xcode CLT 直接报错并提示安装命令; 7zz 由依赖规则自动拉取无需在此处理
preflight:
@command -v swiftc >/dev/null 2>&1 || { \
echo "错误: 未找到 swiftc(Xcode Command Line Tools)"; \
echo "请运行: xcode-select --install"; \
exit 1; \
}
@command -v xcrun >/dev/null 2>&1 || { \
echo "错误: 未找到 xcrun"; \
echo "请运行: xcode-select --install"; \
exit 1; \
}
@command -v codesign >/dev/null 2>&1 || { \
echo "错误: 未找到 codesign"; \
exit 1; \
}
@if [ "$(SIGN_IDENTITY)" = "-" ]; then \
echo "提示: 未找到任何代码签名证书,将使用 ad-hoc 签名(本地开发可用,Finder 扩展可能受限)"; \
fi
kill:
@echo "正在关闭可能正在运行的应用实例..."
@-killall $(APP_NAME);
@sleep 1
# 完整构建流程
build: preflight kill app-bundle ext-bundle sign rename
@echo "=== 构建完成: $(BUILD_DIR)/$(DISPLAY_NAME).app ==="
# 重命名为带空格的显示名(构建和签名后执行)
.PHONY: rename
rename:
@rm -rf "$(BUILD_DIR)/$(DISPLAY_NAME).app"
@cp -R "$(APP_BUNDLE)" "$(BUILD_DIR)/$(DISPLAY_NAME).app"
# --- 主应用 ---
.PHONY: app-bundle
app-bundle: $(APP_EXEC) $(CONTENTS)/Info.plist $(CONTENTS)/PkgInfo $(RES_DIR)/logo.png $(RES_DIR)/AppIcon.icns $(RES_DIR)/7zz $(RES_DIR)/7zz-License.txt
$(APP_EXEC): $(APP_SRCS) | $(MACOS_DIR)
@echo "编译主应用..."
$(SWIFTC) $(APP_FLAGS) $(APP_FRAMEWORKS) -o $@ $^
$(CONTENTS)/Info.plist: Resources/Info.plist | $(CONTENTS)
@cp $< $@
$(CONTENTS)/PkgInfo: | $(CONTENTS)
@echo "APPL????" > $@
$(RES_DIR)/logo.png: Resources/logo.png | $(RES_DIR)
@cp $< $@
$(RES_DIR)/AppIcon.icns: Resources/AppIcon.icns | $(RES_DIR)
@cp $< $@
# --- 7zz 二进制(嵌入到 .app/Contents/Resources/7zz) ---
# 缺失时自动从 GitHub Release 拉取
$(SEVENZIP_BIN):
@echo "未发现 $(SEVENZIP_BIN),从 GitHub 拉取 7-Zip $(SEVENZIP_VERSION)..."
@mkdir -p $(VENDOR_DIR)
@TMPDIR=$$(mktemp -d) && \
curl -fsSL "$(SEVENZIP_URL)" -o $$TMPDIR/7z.tar.xz && \
tar -xJf $$TMPDIR/7z.tar.xz -C $$TMPDIR && \
cp $$TMPDIR/7zz $(SEVENZIP_BIN) && \
cp $$TMPDIR/License.txt $(VENDOR_DIR)/License.txt && \
cp $$TMPDIR/readme.txt $(VENDOR_DIR)/readme.txt 2>/dev/null || true && \
chmod +x $(SEVENZIP_BIN) && \
rm -rf $$TMPDIR
@echo "✓ 7zz 已就绪: $(SEVENZIP_BIN)"
$(RES_DIR)/7zz: $(SEVENZIP_BIN) | $(RES_DIR)
@cp $< $@
@chmod +x $@
$(RES_DIR)/7zz-License.txt: $(VENDOR_DIR)/License.txt | $(RES_DIR)
@cp $< $@
# 显式重新拉取 7zz 二进制
.PHONY: vendor-7zz
vendor-7zz:
@rm -f $(SEVENZIP_BIN)
@$(MAKE) $(SEVENZIP_BIN)
# --- Finder Extension ---
.PHONY: ext-bundle
ext-bundle: $(EXT_EXEC) $(EXT_CONTENTS)/Info.plist
$(EXT_EXEC): $(EXT_SRCS) | $(EXT_MACOS)
@echo "编译 Finder Extension..."
$(SWIFTC) $(EXT_FLAGS) $(EXT_FRAMEWORKS) -o $@ $(EXT_SRCS)
$(EXT_CONTENTS)/Info.plist: Extension/Info.plist | $(EXT_CONTENTS)
@cp $< $@
# --- 签名 ---
# 签名顺序必须自下而上:嵌入的二进制 → Extension → 主 app
# 7zz 默认是 ad-hoc/linker 签名,Extension 沙盒里运行需要重签为与主 App 相同身份,
# 否则 Process 启动 7zz 会被系统拦下。
sign: app-bundle ext-bundle
@echo "使用签名身份: $(SIGN_IDENTITY)"
@echo "签名 7zz..."
@codesign --force --sign "$(SIGN_IDENTITY)" $(RES_DIR)/7zz
@echo "签名 Extension..."
@codesign --force --sign "$(SIGN_IDENTITY)" --entitlements Extension/Entitlements.plist $(EXT_BUNDLE)
@echo "签名主应用..."
@codesign --force --sign "$(SIGN_IDENTITY)" $(APP_BUNDLE)
# --- 公证准备:用 Developer ID 证书 + hardened runtime 重新签名 ---
# 公证要求:
# 1. 全部可执行文件用 Developer ID Application 证书签名(不能是自签证书)
# 2. 启用 hardened runtime (--options runtime)
# 3. 加苹果时间戳服务器签名 (--timestamp)
.PHONY: sign-release
sign-release: build
@if [ -z "$(NOTARY_SIGN_IDENTITY)" ]; then \
echo "错误: Makefile.local 中未配置 NOTARY_SIGN_IDENTITY"; \
echo " 示例: NOTARY_SIGN_IDENTITY := Developer ID Application: Your Name (XXXXXXXXXX)"; \
exit 1; \
fi
@echo "==> 用 release 身份重新签名(hardened runtime + 时间戳): $(NOTARY_SIGN_IDENTITY)"
@codesign --force --options runtime --timestamp --sign "$(NOTARY_SIGN_IDENTITY)" "$(RES_DIR)/7zz"
@codesign --force --options runtime --timestamp --sign "$(NOTARY_SIGN_IDENTITY)" \
--entitlements Extension/Entitlements.plist "$(EXT_BUNDLE)"
@codesign --force --options runtime --timestamp --sign "$(NOTARY_SIGN_IDENTITY)" "$(APP_BUNDLE)"
@# 把重新签名的副本同步到 DISPLAY_NAME 包(rename target 之前已 cp 过,这里覆盖)
@rm -rf "$(BUILD_DIR)/$(DISPLAY_NAME).app"
@cp -R "$(APP_BUNDLE)" "$(BUILD_DIR)/$(DISPLAY_NAME).app"
@echo "==> release 签名完成"
# --- 公证 ---
# 提交到苹果服务器走自动化公证流程,通过后装订票据。
# 凭证读取顺序:
# 1. NOTARY_PROFILE (推荐,xcrun notarytool store-credentials 创建的 keychain profile)
# 2. NOTARY_APPLE_ID + NOTARY_PASSWORD + NOTARY_TEAM_ID
NOTARY_ZIP := $(BUILD_DIR)/$(APP_NAME)-notary.zip
.PHONY: notarize
notarize: sign-release
@echo "==> 打包 zip 用于公证..."
@rm -f "$(NOTARY_ZIP)"
@/usr/bin/ditto -c -k --keepParent "$(BUILD_DIR)/$(DISPLAY_NAME).app" "$(NOTARY_ZIP)"
@echo "==> 提交公证(等待 Apple 返回结果,可能数十秒到数分钟)..."
ifdef NOTARY_PROFILE
xcrun notarytool submit "$(NOTARY_ZIP)" --keychain-profile "$(NOTARY_PROFILE)" --wait
else ifdef NOTARY_APPLE_ID
@if [ -z "$(NOTARY_PASSWORD)" ] || [ -z "$(NOTARY_TEAM_ID)" ]; then \
echo "错误: 使用 NOTARY_APPLE_ID 时,NOTARY_PASSWORD 与 NOTARY_TEAM_ID 都必须配置"; \
exit 1; \
fi
xcrun notarytool submit "$(NOTARY_ZIP)" \
--apple-id "$(NOTARY_APPLE_ID)" \
--password "$(NOTARY_PASSWORD)" \
--team-id "$(NOTARY_TEAM_ID)" \
--wait
else
@echo "错误: Makefile.local 未配置公证凭证"; \
echo " 方式 A(推荐): 先运行 xcrun notarytool store-credentials,然后在 Makefile.local 设置 NOTARY_PROFILE"; \
echo " 方式 B: 在 Makefile.local 设置 NOTARY_APPLE_ID / NOTARY_PASSWORD / NOTARY_TEAM_ID"; \
exit 1
endif
@echo "==> 装订公证票据..."
@xcrun stapler staple "$(BUILD_DIR)/$(DISPLAY_NAME).app"
@echo "==> 验证 Gatekeeper 接受..."
@spctl --assess --type execute --verbose=2 "$(BUILD_DIR)/$(DISPLAY_NAME).app" || \
(echo "警告: spctl 验证失败,但票据已装订,可手动检查"; true)
@echo ""
@echo "=== 公证完成: $(BUILD_DIR)/$(DISPLAY_NAME).app ==="
# --- 安装 ---
install: build
@echo "安装到 $(INSTALL_DIR)/..."
@# 取消 build 目录的 LaunchServices 注册,避免系统设置中出现重复条目
@/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister -u "$(APP_BUNDLE)" 2>/dev/null || true
@/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister -u "$(BUILD_DIR)/$(DISPLAY_NAME).app" 2>/dev/null || true
@mkdir -p "$(INSTALL_DIR)"
@# 清理旧的安装(无空格和有空格两种命名)
@rm -rf "$(INSTALL_DIR)/$(APP_NAME).app"
@rm -rf "$(INSTALL_DIR)/$(DISPLAY_NAME).app"
@cp -R "$(BUILD_DIR)/$(DISPLAY_NAME).app" "$(INSTALL_DIR)/"
@# 重新向 LaunchServices 注册新路径,确保 pluginkit 能识别新版本扩展
@/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister -f "$(INSTALL_DIR)/$(DISPLAY_NAME).app" 2>/dev/null || true
@# 重启 Finder 加载新版本扩展(launchd 会自动拉起)
@echo "重启 Finder..."
@killall Finder 2>/dev/null || true
@echo ""
@echo "=== 安装完成 ==="
@echo "1. 打开应用: open '$(INSTALL_DIR)/$(DISPLAY_NAME).app'"
@echo "2. 启用扩展: 系统设置 > 隐私与安全性 > 扩展 > 已添加的扩展"
@open "$(INSTALL_DIR)/$(DISPLAY_NAME).app"
# --- 开发 ---
dev: build
@echo "启动应用..."
@open "$(BUILD_DIR)/$(DISPLAY_NAME).app"
# --- 目录创建 ---
$(MACOS_DIR) $(RES_DIR):
@mkdir -p $@
$(CONTENTS):
@mkdir -p $@
$(EXT_MACOS):
@mkdir -p $@
$(EXT_CONTENTS):
@mkdir -p $@
$(PLUGINS_DIR):
@mkdir -p $@
# 确保目录依赖顺序
$(MACOS_DIR): | $(CONTENTS)
$(RES_DIR): | $(CONTENTS)
$(EXT_CONTENTS): | $(PLUGINS_DIR)
$(EXT_MACOS): | $(EXT_CONTENTS)
$(PLUGINS_DIR): | $(CONTENTS)
# --- 清理 ---
clean:
@rm -rf $(BUILD_DIR)
@echo "构建目录已清除"
# --- 卸载 ---
uninstall:
@rm -rf "$(INSTALL_DIR)/$(APP_NAME).app"
@rm -rf "$(INSTALL_DIR)/$(DISPLAY_NAME).app"
@echo "已卸载"
# --- 帮助 ---
help:
@echo "Hell RCMenu Makefile"
@echo ""
@echo "构建:"
@echo " make 编译主应用 + Finder Extension"
@echo " make install 编译 + 签名 + 安装到 /Applications/Hell/"
@echo " make dev 编译 + 运行"
@echo " make clean 清除构建产物"
@echo " make uninstall 卸载应用"
@echo " make vendor-7zz 重新下载 7-Zip 二进制到 Vendor/"
@echo ""
@echo "发布(需要 Makefile.local 配置凭证):"
@echo " make sign-release 用 Developer ID 证书 + hardened runtime 重新签名"
@echo " make notarize 签名 + 上传 Apple 公证 + 装订票据"
@echo ""
@echo " Makefile.local 模板见 Makefile.local.example"