From f73f596811923c7c9ab6a45e0a6fc7895c602a53 Mon Sep 17 00:00:00 2001 From: modmuss Date: Sun, 15 Oct 2023 10:46:18 +0100 Subject: [PATCH] Add fabricApi.configureDataGeneration API/DSL to help with setting up data generation. (#960) * Add fabricApi.configureDataGeneration API/DSL to help with setting up datageneration. * Clean + add option to not add to resources * Dont create new task --- .../configuration/FabricApiExtension.java | 146 +++++++++++++++++- .../integration/DataGenerationTest.groovy | 99 ++++++++++++ 2 files changed, 244 insertions(+), 1 deletion(-) create mode 100644 src/test/groovy/net/fabricmc/loom/test/integration/DataGenerationTest.groovy diff --git a/src/main/java/net/fabricmc/loom/configuration/FabricApiExtension.java b/src/main/java/net/fabricmc/loom/configuration/FabricApiExtension.java index 732bfa0f3..81ac13f7c 100644 --- a/src/main/java/net/fabricmc/loom/configuration/FabricApiExtension.java +++ b/src/main/java/net/fabricmc/loom/configuration/FabricApiExtension.java @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2020 FabricMC + * Copyright (c) 2020-2023 FabricMC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -34,19 +34,32 @@ import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; +import org.gradle.api.Action; import org.gradle.api.Project; import org.gradle.api.artifacts.Dependency; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.plugins.JavaPlugin; +import org.gradle.api.provider.Property; +import org.gradle.api.tasks.Delete; +import org.gradle.api.tasks.SourceSet; +import org.gradle.api.tasks.SourceSetContainer; +import org.gradle.api.tasks.TaskContainer; +import org.gradle.jvm.tasks.Jar; +import org.gradle.language.base.plugins.LifecycleBasePlugin; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.util.download.DownloadException; +import net.fabricmc.loom.util.gradle.SourceSetHelper; public abstract class FabricApiExtension { @Inject public abstract Project getProject(); + private static final String DATAGEN_SOURCESET_NAME = "datagen"; + private static final HashMap> moduleVersionCache = new HashMap<>(); private static final HashMap> deprecatedModuleVersionCache = new HashMap<>(); @@ -73,6 +86,137 @@ public String moduleVersion(String moduleName, String fabricApiVersion) { return moduleVersion; } + /** + * Configure data generation with the default options. + */ + public void configureDataGeneration() { + configureDataGeneration(dataGenerationSettings -> { }); + } + + /** + * Configure data generation with custom options. + */ + public void configureDataGeneration(Action action) { + final LoomGradleExtension extension = LoomGradleExtension.get(getProject()); + final TaskContainer taskContainer = getProject().getTasks(); + + DataGenerationSettings settings = getProject().getObjects().newInstance(DataGenerationSettings.class); + settings.getOutputDirectory().set(getProject().file("src/main/generated")); + settings.getCreateRunConfiguration().convention(true); + settings.getCreateSourceSet().convention(true); + settings.getCreateSourceSet().convention(false); + settings.getStrictValidation().convention(false); + settings.getAddToResources().convention(true); + + action.execute(settings); + + final SourceSet mainSourceSet = SourceSetHelper.getMainSourceSet(getProject()); + final File outputDirectory = settings.getOutputDirectory().getAsFile().get(); + + if (settings.getAddToResources().get()) { + mainSourceSet.resources(files -> { + // Add the src/main/generated to the main sourceset's resources. + files.getSrcDirs().add(outputDirectory); + }); + } + + // Exclude the cache dir from the output jar to ensure reproducibility. + taskContainer.getByName(JavaPlugin.JAR_TASK_NAME, task -> { + Jar jar = (Jar) task; + jar.exclude(".cache/**"); + }); + + taskContainer.getByName(LifecycleBasePlugin.CLEAN_TASK_NAME, task -> { + Delete clean = (Delete) task; + clean.delete(outputDirectory); + }); + + if (settings.getCreateSourceSet().get()) { + if (!settings.getModId().isPresent()) { + throw new IllegalStateException("DataGenerationSettings.getModId() must be set when using split sources."); + } + + SourceSetContainer sourceSets = SourceSetHelper.getSourceSets(getProject()); + + // Create the new datagen sourceset, depend on the main sourceset. + sourceSets.create(DATAGEN_SOURCESET_NAME, sourceSet -> { + sourceSet.setCompileClasspath( + sourceSet.getCompileClasspath() + .plus(mainSourceSet.getCompileClasspath()) + .plus(mainSourceSet.getOutput()) + ); + + sourceSet.setRuntimeClasspath( + sourceSet.getRuntimeClasspath() + .plus(mainSourceSet.getRuntimeClasspath()) + .plus(mainSourceSet.getOutput()) + ); + }); + + extension.getMods().create(settings.getModId().get(), mod -> { + // Create a classpath group for this mod. Assume that the main sourceset is already in a group. + mod.sourceSet(DATAGEN_SOURCESET_NAME); + }); + } + + if (settings.getCreateRunConfiguration().get()) { + extension.getRunConfigs().create("datagen", run -> { + run.name("Data Generation"); + run.inherit(extension.getRunConfigs().getByName("server")); + + run.property("fabric-api.datagen"); + run.property("fabric-api.datagen.output-dir", outputDirectory.getAbsolutePath()); + run.runDir("build/datagen"); + + if (settings.getModId().isPresent()) { + run.property("fabric-api.datagen.modid", settings.getModId().get()); + } + + if (settings.getStrictValidation().get()) { + run.property("fabric-api.datagen.strict-validation", "true"); + } + + if (settings.getCreateSourceSet().get()) { + run.source(DATAGEN_SOURCESET_NAME); + } + }); + } + } + + public interface DataGenerationSettings { + /** + * Contains the output directory where generated data files will be stored. + */ + RegularFileProperty getOutputDirectory(); + + /** + * Contains a boolean indicating whether a run configuration should be created for the data generation process. + */ + Property getCreateRunConfiguration(); + + /** + * Contains a boolean property indicating whether a new source set should be created for the data generation process. + */ + Property getCreateSourceSet(); + + /** + * Contains a string property representing the mod ID associated with the data generation process. + * + *

This must be set when {@link #getCreateRunConfiguration()} is set. + */ + Property getModId(); + + /** + * Contains a boolean property indicating whether strict validation is enabled. + */ + Property getStrictValidation(); + + /** + * Contains a boolean property indicating whether the generated resources will be automatically added to the main sourceset. + */ + Property getAddToResources(); + } + private String getDependencyNotation(String moduleName, String fabricApiVersion) { return String.format("net.fabricmc.fabric-api:%s:%s", moduleName, moduleVersion(moduleName, fabricApiVersion)); } diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/DataGenerationTest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/DataGenerationTest.groovy new file mode 100644 index 000000000..a5fb4a333 --- /dev/null +++ b/src/test/groovy/net/fabricmc/loom/test/integration/DataGenerationTest.groovy @@ -0,0 +1,99 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2023 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.test.integration + +import spock.lang.Specification +import spock.lang.Unroll + +import net.fabricmc.loom.test.util.GradleProjectTestTrait + +import static net.fabricmc.loom.test.LoomTestConstants.STANDARD_TEST_VERSIONS +import static org.gradle.testkit.runner.TaskOutcome.SUCCESS + +class DataGenerationTest extends Specification implements GradleProjectTestTrait { + @Unroll + def "dataGeneration (gradle #version)"() { + setup: + def gradle = gradleProject(project: "minimalBase", version: version) + gradle.buildGradle << ''' + fabricApi { + configureDataGeneration() + } + + dependencies { + minecraft "com.mojang:minecraft:1.20.2" + mappings "net.fabricmc:yarn:1.20.2+build.4:v2" + modImplementation "net.fabricmc:fabric-loader:0.14.23" + modImplementation "net.fabricmc.fabric-api:fabric-api:0.90.0+1.20.2" + } + ''' + when: + def result = gradle.run(task: "runDatagen") + + then: + result.task(":runDatagen").outcome == SUCCESS + + where: + version << STANDARD_TEST_VERSIONS + } + + @Unroll + def "dataGeneration sourceset (gradle #version)"() { + setup: + def gradle = gradleProject(project: "minimalBase", version: version) + gradle.buildGradle << ''' + // Must configure the main mod + loom.mods { + "example" { + sourceSet sourceSets.main + } + } + + fabricApi { + configureDataGeneration { + createSourceSet = true + createRunConfiguration = true + modId = "example-datagen" + strictValidation = true + } + } + + dependencies { + minecraft "com.mojang:minecraft:1.20.2" + mappings "net.fabricmc:yarn:1.20.2+build.4:v2" + modImplementation "net.fabricmc:fabric-loader:0.14.23" + modImplementation "net.fabricmc.fabric-api:fabric-api:0.90.0+1.20.2" + } + ''' + when: + def result = gradle.run(task: "runDatagen") + + then: + result.task(":runDatagen").outcome == SUCCESS + + where: + version << STANDARD_TEST_VERSIONS + } +}