mirror of
https://github.com/architectury/architectury-loom.git
synced 2026-03-30 05:05:20 -05:00
Split mod dependencies into client/common as required.
This commit is contained in:
@@ -82,16 +82,12 @@ public interface LoomGradleExtensionAPI {
|
||||
* Optionally register and configure a {@link ModSettings} object. The name should match the modid.
|
||||
* This is generally only required when the mod spans across multiple classpath directories, such as when using split sourcesets.
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
void mods(Action<NamedDomainObjectContainer<ModSettings>> action);
|
||||
|
||||
@ApiStatus.Experimental
|
||||
NamedDomainObjectContainer<ModSettings> getMods();
|
||||
|
||||
@ApiStatus.Experimental
|
||||
NamedDomainObjectList<RemapConfigurationSettings> getRemapConfigurations();
|
||||
|
||||
@ApiStatus.Experimental
|
||||
RemapConfigurationSettings addRemapConfiguration(String name, Action<RemapConfigurationSettings> action);
|
||||
|
||||
void createRemapConfigurations(SourceSet sourceSet);
|
||||
@@ -173,29 +169,25 @@ public interface LoomGradleExtensionAPI {
|
||||
*/
|
||||
Property<String> getIntermediaryUrl();
|
||||
|
||||
@ApiStatus.Experimental
|
||||
Property<MinecraftJarConfiguration> getMinecraftJarConfiguration();
|
||||
|
||||
@ApiStatus.Experimental
|
||||
default void serverOnlyMinecraftJar() {
|
||||
getMinecraftJarConfiguration().set(MinecraftJarConfiguration.SERVER_ONLY);
|
||||
}
|
||||
|
||||
@ApiStatus.Experimental
|
||||
default void clientOnlyMinecraftJar() {
|
||||
getMinecraftJarConfiguration().set(MinecraftJarConfiguration.CLIENT_ONLY);
|
||||
}
|
||||
|
||||
@ApiStatus.Experimental
|
||||
default void splitMinecraftJar() {
|
||||
getMinecraftJarConfiguration().set(MinecraftJarConfiguration.SPLIT);
|
||||
}
|
||||
|
||||
@ApiStatus.Experimental
|
||||
void splitEnvironmentSourceSets();
|
||||
|
||||
@ApiStatus.Experimental
|
||||
boolean areEnvironmentSourceSetsSplit();
|
||||
|
||||
Property<Boolean> getRuntimeOnlyLog4j();
|
||||
|
||||
Property<Boolean> getSplitModDependencies();
|
||||
}
|
||||
|
||||
@@ -41,7 +41,6 @@ import net.fabricmc.loom.util.gradle.SourceSetReference;
|
||||
/**
|
||||
* A {@link Named} object for setting mod-related values. The {@linkplain Named#getName() name} should match the mod id.
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
public abstract class ModSettings implements Named {
|
||||
/**
|
||||
* List of classpath directories, or jar files used to populate the `fabric.classPathGroups` Fabric Loader system property.
|
||||
|
||||
@@ -54,7 +54,7 @@ public abstract class RemapConfigurationSettings implements Named {
|
||||
this.name = name;
|
||||
|
||||
getTargetConfigurationName().finalizeValueOnRead();
|
||||
getClientTargetConfigurationName().finalizeValueOnRead();
|
||||
getClientSourceConfigurationName().finalizeValueOnRead();
|
||||
getOnCompileClasspath().finalizeValueOnRead();
|
||||
getOnRuntimeClasspath().finalizeValueOnRead();
|
||||
getPublishingMode().convention(PublishingMode.NONE).finalizeValueOnRead();
|
||||
@@ -74,9 +74,9 @@ public abstract class RemapConfigurationSettings implements Named {
|
||||
* Optional, only used when split sourcesets are enabled.
|
||||
* When not present client only entries should go onto the target configuration.
|
||||
*
|
||||
* @return The client target configuration name
|
||||
* @return The client source configuration name
|
||||
*/
|
||||
public abstract Property<String> getClientTargetConfigurationName();
|
||||
public abstract Property<String> getClientSourceConfigurationName();
|
||||
|
||||
/**
|
||||
* @return True if this configuration's artifacts should be exposed for compile operations.
|
||||
@@ -119,6 +119,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() {
|
||||
@@ -143,7 +149,17 @@ public abstract class RemapConfigurationSettings implements Named {
|
||||
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(getClientTargetConfigurationName().get()).get();
|
||||
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();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -49,8 +49,8 @@ import net.fabricmc.loom.build.mixin.ScalaApInvoker;
|
||||
import net.fabricmc.loom.configuration.accesswidener.AccessWidenerJarProcessor;
|
||||
import net.fabricmc.loom.configuration.accesswidener.TransitiveAccessWidenerJarProcessor;
|
||||
import net.fabricmc.loom.configuration.ifaceinject.InterfaceInjectionProcessor;
|
||||
import net.fabricmc.loom.configuration.mods.ModJavadocProcessor;
|
||||
import net.fabricmc.loom.configuration.processors.JarProcessorManager;
|
||||
import net.fabricmc.loom.configuration.processors.ModJavadocProcessor;
|
||||
import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftJarConfiguration;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider;
|
||||
@@ -60,6 +60,7 @@ import net.fabricmc.loom.configuration.providers.minecraft.mapped.NamedMinecraft
|
||||
import net.fabricmc.loom.extension.MixinExtension;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
import net.fabricmc.loom.util.ExceptionUtil;
|
||||
import net.fabricmc.loom.util.gradle.GradleUtils;
|
||||
import net.fabricmc.loom.util.gradle.SourceSetHelper;
|
||||
|
||||
public final class CompileConfiguration {
|
||||
@@ -116,15 +117,15 @@ public final class CompileConfiguration {
|
||||
project.getDependencies().add(JavaPlugin.TEST_COMPILE_ONLY_CONFIGURATION_NAME, Constants.Dependencies.JETBRAINS_ANNOTATIONS + Constants.Dependencies.Versions.JETBRAINS_ANNOTATIONS);
|
||||
}
|
||||
|
||||
public static void configureCompile(Project p) {
|
||||
LoomGradleExtension extension = LoomGradleExtension.get(p);
|
||||
public static void configureCompile(Project project) {
|
||||
LoomGradleExtension extension = LoomGradleExtension.get(project);
|
||||
|
||||
p.getTasks().named(JavaPlugin.JAVADOC_TASK_NAME, Javadoc.class).configure(javadoc -> {
|
||||
final SourceSet main = SourceSetHelper.getMainSourceSet(p);
|
||||
project.getTasks().named(JavaPlugin.JAVADOC_TASK_NAME, Javadoc.class).configure(javadoc -> {
|
||||
final SourceSet main = SourceSetHelper.getMainSourceSet(project);
|
||||
javadoc.setClasspath(main.getOutput().plus(main.getCompileClasspath()));
|
||||
});
|
||||
|
||||
p.afterEvaluate(project -> {
|
||||
GradleUtils.afterSuccessfulEvaluation(project, () -> {
|
||||
MinecraftSourceSets.get(project).afterEvaluate(project);
|
||||
|
||||
final boolean previousRefreshDeps = extension.refreshDeps();
|
||||
@@ -156,20 +157,20 @@ public final class CompileConfiguration {
|
||||
configureDecompileTasks(project);
|
||||
});
|
||||
|
||||
finalizedBy(p, "idea", "genIdeaWorkspace");
|
||||
finalizedBy(p, "eclipse", "genEclipseRuns");
|
||||
finalizedBy(p, "cleanEclipse", "cleanEclipseRuns");
|
||||
finalizedBy(project, "idea", "genIdeaWorkspace");
|
||||
finalizedBy(project, "eclipse", "genEclipseRuns");
|
||||
finalizedBy(project, "cleanEclipse", "cleanEclipseRuns");
|
||||
|
||||
// Add the "dev" jar to the "namedElements" configuration
|
||||
p.artifacts(artifactHandler -> artifactHandler.add(Constants.Configurations.NAMED_ELEMENTS, p.getTasks().named("jar")));
|
||||
project.artifacts(artifactHandler -> artifactHandler.add(Constants.Configurations.NAMED_ELEMENTS, project.getTasks().named("jar")));
|
||||
|
||||
// Ensure that the encoding is set to UTF-8, no matter what the system default is
|
||||
// this fixes some edge cases with special characters not displaying correctly
|
||||
// see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html
|
||||
p.getTasks().withType(AbstractCopyTask.class).configureEach(abstractCopyTask -> abstractCopyTask.setFilteringCharset(StandardCharsets.UTF_8.name()));
|
||||
p.getTasks().withType(JavaCompile.class).configureEach(javaCompile -> javaCompile.getOptions().setEncoding(StandardCharsets.UTF_8.name()));
|
||||
project.getTasks().withType(AbstractCopyTask.class).configureEach(abstractCopyTask -> abstractCopyTask.setFilteringCharset(StandardCharsets.UTF_8.name()));
|
||||
project.getTasks().withType(JavaCompile.class).configureEach(javaCompile -> javaCompile.getOptions().setEncoding(StandardCharsets.UTF_8.name()));
|
||||
|
||||
if (p.getPluginManager().hasPlugin("org.jetbrains.kotlin.kapt")) {
|
||||
if (project.getPluginManager().hasPlugin("org.jetbrains.kotlin.kapt")) {
|
||||
// If loom is applied after kapt, then kapt will use the AP arguments too early for loom to pass the arguments we need for mixin.
|
||||
throw new IllegalArgumentException("fabric-loom must be applied BEFORE kapt in the plugins { } block.");
|
||||
}
|
||||
|
||||
@@ -25,8 +25,6 @@
|
||||
package net.fabricmc.loom.configuration;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -41,8 +39,8 @@ import org.gradle.api.artifacts.repositories.MavenArtifactRepository;
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.LoomGradlePlugin;
|
||||
import net.fabricmc.loom.LoomRepositoryPlugin;
|
||||
import net.fabricmc.loom.configuration.mods.ModConfigurationRemapper;
|
||||
import net.fabricmc.loom.configuration.ide.idea.IdeaUtils;
|
||||
import net.fabricmc.loom.configuration.mods.ModConfigurationRemapper;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
import net.fabricmc.loom.util.SourceRemapper;
|
||||
import net.fabricmc.loom.util.ZipUtils;
|
||||
@@ -102,8 +100,8 @@ public class LoomDependencyManager {
|
||||
}
|
||||
|
||||
return LoomGradlePlugin.GSON.fromJson(new String(bytes, StandardCharsets.UTF_8), JsonObject.class);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException("Failed to try and read installer json from", e);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to try and read installer json from " + file, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ import org.gradle.api.publish.PublishingExtension;
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.util.DeprecationHelper;
|
||||
import net.fabricmc.loom.util.GroovyXmlUtil;
|
||||
import net.fabricmc.loom.util.gradle.GradleUtils;
|
||||
|
||||
public final class MavenPublication {
|
||||
// ImmutableMap is needed since it guarantees ordering
|
||||
@@ -61,14 +62,14 @@ public final class MavenPublication {
|
||||
}
|
||||
|
||||
public static void configure(Project project) {
|
||||
project.afterEvaluate((p) -> {
|
||||
GradleUtils.afterSuccessfulEvaluation(project, () -> {
|
||||
AtomicBoolean reportedDeprecation = new AtomicBoolean(false);
|
||||
|
||||
CONFIGURATION_TO_SCOPE.forEach((configurationName, scope) -> {
|
||||
Configuration config = p.getConfigurations().getByName(configurationName);
|
||||
Configuration config = project.getConfigurations().getByName(configurationName);
|
||||
|
||||
// add modsCompile to maven-publish
|
||||
PublishingExtension mavenPublish = p.getExtensions().findByType(PublishingExtension.class);
|
||||
PublishingExtension mavenPublish = project.getExtensions().findByType(PublishingExtension.class);
|
||||
|
||||
if (mavenPublish != null) {
|
||||
processEntry(project, scope, config, mavenPublish, reportedDeprecation);
|
||||
|
||||
@@ -76,11 +76,34 @@ public final class RemapConfigurations {
|
||||
// Apply the client target names to the main configurations
|
||||
for (ConfigurationOption option : getValidOptions(mainSourceSet)) {
|
||||
configurations.getByName(option.name(mainSourceSet), settings -> {
|
||||
settings.getClientTargetConfigurationName().convention(option.targetName(clientSourceSet));
|
||||
String name = option.targetName(clientSourceSet);
|
||||
|
||||
if (name == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (settings.getOnCompileClasspath().get()) {
|
||||
extendsFrom(Constants.Configurations.MOD_COMPILE_CLASSPATH_MAPPED, remappedConfiguration, project);
|
||||
|
||||
extendsFrom(clientSourceSet.getCompileClasspathConfigurationName(), remappedConfiguration, project);
|
||||
}
|
||||
|
||||
if (settings.getOnRuntimeClasspath().get()) {
|
||||
extendsFrom(clientSourceSet.getRuntimeClasspathConfigurationName(), remappedConfiguration, project);
|
||||
}
|
||||
}
|
||||
|
||||
public static void applyToProject(Project project, RemapConfigurationSettings settings) {
|
||||
// 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>
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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.configuration.mods;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
|
||||
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;
|
||||
|
||||
public class AccessWidenerUtils {
|
||||
/**
|
||||
* Remap a mods access widener from intermediary to named, so that loader can apply it in our dev-env.
|
||||
*/
|
||||
public static byte[] remapAccessWidener(byte[] input, Remapper remapper) {
|
||||
int version = AccessWidenerReader.readVersion(input);
|
||||
|
||||
AccessWidenerWriter writer = new AccessWidenerWriter(version);
|
||||
AccessWidenerRemapper awRemapper = new AccessWidenerRemapper(
|
||||
writer,
|
||||
remapper,
|
||||
MappingsNamespace.INTERMEDIARY.toString(),
|
||||
MappingsNamespace.NAMED.toString()
|
||||
);
|
||||
AccessWidenerReader reader = new AccessWidenerReader(awRemapper);
|
||||
reader.read(input);
|
||||
return writer.write();
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (!jsonObject.has("accessWidener")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String accessWidenerPath = jsonObject.get("accessWidener").getAsString();
|
||||
byte[] accessWidener = ZipUtils.unpack(inputJar, accessWidenerPath);
|
||||
AccessWidenerReader.Header header = AccessWidenerReader.readHeader(accessWidener);
|
||||
|
||||
return new AccessWidenerData(accessWidenerPath, header, accessWidener);
|
||||
}
|
||||
|
||||
public record AccessWidenerData(String path, AccessWidenerReader.Header header, byte[] content) {
|
||||
}
|
||||
}
|
||||
@@ -26,17 +26,23 @@ package net.fabricmc.loom.configuration.mods;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.Manifest;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.fabricmc.loom.task.AbstractRemapJarTask;
|
||||
import net.fabricmc.loom.util.FileSystemUtil;
|
||||
|
||||
@@ -47,15 +53,83 @@ public class JarSplitter {
|
||||
this.inputJar = inputJar;
|
||||
}
|
||||
|
||||
public boolean split(Path commonOutputJar, Path clientOutputJar) throws IOException {
|
||||
@Nullable
|
||||
public Target analyseTarget() {
|
||||
try (FileSystemUtil.Delegate input = FileSystemUtil.getJarFileSystem(inputJar)) {
|
||||
final Manifest manifest = input.fromInputStream(Manifest::new, AbstractRemapJarTask.MANIFEST_PATH);
|
||||
|
||||
if (!Boolean.parseBoolean(manifest.getMainAttributes().getValue(AbstractRemapJarTask.MANIFEST_SPLIT_ENV_KEY))) {
|
||||
// Jar was not built with splitting enabled.
|
||||
return null;
|
||||
}
|
||||
|
||||
final HashSet<String> clientEntries = new HashSet<>(readClientEntries(manifest));
|
||||
|
||||
if (clientEntries.isEmpty()) {
|
||||
// No client entries.
|
||||
return Target.COMMON_ONLY;
|
||||
}
|
||||
|
||||
final List<String> entries = new LinkedList<>();
|
||||
|
||||
// Must collect all the input entries to see if this might be a client only jar.
|
||||
try (Stream<Path> walk = Files.walk(input.get().getPath("/"))) {
|
||||
final Iterator<Path> iterator = walk.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
final Path entry = iterator.next();
|
||||
|
||||
if (!Files.isRegularFile(entry)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final Path relativePath = input.get().getPath("/").relativize(entry);
|
||||
|
||||
if (relativePath.startsWith("META-INF")) {
|
||||
if (isSignatureData(relativePath)) {
|
||||
// Ignore any signature data
|
||||
continue;
|
||||
}
|
||||
|
||||
if (relativePath.endsWith("MANIFEST.MF")) {
|
||||
// Ignore the manifest
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
entries.add(relativePath.toString());
|
||||
}
|
||||
}
|
||||
|
||||
for (String entry : entries) {
|
||||
if (!clientEntries.contains(entry)) {
|
||||
// Found a common entry, we need to split,.
|
||||
return Target.SPLIT;
|
||||
}
|
||||
}
|
||||
|
||||
// All input entries are client only entries.
|
||||
return Target.CLIENT_ONLY;
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException("Failed to read jar", e);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean split(Path commonOutputJar, Path clientOutputJar) throws IOException {
|
||||
Files.deleteIfExists(commonOutputJar);
|
||||
Files.deleteIfExists(clientOutputJar);
|
||||
|
||||
try (FileSystemUtil.Delegate input = FileSystemUtil.getJarFileSystem(inputJar)) {
|
||||
final Manifest manifest = input.fromInputStream(Manifest::new, AbstractRemapJarTask.MANIFEST_PATH);
|
||||
|
||||
if (!Boolean.parseBoolean(manifest.getMainAttributes().getValue(AbstractRemapJarTask.MANIFEST_SPLIT_ENV_KEY))) {
|
||||
throw new UnsupportedOperationException("Cannot split jar that has not been built with a split env");
|
||||
}
|
||||
|
||||
final List<String> clientEntries = readClientEntries(manifest);
|
||||
|
||||
if (clientEntries.isEmpty()) {
|
||||
// No client entries, just copy the input jar
|
||||
Files.copy(inputJar, commonOutputJar);
|
||||
return false;
|
||||
throw new IllegalStateException("Expected to split jar with no client entries");
|
||||
}
|
||||
|
||||
try (FileSystemUtil.Delegate commonOutput = FileSystemUtil.getJarFileSystem(commonOutputJar, true);
|
||||
@@ -126,15 +200,10 @@ public class JarSplitter {
|
||||
|
||||
private List<String> readClientEntries(Manifest manifest) {
|
||||
final Attributes attributes = manifest.getMainAttributes();
|
||||
final String splitEnvValue = attributes.getValue(AbstractRemapJarTask.MANIFEST_SPLIT_ENV_KEY);
|
||||
final String clientEntriesValue = attributes.getValue(AbstractRemapJarTask.MANIFEST_CLIENT_ENTRIES_KEY);
|
||||
|
||||
if (splitEnvValue == null || !splitEnvValue.equals("true")) {
|
||||
throw new UnsupportedOperationException("Cannot split jar that has not been built with a split env");
|
||||
}
|
||||
|
||||
if (clientEntriesValue == null) {
|
||||
throw new IllegalStateException("Split jar does not contain any client only classes");
|
||||
if (clientEntriesValue == null || clientEntriesValue.isBlank()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return Arrays.stream(clientEntriesValue.split(";")).toList();
|
||||
@@ -175,4 +244,25 @@ public class JarSplitter {
|
||||
|
||||
Files.write(path, bytes);
|
||||
}
|
||||
|
||||
public enum Target {
|
||||
COMMON_ONLY(true, false),
|
||||
CLIENT_ONLY(false, true),
|
||||
SPLIT(true, true);
|
||||
|
||||
final boolean common, client;
|
||||
|
||||
Target(boolean common, boolean client) {
|
||||
this.common = common;
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
public boolean common() {
|
||||
return common;
|
||||
}
|
||||
|
||||
public boolean client() {
|
||||
return client;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,12 +26,13 @@ package net.fabricmc.loom.configuration.mods;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.google.common.io.Files;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.artifacts.Configuration;
|
||||
import org.gradle.api.artifacts.FileCollectionDependency;
|
||||
@@ -50,8 +51,8 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.api.RemapConfigurationSettings;
|
||||
import net.fabricmc.loom.configuration.processors.dependency.ModDependencyInfo;
|
||||
import net.fabricmc.loom.configuration.processors.dependency.RemapData;
|
||||
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;
|
||||
@@ -67,9 +68,6 @@ public class ModConfigurationRemapper {
|
||||
public static void supplyModConfigurations(Project project, String mappingsSuffix, LoomGradleExtension extension, SourceRemapper sourceRemapper) {
|
||||
final DependencyHandler dependencies = project.getDependencies();
|
||||
|
||||
final File modStore = extension.getFiles().getRemappedModCache();
|
||||
final RemapData remapData = new RemapData(mappingsSuffix, modStore);
|
||||
|
||||
for (RemapConfigurationSettings entry : extension.getRemapConfigurations()) {
|
||||
entry.getRemappedConfiguration().configure(remappedConfig -> {
|
||||
/*
|
||||
@@ -79,15 +77,15 @@ public class ModConfigurationRemapper {
|
||||
*/
|
||||
final Configuration sourceConfig = entry.getSourceConfiguration().get();
|
||||
final Configuration targetConfig = entry.getTargetConfiguration().get();
|
||||
final boolean hasClientTarget = entry.getClientTargetConfigurationName().isPresent();
|
||||
final boolean hasClientTarget = entry.getClientSourceConfigurationName().isPresent();
|
||||
|
||||
Configuration clientRemappedConfig = null;
|
||||
|
||||
if (hasClientTarget) {
|
||||
clientRemappedConfig = entry.getClientTargetConfiguration().get();
|
||||
clientRemappedConfig = entry.getClientRemappedConfiguration().get();
|
||||
}
|
||||
|
||||
final List<ModDependencyInfo> modDependencies = new ArrayList<>();
|
||||
final List<ModDependency> modDependencies = new ArrayList<>();
|
||||
|
||||
for (ArtifactRef artifact : resolveArtifacts(project, sourceConfig)) {
|
||||
if (!ModUtils.isMod(artifact.path())) {
|
||||
@@ -95,47 +93,36 @@ public class ModConfigurationRemapper {
|
||||
continue;
|
||||
}
|
||||
|
||||
final ModDependencyInfo info = new ModDependencyInfo(artifact, remappedConfig, clientRemappedConfig, remapData);
|
||||
|
||||
if (extension.refreshDeps()) {
|
||||
info.forceRemap();
|
||||
}
|
||||
|
||||
if (artifact.sources() != null) {
|
||||
scheduleSourcesRemapping(project, sourceRemapper, artifact.sources().toFile(), info.getRemappedOutput("sources"));
|
||||
}
|
||||
|
||||
modDependencies.add(info);
|
||||
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;
|
||||
}
|
||||
|
||||
try {
|
||||
new ModProcessor(project, sourceConfig).processMods(modDependencies);
|
||||
} catch (IOException e) {
|
||||
// Failed to remap, lets clean up to ensure we try again next time
|
||||
modDependencies.forEach(info -> info.getRemappedOutput().delete());
|
||||
throw new RuntimeException("Failed to remap mods", e);
|
||||
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).processMods(toRemap);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException("Failed to remap mods", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Add all of the remapped mods onto the config
|
||||
for (ModDependencyInfo info : modDependencies) {
|
||||
project.getDependencies().add(info.targetConfig.getName(), info.getRemappedNotation());
|
||||
for (ModDependency info : modDependencies) {
|
||||
info.applyToProject(project);
|
||||
createConstraints(info.getInputArtifact(), targetConfig, sourceConfig, dependencies);
|
||||
|
||||
if (info.getArtifact() instanceof ArtifactRef.ResolvedArtifactRef mavenArtifact) {
|
||||
final String dependencyCoordinate = "%s:%s".formatted(mavenArtifact.group(), mavenArtifact.name());
|
||||
|
||||
// Prevent adding the same un-remapped dependency to the target configuration.
|
||||
targetConfig.getDependencyConstraints().add(dependencies.getConstraints().create(dependencyCoordinate, constraint -> {
|
||||
constraint.because("configuration (%s) already contains the remapped module from configuration (%s)".formatted(
|
||||
targetConfig.getName(),
|
||||
sourceConfig.getName()
|
||||
));
|
||||
|
||||
constraint.version(MutableVersionConstraint::rejectAll);
|
||||
}));
|
||||
if (clientRemappedConfig != null) {
|
||||
createConstraints(info.getInputArtifact(), entry.getClientTargetConfiguration().get(), sourceConfig, dependencies);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,6 +134,27 @@ public class ModConfigurationRemapper {
|
||||
}
|
||||
}
|
||||
|
||||
private static void createConstraints(ArtifactRef artifact, Configuration targetConfig, Configuration sourceConfig, DependencyHandler dependencies) {
|
||||
if (true) {
|
||||
// Disabled due to the gradle module metadata causing issues. Try the MavenProject test to reproduce issue.
|
||||
return;
|
||||
}
|
||||
|
||||
if (artifact instanceof ArtifactRef.ResolvedArtifactRef mavenArtifact) {
|
||||
final String dependencyCoordinate = "%s:%s".formatted(mavenArtifact.group(), mavenArtifact.name());
|
||||
|
||||
// Prevent adding the same un-remapped dependency to the target configuration.
|
||||
targetConfig.getDependencyConstraints().add(dependencies.getConstraints().create(dependencyCoordinate, constraint -> {
|
||||
constraint.because("configuration (%s) already contains the remapped module from configuration (%s)".formatted(
|
||||
targetConfig.getName(),
|
||||
sourceConfig.getName()
|
||||
));
|
||||
|
||||
constraint.version(MutableVersionConstraint::rejectAll);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
private static List<ArtifactRef> resolveArtifacts(Project project, Configuration configuration) {
|
||||
final List<ArtifactRef> artifacts = new ArrayList<>();
|
||||
|
||||
@@ -162,9 +170,8 @@ public class ModConfigurationRemapper {
|
||||
final FileCollection files = dependency.getFiles();
|
||||
|
||||
for (File artifact : files) {
|
||||
final String name = Files.getNameWithoutExtension(artifact.getAbsolutePath());
|
||||
final String name = getNameWithoutExtension(artifact.toPath());
|
||||
final String version = replaceIfNullOrEmpty(dependency.getVersion(), () -> Checksum.truncatedSha256(artifact));
|
||||
|
||||
artifacts.add(new ArtifactRef.FileArtifactRef(artifact.toPath(), group, name, version));
|
||||
}
|
||||
}
|
||||
@@ -172,6 +179,12 @@ public class ModConfigurationRemapper {
|
||||
return artifacts;
|
||||
}
|
||||
|
||||
private static String getNameWithoutExtension(Path file) {
|
||||
final String fileName = file.getFileName().toString();
|
||||
final int dotIndex = fileName.lastIndexOf('.');
|
||||
return (dotIndex == -1) ? fileName : fileName.substring(0, dotIndex);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Path findSources(Project project, ResolvedArtifact artifact) {
|
||||
final DependencyHandler dependencies = project.getDependencies();
|
||||
@@ -191,15 +204,27 @@ public class ModConfigurationRemapper {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void scheduleSourcesRemapping(Project project, SourceRemapper sourceRemapper, File input, File output) {
|
||||
private static void scheduleSourcesRemapping(Project project, SourceRemapper sourceRemapper, ModDependency dependency) {
|
||||
if (OperatingSystem.isCIBuild()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!output.exists() || input.lastModified() <= 0 || input.lastModified() > output.lastModified() || LoomGradleExtension.get(project).refreshDeps()) {
|
||||
sourceRemapper.scheduleRemapSources(input, output, false, true); // Depenedency sources are used in ide only so don't need to be reproducable
|
||||
} else {
|
||||
project.getLogger().info(output.getName() + " is up to date with " + input.getName());
|
||||
final Path sourcesInput = dependency.getInputArtifact().sources();
|
||||
|
||||
if (sourcesInput == null || Files.notExists(sourcesInput)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (dependency.isCacheInvalid(project, "sources")) {
|
||||
final Path output = dependency.getWorkingFile("sources");
|
||||
|
||||
sourceRemapper.scheduleRemapSources(sourcesInput.toFile(), output.toFile(), false, true, () -> {
|
||||
try {
|
||||
dependency.copyToCache(project, output, "sources");
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException("Failed to apply sources to local cache for: " + dependency, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,6 @@ import java.io.IOException;
|
||||
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.List;
|
||||
import java.util.Locale;
|
||||
@@ -41,18 +40,15 @@ import java.util.jar.Manifest;
|
||||
import com.google.gson.JsonObject;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.artifacts.Configuration;
|
||||
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.LoomGradleExtension;
|
||||
import net.fabricmc.loom.api.RemapConfigurationSettings;
|
||||
import net.fabricmc.loom.api.mappings.layered.MappingsNamespace;
|
||||
import net.fabricmc.loom.configuration.processors.dependency.ModDependencyInfo;
|
||||
import net.fabricmc.loom.configuration.mods.dependency.ModDependency;
|
||||
import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl;
|
||||
import net.fabricmc.loom.task.RemapJarTask;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
import net.fabricmc.loom.util.Pair;
|
||||
import net.fabricmc.loom.util.TinyRemapperHelper;
|
||||
import net.fabricmc.loom.util.ZipUtils;
|
||||
import net.fabricmc.loom.util.kotlin.KotlinClasspathService;
|
||||
@@ -74,75 +70,28 @@ public class ModProcessor {
|
||||
this.sourceConfiguration = sourceConfiguration;
|
||||
}
|
||||
|
||||
public void processMods(List<ModDependencyInfo> processList) throws IOException {
|
||||
ArrayList<ModDependencyInfo> remapList = new ArrayList<>();
|
||||
|
||||
for (ModDependencyInfo info : processList) {
|
||||
if (info.requiresRemapping()) {
|
||||
project.getLogger().debug("{} requires remapping", info.getInputFile());
|
||||
Files.deleteIfExists(info.getRemappedOutput().toPath());
|
||||
|
||||
remapList.add(info);
|
||||
}
|
||||
}
|
||||
|
||||
if (remapList.isEmpty()) {
|
||||
project.getLogger().debug("No mods to remap, skipping");
|
||||
return;
|
||||
}
|
||||
|
||||
public void processMods(List<ModDependency> remapList) throws IOException {
|
||||
try {
|
||||
project.getLogger().lifecycle(":remapping {} mods from {}", remapList.size(), sourceConfiguration.getName());
|
||||
remapJars(remapList);
|
||||
} catch (Exception e) {
|
||||
project.getLogger().error(String.format(Locale.ENGLISH, "Failed to remap %d mods", remapList.size()), e);
|
||||
|
||||
for (ModDependencyInfo info : remapList) {
|
||||
Files.deleteIfExists(info.getRemappedOutput().toPath());
|
||||
}
|
||||
|
||||
throw e;
|
||||
}
|
||||
|
||||
// Check all the mods we expect exist
|
||||
for (ModDependencyInfo info : processList) {
|
||||
if (!info.getRemappedOutput().exists()) {
|
||||
throw new RuntimeException("Failed to find remapped mod: " + info);
|
||||
}
|
||||
throw new RuntimeException(String.format(Locale.ENGLISH, "Failed to remap %d mods", remapList.size()), e);
|
||||
}
|
||||
}
|
||||
|
||||
private void stripNestedJars(File file) {
|
||||
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 {
|
||||
ZipUtils.transformJson(JsonObject.class, file.toPath(), Map.of("fabric.mod.json", json -> {
|
||||
ZipUtils.transformJson(JsonObject.class, path, Map.of("fabric.mod.json", json -> {
|
||||
json.remove("jars");
|
||||
return json;
|
||||
}));
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException("Failed to strip nested jars from %s".formatted(file), e);
|
||||
throw new UncheckedIOException("Failed to strip nested jars from %s".formatted(path), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remap another mod's access widener from intermediary to named, so that loader can apply it in our dev-env.
|
||||
*/
|
||||
private byte[] remapAccessWidener(byte[] input, Remapper remapper) {
|
||||
int version = AccessWidenerReader.readVersion(input);
|
||||
|
||||
AccessWidenerWriter writer = new AccessWidenerWriter(version);
|
||||
AccessWidenerRemapper awRemapper = new AccessWidenerRemapper(
|
||||
writer,
|
||||
remapper,
|
||||
MappingsNamespace.INTERMEDIARY.toString(),
|
||||
MappingsNamespace.NAMED.toString()
|
||||
);
|
||||
AccessWidenerReader reader = new AccessWidenerReader(awRemapper);
|
||||
reader.read(input);
|
||||
return writer.write();
|
||||
}
|
||||
|
||||
private void remapJars(List<ModDependencyInfo> remapList) throws IOException {
|
||||
private void remapJars(List<ModDependency> remapList) throws IOException {
|
||||
final LoomGradleExtension extension = LoomGradleExtension.get(project);
|
||||
final MappingsProviderImpl mappingsProvider = extension.getMappingsProvider();
|
||||
Path[] mcDeps = project.getConfigurations().getByName(Constants.Configurations.LOADER_DEPENDENCIES).getFiles()
|
||||
@@ -168,13 +117,13 @@ public class ModProcessor {
|
||||
|
||||
remapper.readClassPathAsync(mcDeps);
|
||||
|
||||
final Map<ModDependencyInfo, InputTag> tagMap = new HashMap<>();
|
||||
final Map<ModDependencyInfo, OutputConsumerPath> outputConsumerMap = new HashMap<>();
|
||||
final Map<ModDependencyInfo, byte[]> accessWidenerMap = new HashMap<>();
|
||||
final Map<ModDependency, InputTag> tagMap = new HashMap<>();
|
||||
final Map<ModDependency, OutputConsumerPath> outputConsumerMap = new HashMap<>();
|
||||
final Map<ModDependency, Pair<byte[], String>> accessWidenerMap = new HashMap<>();
|
||||
|
||||
for (RemapConfigurationSettings entry : extension.getRemapConfigurations()) {
|
||||
for (File inputFile : entry.getSourceConfiguration().get().getFiles()) {
|
||||
if (remapList.stream().noneMatch(info -> info.getInputFile().equals(inputFile))) {
|
||||
if (remapList.stream().noneMatch(info -> info.getInputFile().toFile().equals(inputFile))) {
|
||||
project.getLogger().debug("Adding " + inputFile + " onto the remap classpath");
|
||||
|
||||
remapper.readClassPathAsync(inputFile.toPath());
|
||||
@@ -182,35 +131,37 @@ public class ModProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
for (ModDependencyInfo info : remapList) {
|
||||
for (ModDependency info : remapList) {
|
||||
InputTag tag = remapper.createInputTag();
|
||||
|
||||
project.getLogger().debug("Adding " + info.getInputFile() + " as a remap input");
|
||||
|
||||
remapper.readInputsAsync(tag, info.getInputFile().toPath());
|
||||
remapper.readInputsAsync(tag, info.getInputFile());
|
||||
tagMap.put(info, tag);
|
||||
|
||||
Files.deleteIfExists(getRemappedOutput(info));
|
||||
}
|
||||
|
||||
try {
|
||||
// Apply this in a second loop as we need to ensure all the inputs are on the classpath before remapping.
|
||||
for (ModDependencyInfo info : remapList) {
|
||||
for (ModDependency dependency : remapList) {
|
||||
try {
|
||||
OutputConsumerPath outputConsumer = new OutputConsumerPath.Builder(info.getRemappedOutput().toPath()).build();
|
||||
OutputConsumerPath outputConsumer = new OutputConsumerPath.Builder(getRemappedOutput(dependency)).build();
|
||||
|
||||
outputConsumer.addNonClassFiles(info.getInputFile().toPath(), NonClassCopyMode.FIX_META_INF, remapper);
|
||||
outputConsumerMap.put(info, outputConsumer);
|
||||
outputConsumer.addNonClassFiles(dependency.getInputFile(), NonClassCopyMode.FIX_META_INF, remapper);
|
||||
outputConsumerMap.put(dependency, outputConsumer);
|
||||
|
||||
final ModDependencyInfo.AccessWidenerData accessWidenerData = info.getAccessWidenerData();
|
||||
final AccessWidenerUtils.AccessWidenerData accessWidenerData = AccessWidenerUtils.readAccessWidenerData(dependency.getInputFile());
|
||||
|
||||
if (accessWidenerData != null) {
|
||||
project.getLogger().debug("Remapping access widener in {}", info.getInputFile());
|
||||
byte[] remappedAw = remapAccessWidener(accessWidenerData.content(), remapper.getEnvironment().getRemapper());
|
||||
accessWidenerMap.put(info, remappedAw);
|
||||
project.getLogger().debug("Remapping access widener in {}", dependency.getInputFile());
|
||||
byte[] remappedAw = AccessWidenerUtils.remapAccessWidener(accessWidenerData.content(), remapper.getEnvironment().getRemapper());
|
||||
accessWidenerMap.put(dependency, new Pair<>(remappedAw, accessWidenerData.path()));
|
||||
}
|
||||
|
||||
remapper.apply(outputConsumer, tagMap.get(info));
|
||||
remapper.apply(outputConsumer, tagMap.get(dependency));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to remap: " + info.getRemappedNotation(), e);
|
||||
throw new RuntimeException("Failed to remap: " + dependency, e);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
@@ -221,22 +172,26 @@ public class ModProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
for (ModDependencyInfo info : remapList) {
|
||||
outputConsumerMap.get(info).close();
|
||||
byte[] accessWidener = accessWidenerMap.get(info);
|
||||
for (ModDependency dependency : remapList) {
|
||||
outputConsumerMap.get(dependency).close();
|
||||
|
||||
final Path output = getRemappedOutput(dependency);
|
||||
final Pair<byte[], String> accessWidener = accessWidenerMap.get(dependency);
|
||||
|
||||
if (accessWidener != null) {
|
||||
assert info.getAccessWidenerData() != null;
|
||||
ZipUtils.replace(info.getRemappedOutput().toPath(), info.getAccessWidenerData().path(), accessWidener);
|
||||
ZipUtils.replace(output, accessWidener.right(), accessWidener.left());
|
||||
}
|
||||
|
||||
stripNestedJars(info.getRemappedOutput());
|
||||
remapJarManifestEntries(info.getRemappedOutput().toPath());
|
||||
|
||||
info.finaliseRemapping();
|
||||
stripNestedJars(output);
|
||||
remapJarManifestEntries(output);
|
||||
dependency.copyToCache(project, output, null);
|
||||
}
|
||||
}
|
||||
|
||||
private static Path getRemappedOutput(ModDependency dependency) {
|
||||
return dependency.getWorkingFile(null);
|
||||
}
|
||||
|
||||
private void remapJarManifestEntries(Path jar) throws IOException {
|
||||
ZipUtils.transform(jar, Map.of(RemapJarTask.MANIFEST_PATH, bytes -> {
|
||||
var manifest = new Manifest(new ByteArrayInputStream(bytes));
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* 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.configuration.mods.dependency;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
|
||||
import org.gradle.api.Project;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
|
||||
public final class LocalMavenHelper {
|
||||
private final String group;
|
||||
private final String name;
|
||||
private final String version;
|
||||
@Nullable
|
||||
private final String baseClassifier;
|
||||
private final Project project;
|
||||
|
||||
LocalMavenHelper(String group, String name, String version, @Nullable String classifier, Project project) {
|
||||
this.group = group;
|
||||
this.name = name;
|
||||
this.version = version;
|
||||
this.baseClassifier = classifier;
|
||||
this.project = project;
|
||||
}
|
||||
|
||||
public Path copyToMaven(Path artifact, @Nullable String classifier) throws IOException {
|
||||
if (!artifact.getFileName().toString().endsWith(".jar")) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
Files.createDirectories(getDirectory());
|
||||
savePom();
|
||||
return Files.copy(artifact, getOutputFile(classifier), StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
|
||||
public boolean exists(String classifier) {
|
||||
return Files.exists(getOutputFile(classifier)) && Files.exists(getPomPath());
|
||||
}
|
||||
|
||||
public String getNotation() {
|
||||
if (baseClassifier != null) {
|
||||
return String.format("%s:%s:%s:%s", group, name, version, baseClassifier);
|
||||
}
|
||||
|
||||
return String.format("%s:%s:%s", group, name, version);
|
||||
}
|
||||
|
||||
private void savePom() {
|
||||
try {
|
||||
String pomTemplate;
|
||||
|
||||
try (InputStream input = ModDependency.class.getClassLoader().getResourceAsStream("mod_compile_template.pom")) {
|
||||
pomTemplate = new String(input.readAllBytes(), StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
pomTemplate = pomTemplate
|
||||
.replace("%GROUP%", group)
|
||||
.replace("%NAME%", name)
|
||||
.replace("%VERSION%", version);
|
||||
|
||||
Files.writeString(getPomPath(), pomTemplate, StandardCharsets.UTF_8);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException("Failed to write mod pom", e);
|
||||
}
|
||||
}
|
||||
|
||||
private Path getRoot() {
|
||||
final LoomGradleExtension extension = LoomGradleExtension.get(project);
|
||||
return extension.getFiles().getRemappedModCache().toPath();
|
||||
}
|
||||
|
||||
private Path getDirectory() {
|
||||
return getRoot().resolve("%s/%s/%s".formatted(group.replace(".", "/"), name, version));
|
||||
}
|
||||
|
||||
private Path getPomPath() {
|
||||
return getDirectory().resolve("%s-%s.pom".formatted(name, version));
|
||||
}
|
||||
|
||||
public Path getOutputFile(@Nullable String classifier) {
|
||||
if (classifier == null) {
|
||||
classifier = baseClassifier;
|
||||
}
|
||||
|
||||
final String fileName = classifier == null ? String.format("%s-%s.jar", name, version)
|
||||
: String.format("%s-%s-%s.jar", name, version, classifier);
|
||||
return getDirectory().resolve(fileName);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* This file is part of fabric-loom, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) 2020-2021 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.mods.dependency;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import org.gradle.api.Project;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.configuration.mods.ArtifactRef;
|
||||
|
||||
public abstract sealed class ModDependency permits SplitModDependency, SimpleModDependency {
|
||||
private final ArtifactRef artifact;
|
||||
protected final String group;
|
||||
protected final String name;
|
||||
protected final String version;
|
||||
@Nullable
|
||||
protected final String classifier;
|
||||
protected final String mappingsSuffix;
|
||||
protected final Project project;
|
||||
|
||||
public ModDependency(ArtifactRef artifact, String mappingsSuffix, Project project) {
|
||||
this.artifact = artifact;
|
||||
this.group = artifact.group();
|
||||
this.name = artifact.name();
|
||||
this.version = artifact.version();
|
||||
this.classifier = artifact.classifier();
|
||||
this.mappingsSuffix = mappingsSuffix;
|
||||
this.project = project;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true when the cache is invalid.
|
||||
*/
|
||||
public abstract boolean isCacheInvalid(Project project, @Nullable String variant);
|
||||
|
||||
/**
|
||||
* Write an artifact to the local cache.
|
||||
*/
|
||||
public abstract void copyToCache(Project project, Path path, @Nullable String variant) throws IOException;
|
||||
|
||||
/**
|
||||
* Apply the dependency to the project.
|
||||
*/
|
||||
public abstract void applyToProject(Project project);
|
||||
|
||||
protected LocalMavenHelper createMaven(String name) {
|
||||
return new LocalMavenHelper(getRemappedGroup(), name, this.version, this.classifier, this.project);
|
||||
}
|
||||
|
||||
public ArtifactRef getInputArtifact() {
|
||||
return artifact;
|
||||
}
|
||||
|
||||
protected String getRemappedGroup() {
|
||||
return getMappingsPrefix() + "." + group;
|
||||
}
|
||||
|
||||
private String getMappingsPrefix() {
|
||||
return mappingsSuffix.replace(".", "_").replace("-", "_").replace("+", "_");
|
||||
}
|
||||
|
||||
public Path getInputFile() {
|
||||
return artifact.path();
|
||||
}
|
||||
|
||||
public Path getWorkingFile(@Nullable String classifier) {
|
||||
final LoomGradleExtension extension = LoomGradleExtension.get(project);
|
||||
final String fileName = classifier == null ? String.format("%s-%s-%s.jar", getRemappedGroup(), name, version)
|
||||
: String.format("%s-%s-%s-%s.jar", getRemappedGroup(), name, version, classifier);
|
||||
|
||||
return extension.getFiles().getProjectBuildCache().toPath().resolve("remapped_working").resolve(fileName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ModDependency{" + "group='" + group + '\'' + ", name='" + name + '\'' + ", version='" + version + '\'' + ", classifier='" + classifier + '\'' + '}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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.configuration.mods.dependency;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.artifacts.Configuration;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.configuration.mods.ArtifactRef;
|
||||
import net.fabricmc.loom.configuration.mods.JarSplitter;
|
||||
import net.fabricmc.loom.util.AttributeHelper;
|
||||
|
||||
public class ModDependencyFactory {
|
||||
private static final String TARGET_ATTRIBUTE_KEY = "loom-target";
|
||||
|
||||
public static ModDependency create(ArtifactRef artifact, Configuration targetConfig, @Nullable Configuration targetClientConfig, String mappingsSuffix, Project project) {
|
||||
if (targetClientConfig != null && LoomGradleExtension.get(project).getSplitModDependencies().get()) {
|
||||
final Optional<JarSplitter.Target> cachedTarget = readTarget(artifact);
|
||||
JarSplitter.Target target;
|
||||
|
||||
if (cachedTarget.isPresent()) {
|
||||
target = cachedTarget.get();
|
||||
} else {
|
||||
target = new JarSplitter(artifact.path()).analyseTarget();
|
||||
writeTarget(artifact, target);
|
||||
}
|
||||
|
||||
if (target != null) {
|
||||
return new SplitModDependency(artifact, mappingsSuffix, targetConfig, targetClientConfig, target, project);
|
||||
}
|
||||
}
|
||||
|
||||
return new SimpleModDependency(artifact, mappingsSuffix, targetConfig, project);
|
||||
}
|
||||
|
||||
private static Optional<JarSplitter.Target> readTarget(ArtifactRef artifact) {
|
||||
try {
|
||||
return AttributeHelper.readAttribute(artifact.path(), TARGET_ATTRIBUTE_KEY).map(s -> {
|
||||
if ("null".equals(s)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return JarSplitter.Target.valueOf(s);
|
||||
});
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException("Failed to read artifact target attribute", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeTarget(ArtifactRef artifact, JarSplitter.Target target) {
|
||||
final String value = target != null ? target.name() : "null";
|
||||
|
||||
try {
|
||||
AttributeHelper.writeAttribute(artifact.path(), TARGET_ATTRIBUTE_KEY, value);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException("Failed to write artifact target attribute", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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.configuration.mods.dependency;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.artifacts.Configuration;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.fabricmc.loom.configuration.mods.ArtifactRef;
|
||||
|
||||
// Single jar in and out
|
||||
public final class SimpleModDependency extends ModDependency {
|
||||
private final Configuration targetConfig;
|
||||
private final LocalMavenHelper maven;
|
||||
|
||||
public SimpleModDependency(ArtifactRef artifact, String mappingsSuffix, Configuration targetConfig, Project project) {
|
||||
super(artifact, mappingsSuffix, project);
|
||||
this.targetConfig = Objects.requireNonNull(targetConfig);
|
||||
this.maven = createMaven(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCacheInvalid(Project project, @Nullable String variant) {
|
||||
return !maven.exists(variant);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyToCache(Project project, Path path, @Nullable String variant) throws IOException {
|
||||
maven.copyToMaven(path, variant);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyToProject(Project project) {
|
||||
project.getDependencies().add(targetConfig.getName(), maven.getNotation());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* 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.configuration.mods.dependency;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.artifacts.Configuration;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.configuration.mods.ArtifactRef;
|
||||
import net.fabricmc.loom.configuration.mods.JarSplitter;
|
||||
|
||||
// Single jar in, 2 out.
|
||||
public final class SplitModDependency extends ModDependency {
|
||||
private final Configuration targetCommonConfig;
|
||||
private final Configuration targetClientConfig;
|
||||
private final JarSplitter.Target target;
|
||||
@Nullable
|
||||
private final LocalMavenHelper commonMaven;
|
||||
@Nullable
|
||||
private final LocalMavenHelper clientMaven;
|
||||
|
||||
public SplitModDependency(ArtifactRef artifact, String mappingsSuffix, Configuration targetCommonConfig, Configuration targetClientConfig, JarSplitter.Target target, Project project) {
|
||||
super(artifact, mappingsSuffix, project);
|
||||
this.targetCommonConfig = Objects.requireNonNull(targetCommonConfig);
|
||||
this.targetClientConfig = Objects.requireNonNull(targetClientConfig);
|
||||
this.target = Objects.requireNonNull(target);
|
||||
this.commonMaven = target.common() ? createMaven(name + "-common") : null;
|
||||
this.clientMaven = target.client() ? createMaven(name + "-client") : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCacheInvalid(Project project, @Nullable String variant) {
|
||||
boolean exists = switch (target) {
|
||||
case COMMON_ONLY -> getCommonMaven().exists(variant);
|
||||
case CLIENT_ONLY -> getClientMaven().exists(variant);
|
||||
case SPLIT -> getCommonMaven().exists(variant) && getClientMaven().exists(variant);
|
||||
};
|
||||
|
||||
return !exists;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyToCache(Project project, Path path, @Nullable String variant) throws IOException {
|
||||
// Split dependencies build with loom 0.12 do not contain the required data to split the sources
|
||||
if (target == JarSplitter.Target.SPLIT && variant != null) {
|
||||
final JarSplitter.Target artifactTarget = new JarSplitter(path).analyseTarget();
|
||||
|
||||
if (artifactTarget != target) {
|
||||
// Found a broken artifact, copy it to both locations without splitting.
|
||||
getCommonMaven().copyToMaven(path, variant);
|
||||
getClientMaven().copyToMaven(path, variant);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch (target) {
|
||||
// Split the jar into 2
|
||||
case SPLIT -> {
|
||||
final String suffix = variant == null ? "" : "-" + variant;
|
||||
final Path commonTempJar = getWorkingFile("common" + suffix);
|
||||
final Path clientTempJar = getWorkingFile("client" + suffix);
|
||||
|
||||
final JarSplitter splitter = new JarSplitter(path);
|
||||
splitter.split(commonTempJar, clientTempJar);
|
||||
|
||||
getCommonMaven().copyToMaven(commonTempJar, variant);
|
||||
getClientMaven().copyToMaven(clientTempJar, variant);
|
||||
}
|
||||
|
||||
// No splitting to be done, just copy the input jar to the respective location.
|
||||
case CLIENT_ONLY -> getClientMaven().copyToMaven(path, variant);
|
||||
case COMMON_ONLY -> getCommonMaven().copyToMaven(path, variant);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyToProject(Project project) {
|
||||
if (target.common()) {
|
||||
project.getDependencies().add(targetCommonConfig.getName(), getCommonMaven().getNotation());
|
||||
}
|
||||
|
||||
if (target.client()) {
|
||||
project.getDependencies().add(targetClientConfig.getName(), getClientMaven().getNotation());
|
||||
}
|
||||
|
||||
if (target == JarSplitter.Target.SPLIT) {
|
||||
createModGroup(
|
||||
getCommonMaven().getOutputFile(null),
|
||||
getClientMaven().getOutputFile(null)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public LocalMavenHelper getCommonMaven() {
|
||||
return Objects.requireNonNull(commonMaven, "Cannot get null common maven helper");
|
||||
}
|
||||
|
||||
public LocalMavenHelper getClientMaven() {
|
||||
return Objects.requireNonNull(clientMaven, "Cannot get null client maven helper");
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package net.fabricmc.loom.configuration.mods;
|
||||
package net.fabricmc.loom.configuration.processors;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
@@ -44,7 +44,6 @@ import org.slf4j.LoggerFactory;
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.api.RemapConfigurationSettings;
|
||||
import net.fabricmc.loom.api.mappings.layered.MappingsNamespace;
|
||||
import net.fabricmc.loom.configuration.processors.JarProcessor;
|
||||
import net.fabricmc.loom.task.GenerateSourcesTask;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
import net.fabricmc.loom.util.ModUtils;
|
||||
@@ -1,221 +0,0 @@
|
||||
/*
|
||||
* This file is part of fabric-loom, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) 2020-2021 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.dependency;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.gradle.api.artifacts.Configuration;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.fabricmc.accesswidener.AccessWidenerReader;
|
||||
import net.fabricmc.loom.LoomGradlePlugin;
|
||||
import net.fabricmc.loom.api.mappings.layered.MappingsNamespace;
|
||||
import net.fabricmc.loom.configuration.mods.ArtifactRef;
|
||||
import net.fabricmc.loom.util.ZipUtils;
|
||||
|
||||
public class ModDependencyInfo {
|
||||
private final ArtifactRef artifact;
|
||||
private final String group;
|
||||
public final String name;
|
||||
public final String version;
|
||||
@Nullable
|
||||
public final String classifier;
|
||||
public final Configuration targetConfig;
|
||||
@Nullable
|
||||
public final Configuration targetClientConfig;
|
||||
public final RemapData remapData;
|
||||
|
||||
@Nullable
|
||||
private final AccessWidenerData accessWidenerData;
|
||||
|
||||
private boolean forceRemap = false;
|
||||
|
||||
public ModDependencyInfo(ArtifactRef artifact, Configuration targetConfig, @Nullable Configuration targetClientConfig, RemapData remapData) {
|
||||
this.artifact = artifact;
|
||||
this.group = artifact.group();
|
||||
this.name = artifact.name();
|
||||
this.version = artifact.version();
|
||||
this.classifier = artifact.classifier();
|
||||
this.targetConfig = targetConfig;
|
||||
this.targetClientConfig = targetClientConfig;
|
||||
this.remapData = remapData;
|
||||
|
||||
try {
|
||||
this.accessWidenerData = readAccessWidenerData(artifact.path());
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException("Failed to read access widener data from" + artifact.path(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public ArtifactRef getArtifact() {
|
||||
return artifact;
|
||||
}
|
||||
|
||||
public String getRemappedNotation() {
|
||||
if (!hasClassifier()) {
|
||||
return String.format("%s:%s:%s", getGroup(), name, version);
|
||||
}
|
||||
|
||||
return String.format("%s:%s:%s:%s", getGroup(), name, version, classifier);
|
||||
}
|
||||
|
||||
public String getRemappedFilename(boolean withClassifier) {
|
||||
if (!hasClassifier() || !withClassifier) {
|
||||
return String.format("%s-%s", name, version);
|
||||
}
|
||||
|
||||
return String.format("%s-%s-%s", name, version, classifier);
|
||||
}
|
||||
|
||||
public File getRemappedDir() {
|
||||
return new File(remapData.modStore(), String.format("%s/%s/%s", getGroup().replace(".", "/"), name, version));
|
||||
}
|
||||
|
||||
public File getRemappedOutput() {
|
||||
return new File(getRemappedDir(), getRemappedFilename(true) + ".jar");
|
||||
}
|
||||
|
||||
public File getRemappedOutput(String classifier) {
|
||||
return new File(getRemappedDir(), getRemappedFilename(false) + "-" + classifier + ".jar");
|
||||
}
|
||||
|
||||
private File getRemappedPom() {
|
||||
return new File(getRemappedDir(), String.format("%s-%s", name, version) + ".pom");
|
||||
}
|
||||
|
||||
private String getGroup() {
|
||||
return getMappingsPrefix(remapData.mappingsSuffix()) + "." + group;
|
||||
}
|
||||
|
||||
public static String getMappingsPrefix(String mappings) {
|
||||
return mappings.replace(".", "_").replace("-", "_").replace("+", "_");
|
||||
}
|
||||
|
||||
public File getInputFile() {
|
||||
return artifact.path().toFile();
|
||||
}
|
||||
|
||||
private boolean outputHasInvalidAccessWidener() {
|
||||
if (accessWidenerData == null) {
|
||||
// This mod doesn't use an AW
|
||||
return false;
|
||||
}
|
||||
|
||||
assert getRemappedOutput().exists();
|
||||
final AccessWidenerData outputAWData;
|
||||
|
||||
try {
|
||||
outputAWData = readAccessWidenerData(getRemappedOutput().toPath());
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException("Failed to read output access widener data from " + getRemappedOutput(), e);
|
||||
}
|
||||
|
||||
if (outputAWData == null) {
|
||||
// We know for sure the input has an AW, something is wrong if the output hasn't got one.
|
||||
return true;
|
||||
}
|
||||
|
||||
// The output jar must have an AW in the "named" namespace.
|
||||
return !MappingsNamespace.NAMED.toString().equals(outputAWData.header().getNamespace());
|
||||
}
|
||||
|
||||
public boolean requiresRemapping() {
|
||||
final File inputFile = artifact.path().toFile();
|
||||
final long lastModified = inputFile.lastModified();
|
||||
return !getRemappedOutput().exists() || lastModified <= 0 || lastModified > getRemappedOutput().lastModified() || forceRemap || !getRemappedPom().exists() || outputHasInvalidAccessWidener();
|
||||
}
|
||||
|
||||
public void finaliseRemapping() {
|
||||
getRemappedOutput().setLastModified(artifact.path().toFile().lastModified());
|
||||
savePom();
|
||||
|
||||
// Validate that the remapped AW is what we want.
|
||||
if (outputHasInvalidAccessWidener()) {
|
||||
throw new RuntimeException("Failed to validate remapped access widener in " + getRemappedOutput());
|
||||
}
|
||||
}
|
||||
|
||||
private void savePom() {
|
||||
try {
|
||||
String pomTemplate;
|
||||
|
||||
try (InputStream input = ModDependencyInfo.class.getClassLoader().getResourceAsStream("mod_compile_template.pom")) {
|
||||
pomTemplate = new String(input.readAllBytes(), StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
pomTemplate = pomTemplate
|
||||
.replace("%GROUP%", getGroup())
|
||||
.replace("%NAME%", name)
|
||||
.replace("%VERSION%", version);
|
||||
|
||||
FileUtils.writeStringToFile(getRemappedPom(), pomTemplate, StandardCharsets.UTF_8);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to write mod pom", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void forceRemap() {
|
||||
forceRemap = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getRemappedNotation();
|
||||
}
|
||||
|
||||
public boolean hasClassifier() {
|
||||
return classifier != null && !classifier.isEmpty();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public AccessWidenerData getAccessWidenerData() {
|
||||
return accessWidenerData;
|
||||
}
|
||||
|
||||
private 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);
|
||||
|
||||
if (!jsonObject.has("accessWidener")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String accessWidenerPath = jsonObject.get("accessWidener").getAsString();
|
||||
byte[] accessWidener = ZipUtils.unpack(inputJar, accessWidenerPath);
|
||||
AccessWidenerReader.Header header = AccessWidenerReader.readHeader(accessWidener);
|
||||
|
||||
return new AccessWidenerData(accessWidenerPath, header, accessWidener);
|
||||
}
|
||||
|
||||
public record AccessWidenerData(String path, AccessWidenerReader.Header header, byte[] content) {
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* This file is part of fabric-loom, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) 2016-2021 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.dependency;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public record RemapData(String mappingsSuffix, File modStore) {
|
||||
}
|
||||
@@ -46,7 +46,6 @@ import net.fabricmc.loom.api.mappings.intermediate.IntermediateMappingsProvider;
|
||||
import net.fabricmc.loom.api.mappings.layered.spec.LayeredMappingSpecBuilder;
|
||||
import net.fabricmc.loom.configuration.RemapConfigurations;
|
||||
import net.fabricmc.loom.configuration.ide.RunConfigSettings;
|
||||
import net.fabricmc.loom.configuration.mods.ModVersionParser;
|
||||
import net.fabricmc.loom.configuration.processors.JarProcessor;
|
||||
import net.fabricmc.loom.configuration.providers.mappings.GradleMappingContext;
|
||||
import net.fabricmc.loom.configuration.providers.mappings.LayeredMappingSpec;
|
||||
@@ -71,6 +70,7 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA
|
||||
protected final Property<String> intermediary;
|
||||
protected final Property<IntermediateMappingsProvider> intermediateMappingsProvider;
|
||||
private final Property<Boolean> runtimeOnlyLog4j;
|
||||
private final Property<Boolean> splitModDependencies;
|
||||
private final Property<MinecraftJarConfiguration> minecraftJarConfiguration;
|
||||
private final Property<Boolean> splitEnvironmentalSourceSet;
|
||||
private final InterfaceInjectionExtensionAPI interfaceInjectionExtension;
|
||||
@@ -122,6 +122,9 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA
|
||||
this.runtimeOnlyLog4j = project.getObjects().property(Boolean.class).convention(false);
|
||||
this.runtimeOnlyLog4j.finalizeValueOnRead();
|
||||
|
||||
this.splitModDependencies = project.getObjects().property(Boolean.class).convention(true);
|
||||
this.splitModDependencies.finalizeValueOnRead();
|
||||
|
||||
this.interfaceInjectionExtension = project.getObjects().newInstance(InterfaceInjectionExtensionAPI.class);
|
||||
|
||||
this.splitEnvironmentalSourceSet = project.getObjects().property(Boolean.class).convention(false);
|
||||
@@ -267,6 +270,11 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA
|
||||
return runtimeOnlyLog4j;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Property<Boolean> getSplitModDependencies() {
|
||||
return splitModDependencies;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void splitEnvironmentSourceSets() {
|
||||
splitMinecraftJar();
|
||||
|
||||
@@ -232,7 +232,7 @@ public class LoomGradleExtensionImpl extends LoomGradleExtensionApiImpl implemen
|
||||
builder.offline();
|
||||
}
|
||||
|
||||
if (project.getGradle().getStartParameter().isRefreshDependencies() || Boolean.getBoolean("loom.refresh")) {
|
||||
if (refreshDeps()) {
|
||||
builder.forceDownload();
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package net.fabricmc.loom.configuration.mods;
|
||||
package net.fabricmc.loom.extension;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
@@ -64,7 +64,7 @@ import net.fabricmc.loom.api.decompilers.LoomDecompiler;
|
||||
import net.fabricmc.loom.api.mappings.layered.MappingsNamespace;
|
||||
import net.fabricmc.loom.configuration.accesswidener.TransitiveAccessWidenerMappingsProcessor;
|
||||
import net.fabricmc.loom.configuration.ifaceinject.InterfaceInjectionProcessor;
|
||||
import net.fabricmc.loom.configuration.mods.ModJavadocProcessor;
|
||||
import net.fabricmc.loom.configuration.processors.ModJavadocProcessor;
|
||||
import net.fabricmc.loom.decompilers.LineNumberRemapper;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
import net.fabricmc.loom.util.FileSystemUtil;
|
||||
|
||||
@@ -39,6 +39,7 @@ import net.fabricmc.loom.task.launch.GenerateDLIConfigTask;
|
||||
import net.fabricmc.loom.task.launch.GenerateLog4jConfigTask;
|
||||
import net.fabricmc.loom.task.launch.GenerateRemapClasspathTask;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
import net.fabricmc.loom.util.gradle.GradleUtils;
|
||||
|
||||
public final class LoomTasks {
|
||||
private LoomTasks() {
|
||||
@@ -87,7 +88,7 @@ public final class LoomTasks {
|
||||
registerRunTasks(tasks, project);
|
||||
|
||||
// Must be done in afterEvaluate to allow time for the build script to configure the jar config.
|
||||
project.afterEvaluate(p -> {
|
||||
GradleUtils.afterSuccessfulEvaluation(project, () -> {
|
||||
LoomGradleExtension extension = LoomGradleExtension.get(project);
|
||||
|
||||
if (extension.getMinecraftJarConfiguration().get() == MinecraftJarConfiguration.SERVER_ONLY) {
|
||||
@@ -150,7 +151,7 @@ public final class LoomTasks {
|
||||
extension.getRunConfigs().create("server", RunConfigSettings::server);
|
||||
|
||||
// Remove the client or server run config when not required. Done by name to not remove any possible custom run configs
|
||||
project.afterEvaluate(p -> {
|
||||
GradleUtils.afterSuccessfulEvaluation(project, () -> {
|
||||
String taskName = switch (extension.getMinecraftJarConfiguration().get()) {
|
||||
case SERVER_ONLY -> "client";
|
||||
case CLIENT_ONLY -> "server";
|
||||
|
||||
@@ -38,6 +38,7 @@ import org.gradle.api.tasks.bundling.Jar;
|
||||
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
import net.fabricmc.loom.util.gradle.GradleUtils;
|
||||
import net.fabricmc.loom.util.gradle.SourceSetHelper;
|
||||
|
||||
public class RemapTaskConfiguration {
|
||||
@@ -84,7 +85,7 @@ public class RemapTaskConfiguration {
|
||||
return;
|
||||
}
|
||||
|
||||
project.afterEvaluate(p -> {
|
||||
GradleUtils.afterSuccessfulEvaluation(project, () -> {
|
||||
// Remove -dev jars from the default jar task
|
||||
for (String configurationName : new String[] { JavaPlugin.API_ELEMENTS_CONFIGURATION_NAME, JavaPlugin.RUNTIME_ELEMENTS_CONFIGURATION_NAME }) {
|
||||
Configuration configuration = project.getConfigurations().getByName(configurationName);
|
||||
@@ -135,7 +136,7 @@ public class RemapTaskConfiguration {
|
||||
return;
|
||||
}
|
||||
|
||||
project.afterEvaluate(p -> {
|
||||
GradleUtils.afterSuccessfulEvaluation(project, () -> {
|
||||
final Task sourcesTask = project.getTasks().findByName(sourcesJarTaskName);
|
||||
|
||||
if (!(sourcesTask instanceof Jar sourcesJarTask)) {
|
||||
|
||||
61
src/main/java/net/fabricmc/loom/util/AttributeHelper.java
Normal file
61
src/main/java/net/fabricmc/loom/util/AttributeHelper.java
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.attribute.UserDefinedFileAttributeView;
|
||||
import java.util.Optional;
|
||||
|
||||
public final class AttributeHelper {
|
||||
private AttributeHelper() {
|
||||
}
|
||||
|
||||
public static Optional<String> readAttribute(Path path, String key) throws IOException {
|
||||
final UserDefinedFileAttributeView attributeView = Files.getFileAttributeView(path, UserDefinedFileAttributeView.class);
|
||||
|
||||
if (!attributeView.list().contains(key)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
final ByteBuffer buffer = ByteBuffer.allocate(attributeView.size(key));
|
||||
attributeView.read(key, buffer);
|
||||
buffer.flip();
|
||||
final String value = StandardCharsets.UTF_8.decode(buffer).toString();
|
||||
return Optional.of(value);
|
||||
}
|
||||
|
||||
public static void writeAttribute(Path path, String key, String value) throws IOException {
|
||||
// TODO may need to fallback to creating a separate file if this isnt supported.
|
||||
final UserDefinedFileAttributeView attributeView = Files.getFileAttributeView(path, UserDefinedFileAttributeView.class);
|
||||
final byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
|
||||
final ByteBuffer buffer = ByteBuffer.wrap(bytes);
|
||||
final int written = attributeView.write(key, buffer);
|
||||
assert written == bytes.length;
|
||||
}
|
||||
}
|
||||
@@ -61,13 +61,7 @@ public class SourceRemapper {
|
||||
this.toNamed = toNamed;
|
||||
}
|
||||
|
||||
public static void remapSources(Project project, File input, File output, boolean named, boolean reproducibleFileOrder, boolean preserveFileTimestamps) {
|
||||
SourceRemapper sourceRemapper = new SourceRemapper(project, named);
|
||||
sourceRemapper.scheduleRemapSources(input, output, reproducibleFileOrder, preserveFileTimestamps);
|
||||
sourceRemapper.remapAll();
|
||||
}
|
||||
|
||||
public void scheduleRemapSources(File source, File destination, boolean reproducibleFileOrder, boolean preserveFileTimestamps) {
|
||||
public void scheduleRemapSources(File source, File destination, boolean reproducibleFileOrder, boolean preserveFileTimestamps, Runnable completionCallback) {
|
||||
remapTasks.add((logger) -> {
|
||||
try {
|
||||
logger.progress("remapping sources - " + source.getName());
|
||||
@@ -76,6 +70,7 @@ public class SourceRemapper {
|
||||
|
||||
// Set the remapped sources creation date to match the sources if we're likely succeeded in making it
|
||||
destination.setLastModified(source.lastModified());
|
||||
completionCallback.run();
|
||||
} catch (Exception e) {
|
||||
// Failed to remap, lets clean up to ensure we try again next time
|
||||
destination.delete();
|
||||
|
||||
@@ -34,14 +34,12 @@ import java.net.http.HttpClient;
|
||||
import java.net.http.HttpHeaders;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.attribute.BasicFileAttributeView;
|
||||
import java.nio.file.attribute.FileTime;
|
||||
import java.nio.file.attribute.UserDefinedFileAttributeView;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Locale;
|
||||
@@ -53,6 +51,7 @@ import com.github.mizosoft.methanol.ProgressTracker;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import net.fabricmc.loom.util.AttributeHelper;
|
||||
import net.fabricmc.loom.util.Checksum;
|
||||
|
||||
public class Download {
|
||||
@@ -226,6 +225,11 @@ public class Download {
|
||||
}
|
||||
|
||||
private boolean requiresDownload(Path output) throws DownloadException {
|
||||
if (getAndResetLock(output)) {
|
||||
LOGGER.warn("Forcing downloading {} as existing lock file was found. This may happen if the gradle build was forcefully canceled.", output);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (forceDownload || !exists(output)) {
|
||||
// File does not exist, or we are forced to download again.
|
||||
return true;
|
||||
@@ -236,11 +240,6 @@ public class Download {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (getAndResetLock(output)) {
|
||||
LOGGER.warn("Forcing downloading {} as existing lock file was found. This may happen if the gradle build was forcefully canceled.", output);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (expectedHash != null) {
|
||||
final String hashAttribute = readHash(output).orElse("");
|
||||
|
||||
@@ -301,7 +300,7 @@ public class Download {
|
||||
|
||||
private Optional<String> readEtag(Path output) {
|
||||
try {
|
||||
return readAttribute(output, E_TAG);
|
||||
return AttributeHelper.readAttribute(output, E_TAG);
|
||||
} catch (IOException e) {
|
||||
return Optional.empty();
|
||||
}
|
||||
@@ -309,7 +308,7 @@ public class Download {
|
||||
|
||||
private void writeEtag(Path output, String eTag) throws DownloadException {
|
||||
try {
|
||||
writeAttribute(output, E_TAG, eTag);
|
||||
AttributeHelper.writeAttribute(output, E_TAG, eTag);
|
||||
} catch (IOException e) {
|
||||
throw error(e, "Failed to write etag to (%s)", output);
|
||||
}
|
||||
@@ -317,7 +316,7 @@ public class Download {
|
||||
|
||||
private Optional<String> readHash(Path output) {
|
||||
try {
|
||||
return readAttribute(output, "LoomHash");
|
||||
return AttributeHelper.readAttribute(output, "LoomHash");
|
||||
} catch (IOException e) {
|
||||
return Optional.empty();
|
||||
}
|
||||
@@ -325,7 +324,7 @@ public class Download {
|
||||
|
||||
private void writeHash(Path output, String eTag) throws DownloadException {
|
||||
try {
|
||||
writeAttribute(output, "LoomHash", eTag);
|
||||
AttributeHelper.writeAttribute(output, "LoomHash", eTag);
|
||||
} catch (IOException e) {
|
||||
throw error(e, "Failed to write hash to (%s)", output);
|
||||
}
|
||||
@@ -344,29 +343,6 @@ public class Download {
|
||||
return path.getFileSystem() == FileSystems.getDefault() ? path.toFile().exists() : Files.exists(path);
|
||||
}
|
||||
|
||||
private static Optional<String> readAttribute(Path path, String key) throws IOException {
|
||||
final UserDefinedFileAttributeView attributeView = Files.getFileAttributeView(path, UserDefinedFileAttributeView.class);
|
||||
|
||||
if (!attributeView.list().contains(key)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
final ByteBuffer buffer = ByteBuffer.allocate(attributeView.size(key));
|
||||
attributeView.read(key, buffer);
|
||||
buffer.flip();
|
||||
final String value = StandardCharsets.UTF_8.decode(buffer).toString();
|
||||
return Optional.of(value);
|
||||
}
|
||||
|
||||
private static void writeAttribute(Path path, String key, String value) throws IOException {
|
||||
// TODO may need to fallback to creating a separate file if this isnt supported.
|
||||
final UserDefinedFileAttributeView attributeView = Files.getFileAttributeView(path, UserDefinedFileAttributeView.class);
|
||||
final byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
|
||||
final ByteBuffer buffer = ByteBuffer.wrap(bytes);
|
||||
final int written = attributeView.write(key, buffer);
|
||||
assert written == bytes.length;
|
||||
}
|
||||
|
||||
private FileTime getLastModified(Path path) throws IOException {
|
||||
final BasicFileAttributeView basicView = Files.getFileAttributeView(path, BasicFileAttributeView.class);
|
||||
return basicView.readAttributes().lastModifiedTime();
|
||||
@@ -380,10 +356,12 @@ public class Download {
|
||||
final Path lock = getLockFile(output);
|
||||
final boolean exists = Files.exists(lock);
|
||||
|
||||
try {
|
||||
Files.deleteIfExists(lock);
|
||||
} catch (IOException e) {
|
||||
throw error(e, "Failed to release lock on %s", lock);
|
||||
if (exists) {
|
||||
try {
|
||||
Files.delete(lock);
|
||||
} catch (IOException e) {
|
||||
throw error(e, "Failed to release lock on %s", lock);
|
||||
}
|
||||
}
|
||||
|
||||
return exists;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of fabric-loom, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) 2016-2021 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
|
||||
@@ -24,14 +24,21 @@
|
||||
|
||||
package net.fabricmc.loom.util.gradle;
|
||||
|
||||
import org.gradle.util.GradleVersion;
|
||||
import org.gradle.api.Project;
|
||||
|
||||
// This is used to bridge the gap over large gradle api changes.
|
||||
public class GradleSupport {
|
||||
public static final boolean IS_GRADLE_7_OR_NEWER = isIsGradle7OrNewer();
|
||||
public final class GradleUtils {
|
||||
private GradleUtils() {
|
||||
}
|
||||
|
||||
public static boolean isIsGradle7OrNewer() {
|
||||
String version = GradleVersion.current().getVersion();
|
||||
return Integer.parseInt(version.substring(0, version.indexOf("."))) >= 7;
|
||||
// For some crazy reason afterEvaluate is still invoked when the configuration fails
|
||||
public static void afterSuccessfulEvaluation(Project project, Runnable afterEvaluate) {
|
||||
project.afterEvaluate(p -> {
|
||||
if (p.getState().getFailure() != null) {
|
||||
// Let gradle handle the failure
|
||||
return;
|
||||
}
|
||||
|
||||
afterEvaluate.run();
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user