summaryrefslogtreecommitdiffstats
path: root/buildSrc/src
diff options
context:
space:
mode:
Diffstat (limited to 'buildSrc/src')
-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/gradle/utils/EclipseUtils.java77
-rw-r--r--buildSrc/src/main/java/tools/refinery/gradle/utils/SonarPropertiesUtils.java49
-rw-r--r--buildSrc/src/main/kotlin/tools/refinery/gradle/eclipse.gradle.kts43
-rw-r--r--buildSrc/src/main/kotlin/tools/refinery/gradle/frontend-workspace.gradle.kts39
-rw-r--r--buildSrc/src/main/kotlin/tools/refinery/gradle/frontend-worktree.gradle.kts92
-rw-r--r--buildSrc/src/main/kotlin/tools/refinery/gradle/internal/frontend-conventions.gradle.kts22
-rw-r--r--buildSrc/src/main/kotlin/tools/refinery/gradle/internal/java-conventions.gradle.kts102
-rw-r--r--buildSrc/src/main/kotlin/tools/refinery/gradle/java-application.gradle.kts32
-rw-r--r--buildSrc/src/main/kotlin/tools/refinery/gradle/java-library.gradle.kts11
-rw-r--r--buildSrc/src/main/kotlin/tools/refinery/gradle/java-test-fixtures.gradle.kts36
-rw-r--r--buildSrc/src/main/kotlin/tools/refinery/gradle/jmh.gradle.kts66
-rw-r--r--buildSrc/src/main/kotlin/tools/refinery/gradle/mwe2.gradle.kts26
-rw-r--r--buildSrc/src/main/kotlin/tools/refinery/gradle/sonarqube.gradle.kts10
-rw-r--r--buildSrc/src/main/kotlin/tools/refinery/gradle/xtext-generated.gradle.kts49
26 files changed, 654 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/gradle/utils/EclipseUtils.java b/buildSrc/src/main/java/tools/refinery/gradle/utils/EclipseUtils.java
new file mode 100644
index 00000000..ac7ba3f4
--- /dev/null
+++ b/buildSrc/src/main/java/tools/refinery/gradle/utils/EclipseUtils.java
@@ -0,0 +1,77 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.gradle.utils;
7
8import groovy.lang.Closure;
9import org.gradle.api.Action;
10import org.gradle.plugins.ide.api.XmlFileContentMerger;
11import org.gradle.plugins.ide.eclipse.model.AbstractClasspathEntry;
12import org.gradle.plugins.ide.eclipse.model.Classpath;
13import org.gradle.plugins.ide.eclipse.model.EclipseModel;
14
15import java.util.LinkedHashSet;
16import java.util.List;
17import java.util.Set;
18import java.util.function.Consumer;
19
20public final class EclipseUtils {
21 private static final String GRADLE_USED_BY_SCOPE_ATTRIBUTE = "gradle_used_by_scope";
22 private static final String GRADLE_USED_BY_SCOPE_SEPARATOR = ",";
23
24 private EclipseUtils() {
25 throw new IllegalStateException("This is a static utility class and should not be instantiated directly");
26 }
27
28 public static void patchClasspathEntries(EclipseModel eclipseModel, Consumer<AbstractClasspathEntry> consumer) {
29 whenClasspathFileMerged(eclipseModel.getClasspath().getFile(),
30 classpath -> patchClasspathEntries(classpath, consumer));
31 }
32
33 public static void patchClasspathEntries(Classpath eclipseClasspath, Consumer<AbstractClasspathEntry> consumer) {
34 for (var entry : eclipseClasspath.getEntries()) {
35 if (entry instanceof AbstractClasspathEntry abstractClasspathEntry) {
36 consumer.accept(abstractClasspathEntry);
37 }
38 }
39 }
40
41 /**
42 * Avoids ambiguous call to ({@link XmlFileContentMerger#whenMerged(Closure)} versus
43 * {@link XmlFileContentMerger#whenMerged(Action)}) in Kotlin build scripts.
44 * <p>
45 * The {@code Closure} variant will use the build script itself as {@code this}, and Kotlin will consider any
46 * type ascription as a cast of {@code this} (instead of the argument of the {@code Action<?>}). This results in
47 * a mysterious {@link ClassCastException}, since the class generated from the build script doesn't extend from
48 * {@link Classpath}. Using this helper method selects the correct call and applies the cast properly.
49 *
50 * @param file The Eclipse classpath file.
51 * @param consumer The lambda to run on when the classpath file is merged.
52 */
53 public static void whenClasspathFileMerged(XmlFileContentMerger file, Consumer<Classpath> consumer) {
54 file.whenMerged(untypedClasspath -> {
55 var classpath = (Classpath) untypedClasspath;
56 consumer.accept(classpath);
57 });
58 }
59
60 public static void patchGradleUsedByScope(AbstractClasspathEntry entry, Consumer<Set<String>> consumer) {
61 var entryAttributes = entry.getEntryAttributes();
62 var usedByValue = entryAttributes.get(GRADLE_USED_BY_SCOPE_ATTRIBUTE);
63 Set<String> usedBySet;
64 if (usedByValue instanceof String usedByString) {
65 usedBySet = new LinkedHashSet<>(List.of(usedByString.split(GRADLE_USED_BY_SCOPE_SEPARATOR)));
66 } else {
67 usedBySet = new LinkedHashSet<>();
68 }
69 consumer.accept(usedBySet);
70 if (usedBySet.isEmpty()) {
71 entryAttributes.remove(GRADLE_USED_BY_SCOPE_ATTRIBUTE);
72 } else {
73 var newUsedByString = String.join(GRADLE_USED_BY_SCOPE_SEPARATOR, usedBySet);
74 entryAttributes.put(GRADLE_USED_BY_SCOPE_ATTRIBUTE, newUsedByString);
75 }
76 }
77}
diff --git a/buildSrc/src/main/java/tools/refinery/gradle/utils/SonarPropertiesUtils.java b/buildSrc/src/main/java/tools/refinery/gradle/utils/SonarPropertiesUtils.java
new file mode 100644
index 00000000..183cd56a
--- /dev/null
+++ b/buildSrc/src/main/java/tools/refinery/gradle/utils/SonarPropertiesUtils.java
@@ -0,0 +1,49 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.gradle.utils;
7
8import java.util.ArrayList;
9import java.util.Collections;
10import java.util.Map;
11
12public final class SonarPropertiesUtils {
13 private SonarPropertiesUtils() {
14 throw new IllegalStateException("This is a static utility class and should not be instantiated directly");
15 }
16
17 /**
18 * Adds the entries to a Sonar property of list type.
19 * <p>
20 * According to the Sonar Gradle documentation for {@link org.sonarqube.gradle.SonarProperties}, property values
21 * are converted to Strings as follows:
22 * <ul>
23 * <li>{@code Iterable}s are recursively converted and joined into a comma-separated String.</li>
24 * <li>All other values are converted to Strings by calling their {@code toString()} method.</li>
25 * </ul>
26 * Therefore, we use {@link ArrayList} to retain lists entries, which will be recursively converted later.
27 *
28 * @param properties The sonar properties map returned by
29 * {@link org.sonarqube.gradle.SonarProperties#getProperties()}.
30 * @param propertyName The name of the property to append to.
31 * @param entries The entries to append.
32 */
33 public static void addToList(Map<String, Object> properties, String propertyName, String... entries) {
34 ArrayList<Object> newValue;
35 var currentValue = properties.get(propertyName);
36 if (currentValue instanceof ArrayList<?> currentList) {
37 @SuppressWarnings("unchecked")
38 var objectList = (ArrayList<Object>) currentList;
39 newValue = objectList;
40 } else if (currentValue == null) {
41 newValue = new ArrayList<>(entries.length);
42 } else {
43 newValue = new ArrayList<>(entries.length + 1);
44 newValue.add(currentValue);
45 }
46 Collections.addAll(newValue, entries);
47 properties.put(propertyName, newValue);
48 }
49}
diff --git a/buildSrc/src/main/kotlin/tools/refinery/gradle/eclipse.gradle.kts b/buildSrc/src/main/kotlin/tools/refinery/gradle/eclipse.gradle.kts
new file mode 100644
index 00000000..8bab1d2b
--- /dev/null
+++ b/buildSrc/src/main/kotlin/tools/refinery/gradle/eclipse.gradle.kts
@@ -0,0 +1,43 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.gradle
7
8import java.util.Properties
9
10plugins {
11 eclipse
12}
13
14// Workaround from https://github.com/gradle/gradle/issues/898#issuecomment-885765821
15val eclipseResourceEncoding by tasks.registering {
16 val outputFile = file(".settings/org.eclipse.core.resources.prefs")
17 val encoding = providers.systemProperty("file.encoding")
18
19 inputs.property("file.encoding", encoding)
20 outputs.file(outputFile)
21
22 doLast {
23 val eclipseEncodingProperties = Properties(2)
24 eclipseEncodingProperties["eclipse.preferences.version"] = "1"
25 eclipseEncodingProperties["encoding/<project>"] = encoding.get()
26 outputFile.outputStream().use { outputStream ->
27 eclipseEncodingProperties.store(outputStream, "generated by $name")
28 }
29 }
30}
31
32tasks.eclipse {
33 dependsOn(eclipseResourceEncoding)
34}
35
36eclipse.synchronizationTasks(eclipseResourceEncoding)
37
38tasks.register<Delete>("clobberEclipse") {
39 mustRunAfter(tasks.eclipse)
40 delete(".classpath")
41 delete(".project")
42 delete(".settings")
43}
diff --git a/buildSrc/src/main/kotlin/tools/refinery/gradle/frontend-workspace.gradle.kts b/buildSrc/src/main/kotlin/tools/refinery/gradle/frontend-workspace.gradle.kts
new file mode 100644
index 00000000..f1f6d952
--- /dev/null
+++ b/buildSrc/src/main/kotlin/tools/refinery/gradle/frontend-workspace.gradle.kts
@@ -0,0 +1,39 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.gradle
7
8plugins {
9 id("tools.refinery.gradle.eclipse")
10 id("tools.refinery.gradle.internal.frontend-conventions")
11}
12
13tasks {
14 installNode {
15 dependsOn(rootProject.tasks.named("installNode"))
16 enabled = false
17 }
18
19 installYarnGlobally {
20 dependsOn(rootProject.tasks.named("installYarnGlobally"))
21 enabled = false
22 }
23
24 installYarn {
25 dependsOn(rootProject.tasks.named("installYarn"))
26 enabled = false
27 }
28
29 val rootInstallFrontend = rootProject.tasks.named("installFrontend")
30
31 rootInstallFrontend.configure {
32 inputs.file("$projectDir/package.json")
33 }
34
35 installFrontend {
36 dependsOn(rootInstallFrontend)
37 enabled = false
38 }
39}
diff --git a/buildSrc/src/main/kotlin/tools/refinery/gradle/frontend-worktree.gradle.kts b/buildSrc/src/main/kotlin/tools/refinery/gradle/frontend-worktree.gradle.kts
new file mode 100644
index 00000000..609e6f24
--- /dev/null
+++ b/buildSrc/src/main/kotlin/tools/refinery/gradle/frontend-worktree.gradle.kts
@@ -0,0 +1,92 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.gradle
7
8import java.io.FileInputStream
9import java.io.FileNotFoundException
10import java.io.FileOutputStream
11import java.util.Properties
12
13plugins {
14 id("tools.refinery.gradle.internal.frontend-conventions")
15}
16
17val yarn1Version = providers.gradleProperty("frontend.yarn1Version")
18
19frontend {
20 yarnGlobalInstallScript.set(yarn1Version.map { version -> "install -g yarn@$version" })
21 yarnInstallScript.set(frontend.yarnVersion.map { version -> "set version $version --only-if-needed" })
22 installScript.set(provider {
23 if (project.hasProperty("ci")) "install --immutable --inline-builds" else "install"
24 })
25}
26
27val frontendPropertiesFile: Provider<String> = frontend.nodeInstallDirectory.map { dir -> "$dir/frontend.properties" }
28
29fun readFrontendProperties(): Properties {
30 val props = Properties()
31 try {
32 FileInputStream(frontendPropertiesFile.get()).use { inputStream ->
33 props.load(inputStream)
34 }
35 } catch (ignored: FileNotFoundException) {
36 // Ignore missing file.
37 }
38 return props
39}
40
41fun getFrontendProperty(propertyName: String): String? {
42 val props = readFrontendProperties()
43 return props[propertyName]?.toString()
44}
45
46fun putFrontedProperty(propertyName: String, propertyValue: String) {
47 val props = readFrontendProperties()
48 props[propertyName] = propertyValue
49 FileOutputStream(frontendPropertiesFile.get()).use { outputStream ->
50 props.store(outputStream, "generated by refinery-frontend-worktree")
51 }
52}
53
54tasks {
55 installNode {
56 onlyIf {
57 getFrontendProperty("installedNodeVersion") != frontend.nodeVersion.get()
58 }
59 doLast {
60 putFrontedProperty("installedNodeVersion", frontend.nodeVersion.get())
61 }
62 }
63
64 installYarnGlobally {
65 onlyIf {
66 getFrontendProperty("installedYarn1Version") != yarn1Version.get()
67 }
68 doLast {
69 putFrontedProperty("installedYarn1Version", yarn1Version.get())
70 }
71 outputs.dir(frontend.nodeInstallDirectory.map { dir -> "$dir/lib/node_modules/yarn" })
72 }
73
74 installYarn {
75 outputs.file(frontend.yarnVersion.map { version -> ".yarn/releases/yarn-$version.cjs" })
76 }
77
78 installFrontend {
79 inputs.files("package.json", "yarn.lock")
80 outputs.files(".pnp.cjs", ".pnp.loader.mjs")
81 }
82
83 register("clobberFrontend", Delete::class) {
84 delete(frontend.nodeInstallDirectory)
85 delete(".yarn/cache")
86 delete(".yarn/install-state.gz")
87 delete(".yarn/sdks")
88 delete(".yarn/unplugged")
89 delete(".pnp.cjs")
90 delete(".pnp.loader.mjs")
91 }
92}
diff --git a/buildSrc/src/main/kotlin/tools/refinery/gradle/internal/frontend-conventions.gradle.kts b/buildSrc/src/main/kotlin/tools/refinery/gradle/internal/frontend-conventions.gradle.kts
new file mode 100644
index 00000000..bd5d0197
--- /dev/null
+++ b/buildSrc/src/main/kotlin/tools/refinery/gradle/internal/frontend-conventions.gradle.kts
@@ -0,0 +1,22 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.gradle.internal
7
8plugins {
9 id("org.siouan.frontend-jdk11")
10}
11
12frontend {
13 nodeVersion.set(providers.gradleProperty("frontend.nodeVersion"))
14 nodeInstallDirectory.set(file("$rootDir/.node"))
15 yarnEnabled.set(true)
16 yarnVersion.set(providers.gradleProperty("frontend.yarnVersion"))
17}
18
19tasks.enableYarnBerry {
20 // There is no need to enable berry manually, because berry files are already committed to the repo.
21 enabled = false
22}
diff --git a/buildSrc/src/main/kotlin/tools/refinery/gradle/internal/java-conventions.gradle.kts b/buildSrc/src/main/kotlin/tools/refinery/gradle/internal/java-conventions.gradle.kts
new file mode 100644
index 00000000..7c7cbabd
--- /dev/null
+++ b/buildSrc/src/main/kotlin/tools/refinery/gradle/internal/java-conventions.gradle.kts
@@ -0,0 +1,102 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.gradle.internal
7
8import org.gradle.accessors.dm.LibrariesForLibs
9import org.gradle.plugins.ide.eclipse.model.ProjectDependency
10import tools.refinery.gradle.utils.EclipseUtils
11
12plugins {
13 jacoco
14 java
15 id("tools.refinery.gradle.eclipse")
16}
17
18repositories {
19 mavenCentral()
20 maven {
21 url = uri("https://repo.eclipse.org/content/groups/releases/")
22 }
23}
24
25// Use log4j-over-slf4j instead of log4j 1.x in the tests.
26configurations.testRuntimeClasspath {
27 exclude(group = "log4j", module = "log4j")
28}
29
30val libs = the<LibrariesForLibs>()
31
32dependencies {
33 compileOnly(libs.jetbrainsAnnotations)
34 testCompileOnly(libs.jetbrainsAnnotations)
35 testImplementation(libs.hamcrest)
36 testImplementation(libs.junit.api)
37 testRuntimeOnly(libs.junit.engine)
38 testImplementation(libs.junit.params)
39 testImplementation(libs.mockito.core)
40 testImplementation(libs.mockito.junit)
41 testImplementation(libs.slf4j.simple)
42 testImplementation(libs.slf4j.log4j)
43}
44
45java.toolchain {
46 languageVersion.set(JavaLanguageVersion.of(17))
47}
48
49tasks {
50 test {
51 useJUnitPlatform {
52 excludeTags("slow")
53 }
54 finalizedBy(tasks.jacocoTestReport)
55 }
56
57 jacocoTestReport {
58 dependsOn(tasks.test)
59 reports {
60 xml.required.set(true)
61 }
62 }
63
64 jar {
65 manifest {
66 attributes(
67 "Bundle-SymbolicName" to "${project.group}.${project.name}",
68 "Bundle-Version" to project.version
69 )
70 }
71 }
72
73 val generateEclipseSourceFolders by tasks.registering
74
75 register("prepareEclipse") {
76 dependsOn(generateEclipseSourceFolders)
77 dependsOn(tasks.named("eclipseJdt"))
78 }
79
80 eclipseClasspath {
81 dependsOn(generateEclipseSourceFolders)
82 }
83}
84
85eclipse {
86 EclipseUtils.patchClasspathEntries(this) { entry ->
87 if (entry.path.endsWith("-gen")) {
88 entry.entryAttributes["ignore_optional_problems"] = true
89 }
90 // If a project has a main dependency on a project and a test dependency on the testFixtures of a project,
91 // it will be erroneously added as a test-only dependency to Eclipse. As a workaround, we add all project
92 // dependencies as main dependencies (we do not deliberately use test-only project dependencies).
93 if (entry is ProjectDependency) {
94 entry.entryAttributes.remove("test")
95 }
96 }
97
98 jdt.file.withProperties {
99 // Allow @SuppressWarnings to suppress SonarLint warnings
100 this["org.eclipse.jdt.core.compiler.problem.unhandledWarningToken"] = "ignore"
101 }
102}
diff --git a/buildSrc/src/main/kotlin/tools/refinery/gradle/java-application.gradle.kts b/buildSrc/src/main/kotlin/tools/refinery/gradle/java-application.gradle.kts
new file mode 100644
index 00000000..0924311b
--- /dev/null
+++ b/buildSrc/src/main/kotlin/tools/refinery/gradle/java-application.gradle.kts
@@ -0,0 +1,32 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.gradle
7
8import org.gradle.accessors.dm.LibrariesForLibs
9
10plugins {
11 application
12 id("com.github.johnrengelman.shadow")
13 id("tools.refinery.gradle.internal.java-conventions")
14}
15
16// Use log4j-over-slf4j instead of log4j 1.x when running the application.
17configurations.runtimeClasspath {
18 exclude(group = "log4j", module = "log4j")
19}
20
21val libs = the<LibrariesForLibs>()
22
23dependencies {
24 implementation(libs.slf4j.simple)
25 implementation(libs.slf4j.log4j)
26}
27
28for (taskName in listOf("distTar", "distZip", "shadowDistTar", "shadowDistZip")) {
29 tasks.named(taskName) {
30 enabled = false
31 }
32}
diff --git a/buildSrc/src/main/kotlin/tools/refinery/gradle/java-library.gradle.kts b/buildSrc/src/main/kotlin/tools/refinery/gradle/java-library.gradle.kts
new file mode 100644
index 00000000..3aff3833
--- /dev/null
+++ b/buildSrc/src/main/kotlin/tools/refinery/gradle/java-library.gradle.kts
@@ -0,0 +1,11 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.gradle
7
8plugins {
9 `java-library`
10 id("tools.refinery.gradle.internal.java-conventions")
11}
diff --git a/buildSrc/src/main/kotlin/tools/refinery/gradle/java-test-fixtures.gradle.kts b/buildSrc/src/main/kotlin/tools/refinery/gradle/java-test-fixtures.gradle.kts
new file mode 100644
index 00000000..a28484d4
--- /dev/null
+++ b/buildSrc/src/main/kotlin/tools/refinery/gradle/java-test-fixtures.gradle.kts
@@ -0,0 +1,36 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.gradle
7
8import org.gradle.plugins.ide.eclipse.model.AbstractClasspathEntry
9import tools.refinery.gradle.utils.EclipseUtils
10
11plugins {
12 `java-test-fixtures`
13 id("tools.refinery.gradle.internal.java-conventions")
14}
15
16eclipse.classpath {
17 containsTestFixtures.set(true)
18
19 EclipseUtils.whenClasspathFileMerged(file) { eclipseClasspath ->
20 val hasTest = eclipseClasspath.entries.any { entry ->
21 entry is AbstractClasspathEntry && entry.entryAttributes["gradle_scope"] == "test"
22 }
23 EclipseUtils.patchClasspathEntries(eclipseClasspath) { entry ->
24 // Workaround https://github.com/gradle/gradle/issues/11845 based on
25 // https://discuss.gradle.org/t/gradle-used-by-scope-not-correctly-generated-when-the-java-test-fixtures-plugin-is-used/39935/2
26 EclipseUtils.patchGradleUsedByScope(entry) { usedBy ->
27 if (usedBy.contains("main")) {
28 usedBy += "testFixtures"
29 }
30 if (hasTest && usedBy.contains("testFixtures")) {
31 usedBy += "test"
32 }
33 }
34 }
35 }
36}
diff --git a/buildSrc/src/main/kotlin/tools/refinery/gradle/jmh.gradle.kts b/buildSrc/src/main/kotlin/tools/refinery/gradle/jmh.gradle.kts
new file mode 100644
index 00000000..6fb8fbac
--- /dev/null
+++ b/buildSrc/src/main/kotlin/tools/refinery/gradle/jmh.gradle.kts
@@ -0,0 +1,66 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.gradle
7
8import org.gradle.accessors.dm.LibrariesForLibs
9import tools.refinery.gradle.utils.EclipseUtils
10import tools.refinery.gradle.utils.SonarPropertiesUtils
11
12plugins {
13 id("tools.refinery.gradle.internal.java-conventions")
14 id("tools.refinery.gradle.sonarqube")
15}
16
17val sourceSets = the<SourceSetContainer>()
18
19val jmh: SourceSet by sourceSets.creating {
20 compileClasspath += sourceSets.main.get().output
21 runtimeClasspath += sourceSets.main.get().output
22 // Allow using test classes in benchmarks for now.
23 compileClasspath += sourceSets.test.get().output
24 runtimeClasspath += sourceSets.test.get().output
25}
26
27val jmhImplementation: Configuration by configurations.getting {
28 extendsFrom(configurations.implementation.get(), configurations.testImplementation.get())
29}
30
31val jmhAnnotationProcessor: Configuration by configurations.getting
32
33configurations["jmhRuntimeOnly"].extendsFrom(configurations.runtimeOnly.get(), configurations.testRuntimeOnly.get())
34
35val libs = the<LibrariesForLibs>()
36
37dependencies {
38 jmhImplementation(libs.jmh.core)
39 jmhAnnotationProcessor(libs.jmh.annprocess)
40}
41
42tasks.register<JavaExec>("jmh") {
43 dependsOn(tasks.named("jmhClasses"))
44 mainClass.set("org.openjdk.jmh.Main")
45 classpath = jmh.runtimeClasspath
46}
47
48EclipseUtils.patchClasspathEntries(eclipse) { entry ->
49 // Workaround from https://github.com/gradle/gradle/issues/4802#issuecomment-407902081
50 if (entry.entryAttributes["gradle_scope"] == "jmh") {
51 // Allow test helper classes to be used in benchmarks from Eclipse
52 // and do not expose JMH dependencies to the main source code.
53 entry.entryAttributes["test"] = true
54 } else {
55 EclipseUtils.patchGradleUsedByScope(entry) { usedBy ->
56 if (listOf("main", "test", "testFixtures").any { e -> usedBy.contains(e) }) {
57 // main and test sources are also used by jmh sources.
58 usedBy += "jmh"
59 }
60 }
61 }
62}
63
64sonarqube.properties {
65 SonarPropertiesUtils.addToList(properties, "sonar.tests", "src/jmh/java")
66}
diff --git a/buildSrc/src/main/kotlin/tools/refinery/gradle/mwe2.gradle.kts b/buildSrc/src/main/kotlin/tools/refinery/gradle/mwe2.gradle.kts
new file mode 100644
index 00000000..f4381434
--- /dev/null
+++ b/buildSrc/src/main/kotlin/tools/refinery/gradle/mwe2.gradle.kts
@@ -0,0 +1,26 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.gradle
7
8import org.gradle.accessors.dm.LibrariesForLibs
9
10plugins {
11 id("tools.refinery.gradle.internal.java-conventions")
12}
13
14val mwe2: Configuration by configurations.creating {
15 isCanBeConsumed = false
16 isCanBeResolved = true
17 extendsFrom(configurations.implementation.get())
18}
19
20val libs = the<LibrariesForLibs>()
21
22dependencies {
23 mwe2(libs.mwe2.launch)
24}
25
26eclipse.classpath.plusConfigurations += mwe2
diff --git a/buildSrc/src/main/kotlin/tools/refinery/gradle/sonarqube.gradle.kts b/buildSrc/src/main/kotlin/tools/refinery/gradle/sonarqube.gradle.kts
new file mode 100644
index 00000000..93406492
--- /dev/null
+++ b/buildSrc/src/main/kotlin/tools/refinery/gradle/sonarqube.gradle.kts
@@ -0,0 +1,10 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.gradle
7
8plugins {
9 id("org.sonarqube")
10}
diff --git a/buildSrc/src/main/kotlin/tools/refinery/gradle/xtext-generated.gradle.kts b/buildSrc/src/main/kotlin/tools/refinery/gradle/xtext-generated.gradle.kts
new file mode 100644
index 00000000..6cb1d7b5
--- /dev/null
+++ b/buildSrc/src/main/kotlin/tools/refinery/gradle/xtext-generated.gradle.kts
@@ -0,0 +1,49 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.gradle
7
8import tools.refinery.gradle.utils.SonarPropertiesUtils
9
10plugins {
11 id("tools.refinery.gradle.internal.java-conventions")
12 id("tools.refinery.gradle.sonarqube")
13}
14
15val xtextGenPath = "src/main/xtext-gen"
16
17val xtextGenerated: Configuration by configurations.creating {
18 isCanBeConsumed = false
19 isCanBeResolved = true
20}
21
22sourceSets.main {
23 java.srcDir(xtextGenPath)
24 resources.srcDir(xtextGenPath)
25}
26
27tasks {
28 // Based on the idea from https://stackoverflow.com/a/57788355 to safely consume generated sources in sibling
29 // projects.
30 val syncXtextGeneratedSources by tasks.creating(Sync::class) {
31 from(xtextGenerated)
32 into(xtextGenPath)
33 }
34
35 for (taskName in listOf("compileJava", "processResources", "generateEclipseSourceFolders")) {
36 tasks.named(taskName) {
37 dependsOn(syncXtextGeneratedSources)
38 }
39 }
40
41 clean {
42 delete(xtextGenPath)
43 }
44}
45
46
47sonarqube.properties {
48 SonarPropertiesUtils.addToList(properties, "sonar.exclusions", "$xtextGenPath/**")
49}