Initial support for fabric.mod.json V2

This commit is contained in:
modmuss50
2022-10-01 08:59:39 +01:00
parent ffe5e955c5
commit 514dd24e9e
19 changed files with 1041 additions and 128 deletions

View File

@@ -53,8 +53,8 @@ import org.jetbrains.annotations.Nullable;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.LoomGradlePlugin;
import net.fabricmc.loom.task.RemapTaskConfiguration;
import net.fabricmc.loom.util.ModUtils;
import net.fabricmc.loom.util.ZipUtils;
import net.fabricmc.loom.util.fmj.FabricModJsonFactory;
public final class IncludedJarFactory {
private final Project project;
@@ -143,7 +143,7 @@ public final class IncludedJarFactory {
}
private File getNestableJar(final File input, final Metadata metadata) {
if (ModUtils.isMod(input)) {
if (FabricModJsonFactory.isModJar(input)) {
// Input is a mod, nothing needs to be done.
return input;
}

View File

@@ -38,9 +38,9 @@ import com.google.gson.JsonObject;
import org.gradle.api.UncheckedIOException;
import org.slf4j.Logger;
import net.fabricmc.loom.util.ModUtils;
import net.fabricmc.loom.util.Pair;
import net.fabricmc.loom.util.ZipUtils;
import net.fabricmc.loom.util.fmj.FabricModJsonFactory;
public class JarNester {
public static void nestJars(Collection<File> jars, File modJar, Logger logger) {
@@ -49,7 +49,7 @@ public class JarNester {
return;
}
Preconditions.checkArgument(ModUtils.isMod(modJar), "Cannot nest jars into none mod jar " + modJar.getName());
Preconditions.checkArgument(FabricModJsonFactory.isModJar(modJar), "Cannot nest jars into none mod jar " + modJar.getName());
try {
ZipUtils.add(modJar.toPath(), jars.stream().map(file -> {
@@ -69,7 +69,7 @@ public class JarNester {
for (File file : jars) {
String nestedJarPath = "META-INF/jars/" + file.getName();
Preconditions.checkArgument(ModUtils.isMod(file), "Cannot nest none mod jar: " + file.getName());
Preconditions.checkArgument(FabricModJsonFactory.isModJar(file), "Cannot nest none mod jar: " + file.getName());
for (JsonElement nestedJar : nestedJars) {
JsonObject jsonObject = nestedJar.getAsJsonObject();

View File

@@ -28,7 +28,6 @@ import java.io.File;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
@@ -55,7 +54,6 @@ import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.commons.Remapper;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.LoomGradlePlugin;
import net.fabricmc.loom.api.InterfaceInjectionExtensionAPI;
import net.fabricmc.loom.api.RemapConfigurationSettings;
import net.fabricmc.loom.api.mappings.layered.MappingsNamespace;
@@ -63,10 +61,11 @@ import net.fabricmc.loom.configuration.processors.JarProcessor;
import net.fabricmc.loom.task.GenerateSourcesTask;
import net.fabricmc.loom.util.Checksum;
import net.fabricmc.loom.util.Constants;
import net.fabricmc.loom.util.ModUtils;
import net.fabricmc.loom.util.Pair;
import net.fabricmc.loom.util.TinyRemapperHelper;
import net.fabricmc.loom.util.ZipUtils;
import net.fabricmc.loom.util.fmj.FabricModJson;
import net.fabricmc.loom.util.fmj.FabricModJsonFactory;
import net.fabricmc.mappingio.tree.MappingTree;
import net.fabricmc.mappingio.tree.MemoryMappingTree;
import net.fabricmc.tinyremapper.TinyRemapper;
@@ -198,28 +197,15 @@ public class InterfaceInjectionProcessor implements JarProcessor, GenerateSource
}
private List<InjectedInterface> getSourceInjectedInterface(SourceSet sourceSet) {
final File fabricModJson;
final FabricModJson fabricModJson;
try {
fabricModJson = sourceSet.getResources()
.matching(patternFilterable -> patternFilterable.include("fabric.mod.json"))
.getSingleFile();
} catch (IllegalStateException e) {
// File not found
return Collections.emptyList();
}
final String jsonString;
try {
jsonString = Files.readString(fabricModJson.toPath(), StandardCharsets.UTF_8);
fabricModJson = FabricModJsonFactory.createFromSourceSetNullable(sourceSet);
} catch (IOException e) {
throw new UncheckedIOException("Failed to read fabric.mod.json", e);
throw new UncheckedIOException(e);
}
final JsonObject jsonObject = LoomGradlePlugin.GSON.fromJson(jsonString, JsonObject.class);
return InjectedInterface.fromJson(jsonObject);
return InjectedInterface.fromFabricModJson(fabricModJson);
}
@Override
@@ -271,29 +257,18 @@ public class InterfaceInjectionProcessor implements JarProcessor, GenerateSource
* Reads the injected interfaces contained in a mod jar, or returns empty if there is none.
*/
public static List<InjectedInterface> fromModJar(Path modJarPath) {
final JsonObject jsonObject = ModUtils.getFabricModJson(modJarPath);
if (jsonObject == null) {
return Collections.emptyList();
}
return fromJson(jsonObject);
return fromFabricModJson(FabricModJsonFactory.createFromZip(modJarPath));
}
public static List<InjectedInterface> fromJson(JsonObject jsonObject) {
final String modId = jsonObject.get("id").getAsString();
public static List<InjectedInterface> fromFabricModJson(FabricModJson fabricModJson) {
final String modId = fabricModJson.getId();
final JsonElement jsonElement = fabricModJson.getCustom(Constants.CustomModJsonKeys.INJECTED_INTERFACE);
if (!jsonObject.has("custom")) {
if (jsonElement == null) {
return Collections.emptyList();
}
final JsonObject custom = jsonObject.getAsJsonObject("custom");
if (!custom.has(Constants.CustomModJsonKeys.INJECTED_INTERFACE)) {
return Collections.emptyList();
}
final JsonObject addedIfaces = custom.getAsJsonObject(Constants.CustomModJsonKeys.INJECTED_INTERFACE);
final JsonObject addedIfaces = jsonElement.getAsJsonObject();
final List<InjectedInterface> result = new ArrayList<>();

View File

@@ -25,18 +25,18 @@
package net.fabricmc.loom.configuration.mods;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.List;
import com.google.gson.JsonObject;
import org.objectweb.asm.commons.Remapper;
import net.fabricmc.accesswidener.AccessWidenerReader;
import net.fabricmc.accesswidener.AccessWidenerRemapper;
import net.fabricmc.accesswidener.AccessWidenerWriter;
import net.fabricmc.loom.LoomGradlePlugin;
import net.fabricmc.loom.api.mappings.layered.MappingsNamespace;
import net.fabricmc.loom.util.ZipUtils;
import net.fabricmc.loom.util.fmj.FabricModJson;
import net.fabricmc.loom.util.fmj.FabricModJsonFactory;
import net.fabricmc.loom.util.fmj.ModEnvironment;
public class AccessWidenerUtils {
/**
@@ -58,16 +58,20 @@ public class AccessWidenerUtils {
}
public static AccessWidenerData readAccessWidenerData(Path inputJar) throws IOException {
byte[] modJsonBytes = ZipUtils.unpack(inputJar, "fabric.mod.json");
JsonObject jsonObject = LoomGradlePlugin.GSON.fromJson(new String(modJsonBytes, StandardCharsets.UTF_8), JsonObject.class);
final FabricModJson fabricModJson = FabricModJsonFactory.createFromZip(inputJar);
final List<String> classTweakers = fabricModJson.getClassTweakers(ModEnvironment.UNIVERSAL);
if (!jsonObject.has("accessWidener")) {
if (classTweakers.isEmpty()) {
return null;
}
String accessWidenerPath = jsonObject.get("accessWidener").getAsString();
byte[] accessWidener = ZipUtils.unpack(inputJar, accessWidenerPath);
AccessWidenerReader.Header header = AccessWidenerReader.readHeader(accessWidener);
if (classTweakers.size() != 1) {
throw new UnsupportedOperationException("TODO: support multiple class tweakers");
}
final String accessWidenerPath = classTweakers.get(0);
final byte[] accessWidener = fabricModJson.getSource().read(accessWidenerPath);
final AccessWidenerReader.Header header = AccessWidenerReader.readHeader(accessWidener);
return new AccessWidenerData(accessWidenerPath, header, accessWidener);
}

View File

@@ -55,9 +55,9 @@ import net.fabricmc.loom.configuration.mods.dependency.ModDependency;
import net.fabricmc.loom.configuration.mods.dependency.ModDependencyFactory;
import net.fabricmc.loom.util.Checksum;
import net.fabricmc.loom.util.Constants;
import net.fabricmc.loom.util.ModUtils;
import net.fabricmc.loom.util.OperatingSystem;
import net.fabricmc.loom.util.SourceRemapper;
import net.fabricmc.loom.util.fmj.FabricModJsonFactory;
@SuppressWarnings("UnstableApiUsage")
public class ModConfigurationRemapper {
@@ -88,7 +88,7 @@ public class ModConfigurationRemapper {
final List<ModDependency> modDependencies = new ArrayList<>();
for (ArtifactRef artifact : resolveArtifacts(project, sourceConfig)) {
if (!ModUtils.isMod(artifact.path())) {
if (!FabricModJsonFactory.isModJar(artifact.path())) {
artifact.applyToConfiguration(project, targetConfig);
continue;
}

View File

@@ -35,7 +35,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import com.google.gson.JsonObject;
import com.google.gson.JsonElement;
import org.gradle.api.Project;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
@@ -46,8 +46,9 @@ import net.fabricmc.loom.api.RemapConfigurationSettings;
import net.fabricmc.loom.api.mappings.layered.MappingsNamespace;
import net.fabricmc.loom.task.GenerateSourcesTask;
import net.fabricmc.loom.util.Constants;
import net.fabricmc.loom.util.ModUtils;
import net.fabricmc.loom.util.ZipUtils;
import net.fabricmc.loom.util.fmj.FabricModJson;
import net.fabricmc.loom.util.fmj.FabricModJsonFactory;
import net.fabricmc.mappingio.MappingReader;
import net.fabricmc.mappingio.tree.MappingTree;
import net.fabricmc.mappingio.tree.MemoryMappingTree;
@@ -70,7 +71,7 @@ public final class ModJavadocProcessor implements JarProcessor, GenerateSourcesT
final Set<File> artifacts = entry.getSourceConfiguration().get().resolve();
for (File artifact : artifacts) {
if (!ModUtils.isMod(artifact.toPath())) {
if (!FabricModJsonFactory.isModJar(artifact.toPath())) {
continue;
}
@@ -121,20 +122,15 @@ public final class ModJavadocProcessor implements JarProcessor, GenerateSourcesT
public record ModJavadoc(String modId, MemoryMappingTree mappingTree) {
@Nullable
public static ModJavadoc fromModJar(Path path) throws IOException {
JsonObject jsonObject = ModUtils.getFabricModJson(path);
final FabricModJson fabricModJson = FabricModJsonFactory.createFromZip(path);
final String modId = fabricModJson.getId();
final JsonElement customElement = fabricModJson.getCustom(Constants.CustomModJsonKeys.PROVIDED_JAVADOC);
if (jsonObject == null || !jsonObject.has("custom")) {
if (customElement == null) {
return null;
}
final String modId = jsonObject.get("id").getAsString();
final JsonObject custom = jsonObject.getAsJsonObject("custom");
if (!custom.has(Constants.CustomModJsonKeys.PROVIDED_JAVADOC)) {
return null;
}
final String javaDocPath = custom.getAsJsonPrimitive(Constants.CustomModJsonKeys.PROVIDED_JAVADOC).getAsString();
final String javaDocPath = customElement.getAsString();
final byte[] data = ZipUtils.unpack(path, javaDocPath);
final MemoryMappingTree mappings = new MemoryMappingTree();

View File

@@ -58,7 +58,6 @@ import net.fabricmc.accesswidener.AccessWidenerReader;
import net.fabricmc.accesswidener.AccessWidenerRemapper;
import net.fabricmc.accesswidener.AccessWidenerWriter;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.build.MixinRefmapHelper;
import net.fabricmc.loom.build.nesting.IncludedJarFactory;
import net.fabricmc.loom.build.nesting.JarNester;
import net.fabricmc.loom.configuration.accesswidener.AccessWidenerFile;
@@ -67,10 +66,11 @@ import net.fabricmc.loom.extension.MixinExtension;
import net.fabricmc.loom.task.service.TinyRemapperService;
import net.fabricmc.loom.util.Constants;
import net.fabricmc.loom.util.ExceptionUtil;
import net.fabricmc.loom.util.ModUtils;
import net.fabricmc.loom.util.Pair;
import net.fabricmc.loom.util.SidedClassVisitor;
import net.fabricmc.loom.util.ZipUtils;
import net.fabricmc.loom.util.fmj.FabricModJson;
import net.fabricmc.loom.util.fmj.FabricModJsonFactory;
import net.fabricmc.loom.util.service.UnsafeWorkQueueHelper;
import net.fabricmc.tinyremapper.OutputConsumerPath;
import net.fabricmc.tinyremapper.TinyRemapper;
@@ -138,14 +138,8 @@ public abstract class RemapJarTask extends AbstractRemapJarTask {
final LoomGradleExtension extension = LoomGradleExtension.get(getProject());
final MixinExtension mixinExtension = extension.getMixin();
final JsonObject fabricModJson = ModUtils.getFabricModJson(getInputFile().getAsFile().get().toPath());
if (fabricModJson == null) {
getProject().getLogger().warn("Could not find fabric.mod.json file in: " + getInputFile().getAsFile().get().getName());
return;
}
final Collection<String> allMixinConfigs = MixinRefmapHelper.getMixinConfigurationFiles(fabricModJson);
final FabricModJson fabricModJson = FabricModJsonFactory.createFromZip(getInputFile().getAsFile().get().toPath());
final Collection<String> allMixinConfigs = fabricModJson.getMixinConfigurations();
for (SourceSet sourceSet : mixinExtension.getMixinSourceSets()) {
MixinExtension.MixinInformationContainer container = Objects.requireNonNull(

View File

@@ -1,7 +1,7 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2018-2022 FabricMC
* Copyright (c) 2022 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
@@ -22,40 +22,40 @@
* SOFTWARE.
*/
package net.fabricmc.loom.build;
package net.fabricmc.loom.util.fmj;
import java.util.Collection;
import java.util.Collections;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import static net.fabricmc.loom.util.fmj.FabricModJsonUtils.readString;
import com.google.gson.JsonArray;
import java.util.List;
import java.util.Objects;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public final class MixinRefmapHelper {
private MixinRefmapHelper() { }
public abstract sealed class FabricModJson permits FabricModJsonV0, FabricModJsonV1, FabricModJsonV2 {
protected final JsonObject jsonObject;
private final FabricModJsonSource source;
private static final String FABRIC_MOD_JSON = "fabric.mod.json";
protected FabricModJson(JsonObject jsonObject, FabricModJsonSource source) {
this.jsonObject = Objects.requireNonNull(jsonObject);
this.source = Objects.requireNonNull(source);
}
@NotNull
public static Collection<String> getMixinConfigurationFiles(JsonObject fabricModJson) {
JsonArray mixins = fabricModJson.getAsJsonArray("mixins");
public abstract int getVersion();
if (mixins == null) {
return Collections.emptyList();
}
public String getId() {
return readString(jsonObject, "id");
}
return StreamSupport.stream(mixins.spliterator(), false)
.map(e -> {
if (e instanceof JsonPrimitive str) {
return str.getAsString();
} else if (e instanceof JsonObject obj) {
return obj.get("config").getAsString();
} else {
throw new RuntimeException("Incorrect fabric.mod.json format");
}
}).collect(Collectors.toSet());
@Nullable
public abstract JsonElement getCustom(String key);
public abstract List<String> getMixinConfigurations();
public abstract List<String> getClassTweakers(ModEnvironment modEnvironment);
public final FabricModJsonSource getSource() {
return source;
}
}

View File

@@ -0,0 +1,116 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2022 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.util.fmj;
import static net.fabricmc.loom.util.fmj.FabricModJsonUtils.readInt;
import java.io.File;
import java.io.IOException;
import java.io.Reader;
import java.io.UncheckedIOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import com.google.gson.JsonObject;
import org.gradle.api.tasks.SourceSet;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.VisibleForTesting;
import net.fabricmc.loom.LoomGradlePlugin;
import net.fabricmc.loom.util.ZipUtils;
import net.fabricmc.loom.util.gradle.SourceSetHelper;
public final class FabricModJsonFactory {
private static final String FABRIC_MOD_JSON = "fabric.mod.json";
private FabricModJsonFactory() {
}
@VisibleForTesting
public static FabricModJson create(JsonObject jsonObject, FabricModJsonSource source) {
int schemaVersion = 0;
if (jsonObject.has("schemaVersion")) {
// V0 had no schemaVersion key.
schemaVersion = readInt(jsonObject, "schemaVersion");
}
return switch (schemaVersion) {
case 0 -> new FabricModJsonV0(jsonObject, source);
case 1 -> new FabricModJsonV1(jsonObject, source);
case 2 -> new FabricModJsonV2(jsonObject, source);
default -> throw new UnsupportedOperationException(String.format("This version of fabric-loom doesn't support the newer fabric.mod.json schema version of (%s) Please update fabric-loom to be able to read this.", schemaVersion));
};
}
public static FabricModJson createFromZip(Path zipPath) {
try {
return create(ZipUtils.unpackGson(zipPath, FABRIC_MOD_JSON, JsonObject.class), new FabricModJsonSource.ZipSource(zipPath));
} catch (IOException e) {
throw new UncheckedIOException("Failed to read fabric.mod.json file in zip: " + zipPath, e);
}
}
@Nullable
public static FabricModJson createFromZipNullable(Path zipPath) throws IOException {
JsonObject jsonObject = ZipUtils.unpackGsonNullable(zipPath, FABRIC_MOD_JSON, JsonObject.class);
if (jsonObject == null) {
return null;
}
return create(jsonObject, new FabricModJsonSource.ZipSource(zipPath));
}
public static FabricModJson createFromDirectory(Path directory) throws IOException {
final Path path = directory.resolve(FABRIC_MOD_JSON);
try (Reader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {
return create(LoomGradlePlugin.GSON.fromJson(reader, JsonObject.class), new FabricModJsonSource.DirectorySource(directory));
}
}
@Nullable
public static FabricModJson createFromSourceSetNullable(SourceSet sourceSet) throws IOException {
final File file = SourceSetHelper.findFileInResource(sourceSet, FABRIC_MOD_JSON);
if (file == null) {
return null;
}
try (Reader reader = Files.newBufferedReader(file.toPath(), StandardCharsets.UTF_8)) {
return create(LoomGradlePlugin.GSON.fromJson(reader, JsonObject.class), new FabricModJsonSource.SourceSetSource(sourceSet));
}
}
public static boolean isModJar(File file) {
return isModJar(file.toPath());
}
public static boolean isModJar(Path input) {
return ZipUtils.contains(input, FABRIC_MOD_JSON);
}
}

View File

@@ -0,0 +1,71 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2022 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.util.fmj;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import org.gradle.api.tasks.SourceSet;
import net.fabricmc.loom.util.ZipUtils;
import net.fabricmc.loom.util.gradle.SourceSetHelper;
/**
* A mod may be a zip, directory or Gradle {@link SourceSet}
* This abstraction allows easily reading a contained file from the mod.
*/
public interface FabricModJsonSource {
byte[] read(String path) throws IOException;
record ZipSource(Path zipPath) implements FabricModJsonSource {
@Override
public byte[] read(String path) throws IOException {
return ZipUtils.unpack(zipPath, path);
}
}
record DirectorySource(Path directoryPath) implements FabricModJsonSource {
@Override
public byte[] read(String path) throws IOException {
return Files.readAllBytes(directoryPath.resolve(path));
}
}
record SourceSetSource(SourceSet sourceSet) implements FabricModJsonSource {
@Override
public byte[] read(String path) throws IOException {
final File file = SourceSetHelper.findFileInResource(sourceSet, path);
if (file == null) {
throw new FileNotFoundException("Could not find: " + path);
}
return Files.readAllBytes(file.toPath());
}
}
}

View File

@@ -0,0 +1,73 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2022 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.util.fmj;
import java.util.Locale;
import java.util.function.Predicate;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
final class FabricModJsonUtils {
private FabricModJsonUtils() {
}
public static String readString(JsonObject jsonObject, String key) {
final JsonElement element = getElement(jsonObject, key);
ensurePrimitive(element, JsonPrimitive::isString, key);
return element.getAsString();
}
public static int readInt(JsonObject jsonObject, String key) {
final JsonElement element = getElement(jsonObject, key);
ensurePrimitive(element, JsonPrimitive::isNumber, key);
return element.getAsInt();
}
private static JsonElement getElement(JsonObject jsonObject, String key) {
final JsonElement element = jsonObject.get(key);
if (element == null) {
throw new ParseException("Unable to find json element for key (%s)", key);
}
return element;
}
private static void ensurePrimitive(JsonElement jsonElement, Predicate<JsonPrimitive> predicate, String key) {
if (!jsonElement.isJsonPrimitive() || !predicate.test(jsonElement.getAsJsonPrimitive())) {
throw new ParseException("Unexpected primitive type for key (%s)", key);
}
}
static class ParseException extends RuntimeException {
ParseException(String message, Object... args) {
super(String.format(Locale.ROOT, message, args));
}
}
}

View File

@@ -0,0 +1,89 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2022 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.util.fmj;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import org.jetbrains.annotations.Nullable;
@Deprecated
public final class FabricModJsonV0 extends FabricModJson {
FabricModJsonV0(JsonObject jsonObject, FabricModJsonSource source) {
super(jsonObject, source);
}
@Override
public int getVersion() {
return 0;
}
@Override
@Nullable
public JsonElement getCustom(String key) {
return null;
}
@Override
public List<String> getMixinConfigurations() {
final JsonObject mixinsObject = jsonObject.getAsJsonObject("mixins");
if (mixinsObject == null) {
return Collections.emptyList();
}
final List<String> mixins = new ArrayList<>();
for (String key : mixinsObject.keySet()) {
final JsonElement jsonElement = mixinsObject.get(key);
if (jsonElement instanceof JsonArray jsonArray) {
for (JsonElement arrayElement : jsonArray) {
if (arrayElement instanceof JsonPrimitive jsonPrimitive && jsonPrimitive.isString()) {
mixins.add(jsonPrimitive.getAsString());
} else {
throw new FabricModJsonUtils.ParseException("Expected entries in mixin %s to be an array of strings", key);
}
}
} else if (jsonElement instanceof JsonPrimitive jsonPrimitive && jsonPrimitive.isString()) {
mixins.add(jsonPrimitive.getAsString());
} else {
throw new FabricModJsonUtils.ParseException("Expected mixin %s to be a string or an array of strings", key);
}
}
return Collections.unmodifiableList(mixins);
}
@Override
public List<String> getClassTweakers(ModEnvironment modEnvironment) {
return Collections.emptyList();
}
}

View File

@@ -0,0 +1,101 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2022 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.util.fmj;
import static net.fabricmc.loom.util.fmj.FabricModJsonUtils.readString;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import org.jetbrains.annotations.Nullable;
public final class FabricModJsonV1 extends FabricModJson {
FabricModJsonV1(JsonObject jsonObject, FabricModJsonSource source) {
super(jsonObject, source);
}
@Override
public int getVersion() {
return 1;
}
@Override
@Nullable
public JsonElement getCustom(String key) {
return getCustom(jsonObject, key);
}
static JsonElement getCustom(JsonObject jsonObject, String key) {
if (!jsonObject.has("custom")) {
return null;
}
final JsonObject custom = jsonObject.getAsJsonObject("custom");
if (!custom.has(key)) {
return null;
}
return custom.get(key);
}
@Override
public List<String> getMixinConfigurations() {
final JsonArray mixinArray = jsonObject.getAsJsonArray("mixins");
if (mixinArray == null) {
return Collections.emptyList();
}
return StreamSupport.stream(mixinArray.spliterator(), false)
.map(FabricModJsonV1::readMixinElement)
.collect(Collectors.toList());
}
private static String readMixinElement(JsonElement jsonElement) {
if (jsonElement instanceof JsonPrimitive str) {
return str.getAsString();
} else if (jsonElement instanceof JsonObject obj) {
return obj.get("config").getAsString();
} else {
throw new FabricModJsonUtils.ParseException("Expected mixin element to be an object or string");
}
}
@Override
public List<String> getClassTweakers(ModEnvironment modEnvironment) {
if (!jsonObject.has("accessWidener")) {
return Collections.emptyList();
}
return List.of(readString(jsonObject, "accessWidener"));
}
}

View File

@@ -0,0 +1,133 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2022 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.util.fmj;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
@ApiStatus.Experimental
public final class FabricModJsonV2 extends FabricModJson {
FabricModJsonV2(JsonObject jsonObject, FabricModJsonSource source) {
super(jsonObject, source);
}
@Override
public int getVersion() {
return 2;
}
@Override
@Nullable
public JsonElement getCustom(String key) {
return FabricModJsonV1.getCustom(jsonObject, key);
}
@Override
public List<String> getMixinConfigurations() {
if (!jsonObject.has("mixins")) {
return Collections.emptyList();
}
return getConditionalConfigs(jsonObject.get("mixins"), ModEnvironment.UNIVERSAL);
}
@Override
public List<String> getClassTweakers(ModEnvironment modEnvironment) {
if (!jsonObject.has("classTweakers")) {
return Collections.emptyList();
}
return getConditionalConfigs(jsonObject.get("classTweakers"), modEnvironment);
}
private List<String> getConditionalConfigs(JsonElement jsonElement, ModEnvironment modEnvironment) {
final List<String> values = new ArrayList<>();
if (jsonElement instanceof JsonArray jsonArray) {
for (JsonElement arrayElement : jsonArray) {
final String value = readConditionalConfig(arrayElement, modEnvironment);
if (value != null) {
values.add(value);
}
}
} else if (jsonElement instanceof JsonPrimitive jsonPrimitive && jsonPrimitive.isString()) {
final String value = readConditionalConfig(jsonPrimitive, modEnvironment);
if (value != null) {
values.add(value);
}
} else {
throw new FabricModJsonUtils.ParseException("Must be a string or array of strings");
}
return values;
}
@Nullable
private String readConditionalConfig(JsonElement jsonElement, ModEnvironment modEnvironment) {
if (jsonElement instanceof JsonPrimitive jsonPrimitive && jsonPrimitive.isString()) {
return jsonElement.getAsString();
} else if (jsonElement instanceof JsonObject jsonObject) {
final String config = FabricModJsonUtils.readString(jsonObject, "config");
if (!validForEnvironment(jsonObject, modEnvironment)) {
return null;
}
return config;
} else {
throw new FabricModJsonUtils.ParseException("Must be a string or an object");
}
}
private boolean validForEnvironment(JsonObject jsonObject, ModEnvironment modEnvironment) {
if (!jsonObject.has("environment")) {
// Default enabled for all envs.
return true;
}
if (!(jsonObject.get("environment") instanceof JsonPrimitive jsonPrimitive) || !jsonPrimitive.isString()) {
throw new FabricModJsonUtils.ParseException("Environment must be a string");
}
final String environment = jsonPrimitive.getAsString();
return switch (environment) {
case "*" -> true;
case "client" -> modEnvironment.isClient();
case "server" -> modEnvironment.isServer();
default -> throw new FabricModJsonUtils.ParseException("Invalid environment type: " + environment);
};
}
}

View File

@@ -1,7 +1,7 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2016-2022 FabricMC
* Copyright (c) 2022 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
@@ -22,34 +22,26 @@
* SOFTWARE.
*/
package net.fabricmc.loom.util;
package net.fabricmc.loom.util.fmj;
import java.io.File;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Path;
public enum ModEnvironment {
UNIVERSAL(true, true),
CLIENT(true, false),
SERVER(false, true);
import com.google.gson.JsonObject;
import org.jetbrains.annotations.Nullable;
private final boolean client;
private final boolean server;
public final class ModUtils {
private ModUtils() {
ModEnvironment(boolean client, boolean server) {
this.client = client;
this.server = server;
}
public static boolean isMod(File file) {
return isMod(file.toPath());
public boolean isClient() {
return client;
}
public static boolean isMod(Path input) {
return ZipUtils.contains(input, "fabric.mod.json");
}
@Nullable
public static JsonObject getFabricModJson(Path path) {
try {
return ZipUtils.unpackGsonNullable(path, "fabric.mod.json", JsonObject.class);
} catch (IOException e) {
throw new UncheckedIOException("Failed to extract fabric.mod.json from " + path, e);
}
public boolean isServer() {
return server;
}
}

View File

@@ -272,4 +272,16 @@ public final class SourceSetHelper {
return Collections.singletonList(new File(binDir, reference.sourceSet().getName()));
}
@Nullable
public static File findFileInResource(SourceSet sourceSet, String path) {
try {
return sourceSet.getResources()
.matching(patternFilterable -> patternFilterable.include(path))
.getSingleFile();
} catch (IllegalStateException e) {
// File not found
return null;
}
}
}