mirror of
https://github.com/architectury/architectury-loom.git
synced 2026-03-28 04:07:01 -05:00
Debof spec context (#1424)
* Ahhhhh * Injected interfaces working * Access wideners working
This commit is contained in:
@@ -357,6 +357,8 @@ tasks.register('writeActionsTestMatrix') {
|
||||
|
||||
def className = it.name.replace(".groovy", "")
|
||||
testMatrix.add("net.fabricmc.loom.test.integration.${className}")
|
||||
} else if (it.name.endsWith("noRemap")) {
|
||||
testMatrix.add("net.fabricmc.loom.test.integration.noRemap.*")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,4 +42,6 @@ public interface ProcessorContext {
|
||||
LazyCloseable<TinyRemapper> createRemapper(MappingsNamespace from, MappingsNamespace to);
|
||||
|
||||
MemoryMappingTree getMappings();
|
||||
|
||||
boolean disableObfuscation();
|
||||
}
|
||||
|
||||
@@ -24,12 +24,20 @@
|
||||
|
||||
package net.fabricmc.loom.api.processor;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.plugins.JavaPlugin;
|
||||
|
||||
import net.fabricmc.loom.configuration.processors.speccontext.ProjectView;
|
||||
import net.fabricmc.loom.util.fmj.FabricModJson;
|
||||
|
||||
public interface SpecContext {
|
||||
/**
|
||||
* Returns a list of all the external mods that this project depends on regardless of configuration.
|
||||
*/
|
||||
List<FabricModJson> modDependencies();
|
||||
|
||||
List<FabricModJson> localMods();
|
||||
@@ -47,4 +55,22 @@ public interface SpecContext {
|
||||
default List<FabricModJson> allMods() {
|
||||
return Stream.concat(modDependencies().stream(), localMods().stream()).toList();
|
||||
}
|
||||
|
||||
// Returns all of the loom projects that are depended on in the main sourceset
|
||||
// TODO make project isolation aware
|
||||
static Stream<Project> getDependentProjects(ProjectView projectView) {
|
||||
final Stream<Project> runtimeProjects = projectView.getLoomProjectDependencies(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME);
|
||||
final Stream<Project> compileProjects = projectView.getLoomProjectDependencies(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME);
|
||||
|
||||
return Stream.concat(runtimeProjects, compileProjects)
|
||||
.distinct();
|
||||
}
|
||||
|
||||
// Sort to ensure stable caching
|
||||
static List<FabricModJson> distinctSorted(List<FabricModJson> mods) {
|
||||
return mods.stream()
|
||||
.distinct()
|
||||
.sorted(Comparator.comparing(FabricModJson::getId))
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,4 +45,6 @@ public interface AccessWidenerEntry {
|
||||
String getSortKey();
|
||||
|
||||
void read(ClassTweakerVisitor visitor, LazyCloseable<TinyRemapper> remapper) throws IOException;
|
||||
|
||||
void readOfficial(ClassTweakerVisitor visitor) throws IOException;
|
||||
}
|
||||
|
||||
@@ -129,18 +129,31 @@ public class AccessWidenerJarProcessor implements MinecraftJarProcessor<AccessWi
|
||||
|
||||
@Override
|
||||
public void processJar(Path jar, AccessWidenerJarProcessor.Spec spec, ProcessorContext context) throws IOException {
|
||||
ClassTweaker classTweaker = getClassTweaker(spec, context);
|
||||
AccessWidenerTransformer transformer = new AccessWidenerTransformer(classTweaker);
|
||||
transformer.apply(jar);
|
||||
}
|
||||
|
||||
private ClassTweaker getClassTweaker(AccessWidenerJarProcessor.Spec spec, ProcessorContext context) throws IOException {
|
||||
final List<AccessWidenerEntry> accessWideners = spec.accessWidenersForContext(context);
|
||||
|
||||
final var accessWidener = ClassTweaker.newInstance();
|
||||
final var classTweaker = ClassTweaker.newInstance();
|
||||
|
||||
if (context.disableObfuscation()) {
|
||||
for (AccessWidenerEntry widener : accessWideners) {
|
||||
widener.readOfficial(classTweaker);
|
||||
}
|
||||
|
||||
return classTweaker;
|
||||
}
|
||||
|
||||
try (LazyCloseable<TinyRemapper> remapper = context.createRemapper(MappingsNamespace.INTERMEDIARY, MappingsNamespace.NAMED)) {
|
||||
for (AccessWidenerEntry widener : accessWideners) {
|
||||
widener.read(accessWidener, remapper);
|
||||
widener.read(classTweaker, remapper);
|
||||
}
|
||||
}
|
||||
|
||||
AccessWidenerTransformer transformer = new AccessWidenerTransformer(accessWidener);
|
||||
transformer.apply(jar);
|
||||
return classTweaker;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -32,6 +32,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.fabricmc.classtweaker.api.ClassTweakerReader;
|
||||
import net.fabricmc.classtweaker.api.visitor.ClassTweakerVisitor;
|
||||
import net.fabricmc.loom.api.mappings.layered.MappingsNamespace;
|
||||
import net.fabricmc.loom.util.Checksum;
|
||||
import net.fabricmc.loom.util.LazyCloseable;
|
||||
import net.fabricmc.loom.util.fmj.ModEnvironment;
|
||||
@@ -48,6 +49,19 @@ public record LocalAccessWidenerEntry(Path path, String hash) implements AccessW
|
||||
reader.read(Files.readAllBytes(path), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readOfficial(ClassTweakerVisitor visitor) throws IOException {
|
||||
final byte[] data = Files.readAllBytes(path);
|
||||
final ClassTweakerReader.Header header = ClassTweakerReader.readHeader(data);
|
||||
|
||||
if (!header.getNamespace().equals(MappingsNamespace.OFFICIAL.toString())) {
|
||||
throw new IOException("Expected official namespace for access widener entry, found: " + header.getNamespace());
|
||||
}
|
||||
|
||||
var reader = ClassTweakerReader.create(visitor);
|
||||
reader.read(data, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModEnvironment environment() {
|
||||
return ModEnvironment.UNIVERSAL;
|
||||
|
||||
@@ -85,6 +85,24 @@ public record ModAccessWidenerEntry(FabricModJson mod, String path, ModEnvironme
|
||||
reader.read(data, mod.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readOfficial(ClassTweakerVisitor visitor) throws IOException {
|
||||
if (transitiveOnly) {
|
||||
// Filter for only transitive rules
|
||||
visitor = new TransitiveOnlyFilter(visitor);
|
||||
}
|
||||
|
||||
final byte[] data = readRaw();
|
||||
final ClassTweakerReader.Header header = ClassTweakerReader.readHeader(data);
|
||||
|
||||
if (!header.getNamespace().equals(MappingsNamespace.OFFICIAL.toString())) {
|
||||
throw new IOException("Expected official namespace for access widener entry, found: " + header.getNamespace());
|
||||
}
|
||||
|
||||
var reader = ClassTweakerReader.create(visitor);
|
||||
reader.read(data, mod.getId());
|
||||
}
|
||||
|
||||
private static ClassTweakerRemapperVisitor getRemapper(ClassTweakerVisitor visitor, TinyRemapper tinyRemapper) {
|
||||
return new ClassTweakerRemapperVisitor(
|
||||
visitor,
|
||||
|
||||
@@ -111,6 +111,20 @@ public abstract class InterfaceInjectionProcessor implements MinecraftJarProcess
|
||||
|
||||
@Override
|
||||
public void processJar(Path jar, Spec spec, ProcessorContext context) throws IOException {
|
||||
List<InjectedInterface> injectedInterfaces = getInjectedInterfaces(spec, context);
|
||||
|
||||
try {
|
||||
ZipUtils.transform(jar, getTransformers(injectedInterfaces));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to apply interface injections to " + jar, e);
|
||||
}
|
||||
}
|
||||
|
||||
private List<InjectedInterface> getInjectedInterfaces(Spec spec, ProcessorContext context) throws IOException {
|
||||
if (context.disableObfuscation()) {
|
||||
return spec.injectedInterfaces();
|
||||
}
|
||||
|
||||
// Remap from intermediary->named
|
||||
final MemoryMappingTree mappings = context.getMappings();
|
||||
final int intermediaryIndex = mappings.getNamespaceId(MappingsNamespace.INTERMEDIARY.toString());
|
||||
@@ -128,11 +142,7 @@ public abstract class InterfaceInjectionProcessor implements MinecraftJarProcess
|
||||
tinyRemapper.get().getEnvironment().getRemapper()
|
||||
))
|
||||
.toList();
|
||||
try {
|
||||
ZipUtils.transform(jar, getTransformers(remappedInjectedInterfaces));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to apply interface injections to " + jar, e);
|
||||
}
|
||||
return remappedInjectedInterfaces;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,8 @@ import net.fabricmc.loom.api.processor.MappingProcessorContext;
|
||||
import net.fabricmc.loom.api.processor.MinecraftJarProcessor;
|
||||
import net.fabricmc.loom.api.processor.ProcessorContext;
|
||||
import net.fabricmc.loom.api.processor.SpecContext;
|
||||
import net.fabricmc.loom.configuration.processors.speccontext.DeobfSpecContext;
|
||||
import net.fabricmc.loom.configuration.processors.speccontext.RemappedSpecContext;
|
||||
import net.fabricmc.loom.util.Checksum;
|
||||
import net.fabricmc.mappingio.tree.MemoryMappingTree;
|
||||
|
||||
@@ -69,9 +71,9 @@ public final class MinecraftJarProcessorManager {
|
||||
SpecContext specContext;
|
||||
|
||||
if (extension.disableObfuscation()) {
|
||||
specContext = SpecContextDebofImpl.create();
|
||||
specContext = DeobfSpecContext.create(project);
|
||||
} else {
|
||||
specContext = SpecContextRemappedImpl.create(project);
|
||||
specContext = RemappedSpecContext.create(project);
|
||||
}
|
||||
|
||||
return MinecraftJarProcessorManager.create(processors, specContext);
|
||||
|
||||
@@ -65,4 +65,9 @@ public record ProcessorContextImpl(ConfigContext configContext, MinecraftJar min
|
||||
LoomGradleExtension extension = LoomGradleExtension.get(configContext().project());
|
||||
return extension.getMappingConfiguration().getMappingsService(configContext().project(), configContext().serviceFactory()).getMappingTree();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean disableObfuscation() {
|
||||
return configContext().extension().disableObfuscation();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* This file is part of fabric-loom, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) 2025 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.configuration.processors.speccontext;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.artifacts.Configuration;
|
||||
import org.gradle.api.artifacts.ConfigurationContainer;
|
||||
import org.gradle.api.tasks.SourceSet;
|
||||
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.configuration.LoomConfigurations;
|
||||
import net.fabricmc.loom.util.Strings;
|
||||
import net.fabricmc.loom.util.gradle.SourceSetHelper;
|
||||
|
||||
/**
|
||||
* This mess is created out of the need to resolve the compile/runtime dependencies, without using the actual runtime/compile classpath.
|
||||
* As we need to add the minecraft jar to it later (based on the compile/runtime dependencies).
|
||||
*/
|
||||
public record DebofConfiguration(String name, List<Function<SourceSet, String>> configurationFunctions) {
|
||||
public static final DebofConfiguration COMPILE = new DebofConfiguration("compile", List.of(
|
||||
SourceSet::getImplementationConfigurationName,
|
||||
SourceSet::getApiConfigurationName,
|
||||
SourceSet::getCompileOnlyConfigurationName
|
||||
));
|
||||
public static final DebofConfiguration RUNTIME = new DebofConfiguration("runtime", List.of(
|
||||
SourceSet::getImplementationConfigurationName,
|
||||
SourceSet::getApiConfigurationName,
|
||||
SourceSet::getRuntimeOnlyConfigurationName
|
||||
));
|
||||
public static final List<DebofConfiguration> ALL = List.of(COMPILE, RUNTIME);
|
||||
|
||||
public Configuration getConfiguration(Project project, TargetSourceSet targetSourceSet) {
|
||||
return project.getConfigurations().getByName(resolveConfigurationName(targetSourceSet));
|
||||
}
|
||||
|
||||
public List<Configuration> getConfigurations(Project project) {
|
||||
ConfigurationContainer configurations = project.getConfigurations();
|
||||
return TargetSourceSet.applicable(project).stream()
|
||||
.map(this::resolveConfigurationName)
|
||||
.map(configurations::getByName)
|
||||
.toList();
|
||||
}
|
||||
|
||||
private String resolveConfigurationName(TargetSourceSet targetSourceSet) {
|
||||
return "%s%sDependencyResolve".formatted(targetSourceSet.name, Strings.capitalize(name()));
|
||||
}
|
||||
|
||||
public static void create(Project project) {
|
||||
for (DebofConfiguration debofConfiguration : ALL) {
|
||||
debofConfiguration.createResolveConfiguration(project);
|
||||
}
|
||||
}
|
||||
|
||||
public void createResolveConfiguration(Project project) {
|
||||
ConfigurationContainer configurations = project.getConfigurations();
|
||||
|
||||
for (TargetSourceSet target : TargetSourceSet.applicable(project)) {
|
||||
SourceSet sourceSet = target.getSourceSet(project);
|
||||
|
||||
configurations.register(resolveConfigurationName(target), c -> {
|
||||
LoomConfigurations.Role.RESOLVABLE.apply(c);
|
||||
|
||||
for (Function<SourceSet, String> configProvider : configurationFunctions()) {
|
||||
Configuration sourceConfig = configurations.getByName(configProvider.apply(sourceSet));
|
||||
c.extendsFrom(sourceConfig);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public enum TargetSourceSet {
|
||||
MAIN(SourceSet.MAIN_SOURCE_SET_NAME, e -> Boolean.TRUE),
|
||||
CLIENT("client", LoomGradleExtension::areEnvironmentSourceSetsSplit);
|
||||
|
||||
private final String name;
|
||||
private final Function<LoomGradleExtension, Boolean> enabled;
|
||||
|
||||
TargetSourceSet(String name, Function<LoomGradleExtension, Boolean> enabled) {
|
||||
this.name = name;
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public SourceSet getSourceSet(Project project) {
|
||||
return SourceSetHelper.getSourceSetByName(this.name, project);
|
||||
}
|
||||
|
||||
public static List<TargetSourceSet> applicable(Project project) {
|
||||
LoomGradleExtension extension = LoomGradleExtension.get(project);
|
||||
return Arrays.stream(values())
|
||||
.filter(targetSourceSet -> targetSourceSet.enabled.apply(extension))
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,27 +22,39 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package net.fabricmc.loom.configuration.processors;
|
||||
package net.fabricmc.loom.configuration.processors.speccontext;
|
||||
|
||||
import java.util.List;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.artifacts.Configuration;
|
||||
import org.gradle.api.file.ConfigurableFileCollection;
|
||||
import org.gradle.api.file.FileCollection;
|
||||
|
||||
import net.fabricmc.loom.api.processor.SpecContext;
|
||||
import net.fabricmc.loom.util.fmj.FabricModJson;
|
||||
public interface DeobfProjectView extends ProjectView {
|
||||
FileCollection getDependencies(DebofConfiguration debofConfiguration, DebofConfiguration.TargetSourceSet targetSourceSet);
|
||||
|
||||
// TODO debof - fixme
|
||||
public record SpecContextDebofImpl(List<FabricModJson> modDependencies,
|
||||
List<FabricModJson> localMods) implements SpecContext {
|
||||
public static SpecContext create() {
|
||||
return new SpecContextDebofImpl(List.of(), List.of());
|
||||
}
|
||||
FileCollection getFullClasspath();
|
||||
|
||||
@Override
|
||||
public List<FabricModJson> modDependenciesCompileRuntime() {
|
||||
return List.of();
|
||||
}
|
||||
class Impl extends AbstractProjectView implements DeobfProjectView {
|
||||
protected Impl(Project project) {
|
||||
super(project);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FabricModJson> modDependenciesCompileRuntimeClient() {
|
||||
return List.of();
|
||||
@Override
|
||||
public FileCollection getDependencies(DebofConfiguration debofConfiguration, DebofConfiguration.TargetSourceSet targetSourceSet) {
|
||||
return debofConfiguration.getConfiguration(project, targetSourceSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileCollection getFullClasspath() {
|
||||
ConfigurableFileCollection classpath = project.files();
|
||||
|
||||
for (DebofConfiguration debofConfiguration : DebofConfiguration.ALL) {
|
||||
for (Configuration configuration : debofConfiguration.getConfigurations(project)) {
|
||||
classpath.from(configuration);
|
||||
}
|
||||
}
|
||||
|
||||
return classpath;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* This file is part of fabric-loom, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) 2025 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.configuration.processors.speccontext;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.file.FileCollection;
|
||||
|
||||
import net.fabricmc.loom.api.processor.SpecContext;
|
||||
import net.fabricmc.loom.util.AsyncCache;
|
||||
import net.fabricmc.loom.util.fmj.FabricModJson;
|
||||
import net.fabricmc.loom.util.fmj.FabricModJsonFactory;
|
||||
import net.fabricmc.loom.util.fmj.FabricModJsonHelpers;
|
||||
|
||||
public record DeobfSpecContext(List<FabricModJson> modDependencies,
|
||||
List<FabricModJson> localMods,
|
||||
// Mods that are in the following configurations: [runtimeClasspath, compileClasspath] or [runtimeClientClasspath, compileClientClasspath]
|
||||
// These are mods that will be used to transform both the client and server jars
|
||||
List<FabricModJson> modDependenciesCompileRuntime,
|
||||
// Here we want mods that are ONLY in [runtimeClientClasspath, compileClientClasspath] and not [runtimeClasspath, compileClasspath]
|
||||
// These mods will be excluded from transforming the server jar
|
||||
List<FabricModJson> modDependenciesCompileRuntimeClient
|
||||
) implements SpecContext {
|
||||
public static DeobfSpecContext create(Project project) {
|
||||
DebofConfiguration.create(project);
|
||||
|
||||
return create(new DeobfProjectView.Impl(project));
|
||||
}
|
||||
|
||||
public static DeobfSpecContext create(DeobfProjectView projectView) {
|
||||
AsyncCache<List<FabricModJson>> fmjCache = new AsyncCache<>();
|
||||
List<FabricModJson> dependentMods = getDependentMods(projectView, fmjCache);
|
||||
Map<String, FabricModJson> mods = dependentMods.stream()
|
||||
.collect(HashMap::new, (map, mod) -> map.put(mod.getId(), mod), Map::putAll);
|
||||
|
||||
FileCollection mainRuntimeClasspath = projectView.getDependencies(DebofConfiguration.RUNTIME, DebofConfiguration.TargetSourceSet.MAIN);
|
||||
FileCollection mainCompileClasspath = projectView.getDependencies(DebofConfiguration.COMPILE, DebofConfiguration.TargetSourceSet.MAIN);
|
||||
|
||||
// All mods in both [runtimeClasspath, compileClasspath]
|
||||
Set<String> mainTransformingModIds = common(
|
||||
getModIds(mainRuntimeClasspath, fmjCache),
|
||||
getModIds(mainCompileClasspath, fmjCache)
|
||||
);
|
||||
|
||||
// All mods in both [runtimeClientClasspath, compileClientClasspath]
|
||||
Set<String> clientTransformingModIds;
|
||||
|
||||
if (projectView.areEnvironmentSourceSetsSplit()) {
|
||||
FileCollection clientRuntimeClasspath = projectView.getDependencies(DebofConfiguration.RUNTIME, DebofConfiguration.TargetSourceSet.CLIENT);
|
||||
FileCollection clientCompileClasspath = projectView.getDependencies(DebofConfiguration.COMPILE, DebofConfiguration.TargetSourceSet.CLIENT);
|
||||
|
||||
clientTransformingModIds = common(
|
||||
getModIds(clientRuntimeClasspath, fmjCache),
|
||||
getModIds(clientCompileClasspath, fmjCache)
|
||||
);
|
||||
} else {
|
||||
clientTransformingModIds = Set.of();
|
||||
}
|
||||
|
||||
return new DeobfSpecContext(
|
||||
dependentMods,
|
||||
projectView.getMods(),
|
||||
getMods(mods, combine(mainTransformingModIds, clientTransformingModIds)),
|
||||
getMods(mods, onlyInLeft(clientTransformingModIds, mainTransformingModIds))
|
||||
);
|
||||
}
|
||||
|
||||
// Returns a list of all the mods that the current project depends on
|
||||
private static List<FabricModJson> getDependentMods(DeobfProjectView projectView, AsyncCache<List<FabricModJson>> fmjCache) {
|
||||
var futures = new ArrayList<CompletableFuture<List<FabricModJson>>>();
|
||||
Set<File> artifacts = projectView.getFullClasspath().getFiles();
|
||||
|
||||
for (File artifact : artifacts) {
|
||||
futures.add(fmjCache.get(artifact.toPath().toAbsolutePath().toString(), () -> {
|
||||
return FabricModJsonFactory.createFromZipOptional(artifact.toPath())
|
||||
.map(List::of)
|
||||
.orElseGet(List::of);
|
||||
}));
|
||||
}
|
||||
|
||||
if (!projectView.disableProjectDependantMods()) {
|
||||
// Add all the dependent projects
|
||||
for (Project dependentProject : SpecContext.getDependentProjects(projectView).toList()) {
|
||||
futures.add(fmjCache.get(dependentProject.getPath(), () -> FabricModJsonHelpers.getModsInProject(dependentProject)));
|
||||
}
|
||||
}
|
||||
|
||||
return SpecContext.distinctSorted(AsyncCache.joinList(futures));
|
||||
}
|
||||
|
||||
// Returns a list of mod ids in a given configuration
|
||||
private static Set<String> getModIds(FileCollection configuration, AsyncCache<List<FabricModJson>> fmjCache) {
|
||||
var futures = new ArrayList<CompletableFuture<List<FabricModJson>>>();
|
||||
|
||||
Set<File> artifacts = configuration.getFiles();
|
||||
|
||||
for (File artifact : artifacts) {
|
||||
futures.add(fmjCache.get(artifact.toPath().toAbsolutePath().toString(), () -> {
|
||||
return FabricModJsonFactory.createFromZipOptional(artifact.toPath())
|
||||
.map(List::of)
|
||||
.orElseGet(List::of);
|
||||
}));
|
||||
}
|
||||
|
||||
return SpecContext.distinctSorted(AsyncCache.joinList(futures)).stream()
|
||||
.map(FabricModJson::getId)
|
||||
.collect(HashSet::new, Set::add, Set::addAll);
|
||||
}
|
||||
|
||||
private static List<FabricModJson> getMods(Map<String, FabricModJson> mods, Set<String> ids) {
|
||||
List<FabricModJson> result = new ArrayList<>();
|
||||
|
||||
for (String id : ids) {
|
||||
result.add(Objects.requireNonNull(mods.get(id), "Could not find mod with id: " + id));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Set<String> common(Set<String> a, Set<String> b) {
|
||||
Set<String> copy = new HashSet<>(a);
|
||||
copy.retainAll(b);
|
||||
return copy;
|
||||
}
|
||||
|
||||
private static Set<String> combine(Set<String> a, Set<String> b) {
|
||||
Set<String> copy = new HashSet<>(a);
|
||||
copy.addAll(b);
|
||||
return copy;
|
||||
}
|
||||
|
||||
private static Set<String> onlyInLeft(Set<String> left, Set<String> right) {
|
||||
Set<String> copy = new HashSet<>(left);
|
||||
copy.removeAll(right);
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
@@ -22,12 +22,9 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package net.fabricmc.loom.configuration.processors;
|
||||
package net.fabricmc.loom.configuration.processors.speccontext;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.gradle.api.Project;
|
||||
@@ -36,25 +33,23 @@ import org.gradle.api.artifacts.ProjectDependency;
|
||||
import org.gradle.api.attributes.Usage;
|
||||
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.api.RemapConfigurationSettings;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
import net.fabricmc.loom.util.fmj.FabricModJson;
|
||||
import net.fabricmc.loom.util.fmj.FabricModJsonHelpers;
|
||||
import net.fabricmc.loom.util.gradle.GradleUtils;
|
||||
|
||||
// Used to abstract out the Gradle API usage to ease unit testing.
|
||||
public interface SpecContextProjectView {
|
||||
LoomGradleExtension extension();
|
||||
|
||||
public interface ProjectView {
|
||||
// Returns a list of Loom Projects found in the specified Configuration
|
||||
Stream<Project> getLoomProjectDependencies(String name);
|
||||
|
||||
Function<RemapConfigurationSettings, Stream<Path>> resolveArtifacts(ArtifactUsage artifactUsage);
|
||||
|
||||
// Returns the mods defined in the current project
|
||||
List<FabricModJson> getMods();
|
||||
|
||||
boolean disableProjectDependantMods();
|
||||
|
||||
boolean areEnvironmentSourceSetsSplit();
|
||||
|
||||
enum ArtifactUsage {
|
||||
RUNTIME(Usage.JAVA_RUNTIME),
|
||||
COMPILE(Usage.JAVA_API);
|
||||
@@ -64,9 +59,21 @@ public interface SpecContextProjectView {
|
||||
ArtifactUsage(String gradleUsage) {
|
||||
this.gradleUsage = gradleUsage;
|
||||
}
|
||||
|
||||
public String getGradleUsage() {
|
||||
return gradleUsage;
|
||||
}
|
||||
}
|
||||
|
||||
record Impl(Project project, LoomGradleExtension extension) implements SpecContextProjectView {
|
||||
abstract class AbstractProjectView implements ProjectView {
|
||||
protected final Project project;
|
||||
protected final LoomGradleExtension extension;
|
||||
|
||||
protected AbstractProjectView(Project project) {
|
||||
this.project = project;
|
||||
this.extension = LoomGradleExtension.get(project);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Project> getLoomProjectDependencies(String name) {
|
||||
final Configuration configuration = project.getConfigurations().getByName(name);
|
||||
@@ -77,18 +84,6 @@ public interface SpecContextProjectView {
|
||||
.filter(GradleUtils::isLoomProject);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Function<RemapConfigurationSettings, Stream<Path>> resolveArtifacts(ArtifactUsage artifactUsage) {
|
||||
final Usage usage = project.getObjects().named(Usage.class, artifactUsage.gradleUsage);
|
||||
|
||||
return settings -> {
|
||||
final Configuration configuration = settings.getSourceConfiguration().get().copyRecursive();
|
||||
configuration.setCanBeConsumed(false);
|
||||
configuration.attributes(attributes -> attributes.attribute(Usage.USAGE_ATTRIBUTE, usage));
|
||||
return configuration.resolve().stream().map(File::toPath);
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FabricModJson> getMods() {
|
||||
return FabricModJsonHelpers.getModsInProject(project);
|
||||
@@ -101,5 +96,10 @@ public interface SpecContextProjectView {
|
||||
return extension.isProjectIsolationActive()
|
||||
|| GradleUtils.getBooleanProperty(project, Constants.Properties.DISABLE_PROJECT_DEPENDENT_MODS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areEnvironmentSourceSetsSplit() {
|
||||
return extension.areEnvironmentSourceSetsSplit();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* This file is part of fabric-loom, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) 2025 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.configuration.processors.speccontext;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.gradle.api.NamedDomainObjectList;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.artifacts.Configuration;
|
||||
import org.gradle.api.attributes.Usage;
|
||||
|
||||
import net.fabricmc.loom.api.RemapConfigurationSettings;
|
||||
|
||||
public interface RemappedProjectView extends ProjectView {
|
||||
Function<RemapConfigurationSettings, Stream<Path>> resolveArtifacts(ArtifactUsage artifactUsage);
|
||||
|
||||
NamedDomainObjectList<RemapConfigurationSettings> getRemapConfigurations();
|
||||
|
||||
List<RemapConfigurationSettings> getCompileRemapConfigurations();
|
||||
|
||||
List<RemapConfigurationSettings> getRuntimeRemapConfigurations();
|
||||
|
||||
class Impl extends AbstractProjectView implements RemappedProjectView {
|
||||
public Impl(Project project) {
|
||||
super(project);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Function<RemapConfigurationSettings, Stream<Path>> resolveArtifacts(ArtifactUsage artifactUsage) {
|
||||
final Usage usage = project.getObjects().named(Usage.class, artifactUsage.getGradleUsage());
|
||||
|
||||
return settings -> {
|
||||
final Configuration configuration = settings.getSourceConfiguration().get().copyRecursive();
|
||||
configuration.setCanBeConsumed(false);
|
||||
configuration.attributes(attributes -> attributes.attribute(Usage.USAGE_ATTRIBUTE, usage));
|
||||
return configuration.resolve().stream().map(File::toPath);
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public NamedDomainObjectList<RemapConfigurationSettings> getRemapConfigurations() {
|
||||
return extension.getRemapConfigurations();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RemapConfigurationSettings> getCompileRemapConfigurations() {
|
||||
return extension.getCompileRemapConfigurations();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RemapConfigurationSettings> getRuntimeRemapConfigurations() {
|
||||
return extension.getRuntimeRemapConfigurations();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package net.fabricmc.loom.configuration.processors;
|
||||
package net.fabricmc.loom.configuration.processors.speccontext;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
@@ -42,7 +42,6 @@ import org.gradle.api.plugins.JavaPlugin;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.VisibleForTesting;
|
||||
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.api.RemapConfigurationSettings;
|
||||
import net.fabricmc.loom.api.processor.SpecContext;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftSourceSets;
|
||||
@@ -56,29 +55,29 @@ import net.fabricmc.loom.util.fmj.FabricModJsonHelpers;
|
||||
* @param localMods Mods found in the current project.
|
||||
* @param compileRuntimeMods Dependent mods found in both the compile and runtime classpath.
|
||||
*/
|
||||
public record SpecContextRemappedImpl(
|
||||
public record RemappedSpecContext(
|
||||
List<FabricModJson> modDependencies,
|
||||
List<FabricModJson> localMods,
|
||||
List<ModHolder> compileRuntimeMods) implements SpecContext {
|
||||
public static SpecContextRemappedImpl create(Project project) {
|
||||
return create(new SpecContextProjectView.Impl(project, LoomGradleExtension.get(project)));
|
||||
public static RemappedSpecContext create(Project project) {
|
||||
return create(new RemappedProjectView.Impl(project));
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static SpecContextRemappedImpl create(SpecContextProjectView projectView) {
|
||||
public static RemappedSpecContext create(RemappedProjectView projectView) {
|
||||
AsyncCache<List<FabricModJson>> fmjCache = new AsyncCache<>();
|
||||
return new SpecContextRemappedImpl(
|
||||
return new RemappedSpecContext(
|
||||
getDependentMods(projectView, fmjCache),
|
||||
projectView.getMods(),
|
||||
getCompileRuntimeMods(projectView, fmjCache)
|
||||
);
|
||||
}
|
||||
|
||||
// Reruns a list of mods found on both the compile and/or runtime classpaths
|
||||
private static List<FabricModJson> getDependentMods(SpecContextProjectView projectView, AsyncCache<List<FabricModJson>> fmjCache) {
|
||||
// Returns a list of mods found on both the compile and/or runtime classpaths
|
||||
private static List<FabricModJson> getDependentMods(RemappedProjectView projectView, AsyncCache<List<FabricModJson>> fmjCache) {
|
||||
var futures = new ArrayList<CompletableFuture<List<FabricModJson>>>();
|
||||
|
||||
for (RemapConfigurationSettings entry : projectView.extension().getRemapConfigurations()) {
|
||||
for (RemapConfigurationSettings entry : projectView.getRemapConfigurations()) {
|
||||
final Set<File> artifacts = entry.getSourceConfiguration().get().resolve();
|
||||
|
||||
for (File artifact : artifacts) {
|
||||
@@ -92,24 +91,16 @@ public record SpecContextRemappedImpl(
|
||||
|
||||
if (!projectView.disableProjectDependantMods()) {
|
||||
// Add all the dependent projects
|
||||
for (Project dependentProject : getDependentProjects(projectView).toList()) {
|
||||
for (Project dependentProject : SpecContext.getDependentProjects(projectView).toList()) {
|
||||
futures.add(fmjCache.get(dependentProject.getPath(), () -> FabricModJsonHelpers.getModsInProject(dependentProject)));
|
||||
}
|
||||
}
|
||||
|
||||
return distinctSorted(AsyncCache.joinList(futures));
|
||||
}
|
||||
|
||||
private static Stream<Project> getDependentProjects(SpecContextProjectView projectView) {
|
||||
final Stream<Project> runtimeProjects = projectView.getLoomProjectDependencies(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME);
|
||||
final Stream<Project> compileProjects = projectView.getLoomProjectDependencies(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME);
|
||||
|
||||
return Stream.concat(runtimeProjects, compileProjects)
|
||||
.distinct();
|
||||
return SpecContext.distinctSorted(AsyncCache.joinList(futures));
|
||||
}
|
||||
|
||||
// Returns a list of mods that are on both to compile and runtime classpath
|
||||
private static List<ModHolder> getCompileRuntimeMods(SpecContextProjectView projectView, AsyncCache<List<FabricModJson>> fmjCache) {
|
||||
private static List<ModHolder> getCompileRuntimeMods(RemappedProjectView projectView, AsyncCache<List<FabricModJson>> fmjCache) {
|
||||
var mods = new ArrayList<>(getCompileRuntimeModsFromRemapConfigs(projectView, fmjCache));
|
||||
|
||||
for (Project dependentProject : getCompileRuntimeProjectDependencies(projectView).toList()) {
|
||||
@@ -126,32 +117,32 @@ public record SpecContextRemappedImpl(
|
||||
}
|
||||
|
||||
// Returns a list of jar mods that are found on the compile and runtime remapping configurations
|
||||
private static List<ModHolder> getCompileRuntimeModsFromRemapConfigs(SpecContextProjectView projectView, AsyncCache<List<FabricModJson>> fmjCache) {
|
||||
private static List<ModHolder> getCompileRuntimeModsFromRemapConfigs(RemappedProjectView projectView, AsyncCache<List<FabricModJson>> fmjCache) {
|
||||
// A set of mod ids from all remap configurations that are considered for dependency transforms.
|
||||
final Set<String> runtimeModIds = getModIds(
|
||||
SpecContextProjectView.ArtifactUsage.RUNTIME,
|
||||
ProjectView.ArtifactUsage.RUNTIME,
|
||||
projectView,
|
||||
fmjCache,
|
||||
projectView.extension().getRuntimeRemapConfigurations().stream()
|
||||
projectView.getRuntimeRemapConfigurations().stream()
|
||||
.filter(settings -> settings.getApplyDependencyTransforms().get())
|
||||
);
|
||||
|
||||
// A set of mod ids that are found on one or more remap configurations that target the common source set.
|
||||
// Null when split source sets are not enabled, meaning all mods are common.
|
||||
final Set<String> commonRuntimeModIds = projectView.extension().areEnvironmentSourceSetsSplit() ? getModIds(
|
||||
SpecContextProjectView.ArtifactUsage.RUNTIME,
|
||||
final Set<String> commonRuntimeModIds = projectView.areEnvironmentSourceSetsSplit() ? getModIds(
|
||||
ProjectView.ArtifactUsage.RUNTIME,
|
||||
projectView,
|
||||
fmjCache,
|
||||
projectView.extension().getRuntimeRemapConfigurations().stream()
|
||||
projectView.getRuntimeRemapConfigurations().stream()
|
||||
.filter(settings -> settings.getSourceSet().map(sourceSet -> !sourceSet.getName().equals(MinecraftSourceSets.Split.CLIENT_ONLY_SOURCE_SET_NAME)).get())
|
||||
.filter(settings -> settings.getApplyDependencyTransforms().get()))
|
||||
: null;
|
||||
|
||||
Stream<FabricModJson> compileMods = getMods(
|
||||
SpecContextProjectView.ArtifactUsage.COMPILE,
|
||||
ProjectView.ArtifactUsage.COMPILE,
|
||||
projectView,
|
||||
fmjCache,
|
||||
projectView.extension().getCompileRemapConfigurations().stream()
|
||||
projectView.getCompileRemapConfigurations().stream()
|
||||
.filter(settings -> settings.getApplyDependencyTransforms().get()));
|
||||
|
||||
return compileMods
|
||||
@@ -164,13 +155,13 @@ public record SpecContextRemappedImpl(
|
||||
.toList();
|
||||
}
|
||||
|
||||
private static Stream<FabricModJson> getMods(SpecContextProjectView.ArtifactUsage artifactUsage, SpecContextProjectView projectView, AsyncCache<List<FabricModJson>> fmjCache, Stream<RemapConfigurationSettings> stream) {
|
||||
private static Stream<FabricModJson> getMods(ProjectView.ArtifactUsage artifactUsage, RemappedProjectView projectView, AsyncCache<List<FabricModJson>> fmjCache, Stream<RemapConfigurationSettings> stream) {
|
||||
return stream.flatMap(projectView.resolveArtifacts(artifactUsage))
|
||||
.map(modFromZip(fmjCache))
|
||||
.filter(Objects::nonNull);
|
||||
}
|
||||
|
||||
private static Set<String> getModIds(SpecContextProjectView.ArtifactUsage artifactUsage, SpecContextProjectView projectView, AsyncCache<List<FabricModJson>> fmjCache, Stream<RemapConfigurationSettings> stream) {
|
||||
private static Set<String> getModIds(ProjectView.ArtifactUsage artifactUsage, RemappedProjectView projectView, AsyncCache<List<FabricModJson>> fmjCache, Stream<RemapConfigurationSettings> stream) {
|
||||
return getMods(artifactUsage, projectView, fmjCache, stream)
|
||||
.map(FabricModJson::getId)
|
||||
.collect(Collectors.toSet());
|
||||
@@ -188,7 +179,7 @@ public record SpecContextRemappedImpl(
|
||||
}
|
||||
|
||||
// Returns a list of Loom Projects found in both the runtime and compile classpath
|
||||
private static Stream<Project> getCompileRuntimeProjectDependencies(SpecContextProjectView projectView) {
|
||||
private static Stream<Project> getCompileRuntimeProjectDependencies(ProjectView projectView) {
|
||||
if (projectView.disableProjectDependantMods()) {
|
||||
return Stream.empty();
|
||||
}
|
||||
@@ -200,14 +191,6 @@ public record SpecContextRemappedImpl(
|
||||
.filter(compileProjects::contains); // Use the intersection of the two configurations.
|
||||
}
|
||||
|
||||
// Sort to ensure stable caching
|
||||
private static List<FabricModJson> distinctSorted(List<FabricModJson> mods) {
|
||||
return mods.stream()
|
||||
.distinct()
|
||||
.sorted(Comparator.comparing(FabricModJson::getId))
|
||||
.toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FabricModJson> modDependenciesCompileRuntime() {
|
||||
return compileRuntimeMods.stream()
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* This file is part of fabric-loom, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) 2025 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.noRemap
|
||||
|
||||
import java.nio.file.Path
|
||||
|
||||
import org.intellij.lang.annotations.Language
|
||||
import spock.lang.Specification
|
||||
import spock.lang.TempDir
|
||||
import spock.lang.Unroll
|
||||
|
||||
import net.fabricmc.loom.test.util.GradleProjectTestTrait
|
||||
import net.fabricmc.loom.test.util.MockMavenServerTrait
|
||||
import net.fabricmc.loom.util.ZipUtils
|
||||
|
||||
import static net.fabricmc.loom.test.LoomTestConstants.PRE_RELEASE_GRADLE
|
||||
import static org.gradle.testkit.runner.TaskOutcome.SUCCESS
|
||||
|
||||
class DebofDependenciesTest extends Specification implements GradleProjectTestTrait, MockMavenServerTrait {
|
||||
@TempDir
|
||||
Path tempDir
|
||||
|
||||
@Unroll
|
||||
def "apply interface injection"() {
|
||||
setup:
|
||||
def dep = tempDir.resolve("mod.jar")
|
||||
ZipUtils.add(dep, "fabric.mod.json", FMJ)
|
||||
ZipUtils.add(dep, "test.accesswidener", AW)
|
||||
|
||||
mavenHelper("loom.test", "test", "1.0.0").copyToMaven(dep, null)
|
||||
|
||||
def gradle = gradleProject(project: "minimalBaseNoRemap", version: PRE_RELEASE_GRADLE)
|
||||
gradle.buildGradle << repositoriesBlock
|
||||
gradle.buildGradle << '''
|
||||
dependencies {
|
||||
minecraft 'com.mojang:minecraft:25w45a_unobfuscated'
|
||||
implementation 'loom.test:test:1.0.0'
|
||||
}
|
||||
'''
|
||||
def pkg = new File(gradle.projectDir, "src/main/java/example/")
|
||||
pkg.mkdirs()
|
||||
new File(pkg, "Test.java").text = Test
|
||||
new File(pkg, "InjectedInterface.java").text = InjectedInterface
|
||||
|
||||
when:
|
||||
def result = gradle.run(task: "build")
|
||||
|
||||
then:
|
||||
result.task(":build").outcome == SUCCESS
|
||||
}
|
||||
|
||||
@Language("JSON")
|
||||
private static final String FMJ = """
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "testmod",
|
||||
"version": "1",
|
||||
"name": "Test Mod",
|
||||
"accessWidener": "test.accesswidener",
|
||||
"custom": {
|
||||
"loom:injected_interfaces": {
|
||||
"net/minecraft/resources/Identifier": ["example/InjectedInterface"]
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
@Language("Access Widener")
|
||||
private static final String AW = """
|
||||
accessWidener\tv2\tofficial
|
||||
transitive-accessible field net/minecraft/resources/Identifier path Ljava/lang/String;
|
||||
""".stripIndent().trim()
|
||||
|
||||
@Language("JAVA")
|
||||
private static final String Test = """
|
||||
package example;
|
||||
|
||||
import net.minecraft.resources.Identifier;
|
||||
|
||||
public class Test {
|
||||
public static void main(String[] args) {
|
||||
Identifier id = Identifier.fromNamespaceAndPath("loom", "test");
|
||||
id.testCompiles(); // Test iface injection
|
||||
String path = id.path; // Test AW
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
@Language("JAVA")
|
||||
private static final String InjectedInterface = """
|
||||
package example;
|
||||
|
||||
public interface InjectedInterface {
|
||||
default void testCompiles() {
|
||||
}
|
||||
}
|
||||
"""
|
||||
}
|
||||
@@ -22,7 +22,7 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package net.fabricmc.loom.test.integration
|
||||
package net.fabricmc.loom.test.integration.noRemap
|
||||
|
||||
import org.intellij.lang.annotations.Language
|
||||
import spock.lang.Specification
|
||||
@@ -33,9 +33,9 @@ import net.fabricmc.loom.test.util.GradleProjectTestTrait
|
||||
import static net.fabricmc.loom.test.LoomTestConstants.PRE_RELEASE_GRADLE
|
||||
import static org.gradle.testkit.runner.TaskOutcome.SUCCESS
|
||||
|
||||
class NotObfuscatedTest extends Specification implements GradleProjectTestTrait {
|
||||
class SimpleDebofTest extends Specification implements GradleProjectTestTrait {
|
||||
@Unroll
|
||||
def "Not Obfuscated"() {
|
||||
def "build"() {
|
||||
setup:
|
||||
def gradle = gradleProject(project: "minimalBaseNoRemap", version: PRE_RELEASE_GRADLE)
|
||||
gradle.buildGradle << '''
|
||||
@@ -36,11 +36,11 @@ import org.gradle.api.artifacts.Configuration
|
||||
import spock.lang.Specification
|
||||
import spock.lang.TempDir
|
||||
|
||||
import net.fabricmc.loom.LoomGradleExtension
|
||||
import net.fabricmc.loom.api.RemapConfigurationSettings
|
||||
import net.fabricmc.loom.api.fmj.FabricModJsonV1Spec
|
||||
import net.fabricmc.loom.configuration.processors.SpecContextProjectView
|
||||
import net.fabricmc.loom.configuration.processors.SpecContextRemappedImpl
|
||||
import net.fabricmc.loom.configuration.processors.speccontext.ProjectView
|
||||
import net.fabricmc.loom.configuration.processors.speccontext.RemappedProjectView
|
||||
import net.fabricmc.loom.configuration.processors.speccontext.RemappedSpecContext
|
||||
import net.fabricmc.loom.test.util.GradleTestUtil
|
||||
import net.fabricmc.loom.util.ZipUtils
|
||||
import net.fabricmc.loom.util.fmj.gen.FabricModJsonV1Generator
|
||||
@@ -54,8 +54,7 @@ class SpecContextTest extends Specification {
|
||||
Path tempDir
|
||||
|
||||
Project project
|
||||
LoomGradleExtension extension
|
||||
SpecContextProjectView projectView
|
||||
RemappedProjectView projectView
|
||||
NamedDomainObjectList<RemapConfigurationSettings> remapConfigurations
|
||||
|
||||
RemapConfigurationSettings implementation
|
||||
@@ -67,14 +66,12 @@ class SpecContextTest extends Specification {
|
||||
|
||||
void setup() {
|
||||
project = GradleTestUtil.mockProject()
|
||||
extension = LoomGradleExtension.get(project)
|
||||
projectView = mock(SpecContextProjectView.class)
|
||||
projectView = mock(RemappedProjectView.class)
|
||||
remapConfigurations = project.getObjects().namedDomainObjectList(RemapConfigurationSettings.class)
|
||||
|
||||
when(projectView.extension()).thenReturn(extension)
|
||||
when(extension.getRemapConfigurations()).thenReturn(remapConfigurations)
|
||||
when(projectView.resolveArtifacts(SpecContextProjectView.ArtifactUsage.RUNTIME)).thenReturn(resolve(runtimeArtifacts))
|
||||
when(projectView.resolveArtifacts(SpecContextProjectView.ArtifactUsage.COMPILE)).thenReturn(resolve(apiArtifacts))
|
||||
when(projectView.getRemapConfigurations()).thenReturn(remapConfigurations)
|
||||
when(projectView.resolveArtifacts(ProjectView.ArtifactUsage.RUNTIME)).thenReturn(resolve(runtimeArtifacts))
|
||||
when(projectView.resolveArtifacts(ProjectView.ArtifactUsage.COMPILE)).thenReturn(resolve(apiArtifacts))
|
||||
|
||||
implementation = createConfigurationSettings("implementation")
|
||||
runtimeOnly = createConfigurationSettings("runtimeOnly")
|
||||
@@ -85,8 +82,8 @@ class SpecContextTest extends Specification {
|
||||
compileOnly
|
||||
])
|
||||
|
||||
when(extension.getCompileRemapConfigurations()).thenReturn([implementation, compileOnly])
|
||||
when(extension.getRuntimeRemapConfigurations()).thenReturn([implementation, runtimeOnly])
|
||||
when(projectView.getCompileRemapConfigurations()).thenReturn([implementation, compileOnly])
|
||||
when(projectView.getRuntimeRemapConfigurations()).thenReturn([implementation, runtimeOnly])
|
||||
}
|
||||
|
||||
def "Empty"() {
|
||||
@@ -98,7 +95,7 @@ class SpecContextTest extends Specification {
|
||||
)
|
||||
|
||||
when:
|
||||
def specContext = SpecContextRemappedImpl.create(projectView)
|
||||
def specContext = RemappedSpecContext.create(projectView)
|
||||
|
||||
then:
|
||||
specContext.modDependencies().size() == 0
|
||||
@@ -117,7 +114,7 @@ class SpecContextTest extends Specification {
|
||||
)
|
||||
|
||||
when:
|
||||
def specContext = SpecContextRemappedImpl.create(projectView)
|
||||
def specContext = RemappedSpecContext.create(projectView)
|
||||
|
||||
then:
|
||||
specContext.modDependencies().size() == 1
|
||||
@@ -136,7 +133,7 @@ class SpecContextTest extends Specification {
|
||||
)
|
||||
|
||||
when:
|
||||
def specContext = SpecContextRemappedImpl.create(projectView)
|
||||
def specContext = RemappedSpecContext.create(projectView)
|
||||
|
||||
then:
|
||||
specContext.modDependencies().size() == 1
|
||||
@@ -155,7 +152,7 @@ class SpecContextTest extends Specification {
|
||||
)
|
||||
|
||||
when:
|
||||
def specContext = SpecContextRemappedImpl.create(projectView)
|
||||
def specContext = RemappedSpecContext.create(projectView)
|
||||
|
||||
then:
|
||||
specContext.modDependencies().size() == 1
|
||||
@@ -175,7 +172,7 @@ class SpecContextTest extends Specification {
|
||||
)
|
||||
|
||||
when:
|
||||
def specContext = SpecContextRemappedImpl.create(projectView)
|
||||
def specContext = RemappedSpecContext.create(projectView)
|
||||
|
||||
then:
|
||||
specContext.modDependencies().size() == 1
|
||||
|
||||
@@ -27,9 +27,11 @@ package net.fabricmc.loom.test.util
|
||||
import io.javalin.Javalin
|
||||
import org.apache.commons.io.IOUtils
|
||||
|
||||
import net.fabricmc.loom.configuration.mods.dependency.LocalMavenHelper
|
||||
|
||||
trait MockMavenServerTrait {
|
||||
public final int mavenServerPort = 9876
|
||||
public final File testMavenDir = File.createTempDir()
|
||||
public static final File testMavenDir = File.createTempDir()
|
||||
private Javalin server
|
||||
|
||||
@SuppressWarnings('unused')
|
||||
@@ -87,6 +89,21 @@ trait MockMavenServerTrait {
|
||||
"${mavenServerPort}"
|
||||
}
|
||||
|
||||
String getRepositoriesBlock() {
|
||||
"""
|
||||
repositories {
|
||||
maven {
|
||||
url = "http://localhost:${port()}/"
|
||||
allowInsecureProtocol = true
|
||||
}
|
||||
}
|
||||
"""
|
||||
}
|
||||
|
||||
LocalMavenHelper mavenHelper(String group, String name, String version) {
|
||||
return new LocalMavenHelper(group, name, version, null, getMavenDirectory().toPath(), null)
|
||||
}
|
||||
|
||||
String getLatestSnapshotVersion(String group, String artifact, String version) {
|
||||
File file = new File(getMavenDirectory(), "${group.replace('.', '/')}/${artifact}/${version}/maven-metadata.xml")
|
||||
def root = new groovy.xml.XmlSlurper().parseText(file.text)
|
||||
|
||||
Reference in New Issue
Block a user