mirror of
https://github.com/architectury/architectury-loom.git
synced 2026-03-30 21:05:58 -05:00
Rewrite the internals of mod configuration remapping to fix bugs (#807)
* Add test for #801 * Add test for #572 * Rewrite mod configuration remapping internals to fix bugs Fixes #801. Fixes #572. - Instead of individual mod configs getting remapped, Loom now remaps "collector configs": `mod[Compile/Runtime]Classpath[source set name]` (eg `modRuntimeClasspathMain` -> `modRuntimeClasspathMainRemapped`) - This lets us use Gradle's `org.gradle.usage` attributes to select whether it's a compile-time or runtime dependency - Note: Remap configurations sourcing from `api` are partially left intact due to their special logic in the `namedElements` configuration. - Setting the proper usages fixes #801. - No dependency files are added to the real target configurations (like `api` and `implementation`) anymore. - This fixes #572, since `runtimeClasspath` and `compileClasspath` don't leak transitive dependencies unlike `implementation` etc. * Fix checkstyle * Fix split env dependency consumers * Only use collector configs for remapped deps and let the inputs stay intact This way the code has less duplication. * Improve log messages * Update year * Add some comments * Fix compilation * Use LinkedHashMap for reliable iteration order through remapped configs * Use ImmutableMap.of instead of Map.of for reliable ordering * ModConfigurationRemapper: Move namedElements handling out of forEach * Add regression test for a crash where loader is resolved after other mods * Fix the aforementioned bug * Rename InstallerDataTest -> DependencyOrderTest * Add TODO about refresh dependencies The code currently processes the same deps multiple times when --refresh-dependencies/-Dloom.refresh=true/a cache invalidation is applied. Co-authored-by: modmuss50 <modmuss50@gmail.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of fabric-loom, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) 2022 FabricMC
|
||||
* Copyright (c) 2022-2023 FabricMC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -28,7 +28,6 @@ import java.util.Set;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import org.gradle.api.Named;
|
||||
import org.gradle.api.NamedDomainObjectProvider;
|
||||
import org.gradle.api.Project;
|
||||
@@ -41,9 +40,6 @@ import org.gradle.api.tasks.SourceSet;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftJarConfiguration;
|
||||
|
||||
/**
|
||||
* A {@link Named} object for configuring "proxy" configurations that remap artifacts.
|
||||
*/
|
||||
@@ -132,50 +128,12 @@ public abstract class RemapConfigurationSettings implements Named {
|
||||
return getName() + "Mapped";
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
@Internal
|
||||
public final Provider<String> getClientRemappedConfigurationName() {
|
||||
return getClientSourceConfigurationName().map(s -> s + "Mapped");
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
@Internal
|
||||
public final NamedDomainObjectProvider<Configuration> getSourceConfiguration() {
|
||||
return getConfigurationByName(getName());
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
@Internal
|
||||
public final NamedDomainObjectProvider<Configuration> getRemappedConfiguration() {
|
||||
return getConfigurationByName(getRemappedConfigurationName());
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
@Internal
|
||||
public final NamedDomainObjectProvider<Configuration> getTargetConfiguration() {
|
||||
return getConfigurationByName(getTargetConfigurationName().get());
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
@Internal
|
||||
public final Provider<Configuration> getClientTargetConfiguration() {
|
||||
return getProject().provider(() -> {
|
||||
boolean split = LoomGradleExtension.get(getProject()).getMinecraftJarConfiguration().get() == MinecraftJarConfiguration.SPLIT;
|
||||
Preconditions.checkArgument(split, "Cannot get client target configuration when project is not split");
|
||||
return getConfigurationByName(getClientSourceConfigurationName().get()).get();
|
||||
});
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
@Internal
|
||||
public final Provider<Configuration> getClientRemappedConfiguration() {
|
||||
return getProject().provider(() -> {
|
||||
boolean split = LoomGradleExtension.get(getProject()).getMinecraftJarConfiguration().get() == MinecraftJarConfiguration.SPLIT;
|
||||
Preconditions.checkArgument(split, "Cannot get client remapped configuration when project is not split");
|
||||
return getConfigurationByName(getClientRemappedConfigurationName().get()).get();
|
||||
});
|
||||
}
|
||||
|
||||
@Internal
|
||||
private NamedDomainObjectProvider<Configuration> getConfigurationByName(String name) {
|
||||
return getProject().getConfigurations().named(name);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of fabric-loom, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) 2022 FabricMC
|
||||
* Copyright (c) 2022-2023 FabricMC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -32,6 +32,8 @@ import org.gradle.api.Action;
|
||||
import org.gradle.api.NamedDomainObjectList;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.artifacts.Configuration;
|
||||
import org.gradle.api.artifacts.ConfigurationContainer;
|
||||
import org.gradle.api.attributes.Usage;
|
||||
import org.gradle.api.plugins.JavaPlugin;
|
||||
import org.gradle.api.tasks.SourceSet;
|
||||
import org.jetbrains.annotations.VisibleForTesting;
|
||||
@@ -39,6 +41,7 @@ import org.jetbrains.annotations.VisibleForTesting;
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.api.RemapConfigurationSettings;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
import net.fabricmc.loom.util.Strings;
|
||||
import net.fabricmc.loom.util.gradle.SourceSetHelper;
|
||||
|
||||
public final class RemapConfigurations {
|
||||
@@ -85,54 +88,88 @@ public final class RemapConfigurations {
|
||||
}
|
||||
|
||||
settings.getClientSourceConfigurationName().set(name);
|
||||
createClientMappedConfiguration(project, settings, clientSourceSet);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static void createClientMappedConfiguration(Project project, RemapConfigurationSettings settings, SourceSet clientSourceSet) {
|
||||
final Configuration remappedConfiguration = project.getConfigurations().create(settings.getClientRemappedConfigurationName().get());
|
||||
// Don't get transitive deps of already remapped mods
|
||||
remappedConfiguration.setTransitive(false);
|
||||
/**
|
||||
* Gets or creates the collector configuration for a {@link SourceSet}.
|
||||
* The collector configuration receives all compile-time or runtime remapped mod dependency files.
|
||||
*
|
||||
* @param project the project
|
||||
* @param settings the remap configuration settings
|
||||
* @param runtime if {@code true}, returns the runtime configuration;
|
||||
* if {@code false}, returns the compile-time one
|
||||
* @return the collector configuration
|
||||
*/
|
||||
public static Configuration getOrCreateCollectorConfiguration(Project project, RemapConfigurationSettings settings, boolean runtime) {
|
||||
return getOrCreateCollectorConfiguration(project, settings.getSourceSet().get(), runtime);
|
||||
}
|
||||
|
||||
if (settings.getOnCompileClasspath().get()) {
|
||||
extendsFrom(Constants.Configurations.MOD_COMPILE_CLASSPATH_MAPPED, remappedConfiguration, project);
|
||||
/**
|
||||
* Gets or creates the collector configuration for a {@link RemapConfigurationSettings} instance.
|
||||
* The collector configuration receives all compile-time or runtime remapped mod dependency files.
|
||||
*
|
||||
* @param project the project
|
||||
* @param sourceSet the source set to apply the collector config to, should generally match {@link RemapConfigurationSettings#getSourceSet()}
|
||||
* @param runtime if {@code true}, returns the runtime configuration;
|
||||
* if {@code false}, returns the compile-time one
|
||||
* @return the collector configuration
|
||||
*/
|
||||
// Note: this method is generally called on demand, so these configurations
|
||||
// won't exist at buildscript evaluation time. There's no need for them anyway
|
||||
// since they're internals.
|
||||
public static Configuration getOrCreateCollectorConfiguration(Project project, SourceSet sourceSet, boolean runtime) {
|
||||
final String configurationName = "mod"
|
||||
+ (runtime ? "Runtime" : "Compile")
|
||||
+ "Classpath"
|
||||
+ Strings.capitalize(sourceSet.getName())
|
||||
+ "Mapped";
|
||||
final ConfigurationContainer configurations = project.getConfigurations();
|
||||
Configuration configuration = configurations.findByName(configurationName);
|
||||
|
||||
extendsFrom(clientSourceSet.getCompileClasspathConfigurationName(), remappedConfiguration, project);
|
||||
if (configuration == null) {
|
||||
configuration = configurations.create(configurationName);
|
||||
|
||||
// Don't get transitive deps of already remapped mods
|
||||
configuration.setTransitive(false);
|
||||
|
||||
// Set the usage attribute to fetch the correct artifacts.
|
||||
// Note: Even though most deps are resolved via copies of mod* configurations,
|
||||
// non-remapped mods that get added straight to these collectors will need the attribute.
|
||||
final Usage usage = project.getObjects().named(Usage.class, runtime ? Usage.JAVA_RUNTIME : Usage.JAVA_API);
|
||||
configuration.attributes(attributes -> attributes.attribute(Usage.USAGE_ATTRIBUTE, usage));
|
||||
|
||||
// The main classpath also applies to the test source set like with normal dependencies.
|
||||
final boolean isMainSourceSet = sourceSet.getName().equals("main");
|
||||
|
||||
if (runtime) {
|
||||
extendsFrom(sourceSet.getRuntimeClasspathConfigurationName(), configuration, project);
|
||||
|
||||
if (isMainSourceSet) {
|
||||
extendsFrom(JavaPlugin.TEST_RUNTIME_CLASSPATH_CONFIGURATION_NAME, configuration, project);
|
||||
}
|
||||
} else {
|
||||
extendsFrom(sourceSet.getCompileClasspathConfigurationName(), configuration, project);
|
||||
extendsFrom(Constants.Configurations.MOD_COMPILE_CLASSPATH_MAPPED, configuration, project);
|
||||
|
||||
if (isMainSourceSet) {
|
||||
extendsFrom(JavaPlugin.TEST_COMPILE_CLASSPATH_CONFIGURATION_NAME, configuration, project);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (settings.getOnRuntimeClasspath().get()) {
|
||||
extendsFrom(clientSourceSet.getRuntimeClasspathConfigurationName(), remappedConfiguration, project);
|
||||
}
|
||||
return configuration;
|
||||
}
|
||||
|
||||
public static void applyToProject(Project project, RemapConfigurationSettings settings) {
|
||||
final SourceSet sourceSet = settings.getSourceSet().get();
|
||||
final boolean isMainSourceSet = sourceSet.getName().equals("main");
|
||||
// No point bothering to make it lazily, gradle realises configurations right away.
|
||||
// <https://github.com/gradle/gradle/blob/v7.4.2/subprojects/plugins/src/main/java/org/gradle/api/plugins/BasePlugin.java#L104>
|
||||
final Configuration remappedConfiguration = project.getConfigurations().create(settings.getRemappedConfigurationName());
|
||||
final Configuration configuration = project.getConfigurations().create(settings.getName());
|
||||
configuration.setTransitive(true);
|
||||
// Don't get transitive deps of already remapped mods
|
||||
remappedConfiguration.setTransitive(false);
|
||||
|
||||
if (settings.getOnCompileClasspath().get()) {
|
||||
extendsFrom(Constants.Configurations.MOD_COMPILE_CLASSPATH, configuration, project);
|
||||
extendsFrom(Constants.Configurations.MOD_COMPILE_CLASSPATH_MAPPED, remappedConfiguration, project);
|
||||
extendsFrom(sourceSet.getCompileClasspathConfigurationName(), remappedConfiguration, project);
|
||||
|
||||
if (isMainSourceSet) {
|
||||
extendsFrom(JavaPlugin.TEST_COMPILE_CLASSPATH_CONFIGURATION_NAME, remappedConfiguration, project);
|
||||
}
|
||||
}
|
||||
|
||||
if (settings.getOnRuntimeClasspath().get()) {
|
||||
extendsFrom(sourceSet.getRuntimeClasspathConfigurationName(), remappedConfiguration, project);
|
||||
|
||||
if (isMainSourceSet) {
|
||||
extendsFrom(JavaPlugin.TEST_RUNTIME_CLASSPATH_CONFIGURATION_NAME, remappedConfiguration, project);
|
||||
}
|
||||
}
|
||||
|
||||
for (String outgoingConfigurationName : settings.getPublishingMode().get().outgoingConfigurations()) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of fabric-loom, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) 2019-2022 FabricMC
|
||||
* Copyright (c) 2019-2023 FabricMC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -30,9 +30,13 @@ import java.io.UncheckedIOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.artifacts.Configuration;
|
||||
import org.gradle.api.artifacts.FileCollectionDependency;
|
||||
@@ -43,20 +47,25 @@ import org.gradle.api.artifacts.query.ArtifactResolutionQuery;
|
||||
import org.gradle.api.artifacts.result.ArtifactResult;
|
||||
import org.gradle.api.artifacts.result.ComponentArtifactsResult;
|
||||
import org.gradle.api.artifacts.result.ResolvedArtifactResult;
|
||||
import org.gradle.api.attributes.Usage;
|
||||
import org.gradle.api.file.FileCollection;
|
||||
import org.gradle.api.plugins.JavaPlugin;
|
||||
import org.gradle.api.tasks.SourceSet;
|
||||
import org.gradle.jvm.JvmLibrary;
|
||||
import org.gradle.language.base.artifact.SourcesArtifact;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.api.RemapConfigurationSettings;
|
||||
import net.fabricmc.loom.configuration.RemapConfigurations;
|
||||
import net.fabricmc.loom.configuration.mods.dependency.ModDependency;
|
||||
import net.fabricmc.loom.configuration.mods.dependency.ModDependencyFactory;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftSourceSets;
|
||||
import net.fabricmc.loom.util.Checksum;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
import net.fabricmc.loom.util.OperatingSystem;
|
||||
import net.fabricmc.loom.util.SourceRemapper;
|
||||
import net.fabricmc.loom.util.gradle.SourceSetHelper;
|
||||
import net.fabricmc.loom.util.service.SharedServiceManager;
|
||||
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
@@ -67,88 +76,132 @@ public class ModConfigurationRemapper {
|
||||
|
||||
public static void supplyModConfigurations(Project project, SharedServiceManager serviceManager, String mappingsSuffix, LoomGradleExtension extension, SourceRemapper sourceRemapper) {
|
||||
final DependencyHandler dependencies = project.getDependencies();
|
||||
// The configurations where the source and remapped artifacts go.
|
||||
// key: source, value: target
|
||||
final Map<Configuration, Configuration> configsToRemap = new LinkedHashMap<>();
|
||||
// Client remapped dep collectors for split source sets. Same keys and values.
|
||||
final Map<Configuration, Configuration> clientConfigsToRemap = new HashMap<>();
|
||||
|
||||
for (RemapConfigurationSettings entry : extension.getRemapConfigurations()) {
|
||||
entry.getRemappedConfiguration().configure(remappedConfig -> {
|
||||
/*
|
||||
sourceConfig - The source configuration where the intermediary named artifacts come from. i.e "modApi"
|
||||
remappedConfig - an intermediate configuration where the remapped artifacts go
|
||||
targetConfig - extends from the remappedConfig, such as "api"
|
||||
*/
|
||||
final Configuration sourceConfig = entry.getSourceConfiguration().get();
|
||||
final Configuration targetConfig = entry.getTargetConfiguration().get();
|
||||
final boolean hasClientTarget = entry.getClientSourceConfigurationName().isPresent();
|
||||
// key: true if runtime, false if compile
|
||||
final Map<Boolean, Boolean> envToEnabled = ImmutableMap.of(
|
||||
false, entry.getOnCompileClasspath().get(),
|
||||
true, entry.getOnRuntimeClasspath().get()
|
||||
);
|
||||
|
||||
Configuration clientRemappedConfig = null;
|
||||
envToEnabled.forEach((runtime, enabled) -> {
|
||||
if (!enabled) return;
|
||||
|
||||
if (hasClientTarget) {
|
||||
clientRemappedConfig = entry.getClientRemappedConfiguration().get();
|
||||
}
|
||||
final Configuration target = RemapConfigurations.getOrCreateCollectorConfiguration(project, entry, runtime);
|
||||
// We copy the source with the desired usage type to get only the runtime or api jars, not both.
|
||||
final Configuration sourceCopy = entry.getSourceConfiguration().get().copy();
|
||||
final Usage usage = project.getObjects().named(Usage.class, runtime ? Usage.JAVA_RUNTIME : Usage.JAVA_API);
|
||||
sourceCopy.attributes(attributes -> attributes.attribute(Usage.USAGE_ATTRIBUTE, usage));
|
||||
configsToRemap.put(sourceCopy, target);
|
||||
|
||||
final List<ModDependency> modDependencies = new ArrayList<>();
|
||||
|
||||
for (ArtifactRef artifact : resolveArtifacts(project, sourceConfig)) {
|
||||
final ArtifactMetadata artifactMetadata;
|
||||
|
||||
try {
|
||||
artifactMetadata = ArtifactMetadata.create(artifact);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException("Failed to read metadata from" + artifact.path(), e);
|
||||
}
|
||||
|
||||
if (artifactMetadata.installerData() != null) {
|
||||
if (extension.getInstallerData() != null) {
|
||||
project.getLogger().info("Found another installer JSON in ({}), ignoring", artifact.path());
|
||||
} else {
|
||||
project.getLogger().info("Applying installer data from {}", artifact.path());
|
||||
artifactMetadata.installerData().applyToProject(project);
|
||||
}
|
||||
}
|
||||
|
||||
if (!artifactMetadata.shouldRemap()) {
|
||||
artifact.applyToConfiguration(project, targetConfig);
|
||||
continue;
|
||||
}
|
||||
|
||||
final ModDependency modDependency = ModDependencyFactory.create(artifact, remappedConfig, clientRemappedConfig, mappingsSuffix, project);
|
||||
scheduleSourcesRemapping(project, sourceRemapper, modDependency);
|
||||
modDependencies.add(modDependency);
|
||||
}
|
||||
|
||||
if (modDependencies.isEmpty()) {
|
||||
// Nothing else to do
|
||||
return;
|
||||
}
|
||||
|
||||
final boolean refreshDeps = LoomGradleExtension.get(project).refreshDeps();
|
||||
final List<ModDependency> toRemap = modDependencies.stream()
|
||||
.filter(dependency -> refreshDeps || dependency.isCacheInvalid(project, null))
|
||||
.toList();
|
||||
|
||||
if (!toRemap.isEmpty()) {
|
||||
try {
|
||||
new ModProcessor(project, sourceConfig, serviceManager).processMods(toRemap);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException("Failed to remap mods", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Add all of the remapped mods onto the config
|
||||
for (ModDependency info : modDependencies) {
|
||||
info.applyToProject(project);
|
||||
createConstraints(info.getInputArtifact(), targetConfig, sourceConfig, dependencies);
|
||||
|
||||
if (clientRemappedConfig != null) {
|
||||
createConstraints(info.getInputArtifact(), entry.getClientTargetConfiguration().get(), sourceConfig, dependencies);
|
||||
}
|
||||
}
|
||||
|
||||
// Export to other projects
|
||||
if (entry.getTargetConfigurationName().get().equals(JavaPlugin.API_CONFIGURATION_NAME)) {
|
||||
project.getConfigurations().getByName(Constants.Configurations.NAMED_ELEMENTS).extendsFrom(remappedConfig);
|
||||
// If our remap configuration entry targets the client source set as well,
|
||||
// let's set up a collector for it too.
|
||||
if (entry.getClientSourceConfigurationName().isPresent()) {
|
||||
final SourceSet clientSourceSet = SourceSetHelper.getSourceSetByName(MinecraftSourceSets.Split.CLIENT_ONLY_SOURCE_SET_NAME, project);
|
||||
final Configuration clientTarget = RemapConfigurations.getOrCreateCollectorConfiguration(project, clientSourceSet, runtime);
|
||||
clientConfigsToRemap.put(sourceCopy, clientTarget);
|
||||
}
|
||||
});
|
||||
|
||||
// Export to other projects.
|
||||
if (entry.getTargetConfigurationName().get().equals(JavaPlugin.API_CONFIGURATION_NAME)) {
|
||||
// Note: legacy (pre-1.1) behavior is kept for this remapping since
|
||||
// we don't have a modApiElements/modRuntimeElements kind of configuration.
|
||||
// TODO: Expose API/runtime usage attributes for namedElements to make it work like normal project dependencies.
|
||||
final Configuration remappedConfig = project.getConfigurations().maybeCreate(entry.getRemappedConfigurationName());
|
||||
remappedConfig.setTransitive(false);
|
||||
project.getConfigurations().getByName(Constants.Configurations.NAMED_ELEMENTS).extendsFrom(remappedConfig);
|
||||
configsToRemap.put(entry.getSourceConfiguration().get(), remappedConfig);
|
||||
}
|
||||
}
|
||||
|
||||
// Round 1: Discovery
|
||||
// Go through all the configs to find artifacts to remap and
|
||||
// the installer data. The installer data has to be added before
|
||||
// any mods are remapped since remapping needs the dependencies provided by that data.
|
||||
final Map<Configuration, List<ModDependency>> dependenciesBySourceConfig = new HashMap<>();
|
||||
configsToRemap.forEach((sourceConfig, remappedConfig) -> {
|
||||
/*
|
||||
sourceConfig - The source configuration where the intermediary named artifacts come from. i.e "modApi"
|
||||
remappedConfig - The target configuration where the remapped artifacts go
|
||||
*/
|
||||
final Configuration clientRemappedConfig = clientConfigsToRemap.get(sourceConfig);
|
||||
final List<ModDependency> modDependencies = new ArrayList<>();
|
||||
|
||||
for (ArtifactRef artifact : resolveArtifacts(project, sourceConfig)) {
|
||||
final ArtifactMetadata artifactMetadata;
|
||||
|
||||
try {
|
||||
artifactMetadata = ArtifactMetadata.create(artifact);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException("Failed to read metadata from" + artifact.path(), e);
|
||||
}
|
||||
|
||||
if (artifactMetadata.installerData() != null) {
|
||||
if (extension.getInstallerData() != null) {
|
||||
project.getLogger().info("Found another installer JSON in ({}), ignoring", artifact.path());
|
||||
} else {
|
||||
project.getLogger().info("Applying installer data from {}", artifact.path());
|
||||
artifactMetadata.installerData().applyToProject(project);
|
||||
}
|
||||
}
|
||||
|
||||
if (!artifactMetadata.shouldRemap()) {
|
||||
// Note: not applying to any type of vanilla Gradle target config like
|
||||
// api or implementation to fix https://github.com/FabricMC/fabric-loom/issues/572.
|
||||
artifact.applyToConfiguration(project, remappedConfig);
|
||||
continue;
|
||||
}
|
||||
|
||||
final ModDependency modDependency = ModDependencyFactory.create(artifact, remappedConfig, clientRemappedConfig, mappingsSuffix, project);
|
||||
scheduleSourcesRemapping(project, sourceRemapper, modDependency);
|
||||
modDependencies.add(modDependency);
|
||||
}
|
||||
|
||||
dependenciesBySourceConfig.put(sourceConfig, modDependencies);
|
||||
});
|
||||
|
||||
// Round 2: Remapping
|
||||
// Remap all discovered artifacts.
|
||||
configsToRemap.forEach((sourceConfig, remappedConfig) -> {
|
||||
final List<ModDependency> modDependencies = dependenciesBySourceConfig.get(sourceConfig);
|
||||
|
||||
if (modDependencies.isEmpty()) {
|
||||
// Nothing else to do
|
||||
return;
|
||||
}
|
||||
|
||||
final Configuration clientRemappedConfig = clientConfigsToRemap.get(sourceConfig);
|
||||
final boolean refreshDeps = LoomGradleExtension.get(project).refreshDeps();
|
||||
// TODO: With the same artifacts being considered multiple times for their different
|
||||
// usage attributes, this should probably not process them multiple times even with refreshDeps.
|
||||
final List<ModDependency> toRemap = modDependencies.stream()
|
||||
.filter(dependency -> refreshDeps || dependency.isCacheInvalid(project, null))
|
||||
.toList();
|
||||
|
||||
if (!toRemap.isEmpty()) {
|
||||
try {
|
||||
new ModProcessor(project, sourceConfig, serviceManager).processMods(toRemap);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException("Failed to remap mods", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Add all of the remapped mods onto the config
|
||||
for (ModDependency info : modDependencies) {
|
||||
info.applyToProject(project);
|
||||
createConstraints(info.getInputArtifact(), remappedConfig, sourceConfig, dependencies);
|
||||
|
||||
if (clientRemappedConfig != null) {
|
||||
createConstraints(info.getInputArtifact(), clientRemappedConfig, sourceConfig, dependencies);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void createConstraints(ArtifactRef artifact, Configuration targetConfig, Configuration sourceConfig, DependencyHandler dependencies) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of fabric-loom, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) 2018-2021 FabricMC
|
||||
* Copyright (c) 2018-2023 FabricMC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -36,10 +36,13 @@ import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.jar.Manifest;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.artifacts.Configuration;
|
||||
import org.gradle.api.attributes.Usage;
|
||||
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.api.RemapConfigurationSettings;
|
||||
@@ -63,6 +66,8 @@ public class ModProcessor {
|
||||
private static final String fromM = MappingsNamespace.INTERMEDIARY.toString();
|
||||
private static final String toM = MappingsNamespace.NAMED.toString();
|
||||
|
||||
private static final Pattern COPY_CONFIGURATION_PATTERN = Pattern.compile("^(.+)Copy[0-9]*$");
|
||||
|
||||
private final Project project;
|
||||
private final Configuration sourceConfiguration;
|
||||
private final SharedServiceManager serviceManager;
|
||||
@@ -75,13 +80,40 @@ public class ModProcessor {
|
||||
|
||||
public void processMods(List<ModDependency> remapList) throws IOException {
|
||||
try {
|
||||
project.getLogger().lifecycle(":remapping {} mods from {}", remapList.size(), sourceConfiguration.getName());
|
||||
project.getLogger().lifecycle(":remapping {} mods from {}", remapList.size(), describeConfiguration(sourceConfiguration));
|
||||
remapJars(remapList);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(String.format(Locale.ENGLISH, "Failed to remap %d mods", remapList.size()), e);
|
||||
}
|
||||
}
|
||||
|
||||
// Creates a human-readable descriptive string for the configuration.
|
||||
// This consists primarily of the name with any copy suffixes stripped
|
||||
// (they're not informative), and the usage attribute if present.
|
||||
private String describeConfiguration(Configuration configuration) {
|
||||
String description = configuration.getName();
|
||||
final Matcher copyMatcher = COPY_CONFIGURATION_PATTERN.matcher(description);
|
||||
|
||||
// If we find a copy suffix, remove it.
|
||||
if (copyMatcher.matches()) {
|
||||
final String realName = copyMatcher.group(1);
|
||||
|
||||
// It's only a copy if we find a non-copy version.
|
||||
if (project.getConfigurations().findByName(realName) != null) {
|
||||
description = realName;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the usage if present, e.g. "modImplementation (java-api)"
|
||||
final Usage usage = configuration.getAttributes().getAttribute(Usage.USAGE_ATTRIBUTE);
|
||||
|
||||
if (usage != null) {
|
||||
description += " (" + usage.getName() + ")";
|
||||
}
|
||||
|
||||
return description;
|
||||
}
|
||||
|
||||
private void stripNestedJars(Path path) {
|
||||
// Strip out all contained jar info as we dont want loader to try and load the jars contained in dev.
|
||||
try {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of fabric-loom, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) 2022 FabricMC
|
||||
* Copyright (c) 2022-2023 FabricMC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -33,6 +33,7 @@ import org.gradle.api.artifacts.Configuration;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.api.ModSettings;
|
||||
import net.fabricmc.loom.configuration.mods.ArtifactRef;
|
||||
import net.fabricmc.loom.configuration.mods.JarSplitter;
|
||||
|
||||
@@ -120,11 +121,10 @@ public final class SplitModDependency extends ModDependency {
|
||||
|
||||
private void createModGroup(Path commonJar, Path clientJar) {
|
||||
LoomGradleExtension extension = LoomGradleExtension.get(project);
|
||||
extension.getMods().register(String.format("%s-%s-%s", getRemappedGroup(), name, version), modSettings ->
|
||||
modSettings.getModFiles().from(
|
||||
commonJar.toFile(),
|
||||
clientJar.toFile()
|
||||
)
|
||||
final ModSettings modSettings = extension.getMods().maybeCreate(String.format("%s-%s-%s", getRemappedGroup(), name, version));
|
||||
modSettings.getModFiles().from(
|
||||
commonJar.toFile(),
|
||||
clientJar.toFile()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of fabric-loom, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) 2022 FabricMC
|
||||
* Copyright (c) 2022-2023 FabricMC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -126,7 +126,7 @@ public abstract sealed class MinecraftSourceSets permits MinecraftSourceSets.Sin
|
||||
private static final String MINECRAFT_CLIENT_ONLY_NAMED = "minecraftClientOnlyNamed";
|
||||
private static final String MINECRAFT_COMBINED_NAMED = "minecraftCombinedNamed";
|
||||
|
||||
private static final String CLIENT_ONLY_SOURCE_SET_NAME = "client";
|
||||
public static final String CLIENT_ONLY_SOURCE_SET_NAME = "client";
|
||||
|
||||
private static final Split INSTANCE = new Split();
|
||||
|
||||
|
||||
47
src/main/java/net/fabricmc/loom/util/Strings.java
Normal file
47
src/main/java/net/fabricmc/loom/util/Strings.java
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* This file is part of fabric-loom, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) 2023 FabricMC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package net.fabricmc.loom.util;
|
||||
|
||||
public final class Strings {
|
||||
public static String capitalize(String word) {
|
||||
// Don't capitalize empty strings
|
||||
if (word.isBlank()) {
|
||||
return word;
|
||||
}
|
||||
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
final int codePointCount = word.codePointCount(0, word.length());
|
||||
final int first = Character.toUpperCase(word.codePointAt(0));
|
||||
builder.append(Character.toString(first));
|
||||
|
||||
if (codePointCount > 1) {
|
||||
for (int codePoint = 1; codePoint < codePointCount; codePoint++) {
|
||||
builder.append(Character.toString(word.codePointAt(codePoint)));
|
||||
}
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user