-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbuild.gradle
More file actions
391 lines (336 loc) · 15.5 KB
/
build.gradle
File metadata and controls
391 lines (336 loc) · 15.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
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
import top.howiehz.gradle.YamlPluginVersionSupport
/*
* Halo Plugin Extra API - Build Configuration
*
* 解决的核心问题:Halo 官方插件工具硬编码了对 "jar" 任务的依赖,
* 无法支持多 JAR 变体构建。通过 buildSrc 自定义组件索引生成来绕过限制。
*
* 构建变体:
* - jarLite: 轻量版,不包含 interop 功能 - 用于不需要相关运行时集成的场景
* - jarFullAllPlatforms: 完整版,包含所有平台 Javet 支持 - 用于多平台部署
* - jarFull[Platform]: 特定平台完整版 - 用于单平台优化部署
*/
plugins {
id 'java'
id 'io.freefair.lombok' version '9.4.0'
id 'run.halo.plugin.devtools' version '0.6.2' apply false // 禁用自动应用,因为需要手动控制组件索引生成
}
apply plugin: 'run.halo.plugin.devtools' // 手动应用以便后续禁用默认组件索引任务
group = 'top.howiehz.halo.plugin.extra.api'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21) // 使用 Java 21 以获得最新特性和性能优化
}
}
repositories {
mavenCentral()
}
// 为什么需要多平台 Javet 配置:
// Javet 包含 native 库,不同平台需要不同的二进制文件
// 通过平台特定配置可以生成针对性的部署包,减少包大小
def platforms = [
'Linux-arm64' : ['linux-arm64'],
'Linux-x86_64' : ['linux-x86_64'],
'Macos-arm64' : ['macos-arm64'],
'Macos-x86_64' : ['macos-x86_64'],
'Windows-x86_64': ['windows-x86_64']
]
def minifyHtmlNativeLibsByVariant = [
'Linux-arm64' : 'linux-aarch64.nativelib',
'Linux-x86_64' : 'linux-x64.nativelib',
'Macos-arm64' : 'mac-aarch64.nativelib',
'Macos-x86_64' : 'mac-x64.nativelib',
'Windows-x86_64': 'win-x64.nativelib'
]
def runtimeClasspathTrees = { configuration, String selectedMinifyHtmlNativeLib = null ->
configuration.resolvedConfiguration.resolvedArtifacts.collect { artifact ->
def artifactFile = artifact.file
if (artifactFile.isDirectory()) {
return artifactFile
}
def artifactTree = zipTree(artifactFile)
if (artifact.moduleVersion.id.group == 'in.wilsonl.minifyhtml'
&& artifact.name == 'minify-html'
&& selectedMinifyHtmlNativeLib != null) {
return artifactTree.matching {
exclude { details ->
details.path.endsWith('.nativelib')
&& details.path != selectedMinifyHtmlNativeLib
}
}
}
return artifactTree
}
}
// 为每个平台创建专用配置,继承 runtimeClasspath 以保证依赖一致性
platforms.each { variant, platformList ->
configurations.create("javet${variant}") {
extendsFrom configurations.runtimeClasspath
}
}
// 为每个平台添加对应的 Javet 依赖
// 为什么分开配置:允许按需打包,避免在单平台部署时包含不必要的 native 库
platforms.each { variant, platformList ->
dependencies.add("javet${variant}", 'com.caoccao.javet:javet:5.0.6')
platformList.each { platform ->
dependencies.add("javet${variant}", "com.caoccao.javet:javet-node-${platform}:5.0.6")
}
}
// 全平台配置:用于生成包含所有平台支持的通用版本
configurations {
javetAllPlatforms {
extendsFrom configurations.runtimeClasspath
}
liteRuntimeClasspath {
extendsFrom configurations.runtimeClasspath
exclude group: 'com.caoccao.javet'
exclude group: 'in.wilsonl.minifyhtml', module: 'minify-html'
}
}
dependencies.add('javetAllPlatforms', 'com.caoccao.javet:javet:5.0.6')
platforms.each { variant, platformList ->
platformList.each { platform ->
dependencies.add('javetAllPlatforms', "com.caoccao.javet:javet-node-${platform}:5.0.6")
}
}
dependencies {
// Halo 平台依赖
implementation platform('run.halo.tools.platform:plugin:2.24.0') // BOM 确保版本一致性
compileOnly 'run.halo.app:api' // compileOnly:运行时由 Halo 提供,避免冲突
// JavaScript 引擎 - 为什么选择 Javet:支持 Node.js 模块,性能好,维护活跃
implementation 'com.caoccao.javet:javet:5.0.6'
implementation 'in.wilsonl.minifyhtml:minify-html:0.18.1'
// Pangu - 自动在中日韩字符和英文、数字、符号之间添加空格,提升可读性
implementation 'ws.vinta:pangu:1.1.0'
// Spring 依赖 - compileOnly:Halo 已内置,避免版本冲突和包体积增加
compileOnly 'org.springframework:spring-context'
compileOnly 'org.springframework:spring-core'
compileOnly 'org.springframework:spring-beans'
compileOnly 'org.springframework.data:spring-data-commons'
compileOnly 'io.projectreactor:reactor-core'
// 测试依赖 - testImplementation:测试时需要真实实现
testImplementation 'run.halo.app:api'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
testImplementation 'com.caoccao.javet:javet:5.0.6'
}
test {
useJUnitPlatform()
}
tasks.withType(JavaCompile).configureEach {
options.encoding = 'UTF-8' // 确保中文注释和字符串正确处理
options.release = 21
}
// 前端资源处理
tasks.register('processShikiResources', Copy) {
from project(':shiki').layout.buildDirectory.dir('dist')
into layout.buildDirectory.dir('resources/main/js') // 插件 JS 资源目录
include 'shiki.umd.cjs' // 只包含 UMD 格式,兼容性好
shouldRunAfter tasks.named('processResources')
}
// plugin.yaml 版本同步逻辑 - 使用官方风格的 YAML 处理
// 为什么需要:确保 plugin.yaml 中的版本与构建版本一致,避免部署时版本不匹配
// 为什么用 YamlPluginVersionSupport:比正则表达式更安全,保持 YAML 格式和注释
def configurePluginYamlVersion() {
def manifestFile = file('src/main/resources/plugin.yaml')
return YamlPluginVersionSupport.configurePluginYamlVersion(project, manifestFile)
}
def coreExtensionDefinitionsFile = file('src/main/resources/extensions/extension-definitions-core.yaml')
def interopExtensionDefinitionsFile = file('src/main/resources/extensions/extension-definitions-interop.yaml')
def registerExtensionDefinitionsTask = { String taskName, List<File> sourceFiles, String outputPath ->
tasks.register(taskName) {
group = 'build'
description = "Generates ${taskName} extension definitions"
inputs.files(sourceFiles)
outputs.file(layout.buildDirectory.file(outputPath))
doLast {
def mergedContent = sourceFiles.collect { sourceFile ->
sourceFile.getText('UTF-8').trim()
}.findAll { !it.isEmpty() }.join('\n---\n')
def outputFile = layout.buildDirectory.file(outputPath).get().asFile
outputFile.parentFile.mkdirs()
outputFile.setText(mergedContent + '\n', 'UTF-8')
}
}
}
def generateCoreExtensionDefinitionsTask = registerExtensionDefinitionsTask(
'generateCoreExtensionDefinitions',
[coreExtensionDefinitionsFile],
'generated/variant-resources/core/extensions/extension-definitions.yaml'
)
def generateFullExtensionDefinitionsTask = registerExtensionDefinitionsTask(
'generateFullExtensionDefinitions',
[coreExtensionDefinitionsFile, interopExtensionDefinitionsFile],
'generated/variant-resources/full/extensions/extension-definitions.yaml'
)
def jarLiteIndexTask
def jarFullAllPlatformsIndexTask
/*
* 核心解决方案:自定义组件索引生成
*
* 为什么需要:
* 1. Halo 官方插件工具硬编码了对 "jar" 任务的依赖
* 2. 我们需要多个 JAR 变体,每个都需要正确的组件索引
* 3. 通过 buildSrc 中的自定义任务绕过官方工具限制
* 4. 使用 afterEvaluate 确保所有依赖都已解析
*/
afterEvaluate {
// 禁用官方组件索引任务 - 避免与自定义实现冲突
tasks.matching { it.name == 'generatePluginComponentsIdx' }.configureEach {
enabled = false
}
// 配置子项目依赖 - 为什么在 afterEvaluate:确保子项目任务已注册
tasks.named('processShikiResources') {
dependsOn project(':shiki').tasks.named('assemble')
}
tasks.named('classes') {
dependsOn tasks.named('processShikiResources')
}
// 导入自定义组件索引任务类 - 为什么用自定义:支持多 JAR 任务,官方不支持
def MultiJarPluginComponentsIndexTask = top.howiehz.gradle.MultiJarPluginComponentsIndexTask
// 轻量版组件索引 - 为什么过滤 interop 类:轻量版不包含 interop 功能,避免无效组件注册
jarLiteIndexTask = tasks.register('generatePluginComponentsIdxJarLite', MultiJarPluginComponentsIndexTask) {
group = 'halo server'
description = 'Generates plugin components index for lite version'
classesDirs.from(sourceSets.main.output.classesDirs)
jarTaskName.set('jarLite')
// 排除 interop 相关的包,避免在 lite 版本中注册这些组件
excludePackages.set([
'top.howiehz.halo.plugin.extra.api.service.interop',
'top.howiehz.halo.plugin.extra.api.finder.interop'
])
outputFile.set(layout.buildDirectory.file('tmp/jarLite/META-INF/plugin-components.idx'))
dependsOn 'processResources' // 确保在 processResources 之后运行
doFirst(configurePluginYamlVersion())
}
// 完整版组件索引 - 包含所有类,支持完整功能
jarFullAllPlatformsIndexTask = tasks.register('generatePluginComponentsIdxJarFullAllPlatforms', MultiJarPluginComponentsIndexTask) {
group = 'halo server'
description = 'Generates plugin components index for full version'
classesDirs.from(sourceSets.main.output.classesDirs) // 包含所有编译输出
jarTaskName.set('jarFullAllPlatforms')
outputFile.set(layout.buildDirectory.file('tmp/jarFullAllPlatforms/META-INF/plugin-components.idx'))
doFirst(configurePluginYamlVersion())
}
// 为每个平台生成专用组件索引
platforms.each { variant, platformList ->
def taskName = "jarFull${variant}"
def indexTaskName = "generatePluginComponentsIdx${taskName.capitalize()}"
tasks.register(indexTaskName, MultiJarPluginComponentsIndexTask) {
group = 'halo server'
description = "Generates plugin components index for ${taskName}"
classesDirs.from(sourceSets.main.output.classesDirs)
jarTaskName.set(taskName)
outputFile.set(layout.buildDirectory.file("tmp/${taskName}/META-INF/plugin-components.idx"))
doFirst(configurePluginYamlVersion())
}
}
// JAR 构建任务
// 轻量版:为什么需要:提供不依赖 interop 能力的核心功能版本,减少资源占用
tasks.register('jarLite', Jar) {
group = 'build'
description = 'Assembles lite version without JS features'
archiveFileName = "extra-api-lite-${project.version}.jar"
// 排除 interop 相关类 - 减少包大小,避免未使用的依赖
from(sourceSets.main.output.classesDirs) {
exclude 'top/howiehz/halo/plugin/extra/api/service/interop/**'
exclude 'top/howiehz/halo/plugin/extra/api/finder/interop/**'
}
// 排除 interop 相关资源
from(sourceSets.main.output.resourcesDir) {
exclude 'js/**'
exclude 'extensions/extension-definitions*.yaml' // 变体扩展定义由专门任务生成
}
// 排除部分依赖 - 轻量版不需要 interop 运行时,减少 JAR 大小
from configurations.liteRuntimeClasspath.collect {
it.isDirectory() ? it : zipTree(it)
}
into('extensions') {
from generateCoreExtensionDefinitionsTask
rename { 'extension-definitions.yaml' }
}
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
dependsOn 'compileJava', 'processResources', jarLiteIndexTask, generateCoreExtensionDefinitionsTask
// 包含对应的组件索引文件 - Halo 需要此文件来注册插件组件
into('META-INF') {
from jarLiteIndexTask.get().outputFile
rename { 'plugin-components.idx' }
}
}
// 完整版:包含所有平台 Javet 支持 - 通用部署,无需考虑平台兼容性
tasks.register('jarFullAllPlatforms', Jar) {
group = 'build'
description = 'Assembles full version with all platforms'
archiveFileName = "extra-api-full-all-platforms-${project.version}.jar"
from(sourceSets.main.output) {
exclude 'extensions/extension-definitions-core.yaml'
exclude 'extensions/extension-definitions-interop.yaml'
}
from(runtimeClasspathTrees(configurations.javetAllPlatforms))
into('extensions') {
from generateFullExtensionDefinitionsTask
rename { 'extension-definitions.yaml' }
}
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
dependsOn 'classes', jarFullAllPlatformsIndexTask, generateFullExtensionDefinitionsTask
into('META-INF') {
from jarFullAllPlatformsIndexTask.get().outputFile
rename { 'plugin-components.idx' }
}
}
// 平台特定完整版 - 优化部署包大小,只包含目标平台的 native 库
platforms.each { variant, platformList ->
def taskName = "jarFull${variant}"
def configName = "javet${variant}"
def archiveName = "extra-api-full-${variant.toLowerCase()}-${project.version}.jar"
def indexTaskName = "generatePluginComponentsIdx${taskName.capitalize()}"
def minifyHtmlNativeLib = minifyHtmlNativeLibsByVariant[variant]
tasks.register(taskName, Jar) {
group = 'build'
description = "Assembles full version for ${variant}"
archiveFileName = archiveName
from(sourceSets.main.output) {
exclude 'extensions/extension-definitions-core.yaml'
exclude 'extensions/extension-definitions-interop.yaml'
}
from(runtimeClasspathTrees(configurations.getByName(configName), minifyHtmlNativeLib))
into('extensions') {
from generateFullExtensionDefinitionsTask
rename { 'extension-definitions.yaml' }
}
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
dependsOn 'classes', indexTaskName, generateFullExtensionDefinitionsTask
into('META-INF') {
from tasks.named(indexTaskName).get().outputFile
rename { 'plugin-components.idx' }
}
}
}
}
// 默认任务配置
jar {
enabled = false // 禁用标准 jar 任务 - 使用自定义 JAR 任务替代
dependsOn 'jarFullAllPlatforms'
// dependsOn 'jarLite'
}
build {
dependsOn 'jarFullAllPlatforms' // 默认构建完整版 - 提供完整功能
// dependsOn 'jarLite'
}
// 便捷构建任务
tasks.register('buildAll') {
group = 'build'
description = 'Build all jar variants' // 为什么需要:CI/CD 中一次性构建所有版本
dependsOn 'jarLite', 'jarFullAllPlatforms'
platforms.each { variant, platformList ->
dependsOn "jarFull${variant}"
}
}
tasks.register('buildLite') {
group = 'build'
description = 'Build lite version only' // 为什么需要:快速测试和开发时的轻量构建
dependsOn 'jarLite'
}
halo {
version = '2.24' // 目标 Halo 版本
}