Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,22 @@ public interface LoomGradleExtensionAPI {

RegularFileProperty getAccessWidenerPath();

/**
* Specifies the {@code fabric.mod.json} file location used in injected interface processing.
*
* <p>
* By default, either {@code src/main/resources/fabric.mod.json}
* or {@code src/client/resources/fabric.mod.json} in the project directory is used.
* </p>
*
* <p>
* Providing a path to a different location allows using a shared or preprocessed
* file. However, at the end it must be put into the root of the processed
* resources directory (usually {@code build/resources/main}).
* </p>
*/
RegularFileProperty getFabricModJsonPath();

NamedDomainObjectContainer<DecompilerOptions> getDecompilerOptions();

void decompilers(Action<NamedDomainObjectContainer<DecompilerOptions>> action);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,13 @@

package net.fabricmc.loom.api.fabricapi;

import java.io.File;

import org.gradle.api.file.RegularFileProperty;
import org.gradle.api.provider.Property;

import net.fabricmc.loom.util.fmj.FabricModJsonFactory;

/**
* Represents the settings for data generation.
*/
Expand Down Expand Up @@ -67,4 +71,11 @@ public interface DataGenerationSettings {
* Contains a boolean property indicating whether data generation will be compiled and ran with the client.
*/
Property<Boolean> getClient();

/**
* Sets {@link #getModId()} property based on the {@code id} field defined in the provided file.
*/
default void modId(File fabricModJsonFile) {
getModId().set(FabricModJsonFactory.createFromFile(fabricModJsonFile).getId());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,14 @@

package net.fabricmc.loom.api.fabricapi;

import java.io.File;

import org.gradle.api.provider.Property;
import org.gradle.api.tasks.Optional;
import org.jetbrains.annotations.ApiStatus;

import net.fabricmc.loom.util.fmj.FabricModJsonFactory;

/**
* Represents the settings for game and/or client tests.
*/
Expand Down Expand Up @@ -89,4 +93,11 @@ public interface GameTestSettings {
*/
@Optional
Property<String> getUsername();

/**
* Sets {@link #getModId()} property based on the {@code id} field defined in the provided file.
*/
default void modId(File fabricModJsonFile) {
getModId().set(FabricModJsonFactory.createFromFile(fabricModJsonFile).getId());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@

package net.fabricmc.loom.configuration.fabricapi;

import java.io.IOException;

import javax.inject.Inject;

import org.gradle.api.Project;
Expand Down Expand Up @@ -64,17 +62,13 @@ protected SourceSet configureSourceSet(Property<String> modId, boolean isClient)
});

modId.convention(getProject().provider(() -> {
try {
final FabricModJson fabricModJson = FabricModJsonFactory.createFromSourceSetsNullable(getProject(), sourceSet);

if (fabricModJson == null) {
throw new RuntimeException("Could not find a fabric.mod.json file in the data source set or a value for DataGenerationSettings.getModId()");
}
final FabricModJson fabricModJson = FabricModJsonFactory.createFromSourceSetsNullable(getProject(), sourceSet);

return fabricModJson.getId();
} catch (IOException e) {
throw new org.gradle.api.UncheckedIOException("Failed to read mod id from the datagen source set.", e);
if (fabricModJson == null) {
throw new RuntimeException("Could not find a fabric.mod.json file in the data source set or a value for DataGenerationSettings.getModId()");
}

return fabricModJson.getId();
}));

extension.getMods().create(modId.get(), mod -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,15 @@
package net.fabricmc.loom.extension;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.gradle.api.Action;
import org.gradle.api.NamedDomainObjectContainer;
import org.gradle.api.NamedDomainObjectList;
import org.gradle.api.Project;
import org.gradle.api.UncheckedIOException;
import org.gradle.api.artifacts.Dependency;
import org.gradle.api.file.ConfigurableFileCollection;
import org.gradle.api.file.FileCollection;
Expand Down Expand Up @@ -74,7 +73,7 @@
import net.fabricmc.loom.util.DeprecationHelper;
import net.fabricmc.loom.util.MirrorUtil;
import net.fabricmc.loom.util.fmj.FabricModJson;
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Linters - my greatest nemesis

import net.fabricmc.loom.util.fmj.FabricModJsonFactory;
import net.fabricmc.loom.util.fmj.FabricModJsonHelpers;
import net.fabricmc.loom.util.gradle.SourceSetHelper;

/**
Expand All @@ -86,6 +85,7 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA
protected final ListProperty<JarProcessor> jarProcessors;
protected final ConfigurableFileCollection log4jConfigs;
protected final RegularFileProperty accessWidener;
protected final RegularFileProperty fabricModJsonPath;
protected final ManifestLocations versionsManifests;
protected final Property<String> customMetadata;
protected final SetProperty<String> knownIndyBsms;
Expand Down Expand Up @@ -118,6 +118,7 @@ protected LoomGradleExtensionApiImpl(Project project, LoomFiles directories) {
.empty();
this.log4jConfigs = project.files(directories.getDefaultLog4jConfigFile());
this.accessWidener = project.getObjects().fileProperty();
this.fabricModJsonPath = project.getObjects().fileProperty();
this.versionsManifests = new ManifestLocations();
this.versionsManifests.add("mojang", MirrorUtil.getVersionManifests(project), -2);
this.versionsManifests.add("fabric_experimental", MirrorUtil.getExperimentalVersions(project), -1);
Expand Down Expand Up @@ -205,6 +206,11 @@ public RegularFileProperty getAccessWidenerPath() {
return accessWidener;
}

@Override
public RegularFileProperty getFabricModJsonPath() {
return fabricModJsonPath;
}

@Override
public NamedDomainObjectContainer<DecompilerOptions> getDecompilerOptions() {
return decompilers;
Expand Down Expand Up @@ -293,17 +299,13 @@ public SetProperty<String> getKnownIndyBsms() {

@Override
public String getModVersion() {
try {
final FabricModJson fabricModJson = FabricModJsonFactory.createFromSourceSetsNullable(getProject(), SourceSetHelper.getMainSourceSet(getProject()));
List<FabricModJson> fabricModJsons = FabricModJsonHelpers.getModsInProject(getProject());

if (fabricModJson == null) {
throw new RuntimeException("Could not find a fabric.mod.json file in the main sourceset");
}

return fabricModJson.getModVersion();
} catch (IOException e) {
throw new UncheckedIOException("Failed to read mod version from main sourceset.", e);
if (fabricModJsons.isEmpty()) {
throw new RuntimeException("Could not find a fabric.mod.json file in the main sourceset");
}

return fabricModJsons.getFirst().getModVersion();
}

@Override
Expand Down
25 changes: 18 additions & 7 deletions src/main/java/net/fabricmc/loom/util/fmj/FabricModJsonFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -107,27 +107,38 @@ public static Optional<FabricModJson> createFromZipOptional(Path zipPath) {
return Optional.ofNullable(createFromZipNullable(zipPath));
}

public static FabricModJson createFromFile(File file) {
JsonObject modJson = readFmjJsonObject(file);
return create(modJson, new FabricModJsonSource.DirectorySource(file.toPath().getParent()));
}

@Nullable
public static FabricModJson createFromSourceSetsNullable(Project project, SourceSet... sourceSets) throws IOException {
final File file = SourceSetHelper.findFirstFileInResource(FABRIC_MOD_JSON, project, sourceSets);
public static FabricModJson createFromSourceSetsNullable(Project project, SourceSet... sourceSets) {
File file = SourceSetHelper.findFirstFileInResource(FABRIC_MOD_JSON, project, sourceSets);

if (file == null) {
return null;
}

try {
JsonObject modJson = readFmjJsonObject(file);
return create(modJson, new FabricModJsonSource.SourceSetSource(project, sourceSets));
} catch (JsonSyntaxException e) {
LOGGER.warn("Failed to parse fabric.mod.json: {}", file.getAbsolutePath());
return null;
}
}

private static JsonObject readFmjJsonObject(File file) {
try (Reader reader = Files.newBufferedReader(file.toPath(), StandardCharsets.UTF_8)) {
final JsonObject modJson = LoomGradlePlugin.GSON.fromJson(reader, JsonObject.class);

if (modJson == null) {
// fromJson returns null if the file is empty
LOGGER.warn("Failed to parse empty fabric.mod.json: {}", file.getAbsolutePath());
return null;
}

return create(modJson, new FabricModJsonSource.SourceSetSource(project, sourceSets));
} catch (JsonSyntaxException e) {
LOGGER.warn("Failed to parse fabric.mod.json: {}", file.getAbsolutePath());
return null;
return modJson;
} catch (IOException e) {
throw new UncheckedIOException("Failed to read " + file.getAbsolutePath(), e);
}
Expand Down
28 changes: 17 additions & 11 deletions src/main/java/net/fabricmc/loom/util/fmj/FabricModJsonHelpers.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,37 +24,43 @@

package net.fabricmc.loom.util.fmj;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.gradle.api.Project;
import org.gradle.api.provider.Provider;
import org.gradle.api.tasks.SourceSet;

import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.api.LoomGradleExtensionAPI;
import net.fabricmc.loom.util.gradle.SourceSetHelper;
import net.fabricmc.loom.LoomGradleExtension;

public class FabricModJsonHelpers {
// Returns a list of Mods found in the provided project's main or client sourcesets
/**
* Returns the list of mods provided by either {@link LoomGradleExtensionAPI#getFabricModJsonPath()}
* or {@code fabric.mod.json} in main or client resources.
*/
public static List<FabricModJson> getModsInProject(Project project) {
final LoomGradleExtension extension = LoomGradleExtension.get(project);
Provider<File> overrideFile = extension.getFabricModJsonPath().getAsFile();

if (overrideFile.isPresent()) {
return List.of(FabricModJsonFactory.createFromFile(overrideFile.get()));
}

var sourceSets = new ArrayList<SourceSet>();
sourceSets.add(SourceSetHelper.getMainSourceSet(project));

if (extension.areEnvironmentSourceSetsSplit()) {
sourceSets.add(SourceSetHelper.getSourceSetByName("client", project));
}

try {
final FabricModJson fabricModJson = FabricModJsonFactory.createFromSourceSetsNullable(project, sourceSets.toArray(SourceSet[]::new));
final FabricModJson fabricModJson = FabricModJsonFactory.createFromSourceSetsNullable(project, sourceSets.toArray(SourceSet[]::new));

if (fabricModJson != null) {
return List.of(fabricModJson);
}
} catch (IOException e) {
throw new UncheckedIOException(e);
if (fabricModJson != null) {
return List.of(fabricModJson);
}

return Collections.emptyList();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

package net.fabricmc.loom.test.integration

import org.gradle.testkit.runner.BuildResult
import spock.lang.Specification
import spock.lang.Unroll

Expand All @@ -49,4 +50,34 @@ class InterfaceInjectionTest extends Specification implements GradleProjectTestT
where:
version << STANDARD_TEST_VERSIONS
}

@Unroll
def "Resolve custom FMJ"() {
setup:
GradleProject gradle = gradleProject(project: "fmjPathConfig", version: version)

when:
BuildResult result = gradle.run(task: "build", args: ["-PoverrideFMJ=true"])

then:
result.task(":build").outcome == SUCCESS

where:
version << STANDARD_TEST_VERSIONS
}

@Unroll
def "Fail to find FMJ"() {
setup:
GradleProject gradle = gradleProject(project: "fmjPathConfig", version: version)

when:
BuildResult result = gradle.run(task: "build", expectFailure: true)

then:
result.task(":build") == null

where:
version << STANDARD_TEST_VERSIONS
}
}
37 changes: 37 additions & 0 deletions src/test/resources/projects/fmjPathConfig/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// This is used by a range of tests that append to this file before running the gradle tasks.
// Can be used for tests that require minimal custom setup
plugins {
id 'fabric-loom'
id 'maven-publish'
}

version = "1.0.0"
group = "com.example"

// In multi-version setup this would be a separate project,
// but a source set will suffice for a test.
sourceSets {
custom {
}
main {
compileClasspath += sourceSets.custom.compileClasspath
runtimeClasspath += sourceSets.custom.runtimeClasspath
}
}


dependencies {
minecraft "com.mojang:minecraft:1.17.1"
mappings "net.fabricmc:yarn:1.17.1+build.59:v2"
modImplementation "net.fabricmc:fabric-loader:0.11.6"
}

base {
archivesName = "fabric-example-mod"
}

if (project.hasProperty("overrideFMJ")) {
loom {
fabricModJsonPath = file("src/custom/resources/fabric.mod.json")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"schemaVersion": 1,
"id": "testmod",
"version": "1",
"name": "Test Mod",
"custom": {
"loom:injected_interfaces": {
"net/minecraft/class_310": ["InjectedInterface"]
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import net.minecraft.client.MinecraftClient;

import net.fabricmc.api.ModInitializer;

public class ExampleMod implements ModInitializer {
@Override
public void onInitialize() {
MinecraftClient.getInstance().newMethodThatDidNotExist();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
public interface InjectedInterface {
default void newMethodThatDidNotExist() {
}
}