From 8238db17784baf843833f680e750d31083ec8acd Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Wed, 26 May 2021 11:41:52 +0100 Subject: [PATCH] Improve error messages when using outdated Java or Gradle. --- .github/workflows/test-push.yml | 52 +++++++++++++- .gitignore | 3 +- bootstrap/.gitignore | 7 ++ bootstrap/build.gradle | 30 +++++++++ .../loom/bootstrap/BootstrappedPlugin.java | 7 ++ .../bootstrap/LoomGradlePluginBootstrap.java | 67 +++++++++++++++++++ ...ick-gradle-into-thinking-loom-is-signed.SF | 4 ++ bootstrap/test-project/build.gradle | 8 +++ bootstrap/test-project/settings.gradle | 10 +++ build.gradle | 31 ++++++++- settings.gradle | 1 + .../net/fabricmc/loom/LoomGradlePlugin.java | 4 +- 12 files changed, 218 insertions(+), 6 deletions(-) create mode 100644 bootstrap/.gitignore create mode 100644 bootstrap/build.gradle create mode 100644 bootstrap/src/main/java/net/fabricmc/loom/bootstrap/BootstrappedPlugin.java create mode 100644 bootstrap/src/main/java/net/fabricmc/loom/bootstrap/LoomGradlePluginBootstrap.java create mode 100644 bootstrap/src/main/resources/META-INF/trick-gradle-into-thinking-loom-is-signed.SF create mode 100644 bootstrap/test-project/build.gradle create mode 100644 bootstrap/test-project/settings.gradle diff --git a/.github/workflows/test-push.yml b/.github/workflows/test-push.yml index 960e82d..a7be5e6 100644 --- a/.github/workflows/test-push.yml +++ b/.github/workflows/test-push.yml @@ -89,4 +89,54 @@ jobs: if: ${{ failure() }} with: name: Reproducible Build ${{ matrix.os }} (${{ matrix.java }}) Results - path: build/reports/ \ No newline at end of file + path: build/reports/ + + bootstrap_tests: + needs: build + + strategy: + fail-fast: false + matrix: + java: [ 8, 11, 15, 16 ] + gradle: [ 4.9, 5.2, 6.0.1, 6.9, 7.0.2 ] + exclude: + - java: 16 + gradle: 6.9 + - java: 16 + gradle: 6.0.1 + - java: 16 + gradle: 5.2 + - java: 16 + gradle: 4.9 + - java: 15 + gradle: 6.0.1 + - java: 15 + gradle: 5.2 + - java: 15 + gradle: 4.9 + runs-on: ubuntu-20.04 + container: + image: gradle:7.0.2-jdk16 + options: --user root + + steps: + # Build loom and publish to maven local + - uses: actions/checkout@v2 + - run: gradle build publishToMavenLocal -x test -x check + + - run: gradle wrapper --gradle-version=${{ matrix.gradle }} + working-directory: bootstrap/test-project + - run: gradle --stop + + - uses: actions/setup-java@v2 + with: + java-version: ${{ matrix.java }} + distribution: 'adopt' + + - run: ./gradlew --version + working-directory: bootstrap/test-project + - run: ./gradlew build + working-directory: bootstrap/test-project + continue-on-error: true + + # TODO check the output of the previous step here \ No newline at end of file diff --git a/.gitignore b/.gitignore index c316a77..4354f60 100644 --- a/.gitignore +++ b/.gitignore @@ -20,4 +20,5 @@ !/settings.gradle !/Jenkinsfile !/checkstyle.xml -!/codenarc.groovy \ No newline at end of file +!/codenarc.groovy +!/bootstrap \ No newline at end of file diff --git a/bootstrap/.gitignore b/bootstrap/.gitignore new file mode 100644 index 0000000..f8dc37c --- /dev/null +++ b/bootstrap/.gitignore @@ -0,0 +1,7 @@ +# Ignore everything +/* + +!/src +!/build.gradle +!/.gitignore +!/test-project \ No newline at end of file diff --git a/bootstrap/build.gradle b/bootstrap/build.gradle new file mode 100644 index 0000000..7db4d75 --- /dev/null +++ b/bootstrap/build.gradle @@ -0,0 +1,30 @@ +plugins { + id 'java' + id 'groovy' +} + +sourceCompatibility = 8 +targetCompatibility = 8 + +tasks.withType(JavaCompile).configureEach { + it.options.encoding = "UTF-8" + it.options.release = 8 +} + +repositories { + mavenCentral() +} + +dependencies { + implementation gradleApi() + + testImplementation(gradleTestKit()) + testImplementation('org.spockframework:spock-core:2.0-M5-groovy-3.0') { + exclude module: 'groovy-all' + } +} + +test { + maxHeapSize = "4096m" + useJUnitPlatform() +} \ No newline at end of file diff --git a/bootstrap/src/main/java/net/fabricmc/loom/bootstrap/BootstrappedPlugin.java b/bootstrap/src/main/java/net/fabricmc/loom/bootstrap/BootstrappedPlugin.java new file mode 100644 index 0000000..c64246e --- /dev/null +++ b/bootstrap/src/main/java/net/fabricmc/loom/bootstrap/BootstrappedPlugin.java @@ -0,0 +1,7 @@ +package net.fabricmc.loom.bootstrap; + +import org.gradle.api.plugins.PluginAware; + +public interface BootstrappedPlugin { + void apply(PluginAware project); +} diff --git a/bootstrap/src/main/java/net/fabricmc/loom/bootstrap/LoomGradlePluginBootstrap.java b/bootstrap/src/main/java/net/fabricmc/loom/bootstrap/LoomGradlePluginBootstrap.java new file mode 100644 index 0000000..66408ed --- /dev/null +++ b/bootstrap/src/main/java/net/fabricmc/loom/bootstrap/LoomGradlePluginBootstrap.java @@ -0,0 +1,67 @@ +package net.fabricmc.loom.bootstrap; + +import java.util.ArrayList; +import java.util.List; + +import org.gradle.api.JavaVersion; +import org.gradle.api.Plugin; +import org.gradle.api.plugins.PluginAware; +import org.gradle.util.GradleVersion; + +/** + * This bootstrap is compiled against a minimal gradle API and java 8, this allows us to show a nice error to users who run on unsupported configurations. + */ +@SuppressWarnings("unused") +public class LoomGradlePluginBootstrap implements Plugin { + private static final int MIN_SUPPORTED_MAJOR_GRADLE_VERSION = 7; + private static final int MIN_SUPPORTED_MAJOR_JAVA_VERSION = 16; + + private static final String PLUGIN_CLASS_NAME = "net.fabricmc.loom.LoomGradlePlugin"; + + @Override + public void apply(PluginAware project) { + List errors = new ArrayList<>(); + + if (!isValidGradleRuntime()) { + errors.add(String.format("You are using an outdated version of Gradle (%s). Gradle %d or higher is required.", GradleVersion.current().getVersion(), MIN_SUPPORTED_MAJOR_GRADLE_VERSION)); + } + + if (!isValidJavaRuntime()) { + errors.add(String.format("You are using an outdated version of Java (%s). Java %d or higher is required.", JavaVersion.current().getMajorVersion(), MIN_SUPPORTED_MAJOR_JAVA_VERSION)); + + String javaHome = System.getenv("JAVA_HOME"); + + if (javaHome != null) { + errors.add(String.format("The JAVA_HOME environment variable is currently set to (%s).", javaHome)); + } + } + + if (!errors.isEmpty()) { + throw new UnsupportedOperationException(String.join("\n", errors)); + } + + getActivePlugin().apply(project); + } + + private static boolean isValidJavaRuntime() { + // Note use compareTo to ensure compatibility with gradle < 6.0 + return JavaVersion.current().compareTo(JavaVersion.toVersion(MIN_SUPPORTED_MAJOR_JAVA_VERSION)) >= 0; + } + + private static boolean isValidGradleRuntime() { + return getMajorGradleVersion() >= MIN_SUPPORTED_MAJOR_GRADLE_VERSION; + } + + private static int getMajorGradleVersion() { + String version = GradleVersion.current().getVersion(); + return Integer.parseInt(version.substring(0, version.indexOf("."))); + } + + BootstrappedPlugin getActivePlugin() { + try { + return (BootstrappedPlugin) Class.forName(PLUGIN_CLASS_NAME).getConstructor().newInstance(); + } catch (Exception e) { + throw new RuntimeException("Failed to bootstrap loom", e); + } + } +} diff --git a/bootstrap/src/main/resources/META-INF/trick-gradle-into-thinking-loom-is-signed.SF b/bootstrap/src/main/resources/META-INF/trick-gradle-into-thinking-loom-is-signed.SF new file mode 100644 index 0000000..9a1b230 --- /dev/null +++ b/bootstrap/src/main/resources/META-INF/trick-gradle-into-thinking-loom-is-signed.SF @@ -0,0 +1,4 @@ +Trick gradle into thinking that loom is signed to skip over transforming all classes in the jar. +This is required to get the bootstrap to well bootstrap on older gradle versions that dont support java 16. + +See https://github.com/gradle/gradle/blob/master/subprojects/core/src/main/java/org/gradle/internal/classpath/InstrumentingClasspathFileTransformer.java#L129 \ No newline at end of file diff --git a/bootstrap/test-project/build.gradle b/bootstrap/test-project/build.gradle new file mode 100644 index 0000000..2cb6e85 --- /dev/null +++ b/bootstrap/test-project/build.gradle @@ -0,0 +1,8 @@ +plugins { + id 'fabric-loom' version '0.8.local' +} + +dependencies { + minecraft "com.mojang:minecraft:1.16.5" + mappings loom.officialMojangMappings() +} \ No newline at end of file diff --git a/bootstrap/test-project/settings.gradle b/bootstrap/test-project/settings.gradle new file mode 100644 index 0000000..37a1b9e --- /dev/null +++ b/bootstrap/test-project/settings.gradle @@ -0,0 +1,10 @@ +pluginManagement { + repositories { + maven { + name = 'Fabric' + url = 'https://maven.fabricmc.net/' + } + gradlePluginPortal() + mavenLocal() + } +} diff --git a/build.gradle b/build.gradle index 9d5f41f..bc6fe29 100644 --- a/build.gradle +++ b/build.gradle @@ -39,9 +39,19 @@ repositories { mavenCentral() } +configurations { + bootstrap { + transitive false + } + compileClasspath.extendsFrom bootstrap + runtimeClasspath.extendsFrom bootstrap +} + dependencies { implementation gradleApi() + bootstrap project(":bootstrap") + // libraries implementation ('commons-io:commons-io:2.8.0') implementation ('org.zeroturnaround:zt-zip:1.14') @@ -71,7 +81,7 @@ dependencies { implementation ('org.cadixdev:lorenz-io-proguard:0.5.6') // decompilers - implementation ('net.fabricmc:fabric-fernflower:1.4.0') + implementation ('net.fabricmc:fabric-fernflower:1.4.1') implementation ('org.benf:cfr:0.151') // source code remapping @@ -94,6 +104,8 @@ jar { manifest { attributes 'Implementation-Version': project.version } + + from configurations.bootstrap.collect { it.isDirectory() ? it : zipTree(it) } } task sourcesJar(type: Jar, dependsOn: classes) { @@ -128,7 +140,7 @@ gradlePlugin { plugins { fabricLoom { id = 'fabric-loom' - implementationClass = 'net.fabricmc.loom.LoomGradlePlugin' + implementationClass = 'net.fabricmc.loom.bootstrap.LoomGradlePluginBootstrap' } } } @@ -215,6 +227,21 @@ publishing { } } +// Need to tweak this file to pretend we are compatible with j8 so the bootstrap will run. +tasks.withType(GenerateModuleMetadata) { + doLast { + def file = outputFile.get().asFile + + def metadata = new groovy.json.JsonSlurper().parseText(file.text) + + metadata.variants.each { + it.attributes["org.gradle.jvm.version"] = 8 + } + + file.text = groovy.json.JsonOutput.toJson(metadata) + } +} + // A task to output a json file with a list of all the test to run task writeActionsTestMatrix() { doLast { diff --git a/settings.gradle b/settings.gradle index ae239c6..885070a 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,2 +1,3 @@ rootProject.name = name +include "bootstrap" \ No newline at end of file diff --git a/src/main/java/net/fabricmc/loom/LoomGradlePlugin.java b/src/main/java/net/fabricmc/loom/LoomGradlePlugin.java index 938def6..3d32c4b 100644 --- a/src/main/java/net/fabricmc/loom/LoomGradlePlugin.java +++ b/src/main/java/net/fabricmc/loom/LoomGradlePlugin.java @@ -29,10 +29,10 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.ImmutableMap; import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.plugins.PluginAware; +import net.fabricmc.loom.bootstrap.BootstrappedPlugin; import net.fabricmc.loom.configuration.CompileConfiguration; import net.fabricmc.loom.configuration.FabricApiExtension; import net.fabricmc.loom.configuration.MavenPublication; @@ -41,7 +41,7 @@ import net.fabricmc.loom.configuration.providers.mappings.MappingsCache; import net.fabricmc.loom.decompilers.DecompilerConfiguration; import net.fabricmc.loom.task.LoomTasks; -public class LoomGradlePlugin implements Plugin { +public class LoomGradlePlugin implements BootstrappedPlugin { public static boolean refreshDeps; public static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); public static final ObjectMapper OBJECT_MAPPER = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);