-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathbuild.gradle.kts
More file actions
340 lines (284 loc) · 12.7 KB
/
Copy pathbuild.gradle.kts
File metadata and controls
340 lines (284 loc) · 12.7 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
plugins {
java
// Must be compatible with Kotlin compiler bundled with IntelliJ IDEA 2025.3 (metadata 2.2.0)
kotlin("jvm") version "2.2.0"
kotlin("plugin.serialization") version "2.2.0"
id("org.jetbrains.intellij.platform") version "2.10.5"
}
val ideaVersion = "2025.3"
val javaVersion = 21
group = "com.lsfusion"
version = file("META-INF/plugin.xml").let {
if (it.exists()) {
val doc = javax.xml.parsers.DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(it)
doc.getElementsByTagName("version").item(0).textContent
} else {
"1.0.0"
}
}
repositories {
mavenCentral()
maven("https://plugins.jetbrains.com/maven")
intellijPlatform {
defaultRepositories()
}
}
java {
// Compile with a stable JDK toolchain.
// `runIde` in your environment was using javac 22.0.1 and crashed with StackOverflowError inside javac parser.
// Pinning the toolchain makes builds reproducible on other IDE installs and on CI/Jenkins.
toolchain {
// IntelliJ IDEA 2025.3 platform jars are built with Java 21 bytecode (class version 65).
// Therefore the compiler toolchain must be at least Java 21.
// We still emit Java 17 bytecode via `sourceCompatibility/targetCompatibility` on the tasks below.
languageVersion = JavaLanguageVersion.of(javaVersion)
}
}
kotlin {
jvmToolchain(javaVersion)
}
// IntelliJ Platform is configured via `dependencies { intellijPlatform { ... } }` in plugin 2.x.
sourceSets {
main {
java.srcDirs("src", "gen")
kotlin.srcDirs("src", "gen")
// Keep existing project layout: resources are spread across multiple roots.
resources.srcDirs("resources")
}
}
// Ensure `META-INF/` metadata (plugin.xml, mcp.xml) is present in the final jar.
tasks.named<ProcessResources>("processResources") {
from(layout.projectDirectory.dir("META-INF")) {
into("META-INF")
}
}
val packPsiImplUtils by tasks.registering(Jar::class) {
description = "Pack psiImplUtils jar"
archiveFileName.set("psiImplUtils.jar")
destinationDirectory.set(layout.projectDirectory.dir("buildLib"))
val compileJava = tasks.named<JavaCompile>("compileJava")
val compileKotlin = tasks.named<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>("compileKotlin")
from(compileJava.map { it.destinationDirectory }) {
include("com/lsfusion/migration/lang/psi/MigrationPsiImplUtil.class")
include("com/lsfusion/lang/psi/LSFPsiImplUtil.class")
include("com/lsfusion/lang/psi/LSFPsiImplUtil$*.class")
}
from(compileKotlin.map { it.destinationDirectory }) {
include("com/lsfusion/migration/lang/psi/MigrationPsiImplUtil.class")
include("com/lsfusion/lang/psi/LSFPsiImplUtil.class")
include("com/lsfusion/lang/psi/LSFPsiImplUtil$*.class")
}
dependsOn(compileJava, compileKotlin)
}
// `buildSearchableOptions` запускает headless IDE и падает на старых/кастомных code style providers.
// Для сборки/CI это не критично, поэтому отключаем, как рекомендовано сообщением Gradle задачи.
tasks.matching {
it.name == "buildSearchableOptions"
|| it.name == "prepareJarSearchableOptions"
|| it.name == "jarSearchableOptions"
}.configureEach {
enabled = false
}
tasks.withType<org.jetbrains.intellij.platform.gradle.tasks.PrepareSandboxTask>().configureEach {
// IntelliJ IDEA starts the bundled Kubernetes plugin in dev sandbox, and it crashes
// with missing split/RPC backend APIs. The plugin is unrelated to this project, so disable it.
disabledPlugins.add("com.intellij.kubernetes")
}
// Configurations for lexer and parser generation
val jflexConfig by configurations.creating
val grammarKitConfig by configurations.creating
dependencies {
jflexConfig(files("buildLib/jflex.jar"))
grammarKitConfig(files("buildLib/grammar-kit.jar"))
// Libraries previously in lib/
implementation("commons-beanutils:commons-beanutils:1.8.0")
implementation("org.apache.commons:commons-lang3:3.4")
implementation("jgraph:jgraph:5.13.0.0")
implementation("org.jgrapht:jgrapht-core:0.9.0")
implementation("org.jgrapht:jgrapht-ext:0.9.0")
implementation("org.json:json:20180813")
implementation("org.jsoup:jsoup:1.15.3")
implementation("net.gcardone.junidecode:junidecode:0.5.2")
// Needed by MCP toolset DTOs; compiler plugin generates serializers.
compileOnly("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3")
compileOnly("org.jetbrains.kotlinx:kotlinx-serialization-core:1.7.3")
// Annotations used in Java sources (e.g. `@NonNull`).
compileOnly("org.jspecify:jspecify:1.0.0")
// IntelliJ Platform + required bundled plugins.
// NOTE: In IntelliJ Platform Gradle Plugin 2.x, the IntelliJ SDK is declared as a dependency.
intellijPlatform {
// Target IntelliJ IDEA. This artifact provides the platform for compilation.
intellijIdea(ideaVersion)
// Explicit bundled plugin deps so they are available without manual Project Structure setup.
bundledPlugin("com.intellij.java")
bundledPlugin("com.intellij.properties")
bundledPlugin("com.intellij.mcpServer")
bundledModule("intellij.spellchecker")
}
}
intellijPlatform {
pluginVerification {
ides {
recommended()
}
}
publishing {
token.set(providers.gradleProperty("intellijPublishToken"))
}
}
tasks.publishPlugin {
dependsOn(tasks.verifyPlugin)
}
tasks.withType<JavaCompile>().configureEach {
options.encoding = "UTF-8"
sourceCompatibility = javaVersion.toString()
targetCompatibility = javaVersion.toString()
}
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
compilerOptions {
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.fromTarget(javaVersion.toString()))
freeCompilerArgs.addAll(
"-Xjsr305=strict",
)
}
}
abstract class LexerTask @javax.inject.Inject constructor(
private val fileSystemOperations: org.gradle.api.file.FileSystemOperations
) : JavaExec() {
@get:InputFile
abstract val flexFile: RegularFileProperty
@get:InputFile
abstract val skeletonFile: RegularFileProperty
@get:Internal
abstract val outputDir: DirectoryProperty
@get:OutputFile
abstract val outputFile: RegularFileProperty
init {
mainClass.set("jflex.Main")
}
override fun exec() {
args(
"--skel", skeletonFile.get().asFile.absolutePath,
"--nobak",
flexFile.get().asFile.absolutePath,
"-d", outputDir.get().asFile.absolutePath
)
super.exec()
}
}
@CacheableTask
abstract class ParserTask @Inject constructor(
private val objects: ObjectFactory,
private val layout: ProjectLayout,
private val execOperations: ExecOperations
) : DefaultTask() {
@get:InputFile
@get:PathSensitive(PathSensitivity.RELATIVE)
abstract val bnfFile: RegularFileProperty
@get:Internal
abstract val outputDirs: ListProperty<String>
/**
* IntelliJ SDK jars are required only to RUN the generator,
* but must NOT be a Gradle task input (otherwise Gradle fingerprints hundreds of jars).
*/
@get:Internal
abstract val intellijPlatformJars: ConfigurableFileCollection
/**
* Precomputed IDEA lib directory (…/lib). Stored as Internal to avoid fingerprinting.
* Also avoids resolving IntelliJ configuration during task execution (configuration cache friendly).
*/
@get:Internal
abstract val ideaLibDir: DirectoryProperty
/** Small classpath inputs that really affect generation. */
@get:Classpath
abstract val grammarKitClasspath: ConfigurableFileCollection
@get:Classpath
abstract val psiImplUtilsJar: ConfigurableFileCollection
/** Declare output directories so Gradle can track UP-TO-DATE and cache state. */
@get:OutputDirectories
val outputDirectories: List<Directory>
get() = outputDirs.get().map { layout.projectDirectory.dir(it) }
@TaskAction
fun generate() {
// Clean output directories before generation so stale files from removed rules are removed.
outputDirs.get().forEach { dirPath ->
layout.projectDirectory.dir(dirPath).asFile.deleteRecursively()
}
val ideaLibTree = objects.fileTree().setDir(ideaLibDir.get()).matching { include("*.jar") }
// Running org.intellij.grammar.Main with the IDEA distribution on the classpath boots enough of the
// platform to emit telemetry/log files. Without redirection those land in the IDE distribution inside
// Gradle's immutable transform cache, which corrupts it ("contents of the immutable workspace have been
// modified") and breaks every subsequent build. Point the platform's system/log/config/plugins paths at
// build/ so generation never writes into the shared cache.
val ideaWorkDir = layout.buildDirectory.dir("grammarKitIdea").get().asFile
ideaWorkDir.mkdirs()
execOperations.javaexec {
mainClass.set("org.intellij.grammar.Main")
classpath = grammarKitClasspath + psiImplUtilsJar + ideaLibTree
systemProperty("idea.system.path", ideaWorkDir.resolve("system").absolutePath)
systemProperty("idea.log.path", ideaWorkDir.resolve("log").absolutePath)
systemProperty("idea.config.path", ideaWorkDir.resolve("config").absolutePath)
systemProperty("idea.plugins.path", ideaWorkDir.resolve("plugins").absolutePath)
args("gen", bnfFile.get().asFile.absolutePath)
}
}
}
fun TaskContainer.registerLexerTask(name: String, flexPath: String, outputDirPath: String, outputPath: String) =
register<LexerTask>(name) {
classpath = jflexConfig
flexFile.set(layout.projectDirectory.file(flexPath))
skeletonFile.set(layout.projectDirectory.file("buildLib/idea-flex.skeleton"))
outputDir.set(layout.projectDirectory.dir(outputDirPath))
outputFile.set(layout.projectDirectory.file(outputPath))
}
fun TaskContainer.registerParserTask(name: String, bnfPath: String, outputDirPaths: List<String>) =
register<ParserTask>(name) {
val intellijDep = configurations.named("intellijPlatformDependency")
intellijPlatformJars.from(intellijDep)
// Compute IDEA lib directory ONCE at configuration time.
// This prevents resolving IntelliJ SDK during task execution (config-cache friendly).
val platformFiles = intellijDep.get().files
val platformLib = platformFiles.find { it.name.startsWith("idea") && it.isDirectory }?.resolve("lib")
?: platformFiles.find { it.name.startsWith("idea") && it.name.endsWith(".jar") }?.parentFile
require(platformLib != null && platformLib.exists()) {
"Cannot locate IntelliJ IDEA 'lib' directory from intellijPlatformDependency."
}
ideaLibDir.set(platformLib)
// Previously: classpath(grammarKitConfig, files("lib/psiImplUtils.jar"), intellijPlatformJars)
// Now: keep only small jars as task inputs; IDEA libs are runtime-only and Internal.
grammarKitClasspath.from(grammarKitConfig)
psiImplUtilsJar.from(files("buildLib/psiImplUtils.jar"))
bnfFile.set(layout.projectDirectory.file(bnfPath))
outputDirs.set(outputDirPaths)
outputDirPaths.forEach { outputs.dir(layout.projectDirectory.dir(it)) } // keep as you had
}
val generateLsfLexer by tasks.registerLexerTask(
"generateLsfLexer",
"src/com/lsfusion/lang/LSF.flex",
"gen/com/lsfusion/lang",
"gen/com/lsfusion/lang/LSFLexer.java"
)
val generateMigrationLexer by tasks.registerLexerTask(
"generateMigrationLexer",
"src/com/lsfusion/migration/Migration.flex",
"gen/com/lsfusion/migration",
"gen/com/lsfusion/migration/MigrationLexer.java"
)
val generateLsfParser by tasks.registerParserTask(
"generateLsfParser",
"src/com/lsfusion/lang/LSF.bnf",
listOf("gen/com/lsfusion/lang/parser", "gen/com/lsfusion/lang/psi")
)
val generateMigrationParser by tasks.registerParserTask(
"generateMigrationParser",
"src/com/lsfusion/migration/Migration.bnf",
listOf("gen/com/lsfusion/migration/lang/parser", "gen/com/lsfusion/migration/lang/psi")
)
tasks.withType<JavaCompile>().configureEach {
dependsOn(generateLsfLexer, generateMigrationLexer, generateLsfParser, generateMigrationParser)
finalizedBy(packPsiImplUtils)
}
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
dependsOn(generateLsfLexer, generateMigrationLexer, generateLsfParser, generateMigrationParser)
finalizedBy(packPsiImplUtils)
}