Skip to content

Commit

Permalink
Add fabricApi.configureDataGeneration API/DSL to help with setting up…
Browse files Browse the repository at this point in the history
… 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
  • Loading branch information
modmuss50 authored Oct 15, 2023
1 parent e29d66f commit f73f596
Show file tree
Hide file tree
Showing 2 changed files with 244 additions and 1 deletion.
146 changes: 145 additions & 1 deletion src/main/java/net/fabricmc/loom/configuration/FabricApiExtension.java
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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<String, Map<String, String>> moduleVersionCache = new HashMap<>();
private static final HashMap<String, Map<String, String>> deprecatedModuleVersionCache = new HashMap<>();

Expand All @@ -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<DataGenerationSettings> 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<Boolean> getCreateRunConfiguration();

/**
* Contains a boolean property indicating whether a new source set should be created for the data generation process.
*/
Property<Boolean> getCreateSourceSet();

/**
* Contains a string property representing the mod ID associated with the data generation process.
*
* <p>This must be set when {@link #getCreateRunConfiguration()} is set.
*/
Property<String> getModId();

/**
* Contains a boolean property indicating whether strict validation is enabled.
*/
Property<Boolean> getStrictValidation();

/**
* Contains a boolean property indicating whether the generated resources will be automatically added to the main sourceset.
*/
Property<Boolean> getAddToResources();
}

private String getDependencyNotation(String moduleName, String fabricApiVersion) {
return String.format("net.fabricmc.fabric-api:%s:%s", moduleName, moduleVersion(moduleName, fabricApiVersion));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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
}
}

0 comments on commit f73f596

Please sign in to comment.