aboutsummaryrefslogtreecommitdiffstats
path: root/buildSrc/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'buildSrc/src/main')
-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
26 files changed, 529 insertions, 418 deletions
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}