aboutsummaryrefslogtreecommitdiffstats
path: root/buildSrc
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2023-04-08 22:56:44 +0200
committerLibravatar Kristóf Marussy <kristof@marussy.com>2023-04-08 22:58:21 +0200
commit561fac70fd3dc3ebe1cfbc50146757495fb828d5 (patch)
tree20aa72bbe438aaa70c8de264ff0d366758e7772d /buildSrc
parentrefactor: remove TupleLike (diff)
downloadrefinery-561fac70fd3dc3ebe1cfbc50146757495fb828d5.tar.gz
refinery-561fac70fd3dc3ebe1cfbc50146757495fb828d5.tar.zst
refinery-561fac70fd3dc3ebe1cfbc50146757495fb828d5.zip
build: convert Gradle scripts to Kotlin
Improves IDE support build scripts in IntelliJ. There is no Eclipse IDE support, but Eclipse didn't have support for Groovy either, so there is no degradation of functionality.
Diffstat (limited to 'buildSrc')
-rw-r--r--buildSrc/build.gradle14
-rw-r--r--buildSrc/build.gradle.kts19
-rw-r--r--buildSrc/settings.gradle7
-rw-r--r--buildSrc/settings.gradle.kts7
-rw-r--r--buildSrc/src/main/groovy/refinery-eclipse.gradle28
-rw-r--r--buildSrc/src/main/groovy/refinery-frontend-conventions.gradle14
-rw-r--r--buildSrc/src/main/groovy/refinery-frontend-workspace.gradle29
-rw-r--r--buildSrc/src/main/groovy/refinery-frontend-worktree.gradle95
-rw-r--r--buildSrc/src/main/groovy/refinery-java-application.gradle11
-rw-r--r--buildSrc/src/main/groovy/refinery-java-conventions.gradle93
-rw-r--r--buildSrc/src/main/groovy/refinery-java-library.gradle4
-rw-r--r--buildSrc/src/main/groovy/refinery-java-test-fixtures.gradle35
-rw-r--r--buildSrc/src/main/groovy/refinery-jmh.gradle64
-rw-r--r--buildSrc/src/main/groovy/refinery-mwe2.gradle16
-rw-r--r--buildSrc/src/main/groovy/refinery-sonarqube.gradle8
-rw-r--r--buildSrc/src/main/groovy/refinery-xtext-conventions.gradle21
-rw-r--r--buildSrc/src/main/java/tools/refinery/buildsrc/EclipseUtils.java72
-rw-r--r--buildSrc/src/main/java/tools/refinery/buildsrc/SonarPropertiesUtils.java44
-rw-r--r--buildSrc/src/main/kotlin/refinery-eclipse.gradle.kts30
-rw-r--r--buildSrc/src/main/kotlin/refinery-frontend-conventions.gradle.kts18
-rw-r--r--buildSrc/src/main/kotlin/refinery-frontend-workspace.gradle.kts32
-rw-r--r--buildSrc/src/main/kotlin/refinery-frontend-worktree.gradle.kts84
-rw-r--r--buildSrc/src/main/kotlin/refinery-java-application.gradle.kts12
-rw-r--r--buildSrc/src/main/kotlin/refinery-java-conventions.gradle.kts96
-rw-r--r--buildSrc/src/main/kotlin/refinery-java-library.gradle.kts5
-rw-r--r--buildSrc/src/main/kotlin/refinery-java-test-fixtures.gradle.kts31
-rw-r--r--buildSrc/src/main/kotlin/refinery-jmh.gradle.kts63
-rw-r--r--buildSrc/src/main/kotlin/refinery-mwe2.gradle.kts18
-rw-r--r--buildSrc/src/main/kotlin/refinery-sonarqube.gradle.kts3
-rw-r--r--buildSrc/src/main/kotlin/refinery-xtext-conventions.gradle.kts21
30 files changed, 555 insertions, 439 deletions
diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle
deleted file mode 100644
index cd3101be..00000000
--- a/buildSrc/build.gradle
+++ /dev/null
@@ -1,14 +0,0 @@
1plugins {
2 id 'groovy-gradle-plugin'
3 alias libs.plugins.versions
4}
5
6repositories {
7 gradlePluginPortal()
8}
9
10dependencies {
11 implementation libs.gradlePlugin.frontend
12 implementation libs.gradlePlugin.shadow
13 implementation libs.gradlePlugin.sonarqube
14}
diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts
new file mode 100644
index 00000000..7a5ee5c4
--- /dev/null
+++ b/buildSrc/build.gradle.kts
@@ -0,0 +1,19 @@
1plugins {
2 `kotlin-dsl`
3 // Workaround for https://github.com/gradle/gradle/issues/22797
4 @Suppress("DSL_SCOPE_VIOLATION")
5 alias(libs.plugins.versions)
6}
7
8repositories {
9 gradlePluginPortal()
10 mavenCentral()
11}
12
13dependencies {
14 implementation(libs.gradlePlugin.frontend)
15 implementation(libs.gradlePlugin.shadow)
16 implementation(libs.gradlePlugin.sonarqube)
17 // https://github.com/gradle/gradle/issues/15383
18 implementation(files(libs.javaClass.superclass.protectionDomain.codeSource.location))
19}
diff --git a/buildSrc/settings.gradle b/buildSrc/settings.gradle
deleted file mode 100644
index b54c8e5d..00000000
--- a/buildSrc/settings.gradle
+++ /dev/null
@@ -1,7 +0,0 @@
1dependencyResolutionManagement {
2 versionCatalogs {
3 libs {
4 from files('../gradle/libs.versions.toml')
5 }
6 }
7}
diff --git a/buildSrc/settings.gradle.kts b/buildSrc/settings.gradle.kts
new file mode 100644
index 00000000..8e6efa3d
--- /dev/null
+++ b/buildSrc/settings.gradle.kts
@@ -0,0 +1,7 @@
1dependencyResolutionManagement {
2 versionCatalogs {
3 create("libs") {
4 from(files("../gradle/libs.versions.toml"))
5 }
6 }
7}
diff --git a/buildSrc/src/main/groovy/refinery-eclipse.gradle b/buildSrc/src/main/groovy/refinery-eclipse.gradle
deleted file mode 100644
index 15dcb5ce..00000000
--- a/buildSrc/src/main/groovy/refinery-eclipse.gradle
+++ /dev/null
@@ -1,28 +0,0 @@
1plugins {
2 id 'eclipse'
3}
4
5// Workaround from https://github.com/gradle/gradle/issues/898#issuecomment-885765821
6def eclipseResourceEncoding = tasks.register('eclipseResourceEncoding') {
7 ext.outputFile = file('.settings/org.eclipse.core.resources.prefs')
8 def compileTask = tasks.findByName('compileJava')
9 ext.encoding = provider({ compileTask?.options?.encoding }).orElse(providers.systemProperty('file.encoding'))
10
11 inputs.property('file.encoding', encoding)
12 outputs.file(outputFile).withPropertyName('outputFile')
13
14 doLast {
15 Properties eclipseEncodingProperties =
16 new Properties(Collections.singletonMap('eclipse.preferences.version', '1'))
17 eclipseEncodingProperties.put('encoding/<project>', encoding.get())
18 outputFile.withOutputStream {
19 eclipseEncodingProperties.store(it, 'generated by ' + name)
20 }
21 }
22}
23
24tasks.named('eclipse') {
25 dependsOn(eclipseResourceEncoding)
26}
27
28eclipse.synchronizationTasks(eclipseResourceEncoding)
diff --git a/buildSrc/src/main/groovy/refinery-frontend-conventions.gradle b/buildSrc/src/main/groovy/refinery-frontend-conventions.gradle
deleted file mode 100644
index da9370fe..00000000
--- a/buildSrc/src/main/groovy/refinery-frontend-conventions.gradle
+++ /dev/null
@@ -1,14 +0,0 @@
1plugins {
2 id 'org.siouan.frontend-jdk11'
3}
4
5frontend {
6 nodeVersion = project.ext['frontend.nodeVersion']
7 nodeInstallDirectory = file("${rootDir}/.node")
8 yarnEnabled = true
9 yarnVersion = project.ext['frontend.yarnVersion']
10}
11
12tasks.named('enableYarnBerry') {
13 enabled = false
14}
diff --git a/buildSrc/src/main/groovy/refinery-frontend-workspace.gradle b/buildSrc/src/main/groovy/refinery-frontend-workspace.gradle
deleted file mode 100644
index 9c6d7b13..00000000
--- a/buildSrc/src/main/groovy/refinery-frontend-workspace.gradle
+++ /dev/null
@@ -1,29 +0,0 @@
1plugins {
2 id 'refinery-eclipse'
3 id 'refinery-frontend-conventions'
4}
5
6tasks.named('installNode') {
7 dependsOn rootProject.tasks.named('installNode')
8 enabled = false
9}
10
11tasks.named('installYarnGlobally') {
12 dependsOn rootProject.tasks.named('installYarnGlobally')
13 enabled = false
14}
15
16tasks.named('installYarn') {
17 dependsOn rootProject.tasks.named('installYarn')
18 enabled = false
19}
20
21def rootInstallFrontend = rootProject.tasks.named('installFrontend')
22rootInstallFrontend.configure {
23 inputs.file "${projectDir}/package.json"
24}
25
26tasks.named('installFrontend') {
27 dependsOn rootInstallFrontend
28 enabled = false
29}
diff --git a/buildSrc/src/main/groovy/refinery-frontend-worktree.gradle b/buildSrc/src/main/groovy/refinery-frontend-worktree.gradle
deleted file mode 100644
index 1d239fd5..00000000
--- a/buildSrc/src/main/groovy/refinery-frontend-worktree.gradle
+++ /dev/null
@@ -1,95 +0,0 @@
1plugins {
2 id 'refinery-frontend-conventions'
3}
4
5frontend {
6 yarnGlobalInstallScript = "install -g yarn@${project.ext['frontend.yarn1Version']}"
7 yarnInstallScript = "set version ${frontend.yarnVersion.get()} --only-if-needed"
8 if (project.hasProperty('ci')) {
9 installScript = 'install --immutable --inline-builds'
10 } else {
11 installScript = 'install'
12 }
13}
14
15ext.frontedPropertiesFile = "${frontend.nodeInstallDirectory.get()}/frontend.properties"
16
17String getFrontendProperty(String propertyName) {
18 FileInputStream inputStream = null
19 Properties props = new Properties()
20 try {
21 inputStream = new FileInputStream(frontedPropertiesFile)
22 props.load(inputStream)
23 } catch (IOException ignored) {
24 return null
25 } finally {
26 if (inputStream != null) {
27 inputStream.close()
28 }
29 }
30 return props.get(propertyName)
31}
32
33void putFrontedProperty(String propertyName, String propertyValue) {
34 FileInputStream inputStream = null
35 Properties props = new Properties()
36 try {
37 inputStream = new FileInputStream(frontedPropertiesFile)
38 props.load(inputStream)
39 } catch (FileNotFoundException ignored) {
40 // Use an empty Properties object instead
41 } finally {
42 if (inputStream != null) {
43 inputStream.close()
44 }
45 }
46 props.put(propertyName, propertyValue)
47 FileOutputStream outputStream = null
48 try {
49 outputStream = new FileOutputStream(frontedPropertiesFile)
50 props.store(outputStream, null)
51 } catch (IOException ignored) {
52 } finally {
53 if (outputStream != null) {
54 outputStream.close()
55 }
56 }
57}
58
59tasks.named('installNode') {
60 onlyIf {
61 getFrontendProperty('installedNodeVersion') != frontend.nodeVersion.get()
62 }
63 doLast {
64 putFrontedProperty('installedNodeVersion', frontend.nodeVersion.get())
65 }
66}
67
68tasks.named('installYarnGlobally') {
69 onlyIf {
70 getFrontendProperty('installedYarn1Version') != project.ext['frontend.yarn1Version']
71 }
72 doLast {
73 putFrontedProperty('installedYarn1Version', project.ext['frontend.yarn1Version'])
74 }
75 outputs.dir "${frontend.nodeInstallDirectory.get()}/lib/node_modules/yarn"
76}
77
78tasks.named('installYarn') {
79 outputs.file ".yarn/releases/yarn-${frontend.yarnVersion.get()}.cjs"
80}
81
82tasks.named('installFrontend') {
83 inputs.files('package.json', 'yarn.lock')
84 outputs.files('.pnp.cjs', '.pnp.loader.mjs')
85}
86
87tasks.register('clobberFrontend', Delete) {
88 delete frontend.nodeInstallDirectory.get()
89 delete '.yarn/cache'
90 delete '.yarn/install-state.gz'
91 delete '.yarn/sdks'
92 delete '.yarn/unplugged'
93 delete '.pnp.cjs'
94 delete '.pnp.loader.mjs'
95}
diff --git a/buildSrc/src/main/groovy/refinery-java-application.gradle b/buildSrc/src/main/groovy/refinery-java-application.gradle
deleted file mode 100644
index c38ccdb3..00000000
--- a/buildSrc/src/main/groovy/refinery-java-application.gradle
+++ /dev/null
@@ -1,11 +0,0 @@
1plugins {
2 id 'application'
3 id 'com.github.johnrengelman.shadow'
4 id 'refinery-java-conventions'
5}
6
7for (taskName in ['distTar', 'distZip', 'shadowDistTar', 'shadowDistZip']) {
8 tasks.named(taskName) {
9 enabled = false
10 }
11}
diff --git a/buildSrc/src/main/groovy/refinery-java-conventions.gradle b/buildSrc/src/main/groovy/refinery-java-conventions.gradle
deleted file mode 100644
index fdf5818a..00000000
--- a/buildSrc/src/main/groovy/refinery-java-conventions.gradle
+++ /dev/null
@@ -1,93 +0,0 @@
1plugins {
2 id 'jacoco'
3 id 'java'
4 id 'refinery-eclipse'
5}
6
7repositories {
8 mavenCentral()
9 maven {
10 url 'https://repo.eclipse.org/content/groups/releases/'
11 }
12}
13
14dependencies {
15 compileOnly libs.jetbrainsAnnotations
16 testCompileOnly libs.jetbrainsAnnotations
17 testImplementation libs.hamcrest
18 testImplementation libs.junit.api
19 testRuntimeOnly libs.junit.engine
20 testImplementation libs.junit.params
21 testImplementation libs.mockito.core
22 testImplementation libs.mockito.junit
23}
24
25java.toolchain {
26 languageVersion = JavaLanguageVersion.of(19)
27}
28
29tasks.withType(JavaCompile) {
30 options.release.set(17)
31}
32
33def jacocoTestReport = tasks.named('jacocoTestReport')
34jacocoTestReport.configure {
35 dependsOn test
36 reports {
37 xml.required = true
38 }
39}
40
41tasks.named('test') {
42 useJUnitPlatform {
43 excludeTags 'slow'
44 }
45 finalizedBy jacocoTestReport
46}
47
48tasks.register('slowTest', Test) {
49 useJUnitPlatform()
50 finalizedBy jacocoTestReport
51}
52
53tasks.named('jar') {
54 manifest {
55 attributes(
56 'Bundle-SymbolicName': "${project.group}.${project.name}",
57 'Bundle-Version': project.version
58 )
59 }
60}
61
62def generateEclipseSourceFolders = tasks.register('generateEclipseSourceFolders')
63
64tasks.register('prepareEclipse') {
65 dependsOn generateEclipseSourceFolders
66 dependsOn tasks.named('eclipseJdt')
67}
68
69tasks.named('eclipseClasspath') {
70 dependsOn generateEclipseSourceFolders
71}
72
73eclipse {
74 classpath.file.whenMerged {
75 for (entry in entries) {
76 if (entry.path.endsWith('-gen')) {
77 entry.entryAttributes['ignore_optional_problems'] = true
78 }
79 // If a project has a main dependency on a project and an test dependency on the testFixtures of a project,
80 // it will be erroneously added as a test-only dependency to Eclipse.
81 // As a workaround, we add all project dependencies as main dependencies
82 // (we do not deliberately use test-only project dependencies).
83 if (entry in org.gradle.plugins.ide.eclipse.model.ProjectDependency) {
84 entry.entryAttributes.remove('test')
85 }
86 }
87 }
88
89 jdt.file.withProperties { properties ->
90 // Allow @SuppressWarnings to suppress SonarLint warnings
91 properties['org.eclipse.jdt.core.compiler.problem.unhandledWarningToken'] = 'ignore'
92 }
93}
diff --git a/buildSrc/src/main/groovy/refinery-java-library.gradle b/buildSrc/src/main/groovy/refinery-java-library.gradle
deleted file mode 100644
index daa80f17..00000000
--- a/buildSrc/src/main/groovy/refinery-java-library.gradle
+++ /dev/null
@@ -1,4 +0,0 @@
1plugins {
2 id 'java-library'
3 id 'refinery-java-conventions'
4}
diff --git a/buildSrc/src/main/groovy/refinery-java-test-fixtures.gradle b/buildSrc/src/main/groovy/refinery-java-test-fixtures.gradle
deleted file mode 100644
index 02568abd..00000000
--- a/buildSrc/src/main/groovy/refinery-java-test-fixtures.gradle
+++ /dev/null
@@ -1,35 +0,0 @@
1import org.gradle.plugins.ide.eclipse.model.AbstractClasspathEntry
2
3plugins {
4 id 'java-test-fixtures'
5 id 'refinery-java-conventions'
6}
7
8eclipse.classpath {
9 containsTestFixtures = true
10
11 file.whenMerged { classpath ->
12 def hasTest = classpath.entries.any { entry ->
13 entry in AbstractClasspathEntry &&
14 entry.entryAttributes['gradle_scope'] == 'test'
15 }
16 for (entry in classpath.entries) {
17 // Workaround https://github.com/gradle/gradle/issues/11845 based on
18 // https://discuss.gradle.org/t/gradle-used-by-scope-not-correctly-generated-when-the-java-test-fixtures-plugin-is-used/39935/2
19 if (entry in AbstractClasspathEntry) {
20 def usedBy = new LinkedHashSet(
21 Arrays.asList((entry.entryAttributes['gradle_used_by_scope'] ?: '').split(','))
22 )
23 if (usedBy.contains('main')) {
24 usedBy += 'testFixtures'
25 }
26 if (hasTest && usedBy.contains('testFixtures')) {
27 usedBy += 'test'
28 }
29 if (!usedBy.empty) {
30 entry.entryAttributes['gradle_used_by_scope'] = usedBy.join(',')
31 }
32 }
33 }
34 }
35}
diff --git a/buildSrc/src/main/groovy/refinery-jmh.gradle b/buildSrc/src/main/groovy/refinery-jmh.gradle
deleted file mode 100644
index 1ab9edc3..00000000
--- a/buildSrc/src/main/groovy/refinery-jmh.gradle
+++ /dev/null
@@ -1,64 +0,0 @@
1import org.gradle.plugins.ide.eclipse.model.AbstractClasspathEntry
2
3plugins {
4 id 'refinery-java-conventions'
5 id 'refinery-sonarqube'
6}
7
8configurations {
9 jmh {
10 extendsFrom implementation
11 }
12}
13
14sourceSets {
15 jmh {
16 java.srcDirs = ['src/jmh/java']
17 resources.srcDirs = ['src/jmh/resources']
18 compileClasspath += sourceSets.main.runtimeClasspath
19 compileClasspath += sourceSets.test.runtimeClasspath
20 }
21}
22
23dependencies {
24 jmhImplementation libs.jmh.core
25 jmhAnnotationProcessor libs.jmh.annprocess
26}
27
28tasks.register('jmh', JavaExec) {
29 dependsOn tasks.named('jmhClasses')
30 mainClass = 'org.openjdk.jmh.Main'
31 classpath = sourceSets.jmh.compileClasspath + sourceSets.jmh.runtimeClasspath
32}
33
34eclipse.classpath.file.whenMerged { classpath ->
35 for (entry in classpath.entries) {
36 if (entry in AbstractClasspathEntry) {
37 // Workaround from https://github.com/gradle/gradle/issues/4802#issuecomment-407902081
38 if (entry.entryAttributes['gradle_scope'] == 'jmh') {
39 // Allow test helper classes to be used in benchmarks from Eclipse
40 // and do not expose JMH dependencies to the main source code.
41 entry.entryAttributes['test'] = true
42 } else {
43 // Workaround based on
44 // https://discuss.gradle.org/t/gradle-used-by-scope-not-correctly-generated-when-the-java-test-fixtures-plugin-is-used/39935/2
45 def usedBy = new LinkedHashSet(
46 Arrays.asList((entry.entryAttributes['gradle_used_by_scope'] ?: '').split(','))
47 )
48 if (['main', 'test', 'testFixtures'].any { e -> usedBy.contains(e) }) {
49 // main and test sources are also used by jmh sources.
50 usedBy += 'jmh'
51 }
52 if (!usedBy.empty) {
53 entry.entryAttributes['gradle_used_by_scope'] = usedBy.join(',')
54 }
55 }
56 }
57 }
58}
59
60sonarqube.properties {
61 properties['sonar.tests'] += [
62 'src/jmh/java',
63 ]
64}
diff --git a/buildSrc/src/main/groovy/refinery-mwe2.gradle b/buildSrc/src/main/groovy/refinery-mwe2.gradle
deleted file mode 100644
index c7f15e82..00000000
--- a/buildSrc/src/main/groovy/refinery-mwe2.gradle
+++ /dev/null
@@ -1,16 +0,0 @@
1plugins {
2 id 'eclipse'
3 id 'refinery-java-conventions'
4}
5
6configurations {
7 mwe2 {
8 extendsFrom implementation
9 }
10}
11
12dependencies {
13 mwe2 libs.mwe2.launch
14}
15
16eclipse.classpath.plusConfigurations += [configurations.mwe2]
diff --git a/buildSrc/src/main/groovy/refinery-sonarqube.gradle b/buildSrc/src/main/groovy/refinery-sonarqube.gradle
deleted file mode 100644
index d84f4ada..00000000
--- a/buildSrc/src/main/groovy/refinery-sonarqube.gradle
+++ /dev/null
@@ -1,8 +0,0 @@
1plugins {
2 id 'org.sonarqube'
3}
4
5sonarqube.properties {
6 // Make sure `exclusions` is a List in every subproject
7 property 'sonar.exclusions', []
8}
diff --git a/buildSrc/src/main/groovy/refinery-xtext-conventions.gradle b/buildSrc/src/main/groovy/refinery-xtext-conventions.gradle
deleted file mode 100644
index 0c7c82f0..00000000
--- a/buildSrc/src/main/groovy/refinery-xtext-conventions.gradle
+++ /dev/null
@@ -1,21 +0,0 @@
1plugins {
2 id 'refinery-java-conventions'
3 id 'refinery-sonarqube'
4}
5
6sourceSets {
7 main {
8 java.srcDirs += ['src/main/xtext-gen']
9 resources.srcDirs += ['src/main/xtext-gen']
10 }
11}
12
13tasks.named('clean') {
14 delete 'src/main/xtext-gen'
15}
16
17sonarqube.properties {
18 properties['sonar.exclusions'] += [
19 'src/main/xtext-gen/**',
20 ]
21}
diff --git a/buildSrc/src/main/java/tools/refinery/buildsrc/EclipseUtils.java b/buildSrc/src/main/java/tools/refinery/buildsrc/EclipseUtils.java
new file mode 100644
index 00000000..0014a35d
--- /dev/null
+++ b/buildSrc/src/main/java/tools/refinery/buildsrc/EclipseUtils.java
@@ -0,0 +1,72 @@
1package tools.refinery.buildsrc;
2
3import groovy.lang.Closure;
4import org.gradle.api.Action;
5import org.gradle.plugins.ide.api.XmlFileContentMerger;
6import org.gradle.plugins.ide.eclipse.model.AbstractClasspathEntry;
7import org.gradle.plugins.ide.eclipse.model.Classpath;
8import org.gradle.plugins.ide.eclipse.model.EclipseModel;
9
10import java.util.LinkedHashSet;
11import java.util.List;
12import java.util.Set;
13import java.util.function.Consumer;
14
15public final class EclipseUtils {
16 private static final String GRADLE_USED_BY_SCOPE_ATTRIBUTE = "gradle_used_by_scope";
17 private static final String GRADLE_USED_BY_SCOPE_SEPARATOR = ",";
18
19 private EclipseUtils() {
20 throw new IllegalStateException("This is a static utility class and should not be instantiated directly");
21 }
22
23 public static void patchClasspathEntries(EclipseModel eclipseModel, Consumer<AbstractClasspathEntry> consumer) {
24 whenClasspathFileMerged(eclipseModel.getClasspath().getFile(),
25 classpath -> patchClasspathEntries(classpath, consumer));
26 }
27
28 public static void patchClasspathEntries(Classpath eclipseClasspath, Consumer<AbstractClasspathEntry> consumer) {
29 for (var entry : eclipseClasspath.getEntries()) {
30 if (entry instanceof AbstractClasspathEntry abstractClasspathEntry) {
31 consumer.accept(abstractClasspathEntry);
32 }
33 }
34 }
35
36 /**
37 * Avoids ambiguous call to ({@link XmlFileContentMerger#whenMerged(Closure)} versus
38 * {@link XmlFileContentMerger#whenMerged(Action)}) in Kotlin build scripts.
39 * <p>
40 * The {@code Closure} variant will use the build script itself as {@code this}, and Kotlin will consider any
41 * type ascription as a cast of {@code this} (instead of the argument of the {@code Action<?>}). This results in
42 * a mysterious {@link ClassCastException}, since the class generated from the build script doesn't extend from
43 * {@link Classpath}. Using this helper method selects the correct call and applies the cast properly.
44 *
45 * @param file The Eclipse classpath file.
46 * @param consumer The lambda to run on when the classpath file is merged.
47 */
48 public static void whenClasspathFileMerged(XmlFileContentMerger file, Consumer<Classpath> consumer) {
49 file.whenMerged(untypedClasspath -> {
50 var classpath = (Classpath) untypedClasspath;
51 consumer.accept(classpath);
52 });
53 }
54
55 public static void patchGradleUsedByScope(AbstractClasspathEntry entry, Consumer<Set<String>> consumer) {
56 var entryAttributes = entry.getEntryAttributes();
57 var usedByValue = entryAttributes.get(GRADLE_USED_BY_SCOPE_ATTRIBUTE);
58 Set<String> usedBySet;
59 if (usedByValue instanceof String usedByString) {
60 usedBySet = new LinkedHashSet<>(List.of(usedByString.split(GRADLE_USED_BY_SCOPE_SEPARATOR)));
61 } else {
62 usedBySet = new LinkedHashSet<>();
63 }
64 consumer.accept(usedBySet);
65 if (usedBySet.isEmpty()) {
66 entryAttributes.remove(GRADLE_USED_BY_SCOPE_ATTRIBUTE);
67 } else {
68 var newUsedByString = String.join(GRADLE_USED_BY_SCOPE_SEPARATOR, usedBySet);
69 entryAttributes.put(GRADLE_USED_BY_SCOPE_ATTRIBUTE, newUsedByString);
70 }
71 }
72}
diff --git a/buildSrc/src/main/java/tools/refinery/buildsrc/SonarPropertiesUtils.java b/buildSrc/src/main/java/tools/refinery/buildsrc/SonarPropertiesUtils.java
new file mode 100644
index 00000000..1d89841e
--- /dev/null
+++ b/buildSrc/src/main/java/tools/refinery/buildsrc/SonarPropertiesUtils.java
@@ -0,0 +1,44 @@
1package tools.refinery.buildsrc;
2
3import java.util.ArrayList;
4import java.util.Collections;
5import java.util.Map;
6
7public final class SonarPropertiesUtils {
8 private SonarPropertiesUtils() {
9 throw new IllegalStateException("This is a static utility class and should not be instantiated directly");
10 }
11
12 /**
13 * Adds the entries to a Sonar property of list type.
14 * <p>
15 * According to the Sonar Gradle documentation for {@link org.sonarqube.gradle.SonarProperties}, property values
16 * are converted to Strings as follows:
17 * <ul>
18 * <li>{@code Iterable}s are recursively converted and joined into a comma-separated String.</li>
19 * <li>All other values are converted to Strings by calling their {@code toString()} method.</li>
20 * </ul>
21 * Therefore, we use {@link ArrayList} to retain lists entries, which will be recursively converted later.
22 *
23 * @param properties The sonar properties map returned by
24 * {@link org.sonarqube.gradle.SonarProperties#getProperties()}.
25 * @param propertyName The name of the property to append to.
26 * @param entries The entries to append.
27 */
28 public static void addToList(Map<String, Object> properties, String propertyName, String... entries) {
29 ArrayList<Object> newValue;
30 var currentValue = properties.get(propertyName);
31 if (currentValue instanceof ArrayList<?> currentList) {
32 @SuppressWarnings("unchecked")
33 var objectList = (ArrayList<Object>) currentList;
34 newValue = objectList;
35 } else if (currentValue == null) {
36 newValue = new ArrayList<>(entries.length);
37 } else {
38 newValue = new ArrayList<>(entries.length + 1);
39 newValue.add(currentValue);
40 }
41 Collections.addAll(newValue, entries);
42 properties.put(propertyName, newValue);
43 }
44}
diff --git a/buildSrc/src/main/kotlin/refinery-eclipse.gradle.kts b/buildSrc/src/main/kotlin/refinery-eclipse.gradle.kts
new file mode 100644
index 00000000..ed7c5250
--- /dev/null
+++ b/buildSrc/src/main/kotlin/refinery-eclipse.gradle.kts
@@ -0,0 +1,30 @@
1import org.gradle.plugins.ide.eclipse.model.EclipseModel
2import java.util.*
3
4plugins {
5 eclipse
6}
7
8// Workaround from https://github.com/gradle/gradle/issues/898#issuecomment-885765821
9val eclipseResourceEncoding by tasks.registering {
10 val outputFile = file(".settings/org.eclipse.core.resources.prefs")
11 val encoding = providers.systemProperty("file.encoding")
12
13 inputs.property("file.encoding", encoding)
14 outputs.file(outputFile)
15
16 doLast {
17 val eclipseEncodingProperties = Properties(2)
18 eclipseEncodingProperties["eclipse.preferences.version"] = "1"
19 eclipseEncodingProperties["encoding/<project>"] = encoding.get()
20 outputFile.outputStream().use { outputStream ->
21 eclipseEncodingProperties.store(outputStream, "generated by $name")
22 }
23 }
24}
25
26tasks.named("eclipse") {
27 dependsOn(eclipseResourceEncoding)
28}
29
30the<EclipseModel>().synchronizationTasks(eclipseResourceEncoding)
diff --git a/buildSrc/src/main/kotlin/refinery-frontend-conventions.gradle.kts b/buildSrc/src/main/kotlin/refinery-frontend-conventions.gradle.kts
new file mode 100644
index 00000000..c4658948
--- /dev/null
+++ b/buildSrc/src/main/kotlin/refinery-frontend-conventions.gradle.kts
@@ -0,0 +1,18 @@
1import org.siouan.frontendgradleplugin.infrastructure.gradle.EnableYarnBerryTask
2import org.siouan.frontendgradleplugin.infrastructure.gradle.FrontendExtension
3
4plugins {
5 id("org.siouan.frontend-jdk11")
6}
7
8configure<FrontendExtension> {
9 nodeVersion.set(providers.gradleProperty("frontend.nodeVersion"))
10 nodeInstallDirectory.set(file("$rootDir/.node"))
11 yarnEnabled.set(true)
12 yarnVersion.set(providers.gradleProperty("frontend.yarnVersion"))
13}
14
15tasks.named<EnableYarnBerryTask>("enableYarnBerry") {
16 // There is no need to enable berry manually, because berry files are already committed to the repo.
17 enabled = false
18}
diff --git a/buildSrc/src/main/kotlin/refinery-frontend-workspace.gradle.kts b/buildSrc/src/main/kotlin/refinery-frontend-workspace.gradle.kts
new file mode 100644
index 00000000..198f73f3
--- /dev/null
+++ b/buildSrc/src/main/kotlin/refinery-frontend-workspace.gradle.kts
@@ -0,0 +1,32 @@
1import org.siouan.frontendgradleplugin.infrastructure.gradle.*
2
3plugins {
4 id("refinery-eclipse")
5 id("refinery-frontend-conventions")
6}
7
8tasks.named<NodeInstallTask>("installNode") {
9 dependsOn(rootProject.tasks.named("installNode"))
10 enabled = false
11}
12
13tasks.named<YarnGlobalInstallTask>("installYarnGlobally") {
14 dependsOn(rootProject.tasks.named("installYarnGlobally"))
15 enabled = false
16}
17
18tasks.named<InstallYarnTask>("installYarn") {
19 dependsOn(rootProject.tasks.named("installYarn"))
20 enabled = false
21}
22
23val rootInstallFrontend = rootProject.tasks.named("installFrontend")
24
25rootInstallFrontend.configure {
26 inputs.file("$projectDir/package.json")
27}
28
29tasks.named("installFrontend") {
30 dependsOn(rootInstallFrontend)
31 enabled = false
32}
diff --git a/buildSrc/src/main/kotlin/refinery-frontend-worktree.gradle.kts b/buildSrc/src/main/kotlin/refinery-frontend-worktree.gradle.kts
new file mode 100644
index 00000000..d8c3d51f
--- /dev/null
+++ b/buildSrc/src/main/kotlin/refinery-frontend-worktree.gradle.kts
@@ -0,0 +1,84 @@
1import org.siouan.frontendgradleplugin.infrastructure.gradle.*
2import java.io.FileInputStream
3import java.io.FileNotFoundException
4import java.io.FileOutputStream
5import java.util.*
6
7plugins {
8 id("refinery-frontend-conventions")
9}
10
11val frontend = the<FrontendExtension>()
12
13val yarn1Version = providers.gradleProperty("frontend.yarn1Version")
14
15frontend.yarnGlobalInstallScript.set(yarn1Version.map { version -> "install -g yarn@$version" })
16frontend.yarnInstallScript.set(frontend.yarnVersion.map { version -> "set version $version --only-if-needed" })
17frontend.installScript.set(provider {
18 if (project.hasProperty("ci")) "install --immutable --inline-builds" else "install"
19})
20
21val frontendPropertiesFile = frontend.nodeInstallDirectory.map { dir -> "$dir/frontend.properties" }
22
23fun readFrontendProperties(): Properties {
24 val props = Properties()
25 try {
26 FileInputStream(frontendPropertiesFile.get()).use { inputStream ->
27 props.load(inputStream)
28 }
29 } catch (ignored: FileNotFoundException) {
30 // Ignore missing file.
31 }
32 return props
33}
34
35fun getFrontendProperty(propertyName: String): String? {
36 val props = readFrontendProperties()
37 return props[propertyName]?.toString()
38}
39
40fun putFrontedProperty(propertyName: String, propertyValue: String) {
41 val props = readFrontendProperties()
42 props[propertyName] = propertyValue
43 FileOutputStream(frontendPropertiesFile.get()).use { outputStream ->
44 props.store(outputStream, "generated by refinery-frontend-worktree")
45 }
46}
47
48tasks.named<NodeInstallTask>("installNode") {
49 onlyIf {
50 getFrontendProperty("installedNodeVersion") != frontend.nodeVersion.get()
51 }
52 doLast {
53 putFrontedProperty("installedNodeVersion", frontend.nodeVersion.get())
54 }
55}
56
57tasks.named<YarnGlobalInstallTask>("installYarnGlobally") {
58 onlyIf {
59 getFrontendProperty("installedYarn1Version") != yarn1Version.get()
60 }
61 doLast {
62 putFrontedProperty("installedYarn1Version", yarn1Version.get())
63 }
64 outputs.dir(frontend.nodeInstallDirectory.map { dir -> "$dir/lib/node_modules/yarn" })
65}
66
67tasks.named<InstallYarnTask>("installYarn") {
68 outputs.file(frontend.yarnVersion.map { version -> ".yarn/releases/yarn-$version.cjs" })
69}
70
71tasks.named<InstallDependenciesTask>("installFrontend") {
72 inputs.files("package.json", "yarn.lock")
73 outputs.files(".pnp.cjs", ".pnp.loader.mjs")
74}
75
76tasks.register("clobberFrontend", Delete::class) {
77 delete(frontend.nodeInstallDirectory)
78 delete(".yarn/cache")
79 delete(".yarn/install-state.gz")
80 delete(".yarn/sdks")
81 delete(".yarn/unplugged")
82 delete(".pnp.cjs")
83 delete(".pnp.loader.mjs")
84}
diff --git a/buildSrc/src/main/kotlin/refinery-java-application.gradle.kts b/buildSrc/src/main/kotlin/refinery-java-application.gradle.kts
new file mode 100644
index 00000000..b3fab1fa
--- /dev/null
+++ b/buildSrc/src/main/kotlin/refinery-java-application.gradle.kts
@@ -0,0 +1,12 @@
1plugins {
2 application
3 id("com.github.johnrengelman.shadow")
4}
5
6apply(plugin = "refinery-java-conventions")
7
8for (taskName in listOf("distTar", "distZip", "shadowDistTar", "shadowDistZip")) {
9 tasks.named(taskName) {
10 enabled = false
11 }
12}
diff --git a/buildSrc/src/main/kotlin/refinery-java-conventions.gradle.kts b/buildSrc/src/main/kotlin/refinery-java-conventions.gradle.kts
new file mode 100644
index 00000000..2d5ce8b5
--- /dev/null
+++ b/buildSrc/src/main/kotlin/refinery-java-conventions.gradle.kts
@@ -0,0 +1,96 @@
1import org.gradle.accessors.dm.LibrariesForLibs
2import org.gradle.plugins.ide.eclipse.model.EclipseModel
3import org.gradle.plugins.ide.eclipse.model.ProjectDependency
4import tools.refinery.buildsrc.EclipseUtils
5
6plugins {
7 jacoco
8 java
9}
10
11apply(plugin = "refinery-eclipse")
12
13repositories {
14 mavenCentral()
15 maven {
16 url = uri("https://repo.eclipse.org/content/groups/releases/")
17 }
18}
19
20val libs = the<LibrariesForLibs>()
21
22dependencies {
23 compileOnly(libs.jetbrainsAnnotations)
24 testCompileOnly(libs.jetbrainsAnnotations)
25 testImplementation(libs.hamcrest)
26 testImplementation(libs.junit.api)
27 testRuntimeOnly(libs.junit.engine)
28 testImplementation(libs.junit.params)
29 testImplementation(libs.mockito.core)
30 testImplementation(libs.mockito.junit)
31}
32
33java.toolchain {
34 languageVersion.set(JavaLanguageVersion.of(19))
35}
36
37tasks.withType(JavaCompile::class) {
38 options.release.set(17)
39}
40
41val test = tasks.named<Test>("test")
42
43val jacocoTestReport = tasks.named<JacocoReport>("jacocoTestReport")
44
45test.configure {
46 useJUnitPlatform {
47 excludeTags("slow")
48 }
49 finalizedBy(jacocoTestReport)
50}
51
52jacocoTestReport.configure {
53 dependsOn(test)
54 reports {
55 xml.required.set(true)
56 }
57}
58
59tasks.named<org.gradle.jvm.tasks.Jar>("jar") {
60 manifest {
61 attributes(
62 "Bundle-SymbolicName" to "${project.group}.${project.name}",
63 "Bundle-Version" to project.version
64 )
65 }
66}
67
68val generateEclipseSourceFolders by tasks.registering
69
70tasks.register("prepareEclipse") {
71 dependsOn(generateEclipseSourceFolders)
72 dependsOn(tasks.named("eclipseJdt"))
73}
74
75tasks.named("eclipseClasspath") {
76 dependsOn(generateEclipseSourceFolders)
77}
78
79configure<EclipseModel> {
80 EclipseUtils.patchClasspathEntries(this) { entry ->
81 if (entry.path.endsWith("-gen")) {
82 entry.entryAttributes["ignore_optional_problems"] = true
83 }
84 // If a project has a main dependency on a project and a test dependency on the testFixtures of a project,
85 // it will be erroneously added as a test-only dependency to Eclipse. As a workaround, we add all project
86 // dependencies as main dependencies (we do not deliberately use test-only project dependencies).
87 if (entry is ProjectDependency) {
88 entry.entryAttributes.remove("test")
89 }
90 }
91
92 jdt.file.withProperties {
93 // Allow @SuppressWarnings to suppress SonarLint warnings
94 this["org.eclipse.jdt.core.compiler.problem.unhandledWarningToken"] = "ignore"
95 }
96}
diff --git a/buildSrc/src/main/kotlin/refinery-java-library.gradle.kts b/buildSrc/src/main/kotlin/refinery-java-library.gradle.kts
new file mode 100644
index 00000000..5a6200e0
--- /dev/null
+++ b/buildSrc/src/main/kotlin/refinery-java-library.gradle.kts
@@ -0,0 +1,5 @@
1plugins {
2 `java-library`
3}
4
5apply(plugin = "refinery-java-conventions")
diff --git a/buildSrc/src/main/kotlin/refinery-java-test-fixtures.gradle.kts b/buildSrc/src/main/kotlin/refinery-java-test-fixtures.gradle.kts
new file mode 100644
index 00000000..86b0a04b
--- /dev/null
+++ b/buildSrc/src/main/kotlin/refinery-java-test-fixtures.gradle.kts
@@ -0,0 +1,31 @@
1import org.gradle.plugins.ide.eclipse.model.AbstractClasspathEntry
2import org.gradle.plugins.ide.eclipse.model.EclipseModel
3import tools.refinery.buildsrc.EclipseUtils
4
5plugins {
6 `java-test-fixtures`
7}
8
9apply(plugin = "refinery-java-conventions")
10
11the<EclipseModel>().classpath {
12 containsTestFixtures.set(true)
13
14 EclipseUtils.whenClasspathFileMerged(file) { eclipseClasspath ->
15 val hasTest = eclipseClasspath.entries.any { entry ->
16 entry is AbstractClasspathEntry && entry.entryAttributes["gradle_scope"] == "test"
17 }
18 EclipseUtils.patchClasspathEntries(eclipseClasspath) { entry ->
19 // Workaround https://github.com/gradle/gradle/issues/11845 based on
20 // https://discuss.gradle.org/t/gradle-used-by-scope-not-correctly-generated-when-the-java-test-fixtures-plugin-is-used/39935/2
21 EclipseUtils.patchGradleUsedByScope(entry) { usedBy ->
22 if (usedBy.contains("main")) {
23 usedBy += "testFixtures"
24 }
25 if (hasTest && usedBy.contains("testFixtures")) {
26 usedBy += "test"
27 }
28 }
29 }
30 }
31}
diff --git a/buildSrc/src/main/kotlin/refinery-jmh.gradle.kts b/buildSrc/src/main/kotlin/refinery-jmh.gradle.kts
new file mode 100644
index 00000000..11888b59
--- /dev/null
+++ b/buildSrc/src/main/kotlin/refinery-jmh.gradle.kts
@@ -0,0 +1,63 @@
1import org.gradle.accessors.dm.LibrariesForLibs
2import org.gradle.plugins.ide.eclipse.model.EclipseModel
3import org.sonarqube.gradle.SonarExtension
4import tools.refinery.buildsrc.EclipseUtils
5import tools.refinery.buildsrc.SonarPropertiesUtils
6
7apply(plugin = "refinery-java-conventions")
8apply(plugin = "refinery-sonarqube")
9
10val sourceSets = the<SourceSetContainer>()
11
12val main: SourceSet by sourceSets.getting
13
14val test: SourceSet by sourceSets.getting
15
16val jmh: SourceSet by sourceSets.creating {
17 compileClasspath += main.output
18 runtimeClasspath += main.output
19 // Allow using test classes in benchmarks for now.
20 compileClasspath += test.output
21 runtimeClasspath += test.output
22}
23
24val jmhImplementation: Configuration by configurations.getting {
25 extendsFrom(configurations["implementation"], configurations["testImplementation"])
26}
27
28val jmhAnnotationProcessor: Configuration by configurations.getting
29
30configurations["jmhRuntimeOnly"].extendsFrom(configurations["runtimeOnly"], configurations["testRuntimeOnly"])
31
32val libs = the<LibrariesForLibs>()
33
34dependencies {
35 jmhImplementation(libs.jmh.core)
36 jmhAnnotationProcessor(libs.jmh.annprocess)
37}
38
39tasks.register("jmh", JavaExec::class) {
40 dependsOn(tasks.named("jmhClasses"))
41 mainClass.set("org.openjdk.jmh.Main")
42 classpath = jmh.runtimeClasspath
43}
44
45EclipseUtils.patchClasspathEntries(the<EclipseModel>()) { entry ->
46 // Workaround from https://github.com/gradle/gradle/issues/4802#issuecomment-407902081
47 if (entry.entryAttributes["gradle_scope"] == "jmh") {
48 // Allow test helper classes to be used in benchmarks from Eclipse
49 // and do not expose JMH dependencies to the main source code.
50 entry.entryAttributes["test"] = true
51 } else {
52 EclipseUtils.patchGradleUsedByScope(entry) { usedBy ->
53 if (listOf("main", "test", "testFixtures").any { e -> usedBy.contains(e) }) {
54 // main and test sources are also used by jmh sources.
55 usedBy += "jmh"
56 }
57 }
58 }
59}
60
61the<SonarExtension>().properties {
62 SonarPropertiesUtils.addToList(properties, "sonar.tests", "src/jmh/java")
63}
diff --git a/buildSrc/src/main/kotlin/refinery-mwe2.gradle.kts b/buildSrc/src/main/kotlin/refinery-mwe2.gradle.kts
new file mode 100644
index 00000000..26963837
--- /dev/null
+++ b/buildSrc/src/main/kotlin/refinery-mwe2.gradle.kts
@@ -0,0 +1,18 @@
1import org.gradle.accessors.dm.LibrariesForLibs
2import org.gradle.plugins.ide.eclipse.model.EclipseModel
3
4apply(plugin = "refinery-java-conventions")
5
6val mwe2: Configuration by configurations.creating {
7 isCanBeConsumed = false
8 isCanBeResolved = true
9 extendsFrom(configurations["implementation"])
10}
11
12val libs = the<LibrariesForLibs>()
13
14dependencies {
15 mwe2(libs.mwe2.launch)
16}
17
18the<EclipseModel>().classpath.plusConfigurations += mwe2
diff --git a/buildSrc/src/main/kotlin/refinery-sonarqube.gradle.kts b/buildSrc/src/main/kotlin/refinery-sonarqube.gradle.kts
new file mode 100644
index 00000000..6a1dbbf6
--- /dev/null
+++ b/buildSrc/src/main/kotlin/refinery-sonarqube.gradle.kts
@@ -0,0 +1,3 @@
1plugins {
2 id("org.sonarqube")
3}
diff --git a/buildSrc/src/main/kotlin/refinery-xtext-conventions.gradle.kts b/buildSrc/src/main/kotlin/refinery-xtext-conventions.gradle.kts
new file mode 100644
index 00000000..34fbae99
--- /dev/null
+++ b/buildSrc/src/main/kotlin/refinery-xtext-conventions.gradle.kts
@@ -0,0 +1,21 @@
1import org.gradle.api.tasks.SourceSetContainer
2import org.sonarqube.gradle.SonarExtension
3import tools.refinery.buildsrc.SonarPropertiesUtils
4
5apply(plugin = "refinery-java-conventions")
6apply(plugin = "refinery-sonarqube")
7
8val xtextGenPath = "src/main/xtext-gen"
9
10the<SourceSetContainer>().named("main") {
11 java.srcDir(xtextGenPath)
12 resources.srcDir(xtextGenPath)
13}
14
15tasks.named<Delete>("clean") {
16 delete(xtextGenPath)
17}
18
19the<SonarExtension>().properties {
20 SonarPropertiesUtils.addToList(properties, "sonar.exclusions", "$xtextGenPath/**")
21}