Rewrire Jar and Source remapping tasks. (#504)

This commit is contained in:
modmuss50 2021-12-20 16:29:11 +00:00 committed by GitHub
parent 5a16440c1e
commit ab21e0e550
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
36 changed files with 1154 additions and 1423 deletions

View file

@ -26,7 +26,6 @@ package net.fabricmc.loom;
import java.io.File;
import java.util.List;
import java.util.Set;
import java.util.function.Supplier;
import org.cadixdev.lorenz.MappingSet;
@ -36,6 +35,8 @@ import org.gradle.api.NamedDomainObjectProvider;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.file.ConfigurableFileCollection;
import org.gradle.api.file.FileCollection;
import org.gradle.api.tasks.SourceSet;
import net.fabricmc.loom.api.LoomGradleExtensionAPI;
import net.fabricmc.loom.configuration.InstallerData;
@ -94,9 +95,9 @@ public interface LoomGradleExtension extends LoomGradleExtensionAPI {
return getMappingsProvider().mappedProvider;
}
File getNextMixinMappings();
File getMixinMappings(SourceSet sourceSet);
Set<File> getAllMixinMappings();
FileCollection getAllMixinMappings();
boolean isRootProject();

View file

@ -1,159 +0,0 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2020 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.build;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import org.gradle.api.Action;
import org.objectweb.asm.commons.Remapper;
import net.fabricmc.loom.util.CloseableList;
import net.fabricmc.stitch.util.Pair;
import net.fabricmc.tinyremapper.IMappingProvider;
import net.fabricmc.tinyremapper.InputTag;
import net.fabricmc.tinyremapper.OutputConsumerPath;
import net.fabricmc.tinyremapper.TinyRemapper;
public class JarRemapper {
private final List<IMappingProvider> mappingProviders = new ArrayList<>();
private final Set<Path> classPath = new HashSet<>();
private final List<RemapData> remapData = new ArrayList<>();
private List<Action<TinyRemapper.Builder>> remapOptions;
public void addMappings(IMappingProvider mappingProvider) {
mappingProviders.add(mappingProvider);
}
public void addToClasspath(Path... paths) {
classPath.addAll(Arrays.asList(paths));
}
public RemapData scheduleRemap(Path input, Path output) {
RemapData data = new RemapData(input, output);
remapData.add(data);
return data;
}
public void remap() throws IOException {
TinyRemapper.Builder remapperBuilder = TinyRemapper.newRemapper();
mappingProviders.forEach(remapperBuilder::withMappings);
if (remapOptions != null) {
for (Action<TinyRemapper.Builder> remapOption : remapOptions) {
remapOption.execute(remapperBuilder);
}
}
TinyRemapper remapper = remapperBuilder.build();
Path[] remapClasspath = classPath.stream()
.filter(path ->
remapData.stream().noneMatch(remapData -> remapData.input.equals(path))
)
.toArray(Path[]::new);
remapper.readClassPathAsync(remapClasspath);
for (RemapData data : remapData) {
InputTag tag = remapper.createInputTag();
data.tag = tag;
remapper.readInputsAsync(tag, data.input);
}
//noinspection MismatchedQueryAndUpdateOfCollection
try (CloseableList<OutputConsumerPath> outputConsumers = new CloseableList<>()) {
for (RemapData data : remapData) {
OutputConsumerPath outputConsumer = new OutputConsumerPath.Builder(data.output).build();
outputConsumers.add(outputConsumer);
outputConsumer.addNonClassFiles(data.input);
data.processAccessWidener(remapper.getEnvironment().getRemapper());
remapper.apply(outputConsumer, data.tag);
}
remapper.finish();
} catch (Exception e) {
for (RemapData data : remapData) {
// Cleanup bad outputs
Files.deleteIfExists(data.output);
}
throw new IOException("Failed to remap %s files".formatted(remapData.size()), e);
}
remapData.forEach(RemapData::complete);
}
public void addOptions(List<Action<TinyRemapper.Builder>> remapOptions) {
this.remapOptions = remapOptions;
}
public static class RemapData {
public final Path input;
public final Path output;
BiFunction<RemapData, Remapper, Pair<String, byte[]>> accesWidenerSupplier;
BiConsumer<RemapData, Pair<String, byte[]>> onComplete;
private InputTag tag;
private Pair<String, byte[]> accessWidener;
public RemapData(Path input, Path output) {
this.input = input;
this.output = output;
}
public RemapData complete(BiConsumer<RemapData, Pair<String, byte[]>> onComplete) {
this.onComplete = onComplete;
return this;
}
public RemapData supplyAccessWidener(BiFunction<RemapData, Remapper, Pair<String, byte[]>> beforeFinish) {
this.accesWidenerSupplier = beforeFinish;
return this;
}
private void complete() {
if (onComplete != null) {
onComplete.accept(this, accessWidener);
}
}
private void processAccessWidener(Remapper remapper) {
if (accesWidenerSupplier != null) {
accessWidener = accesWidenerSupplier.apply(this, remapper);
}
}
}
}

View file

@ -27,13 +27,9 @@ package net.fabricmc.loom.build;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UncheckedIOException;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Collections;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
@ -41,74 +37,35 @@ import java.util.zip.ZipFile;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import org.gradle.api.Project;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.LoomGradlePlugin;
import net.fabricmc.loom.extension.MixinExtension;
import net.fabricmc.loom.util.Pair;
import net.fabricmc.loom.util.ZipUtils;
public final class MixinRefmapHelper {
private MixinRefmapHelper() { }
private static final String FABRIC_MOD_JSON = "fabric.mod.json";
public static boolean addRefmapName(Project project, Path outputPath) {
try {
MixinExtension mixin = LoomGradleExtension.get(project).getMixin();
File output = outputPath.toFile();
Collection<String> allMixinConfigs = getMixinConfigurationFiles(readFabricModJson(output));
return mixin.getMixinSourceSetsStream().map(sourceSet -> {
MixinExtension.MixinInformationContainer container = Objects.requireNonNull(
MixinExtension.getMixinInformationContainer(sourceSet)
);
Stream<String> mixinConfigs = sourceSet.getResources()
.matching(container.mixinConfigPattern())
.getFiles()
.stream()
.map(File::getName)
.filter(allMixinConfigs::contains);
String refmapName = container.refmapNameProvider().get();
try {
return ZipUtils.transformJson(JsonObject.class, outputPath, mixinConfigs.map(f -> new Pair<>(f, json -> {
if (!json.has("refmap")) {
json.addProperty("refmap", refmapName);
}
return json;
}))) > 0;
} catch (IOException e) {
throw new UncheckedIOException("Failed to transform mixin configs in jar", e);
}
}).reduce(false, Boolean::logicalOr);
} catch (Exception e) {
project.getLogger().error(e.getMessage());
return false;
}
}
@NotNull
private static JsonObject readFabricModJson(File output) {
@Nullable
public static JsonObject readFabricModJson(File output) {
try (ZipFile zip = new ZipFile(output)) {
ZipEntry entry = zip.getEntry(FABRIC_MOD_JSON);
if (entry == null) {
return null;
}
try (InputStreamReader reader = new InputStreamReader(zip.getInputStream(entry))) {
return LoomGradlePlugin.GSON.fromJson(reader, JsonObject.class);
}
} catch (IOException e) {
throw new RuntimeException("Cannot read file fabric.mod.json in the output jar.", e);
throw new RuntimeException("Cannot read file fabric.mod.json in the jar.", e);
}
}
@NotNull
private static Collection<String> getMixinConfigurationFiles(JsonObject fabricModJson) {
public static Collection<String> getMixinConfigurationFiles(JsonObject fabricModJson) {
JsonArray mixins = fabricModJson.getAsJsonArray("mixins");
if (mixins == null) {

View file

@ -84,7 +84,7 @@ public abstract class AnnotationProcessorInvoker<T extends Task> {
String refmapName = Objects.requireNonNull(MixinExtension.getMixinInformationContainer(sourceSet)).refmapNameProvider().get();
Map<String, String> args = new HashMap<>() {{
put(Constants.MixinArguments.IN_MAP_FILE_NAMED_INTERMEDIARY, loom.getMappingsProvider().tinyMappings.toFile().getCanonicalPath());
put(Constants.MixinArguments.OUT_MAP_FILE_NAMED_INTERMEDIARY, loom.getNextMixinMappings().getCanonicalPath());
put(Constants.MixinArguments.OUT_MAP_FILE_NAMED_INTERMEDIARY, loom.getMixinMappings(sourceSet).getCanonicalPath());
put(Constants.MixinArguments.OUT_REFMAP_FILE, getRefmapDestination(task, refmapName));
put(Constants.MixinArguments.DEFAULT_OBFUSCATION_ENV, "named:intermediary");
}};

View file

@ -0,0 +1,203 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 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.build.nesting;
import java.io.File;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.Locale;
import java.util.Set;
import com.google.common.collect.Sets;
import com.google.gson.JsonObject;
import org.apache.commons.io.FileUtils;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.Dependency;
import org.gradle.api.artifacts.ProjectDependency;
import org.gradle.api.artifacts.ResolvedArtifact;
import org.gradle.api.artifacts.ResolvedConfiguration;
import org.gradle.api.artifacts.ResolvedDependency;
import org.gradle.api.file.ConfigurableFileCollection;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.provider.Provider;
import org.gradle.api.tasks.bundling.AbstractArchiveTask;
import org.jetbrains.annotations.Nullable;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.LoomGradlePlugin;
import net.fabricmc.loom.task.RemapTaskConfiguration;
import net.fabricmc.loom.util.ModUtils;
import net.fabricmc.loom.util.ZipUtils;
public final class IncludedJarFactory {
private final Project project;
public IncludedJarFactory(Project project) {
this.project = project;
}
public Provider<ConfigurableFileCollection> getNestedJars(final Configuration configuration) {
return project.provider(() -> {
final ConfigurableFileCollection files = project.files();
final Set<String> visited = Sets.newHashSet();
files.from(getProjectDeps(configuration, visited));
files.from(getFileDeps(configuration, visited));
files.builtBy(configuration.getBuildDependencies());
return files;
});
}
private ConfigurableFileCollection getFileDeps(Configuration configuration, Set<String> visited) {
final ConfigurableFileCollection files = project.files();
final ResolvedConfiguration resolvedConfiguration = configuration.getResolvedConfiguration();
final Set<ResolvedDependency> dependencies = resolvedConfiguration.getFirstLevelModuleDependencies();
for (ResolvedDependency dependency : dependencies) {
if (!visited.add(dependency.getModuleGroup() + ":" + dependency.getModuleName() + ":" + dependency.getModuleVersion())) {
continue;
}
for (ResolvedArtifact artifact : dependency.getModuleArtifacts()) {
Metadata metadata = new Metadata(
dependency.getModuleGroup(),
dependency.getModuleName(),
dependency.getModuleVersion(),
artifact.getClassifier()
);
files.from(project.provider(() -> getNestableJar(artifact.getFile(), metadata)));
}
}
return files;
}
private ConfigurableFileCollection getProjectDeps(Configuration configuration, Set<String> visited) {
final ConfigurableFileCollection files = project.files();
for (Dependency dependency : configuration.getDependencies()) {
if (dependency instanceof ProjectDependency projectDependency) {
if (!visited.add(dependency.getGroup() + ":" + dependency.getName() + ":" + dependency.getVersion())) {
continue;
}
// Get the outputs of the project
final Project dependentProject = projectDependency.getDependencyProject();
Collection<Task> remapJarTasks = dependentProject.getTasksByName(RemapTaskConfiguration.REMAP_JAR_TASK_NAME, false);
Collection<Task> jarTasks = dependentProject.getTasksByName(JavaPlugin.JAR_TASK_NAME, false);
if (remapJarTasks.isEmpty() && jarTasks.isEmpty()) {
throw new UnsupportedOperationException("%s does not have a remapJar or jar task, cannot nest it".formatted(dependentProject.getName()));
}
for (Task task : remapJarTasks.isEmpty() ? jarTasks : remapJarTasks) {
if (task instanceof AbstractArchiveTask archiveTask) {
final Metadata metadata = new Metadata(
projectDependency.getGroup(),
projectDependency.getName(),
projectDependency.getVersion(),
archiveTask.getArchiveClassifier().getOrNull()
);
Provider<File> provider = archiveTask.getArchiveFile().map(regularFile -> getNestableJar(regularFile.getAsFile(), metadata));
files.from(provider);
files.builtBy(task);
} else {
throw new UnsupportedOperationException("Cannot nest none AbstractArchiveTask task: " + task.getName());
}
}
}
}
return files;
}
private File getNestableJar(final File input, final Metadata metadata) {
if (ModUtils.isMod(input)) {
// Input is a mod, nothing needs to be done.
return input;
}
LoomGradleExtension extension = LoomGradleExtension.get(project);
File tempDir = new File(extension.getFiles().getUserCache(), "temp/modprocessing");
if (!tempDir.exists()) {
tempDir.mkdirs();
}
File tempFile = new File(tempDir, input.getName());
if (tempFile.exists()) {
tempFile.delete();
}
try {
FileUtils.copyFile(input, tempFile);
ZipUtils.add(tempFile.toPath(), "fabric.mod.json", generateModForDependency(metadata).getBytes(StandardCharsets.UTF_8));
} catch (IOException e) {
throw new UncheckedIOException("Failed to add dummy mod while including %s".formatted(input), e);
}
return tempFile;
}
// Generates a barebones mod for a dependency
private static String generateModForDependency(Metadata metadata) {
final String modId = (metadata.group() + "_" + metadata.name() + metadata.classifier())
.replaceAll("\\.", "_")
.toLowerCase(Locale.ENGLISH);
final JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("schemaVersion", 1);
jsonObject.addProperty("id", modId);
jsonObject.addProperty("version", metadata.version());
jsonObject.addProperty("name", metadata.name());
JsonObject custom = new JsonObject();
custom.addProperty("fabric-loom:generated", true);
jsonObject.add("custom", custom);
return LoomGradlePlugin.GSON.toJson(jsonObject);
}
private record Metadata(String group, String name, String version, @Nullable String classifier) {
@Override
public String classifier() {
if (classifier == null) {
return "";
} else {
return "_" + classifier;
}
}
}
}

View file

@ -36,7 +36,7 @@ import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import org.gradle.api.UncheckedIOException;
import org.gradle.api.logging.Logger;
import org.slf4j.Logger;
import net.fabricmc.loom.util.ModUtils;
import net.fabricmc.loom.util.Pair;
@ -69,6 +69,7 @@ public class JarNester {
for (File file : jars) {
String nestedJarPath = "META-INF/jars/" + file.getName();
Preconditions.checkArgument(ModUtils.isMod(file), "Cannot nest none mod jar: " + file.getName());
for (JsonElement nestedJar : nestedJars) {
JsonObject jsonObject = nestedJar.getAsJsonObject();

View file

@ -1,47 +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.build.nesting;
import java.io.File;
import java.util.Arrays;
import java.util.Collection;
import java.util.stream.Collectors;
import org.gradle.api.Project;
public record MergedNestedJarProvider(NestedJarProvider... children) implements NestedJarProvider {
@Override
public Collection<File> provide() {
return Arrays.stream(children)
.map(NestedJarProvider::provide)
.flatMap(Collection::stream)
.collect(Collectors.toList());
}
@Override
public void prepare(Project project) {
Arrays.stream(children).forEach(nestedJarProvider -> nestedJarProvider.prepare(project));
}
}

View file

@ -1,282 +0,0 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2019-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.build.nesting;
import java.io.File;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import javax.annotation.Nullable;
import com.google.gson.JsonObject;
import org.apache.commons.io.FileUtils;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.Dependency;
import org.gradle.api.artifacts.DependencySet;
import org.gradle.api.artifacts.ProjectDependency;
import org.gradle.api.artifacts.ResolvedArtifact;
import org.gradle.api.artifacts.ResolvedConfiguration;
import org.gradle.api.artifacts.ResolvedDependency;
import org.gradle.api.tasks.bundling.AbstractArchiveTask;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.LoomGradlePlugin;
import net.fabricmc.loom.task.RemapJarTask;
import net.fabricmc.loom.util.Constants;
import net.fabricmc.loom.util.ModUtils;
import net.fabricmc.loom.util.ZipUtils;
public final class NestedDependencyProvider implements NestedJarProvider {
final Project project;
final List<DependencyInfo<?>> files;
private NestedDependencyProvider(Project project, List<DependencyInfo<?>> files) {
this.project = project;
this.files = files;
}
public static NestedDependencyProvider createNestedDependencyProviderFromConfiguration(Project project, Configuration configuration) {
List<DependencyInfo<?>> fileList = new ArrayList<>();
Set<String> visited = new HashSet<>();
fileList.addAll(populateProjectDependencies(configuration, visited));
fileList.addAll(populateResolvedDependencies(configuration, visited));
return new NestedDependencyProvider(project, fileList);
}
// Looks for any deps that require a sub project to be built first
public static List<RemapJarTask> getRequiredTasks(Project project) {
List<RemapJarTask> remapTasks = new ArrayList<>();
Configuration configuration = project.getConfigurations().getByName(Constants.Configurations.INCLUDE);
DependencySet dependencies = configuration.getDependencies();
for (Dependency dependency : dependencies) {
if (dependency instanceof ProjectDependency projectDependency) {
Project dependencyProject = projectDependency.getDependencyProject();
for (Task task : dependencyProject.getTasksByName("remapJar", false)) {
if (task instanceof RemapJarTask remapJarTask) {
remapTasks.add(remapJarTask);
}
}
}
}
return remapTasks;
}
private static List<DependencyInfo<ProjectDependency>> populateProjectDependencies(Configuration configuration, Set<String> visited) {
List<DependencyInfo<ProjectDependency>> fileList = new ArrayList<>();
for (Dependency dependency : configuration.getDependencies()) {
if (dependency instanceof ProjectDependency projectDependency) {
Project dependencyProject = projectDependency.getDependencyProject();
visited.add(dependency.getGroup() + ":" + dependency.getName() + ":" + dependency.getVersion());
Collection<Task> remapJarTasks = dependencyProject.getTasksByName("remapJar", false);
Collection<Task> jarTasks = dependencyProject.getTasksByName("jar", false);
for (Task task : remapJarTasks.isEmpty() ? jarTasks : remapJarTasks) {
if (task instanceof AbstractArchiveTask abstractArchiveTask) {
fileList.add(new DependencyInfo<>(
projectDependency,
new ProjectDependencyMetaExtractor(),
abstractArchiveTask.getArchiveFile().get().getAsFile(),
abstractArchiveTask.getArchiveClassifier().getOrNull()
));
}
}
}
}
return fileList;
}
private static List<DependencyInfo<ResolvedDependency>> populateResolvedDependencies(Configuration configuration, Set<String> visited) {
ResolvedConfiguration resolvedConfiguration = configuration.getResolvedConfiguration();
Set<ResolvedDependency> dependencies = resolvedConfiguration.getFirstLevelModuleDependencies();
List<DependencyInfo<ResolvedDependency>> fileList = new ArrayList<>();
for (ResolvedDependency dependency : dependencies) {
if (visited.contains(dependency.getModuleGroup() + ":" + dependency.getModuleName() + ":" + dependency.getModuleVersion())) {
continue;
}
for (ResolvedArtifact artifact : dependency.getModuleArtifacts()) {
fileList.add(new DependencyInfo<>(
dependency,
new ResolvedDependencyMetaExtractor(),
artifact.getFile(),
artifact.getClassifier()
));
}
}
return fileList;
}
@Override
public List<File> provide() {
List<File> fileList = new ArrayList<>();
for (DependencyInfo<?> metaFile : files) {
metaFile.validateInputs();
File file = metaFile.file;
//A lib that doesnt have a mod.json, we turn it into a fake mod
if (!ModUtils.isMod(file)) {
LoomGradleExtension extension = LoomGradleExtension.get(project);
File tempDir = new File(extension.getFiles().getUserCache(), "temp/modprocessing");
if (!tempDir.exists()) {
tempDir.mkdirs();
}
File tempFile = new File(tempDir, file.getName());
if (tempFile.exists()) {
tempFile.delete();
}
try {
FileUtils.copyFile(file, tempFile);
} catch (IOException e) {
throw new RuntimeException("Failed to copy file", e);
}
try {
ZipUtils.add(tempFile.toPath(), "fabric.mod.json", generateModForDependency(metaFile).getBytes());
} catch (IOException e) {
throw new UncheckedIOException("Failed to add dummy mod while including %s".formatted(file), e);
}
fileList.add(tempFile);
} else {
// Default copy the jar right in
fileList.add(file);
}
}
return fileList;
}
// Generates a barebones mod for a dependency
private static <D> String generateModForDependency(DependencyInfo<D> info) {
DependencyMetaExtractor<D> metaExtractor = info.metaExtractor;
D dependency = info.dependency;
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("schemaVersion", 1);
jsonObject.addProperty("id",
(metaExtractor.group(dependency) + "_" + metaExtractor.name(dependency) + info.getClassifierSuffix())
.replaceAll("\\.", "_")
.toLowerCase(Locale.ENGLISH)
);
jsonObject.addProperty("version", metaExtractor.version(dependency));
jsonObject.addProperty("name", metaExtractor.name(dependency));
JsonObject custom = new JsonObject();
custom.addProperty("fabric-loom:generated", true);
jsonObject.add("custom", custom);
return LoomGradlePlugin.GSON.toJson(jsonObject);
}
private record DependencyInfo<D>(D dependency, DependencyMetaExtractor<D> metaExtractor, File file, @Nullable String classifier) {
void validateInputs() {
if (!file.exists()) {
throw new RuntimeException("Failed to include nested jars, as it could not be found @ " + file.getAbsolutePath());
}
if (file.isDirectory() || !file.getName().endsWith(".jar")) {
throw new RuntimeException("Failed to include nested jars, as file was not a jar: " + file.getAbsolutePath());
}
}
String getClassifierSuffix() {
if (classifier == null) {
return "";
} else {
return "_" + classifier;
}
}
}
private interface DependencyMetaExtractor<D> {
String group(D dependency);
String version(D dependency);
String name(D dependency);
}
private static final class ProjectDependencyMetaExtractor implements DependencyMetaExtractor<ProjectDependency> {
@Override
public String group(ProjectDependency dependency) {
return dependency.getGroup();
}
@Override
public String version(ProjectDependency dependency) {
return dependency.getVersion();
}
@Override
public String name(ProjectDependency dependency) {
return dependency.getName();
}
}
private static final class ResolvedDependencyMetaExtractor implements DependencyMetaExtractor<ResolvedDependency> {
@Override
public String group(ResolvedDependency dependency) {
return dependency.getModuleGroup();
}
@Override
public String version(ResolvedDependency dependency) {
return dependency.getModuleVersion();
}
@Override
public String name(ResolvedDependency dependency) {
return dependency.getModuleName();
}
}
}

View file

@ -1,69 +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.build.nesting;
import java.io.File;
import java.util.Collection;
import java.util.Set;
import com.google.common.base.Preconditions;
import org.gradle.api.Project;
import net.fabricmc.loom.util.ModUtils;
public final class NestedJarPathProvider implements NestedJarProvider {
private final Set<Object> nestedPaths;
private Set<File> files = null;
public NestedJarPathProvider(Set<Object> nestedPaths) {
this.nestedPaths = nestedPaths;
}
private Set<File> resolve(Project project) {
return project.files(nestedPaths).getFiles();
}
@Override
public void prepare(Project project) {
if (files == null) {
files = resolve(project);
}
}
@Override
public Collection<File> provide() {
validateFiles();
return files;
}
private void validateFiles() {
Preconditions.checkNotNull(files, "null files to nest, was prepare called?");
for (File file : files) {
Preconditions.checkArgument(file.getName().endsWith(".jar"), String.format("Tried to nest %s but it is not a jar", file.getAbsolutePath()));
Preconditions.checkArgument(file.exists(), String.format("Tried to nest jar %s but it does not exist", file.getAbsolutePath()));
Preconditions.checkArgument(ModUtils.isMod(file), String.format("Cannot use nest none mod jar %s", file.getAbsolutePath()));
}
}
}

View file

@ -1,40 +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.build.nesting;
import java.io.File;
import java.util.Collection;
import org.gradle.api.Project;
import org.jetbrains.annotations.ApiStatus;
@ApiStatus.Internal
public interface NestedJarProvider {
// provide all the files to be included, they should already be resolved but can be transformed here
Collection<File> provide();
// Setup the files ready to be provided
default void prepare(Project project) { }
}

View file

@ -35,7 +35,6 @@ import org.gradle.api.tasks.AbstractCopyTask;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.compile.JavaCompile;
import org.gradle.api.tasks.javadoc.Javadoc;
import org.gradle.jvm.tasks.Jar;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.build.mixin.JavaApInvoker;
@ -124,6 +123,7 @@ public final class CompileConfiguration {
public static void configureCompile(Project p) {
final JavaPluginExtension javaPluginExtension = p.getExtensions().getByType(JavaPluginExtension.class);
LoomGradleExtension extension = LoomGradleExtension.get(p);
SourceSet main = javaPluginExtension.getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME);
@ -131,8 +131,6 @@ public final class CompileConfiguration {
javadoc.setClasspath(main.getOutput().plus(main.getCompileClasspath()));
p.afterEvaluate(project -> {
LoomGradleExtension extension = LoomGradleExtension.get(project);
LoomDependencyManager dependencyManager = new LoomDependencyManager();
extension.setDependencyManager(dependencyManager);
@ -148,14 +146,6 @@ public final class CompileConfiguration {
extension.getRemapArchives().finalizeValue();
// Enables the default mod remapper
if (extension.getRemapArchives().get()) {
RemapConfiguration.setupDefaultRemap(project);
} else {
Jar jarTask = (Jar) project.getTasks().getByName("jar");
extension.getUnmappedModCollection().from(jarTask);
}
MixinExtension mixin = LoomGradleExtension.get(project).getMixin();
if (mixin.getUseLegacyMixinAp().get()) {

View file

@ -1,92 +0,0 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 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;
import java.util.Optional;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Dependency;
import org.gradle.util.GradleVersion;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.LoomGradlePlugin;
import net.fabricmc.loom.util.Constants;
import net.fabricmc.tinyremapper.TinyRemapper;
public final record JarManifestConfiguration(Project project) {
public void configure(Manifest manifest) {
// Dont set when running the reproducible build tests as it will break them when anything updates
if (Boolean.getBoolean("loom.test.reproducible")) {
return;
}
LoomGradleExtension extension = LoomGradleExtension.get(project);
Attributes attributes = manifest.getMainAttributes();
Optional<String> tinyRemapperVersion = Optional.ofNullable(TinyRemapper.class.getPackage().getImplementationVersion());
attributes.putValue("Fabric-Gradle-Version", GradleVersion.current().getVersion());
attributes.putValue("Fabric-Loom-Version", LoomGradlePlugin.LOOM_VERSION);
attributes.putValue("Fabric-Mixin-Compile-Extensions-Version", Constants.Dependencies.Versions.MIXIN_COMPILE_EXTENSIONS);
attributes.putValue("Fabric-Minecraft-Version", extension.getMinecraftProvider().minecraftVersion());
tinyRemapperVersion.ifPresent(s -> attributes.putValue("Fabric-Tiny-Remapper-Version", s));
getLoaderVersion().ifPresent(s -> attributes.putValue("Fabric-Loader-Version", s));
// This can be overridden by mods if required
if (!attributes.containsKey("Fabric-Mixin-Version")) {
addMixinVersion(attributes);
}
}
private void addMixinVersion(Attributes attributes) {
// Not super ideal that this uses the mod compile classpath, should prob look into making this not a thing at somepoint
Optional<Dependency> dependency = project.getConfigurations().getByName(Constants.Configurations.LOADER_DEPENDENCIES)
.getDependencies()
.stream()
.filter(dep -> "sponge-mixin".equals(dep.getName()))
.findFirst();
if (dependency.isEmpty()) {
project.getLogger().warn("Could not determine Mixin version for jar manifest");
return;
}
attributes.putValue("Fabric-Mixin-Version", dependency.get().getVersion());
attributes.putValue("Fabric-Mixin-Group", dependency.get().getGroup());
}
private Optional<String> getLoaderVersion() {
LoomGradleExtension extension = LoomGradleExtension.get(project);
if (extension.getInstallerData() == null) {
project.getLogger().warn("Could not determine fabric loader version for jar manifest");
return Optional.empty();
}
return Optional.of(extension.getInstallerData().version());
}
}

View file

@ -1,220 +0,0 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 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;
import java.io.IOException;
import com.google.common.base.Preconditions;
import org.gradle.api.Action;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.UnknownTaskException;
import org.gradle.api.artifacts.ConfigurablePublishArtifact;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.PublishArtifact;
import org.gradle.api.artifacts.dsl.ArtifactHandler;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.tasks.bundling.AbstractArchiveTask;
import org.jetbrains.annotations.ApiStatus;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.build.JarRemapper;
import net.fabricmc.loom.build.nesting.NestedDependencyProvider;
import net.fabricmc.loom.task.AbstractLoomTask;
import net.fabricmc.loom.task.RemapAllSourcesTask;
import net.fabricmc.loom.task.RemapJarTask;
import net.fabricmc.loom.task.RemapSourcesJarTask;
import net.fabricmc.loom.util.SourceRemapper;
public class RemapConfiguration {
private static final String DEFAULT_JAR_TASK_NAME = JavaPlugin.JAR_TASK_NAME;
private static final String DEFAULT_SOURCES_JAR_TASK_NAME = "sourcesJar";
private static final String DEFAULT_REMAP_JAR_TASK_NAME = "remapJar";
private static final String DEFAULT_REMAP_SOURCES_JAR_TASK_NAME = "remapSourcesJar";
private static final String DEFAULT_REMAP_ALL_JARS_TASK_NAME = "remapAllJars";
private static final String DEFAULT_REMAP_ALL_SOURCES_TASK_NAME = "remapAllSources";
public static void setupDefaultRemap(Project project) {
setupRemap(project, true, DEFAULT_JAR_TASK_NAME, DEFAULT_SOURCES_JAR_TASK_NAME, DEFAULT_REMAP_JAR_TASK_NAME, DEFAULT_REMAP_SOURCES_JAR_TASK_NAME, DEFAULT_REMAP_ALL_JARS_TASK_NAME, DEFAULT_REMAP_ALL_SOURCES_TASK_NAME);
LoomGradleExtension extension = LoomGradleExtension.get(project);
extension.getSetupRemappedVariants().finalizeValue();
if (extension.getSetupRemappedVariants().get()) {
ArtifactHandler artifacts = project.getArtifacts();
project.getTasks().named(DEFAULT_REMAP_JAR_TASK_NAME, task -> {
artifacts.add(JavaPlugin.API_ELEMENTS_CONFIGURATION_NAME, task, artifactConfigurationAction(task, DEFAULT_REMAP_JAR_TASK_NAME, project));
artifacts.add(JavaPlugin.RUNTIME_ELEMENTS_CONFIGURATION_NAME, task, artifactConfigurationAction(task, DEFAULT_REMAP_JAR_TASK_NAME, project));
});
project.getTasks().named(DEFAULT_REMAP_SOURCES_JAR_TASK_NAME, RemapSourcesJarTask.class, task -> {
if (!project.getConfigurations().getNames().contains(JavaPlugin.SOURCES_ELEMENTS_CONFIGURATION_NAME)) {
// Sources jar may not have been created with withSourcesJar
project.getLogger().info("Not publishing sources jar as it was not found. Use java.withSourcesJar() to fix.");
return;
}
PublishArtifact artifact = artifacts.add(JavaPlugin.SOURCES_ELEMENTS_CONFIGURATION_NAME, task.getOutput(), artifactConfigurationAction(task, DEFAULT_REMAP_ALL_SOURCES_TASK_NAME, project));
// Remove the existing artifact that does not run remapSourcesJar.
// It doesn't seem to hurt, but I'm not sure if the file-level duplicates cause issues.
Configuration configuration = project.getConfigurations().getByName(JavaPlugin.SOURCES_ELEMENTS_CONFIGURATION_NAME);
configuration.getArtifacts().removeIf(a -> a != artifact && artifact.getFile().equals(a.getFile()));
});
// 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);
configuration.getArtifacts().removeIf(artifact -> {
Task jarTask = project.getTasks().getByName(DEFAULT_JAR_TASK_NAME);
// if the artifact is a -dev jar and "builtBy jar"
return "dev".equals(artifact.getClassifier()) && artifact.getBuildDependencies().getDependencies(null).contains(jarTask);
});
}
}
}
@ApiStatus.Experimental // This is only an api if you squint really hard, expect it to explode every 5 mins. If you must call in afterEvaluate on all projects
public static void setupRemap(Project project, String jarTaskName, String sourcesJarTaskName, String remapJarTaskName, String remapSourcesJarTaskName, String remapAllJarsTaskName, String remapAllSourcesTaskName) {
setupRemap(project, false, jarTaskName, sourcesJarTaskName, remapJarTaskName, remapSourcesJarTaskName, remapAllJarsTaskName, remapAllSourcesTaskName);
}
// isDefaultRemap is set to true for the standard remap task, some defaults are left out when this is false.
private static void setupRemap(Project project, boolean isDefaultRemap, String jarTaskName, String sourcesJarTaskName, String remapJarTaskName, String remapSourcesJarTaskName, String remapAllJarsTaskName, String remapAllSourcesTaskName) {
LoomGradleExtension extension = LoomGradleExtension.get(project);
AbstractArchiveTask jarTask = (AbstractArchiveTask) project.getTasks().getByName(jarTaskName);
RemapJarTask remapJarTask = (RemapJarTask) project.getTasks().findByName(remapJarTaskName);
assert remapJarTask != null;
if (!remapJarTask.getInput().isPresent() && isDefaultRemap) {
jarTask.getArchiveClassifier().convention("dev");
remapJarTask.getArchiveClassifier().convention("");
remapJarTask.getInput().convention(jarTask.getArchiveFile());
}
if (isDefaultRemap) {
extension.getUnmappedModCollection().from(jarTask);
remapJarTask.getAddNestedDependencies().convention(true);
remapJarTask.getRemapAccessWidener().convention(true);
project.getArtifacts().add("archives", remapJarTask);
}
remapJarTask.dependsOn(jarTask);
project.getTasks().getByName("build").dependsOn(remapJarTask);
// TODO this might be wrong?
project.getTasks().withType(RemapJarTask.class).forEach(task -> {
if (task.getAddNestedDependencies().getOrElse(false)) {
NestedDependencyProvider.getRequiredTasks(project).forEach(task::dependsOn);
}
});
SourceRemapper remapper = null;
// TODO what is this for?
Task parentTask = project.getTasks().getByName("build");
if (extension.getShareRemapCaches().get()) {
Project rootProject = project.getRootProject();
if (extension.isRootProject()) {
SourceRemapper sourceRemapper = new SourceRemapper(rootProject, false);
JarRemapper jarRemapper = new JarRemapper();
remapJarTask.jarRemapper = jarRemapper;
rootProject.getTasks().register(remapAllSourcesTaskName, RemapAllSourcesTask.class, task -> {
task.sourceRemapper = sourceRemapper;
task.doLast(t -> sourceRemapper.remapAll());
});
parentTask = rootProject.getTasks().getByName(remapAllSourcesTaskName);
rootProject.getTasks().register(remapAllJarsTaskName, AbstractLoomTask.class, task -> {
task.doLast(t -> {
try {
jarRemapper.remap();
} catch (IOException e) {
throw new RuntimeException("Failed to remap jars", e);
}
});
});
} else {
parentTask = rootProject.getTasks().getByName(remapAllSourcesTaskName);
remapper = ((RemapAllSourcesTask) parentTask).sourceRemapper;
Preconditions.checkNotNull(remapper);
remapJarTask.jarRemapper = ((RemapJarTask) rootProject.getTasks().getByName(remapJarTaskName)).jarRemapper;
project.getTasks().getByName("build").dependsOn(parentTask);
project.getTasks().getByName("build").dependsOn(rootProject.getTasks().getByName(remapAllJarsTaskName));
rootProject.getTasks().getByName(remapAllJarsTaskName).dependsOn(project.getTasks().getByName(remapJarTaskName));
}
}
try {
AbstractArchiveTask sourcesTask = (AbstractArchiveTask) project.getTasks().getByName(sourcesJarTaskName);
RemapSourcesJarTask remapSourcesJarTask = (RemapSourcesJarTask) project.getTasks().findByName(remapSourcesJarTaskName);
Preconditions.checkNotNull(remapSourcesJarTask, "Could not find " + remapSourcesJarTaskName + " in " + project.getName());
remapSourcesJarTask.getInput().convention(sourcesTask.getArchiveFile());
remapSourcesJarTask.getOutput().convention(sourcesTask.getArchiveFile());
remapSourcesJarTask.dependsOn(project.getTasks().getByName(sourcesJarTaskName));
if (isDefaultRemap) {
// Do not use lambda here, see: https://github.com/gradle/gradle/pull/17087
//noinspection Convert2Lambda
remapSourcesJarTask.doLast(new Action<>() {
@Override
public void execute(Task task) {
project.getArtifacts().add("archives", remapSourcesJarTask.getOutput());
}
});
}
if (extension.getShareRemapCaches().get()) {
remapSourcesJarTask.setSourceRemapper(remapper);
}
parentTask.dependsOn(remapSourcesJarTask);
} catch (UnknownTaskException ignored) {
// pass
}
}
private static Action<ConfigurablePublishArtifact> artifactConfigurationAction(Task standardTask, String sharedTaskName, Project project) {
LoomGradleExtension extension = LoomGradleExtension.get(project);
return artifact -> {
Task remapTask = standardTask;
if (extension.getShareRemapCaches().get()) {
remapTask = project.getRootProject().getTasks().getByName(sharedTaskName);
}
artifact.builtBy(remapTask);
};
}
}

View file

@ -37,7 +37,7 @@ import com.google.gson.JsonObject;
import net.fabricmc.loom.util.ZipUtils;
public record AccessWidenerFile(
String name,
String path,
String modId,
byte[] content
) {
@ -83,7 +83,7 @@ public record AccessWidenerFile(
@Override
public int hashCode() {
int result = Objects.hash(name, modId);
int result = Objects.hash(path, modId);
result = 31 * result + Arrays.hashCode(content);
return result;
}

View file

@ -34,14 +34,10 @@ import java.util.Arrays;
import com.google.common.hash.Hashing;
import org.gradle.api.Project;
import org.objectweb.asm.commons.Remapper;
import net.fabricmc.accesswidener.AccessWidener;
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.mappings.layered.MappingsNamespace;
import net.fabricmc.loom.configuration.processors.JarProcessor;
import net.fabricmc.loom.util.ZipUtils;
@ -96,25 +92,6 @@ public class AccessWidenerJarProcessor implements JarProcessor {
}
}
/**
* Get this mods access widener remapped to the intermediary namespace.
*/
public byte[] getRemappedAccessWidener(Remapper asmRemapper, String targetNamespace) throws IOException {
int version = AccessWidenerReader.readVersion(modAccessWidener);
AccessWidenerWriter writer = new AccessWidenerWriter(version);
AccessWidenerRemapper remapper = new AccessWidenerRemapper(
writer,
asmRemapper,
MappingsNamespace.NAMED.toString(),
targetNamespace
);
AccessWidenerReader reader = new AccessWidenerReader(remapper);
reader.read(modAccessWidener);
return writer.write();
}
@Override
public boolean isInvalid(File file) {
byte[] hash;

View file

@ -491,6 +491,10 @@ public class MappingsProviderImpl extends DependencyProvider implements Mappings
}
}
public String getBuildServiceName(String name, String from, String to) {
return "%s:%s:%s>%S".formatted(name, mappingsIdentifier(), from, to);
}
public record UnpickMetadata(String unpickGroup, String unpickVersion) {
}
}

View file

@ -26,13 +26,10 @@ package net.fabricmc.loom.extension;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Supplier;
import org.cadixdev.lorenz.MappingSet;
@ -42,6 +39,8 @@ import org.gradle.api.NamedDomainObjectProvider;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.file.ConfigurableFileCollection;
import org.gradle.api.file.FileCollection;
import org.gradle.api.tasks.SourceSet;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.configuration.InstallerData;
@ -55,7 +54,7 @@ public class LoomGradleExtensionImpl extends LoomGradleExtensionApiImpl implemen
private final LoomFiles loomFiles;
private final ConfigurableFileCollection unmappedMods;
private final Set<File> mixinMappings = Collections.synchronizedSet(new HashSet<>());
private final ConfigurableFileCollection mixinMappings;
private final MappingSet[] srcMappingCache = new MappingSet[2];
private final Mercury[] srcMercuryCache = new Mercury[2];
private final Map<String, NamedDomainObjectProvider<Configuration>> lazyConfigurations = new HashMap<>();
@ -70,6 +69,7 @@ public class LoomGradleExtensionImpl extends LoomGradleExtensionApiImpl implemen
this.project = project;
// Initiate with newInstance to allow gradle to decorate our extension
this.mixinApExtension = project.getObjects().newInstance(MixinExtensionImpl.class, project);
this.mixinMappings = project.getObjects().fileCollection();
this.loomFiles = files;
this.unmappedMods = project.files();
}
@ -85,15 +85,15 @@ public class LoomGradleExtensionImpl extends LoomGradleExtensionApiImpl implemen
}
@Override
public synchronized File getNextMixinMappings() {
File mixinMapping = new File(getFiles().getProjectBuildCache(), "mixin-map-" + getMappingsProvider().mappingsIdentifier() + "." + mixinMappings.size() + ".tiny");
mixinMappings.add(mixinMapping);
public synchronized File getMixinMappings(SourceSet sourceSet) {
File mixinMapping = new File(getFiles().getProjectBuildCache(), "mixin-map-" + getMappingsProvider().mappingsIdentifier() + "." + sourceSet.getName() + ".tiny");
mixinMappings.from(getProject().files(mixinMapping));
return mixinMapping;
}
@Override
public Set<File> getAllMixinMappings() {
return mixinMappings;
public FileCollection getAllMixinMappings() {
return mixinMappings.filter(File::exists);
}
@Override

View file

@ -0,0 +1,123 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 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.task;
import java.io.IOException;
import java.nio.file.Path;
import javax.inject.Inject;
import org.gradle.api.Action;
import org.gradle.api.file.ConfigurableFileCollection;
import org.gradle.api.file.RegularFileProperty;
import org.gradle.api.provider.Property;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.InputFile;
import org.gradle.api.tasks.InputFiles;
import org.gradle.jvm.tasks.Jar;
import org.gradle.workers.WorkAction;
import org.gradle.workers.WorkParameters;
import org.gradle.workers.WorkQueue;
import org.gradle.workers.WorkerExecutor;
import net.fabricmc.loom.api.mappings.layered.MappingsNamespace;
import net.fabricmc.loom.util.ZipReprocessorUtil;
public abstract class AbstractRemapJarTask extends Jar {
@InputFile
public abstract RegularFileProperty getInputFile();
@InputFiles
public abstract ConfigurableFileCollection getClasspath();
@Input
public abstract Property<String> getSourceNamespace();
@Input
public abstract Property<String> getTargetNamespace();
@Inject
protected abstract WorkerExecutor getWorkerExecutor();
@Inject
public AbstractRemapJarTask() {
getSourceNamespace().convention(MappingsNamespace.NAMED.toString()).finalizeValueOnRead();
getTargetNamespace().convention(MappingsNamespace.INTERMEDIARY.toString()).finalizeValueOnRead();
}
public final <P extends AbstractRemapParams> void submitWork(Class<? extends AbstractRemapAction<P>> workAction, Action<P> action) {
final WorkQueue workQueue = getWorkerExecutor().noIsolation();
workQueue.submit(workAction, params -> {
params.getInputFile().set(getInputFile());
params.getOutputFile().set(getArchiveFile());
params.getSourceNamespace().set(getSourceNamespace());
params.getTargetNamespace().set(getTargetNamespace());
params.getArchivePreserveFileTimestamps().set(isPreserveFileTimestamps());
params.getArchiveReproducibleFileOrder().set(isReproducibleFileOrder());
action.execute(params);
});
}
public interface AbstractRemapParams extends WorkParameters {
RegularFileProperty getInputFile();
RegularFileProperty getOutputFile();
Property<String> getSourceNamespace();
Property<String> getTargetNamespace();
Property<Boolean> getArchivePreserveFileTimestamps();
Property<Boolean> getArchiveReproducibleFileOrder();
}
public abstract static class AbstractRemapAction<T extends AbstractRemapParams> implements WorkAction<T> {
protected final Path inputFile;
protected final Path outputFile;
@Inject
public AbstractRemapAction() {
inputFile = getParameters().getInputFile().getAsFile().get().toPath();
outputFile = getParameters().getOutputFile().getAsFile().get().toPath();
}
protected void rewriteJar() throws IOException {
final boolean isReproducibleFileOrder = getParameters().getArchiveReproducibleFileOrder().get();
final boolean isPreserveFileTimestamps = getParameters().getArchivePreserveFileTimestamps().get();
if (isReproducibleFileOrder || !isPreserveFileTimestamps) {
ZipReprocessorUtil.reprocessZip(outputFile.toFile(), isReproducibleFileOrder, isPreserveFileTimestamps);
}
}
}
@Deprecated
@InputFile
public RegularFileProperty getInput() {
return getInputFile();
}
}

View file

@ -50,17 +50,13 @@ public final class LoomTasks {
t.getOutputs().upToDateWhen(o -> false);
});
tasks.register("remapJar", RemapJarTask.class, t -> {
t.setDescription("Remaps the built project jar to intermediary mappings.");
t.setGroup(Constants.TaskGroup.FABRIC);
});
RemapTaskConfiguration.setupRemap(project);
TaskProvider<ExtractNativesTask> extractNatives = tasks.register("extractNatives", ExtractNativesTask.class);
tasks.register("downloadAssets", DownloadAssetsTask.class, t -> {
t.dependsOn(extractNatives);
t.setDescription("Downloads required assets for Fabric.");
});
tasks.register("remapSourcesJar", RemapSourcesJarTask.class, t -> t.setDescription("Remaps the project sources jar to intermediary names."));
TaskProvider<ValidateAccessWidenerTask> validateAccessWidener = tasks.register("validateAccessWidener", ValidateAccessWidenerTask.class, t -> {
t.setDescription("Validate all the rules in the access widener against the Minecraft jar");

View file

@ -1,38 +0,0 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2016-2020 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.task;
import org.gradle.api.tasks.Internal;
import net.fabricmc.loom.util.SourceRemapper;
public class RemapAllSourcesTask extends AbstractLoomTask {
public SourceRemapper sourceRemapper;
@Internal
public SourceRemapper getSourceRemapper() {
return sourceRemapper;
}
}

View file

@ -1,7 +1,7 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2016-2021 FabricMC
* Copyright (c) 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
@ -27,276 +27,281 @@ package net.fabricmc.loom.task;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.io.Serializable;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Objects;
import java.util.jar.Manifest;
import java.util.stream.Collectors;
import javax.inject.Inject;
import com.google.common.base.Preconditions;
import org.gradle.api.Action;
import org.gradle.api.Project;
import com.google.gson.JsonObject;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.file.ConfigurableFileCollection;
import org.gradle.api.file.FileCollection;
import org.gradle.api.file.RegularFileProperty;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.provider.ListProperty;
import org.gradle.api.provider.Property;
import org.gradle.api.provider.Provider;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.InputFile;
import org.gradle.api.tasks.InputFiles;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.TaskAction;
import org.gradle.jvm.tasks.Jar;
import org.jetbrains.annotations.ApiStatus;
import org.objectweb.asm.commons.Remapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.mappings.layered.MappingsNamespace;
import net.fabricmc.loom.build.JarRemapper;
import net.fabricmc.loom.build.MixinRefmapHelper;
import net.fabricmc.loom.build.nesting.IncludedJarFactory;
import net.fabricmc.loom.build.nesting.JarNester;
import net.fabricmc.loom.build.nesting.MergedNestedJarProvider;
import net.fabricmc.loom.build.nesting.NestedDependencyProvider;
import net.fabricmc.loom.build.nesting.NestedJarPathProvider;
import net.fabricmc.loom.build.nesting.NestedJarProvider;
import net.fabricmc.loom.configuration.JarManifestConfiguration;
import net.fabricmc.loom.configuration.accesswidener.AccessWidenerFile;
import net.fabricmc.loom.configuration.accesswidener.AccessWidenerJarProcessor;
import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl;
import net.fabricmc.loom.extension.MixinExtension;
import net.fabricmc.loom.task.service.JarManifestService;
import net.fabricmc.loom.task.service.MappingsService;
import net.fabricmc.loom.util.Constants;
import net.fabricmc.loom.util.TinyRemapperHelper;
import net.fabricmc.loom.util.ZipReprocessorUtil;
import net.fabricmc.loom.util.ZipUtils;
import net.fabricmc.stitch.util.Pair;
import net.fabricmc.tinyremapper.InputTag;
import net.fabricmc.tinyremapper.OutputConsumerPath;
import net.fabricmc.tinyremapper.TinyRemapper;
import net.fabricmc.tinyremapper.TinyUtils;
import net.fabricmc.tinyremapper.extension.mixin.MixinExtension;
public class RemapJarTask extends Jar {
public abstract class RemapJarTask extends AbstractRemapJarTask {
private static final String MANIFEST_PATH = "META-INF/MANIFEST.MF";
private final RegularFileProperty input;
private final Property<Boolean> addNestedDependencies;
private final Property<Boolean> addDefaultNestedDependencies;
private final Property<Boolean> remapAccessWidener;
private final List<Action<TinyRemapper.Builder>> remapOptions = new ArrayList<>();
public JarRemapper jarRemapper;
private FileCollection classpath;
private final Set<Object> nestedPaths = new LinkedHashSet<>();
@InputFiles
public abstract ConfigurableFileCollection getNestedJars();
@Input
public abstract Property<Boolean> getAddNestedDependencies();
@Inject
public RemapJarTask() {
super();
LoomGradleExtension extension = LoomGradleExtension.get(getProject());
input = getProject().getObjects().fileProperty();
addNestedDependencies = getProject().getObjects().property(Boolean.class)
.convention(false);
addDefaultNestedDependencies = getProject().getObjects().property(Boolean.class)
.convention(true);
remapAccessWidener = getProject().getObjects().property(Boolean.class)
.convention(false);
if (!extension.getMixin().getUseLegacyMixinAp().get()) {
remapOptions.add(b -> b.extension(new MixinExtension()));
}
getClasspath().from(getProject().getConfigurations().getByName(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME));
getAddNestedDependencies().convention(true).finalizeValueOnRead();
Configuration includeConfiguration = getProject().getConfigurations().getByName(Constants.Configurations.INCLUDE);
getNestedJars().from(new IncludedJarFactory(getProject()).getNestedJars(includeConfiguration));
}
@TaskAction
public void doTask() throws Throwable {
boolean singleRemap = false;
public void run() {
final LoomGradleExtension extension = LoomGradleExtension.get(getProject());
if (jarRemapper == null) {
singleRemap = true;
jarRemapper = new JarRemapper();
submitWork(RemapAction.class, params -> {
if (getAddNestedDependencies().get()) {
params.getNestedJars().from(getNestedJars());
}
params.getJarManifestService().set(JarManifestService.get(getProject()));
params.getRemapClasspath().from(getClasspath());
params.getMappings().add(MappingsService.createDefault(getProject(), getSourceNamespace().get(), getTargetNamespace().get()));
final boolean legacyMixin = extension.getMixin().getUseLegacyMixinAp().get();
params.getUseMixinExtension().set(!legacyMixin);
if (legacyMixin) {
params.getMixinMappings().from(extension.getAllMixinMappings());
setupLegacyMixinRefmapRemapping(params);
}
});
}
private void setupLegacyMixinRefmapRemapping(RemapParams params) {
final LoomGradleExtension extension = LoomGradleExtension.get(getProject());
final MixinExtension mixinExtension = extension.getMixin();
final JsonObject fabricModJson = MixinRefmapHelper.readFabricModJson(getInputFile().getAsFile().get());
if (fabricModJson == null) {
getProject().getLogger().warn("Could not find fabric.mod.json file in: " + getInputFile().getAsFile().get().getName());
return;
}
scheduleRemap(singleRemap || LoomGradleExtension.get(getProject()).isRootProject());
final Collection<String> allMixinConfigs = MixinRefmapHelper.getMixinConfigurationFiles(fabricModJson);
if (singleRemap) {
jarRemapper.remap();
for (SourceSet sourceSet : mixinExtension.getMixinSourceSets()) {
MixinExtension.MixinInformationContainer container = Objects.requireNonNull(
MixinExtension.getMixinInformationContainer(sourceSet)
);
final String refmapName = container.refmapNameProvider().get();
final List<String> mixinConfigs = container.sourceSet().getResources()
.matching(container.mixinConfigPattern())
.getFiles()
.stream()
.map(File::getName)
.filter(allMixinConfigs::contains)
.toList();
params.getMixinData().add(new RemapParams.RefmapData(mixinConfigs, refmapName));
}
}
public void scheduleRemap(boolean isMainRemapTask) throws Throwable {
Project project = getProject();
LoomGradleExtension extension = LoomGradleExtension.get(getProject());
Path input = this.getInput().getAsFile().get().toPath();
Path output = this.getArchiveFile().get().getAsFile().toPath();
public interface RemapParams extends AbstractRemapParams {
ConfigurableFileCollection getNestedJars();
ConfigurableFileCollection getRemapClasspath();
ConfigurableFileCollection getMixinMappings();
ListProperty<Provider<MappingsService>> getMappings();
if (!Files.exists(input)) {
throw new FileNotFoundException(input.toString());
}
Property<Boolean> getUseMixinExtension();
MappingsProviderImpl mappingsProvider = extension.getMappingsProvider();
record RefmapData(List<String> mixinConfigs, String refmapName) implements Serializable { }
ListProperty<RefmapData> getMixinData();
String fromM = MappingsNamespace.NAMED.toString();
String toM = MappingsNamespace.INTERMEDIARY.toString();
Property<JarManifestService> getJarManifestService();
}
if (isMainRemapTask) {
jarRemapper.addToClasspath(getRemapClasspath());
public abstract static class RemapAction extends AbstractRemapAction<RemapParams> {
private static final Logger LOGGER = LoggerFactory.getLogger(RemapAction.class);
jarRemapper.addMappings(TinyRemapperHelper.create(mappingsProvider.getMappings(), fromM, toM, false));
}
private TinyRemapper tinyRemapper;
for (File mixinMapFile : extension.getAllMixinMappings()) {
if (mixinMapFile.exists()) {
jarRemapper.addMappings(TinyUtils.createTinyMappingProvider(mixinMapFile.toPath(), fromM, toM));
@Override
public void execute() {
try {
LOGGER.info("Remapping {} to {}", inputFile, outputFile);
tinyRemapper = createTinyRemapper();
remap();
remapAccessWidener();
addRefmaps();
addNestedJars();
modifyJarManifest();
rewriteJar();
tinyRemapper.finish();
tinyRemapper = null;
LOGGER.debug("Finished remapping {}", inputFile);
} catch (Exception e) {
try {
Files.deleteIfExists(outputFile);
} catch (IOException ex) {
LOGGER.error("Failed to delete output file", ex);
}
throw new RuntimeException("Failed to remap", e);
}
}
// Add remap options to the jar remapper
jarRemapper.addOptions(this.remapOptions);
private void remap() throws IOException {
final InputTag inputTag = tinyRemapper.createInputTag();
NestedJarProvider nestedJarProvider = getNestedJarProvider();
nestedJarProvider.prepare(getProject());
tinyRemapper.readInputsAsync(inputTag, inputFile);
jarRemapper.scheduleRemap(input, output)
.supplyAccessWidener((remapData, remapper) -> {
if (getRemapAccessWidener().getOrElse(false) && extension.getAccessWidenerPath().isPresent()) {
AccessWidenerJarProcessor accessWidenerJarProcessor = extension.getJarProcessorManager().getByType(AccessWidenerJarProcessor.class);
byte[] data;
try {
data = accessWidenerJarProcessor.getRemappedAccessWidener(remapper, toM);
} catch (IOException e) {
throw new RuntimeException("Failed to remap access widener", e);
}
AccessWidenerFile awFile = AccessWidenerFile.fromModJar(remapData.input);
Preconditions.checkNotNull(awFile, "Failed to find accessWidener in fabric.mod.json: " + remapData.input);
return Pair.of(awFile.name(), data);
}
return null;
})
.complete((data, accessWidener) -> {
if (!Files.exists(output)) {
throw new RuntimeException("Failed to remap " + input + " to " + output + " - file missing!");
}
if (extension.getMixin().getUseLegacyMixinAp().get()) {
if (MixinRefmapHelper.addRefmapName(project, output)) {
project.getLogger().debug("Transformed mixin reference maps in output JAR!");
}
}
if (getAddNestedDependencies().getOrElse(false)) {
JarNester.nestJars(nestedJarProvider.provide(), output.toFile(), project.getLogger());
}
if (accessWidener != null) {
try {
ZipUtils.replace(data.output, accessWidener.getLeft(), accessWidener.getRight());
} catch (IOException e) {
throw new UncheckedIOException("Failed to replace access widener in output jar", e);
}
}
// Add data to the manifest
try {
int count = ZipUtils.transform(data.output, Map.of(MANIFEST_PATH, bytes -> {
var manifest = new Manifest(new ByteArrayInputStream(bytes));
var manifestConfiguration = new JarManifestConfiguration(project);
manifestConfiguration.configure(manifest);
manifest.getMainAttributes().putValue("Fabric-Mapping-Namespace", toM);
ByteArrayOutputStream out = new ByteArrayOutputStream();
manifest.write(out);
return out.toByteArray();
}));
Preconditions.checkState(count > 0, "Did not transform any jar manifest");
} catch (IOException e) {
throw new UncheckedIOException("Failed to transform jar manifest", e);
}
if (isReproducibleFileOrder() || !isPreserveFileTimestamps()) {
try {
ZipReprocessorUtil.reprocessZip(output.toFile(), isReproducibleFileOrder(), isPreserveFileTimestamps());
} catch (IOException e) {
throw new RuntimeException("Failed to re-process jar", e);
}
}
});
}
private NestedJarProvider getNestedJarProvider() {
Configuration includeConfiguration = getProject().getConfigurations().getByName(Constants.Configurations.INCLUDE);
if (!addDefaultNestedDependencies.getOrElse(true)) {
return new NestedJarPathProvider(nestedPaths);
try (OutputConsumerPath outputConsumer = new OutputConsumerPath.Builder(outputFile).build()) {
outputConsumer.addNonClassFiles(inputFile);
tinyRemapper.apply(outputConsumer, inputTag);
}
}
NestedJarProvider baseProvider = NestedDependencyProvider.createNestedDependencyProviderFromConfiguration(getProject(), includeConfiguration);
private void remapAccessWidener() throws IOException {
final AccessWidenerFile accessWidenerFile = AccessWidenerFile.fromModJar(inputFile);
if (nestedPaths.isEmpty()) {
return baseProvider;
if (accessWidenerFile == null) {
return;
}
byte[] remapped = remapAccessWidener(accessWidenerFile.content(), tinyRemapper.getEnvironment().getRemapper(), MappingsNamespace.INTERMEDIARY.toString());
// Finally, replace the output with the remaped aw
ZipUtils.replace(outputFile, accessWidenerFile.path(), remapped);
}
return new MergedNestedJarProvider(
baseProvider,
new NestedJarPathProvider(nestedPaths)
);
}
private static byte[] remapAccessWidener(byte[] input, Remapper asmRemapper, String targetNamespace) {
int version = AccessWidenerReader.readVersion(input);
private Path[] getRemapClasspath() {
FileCollection files = this.classpath;
AccessWidenerWriter writer = new AccessWidenerWriter(version);
AccessWidenerRemapper remapper = new AccessWidenerRemapper(
writer,
asmRemapper,
MappingsNamespace.NAMED.toString(),
targetNamespace
);
AccessWidenerReader reader = new AccessWidenerReader(remapper);
reader.read(input);
if (files == null) {
files = getProject().getConfigurations().getByName(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME);
return writer.write();
}
return files.getFiles().stream()
.map(File::toPath)
.filter(Files::exists)
.toArray(Path[]::new);
}
private void addNestedJars() {
FileCollection nestedJars = getParameters().getNestedJars();
@InputFile
public RegularFileProperty getInput() {
return input;
}
if (nestedJars.isEmpty()) {
LOGGER.info("No jars to nest");
return;
}
@Input
public Property<Boolean> getAddNestedDependencies() {
return addNestedDependencies;
}
@Input
public Property<Boolean> getAddDefaultNestedDependencies() {
return addDefaultNestedDependencies;
}
@Input
public Property<Boolean> getRemapAccessWidener() {
return remapAccessWidener;
}
public void remapOptions(Action<TinyRemapper.Builder> action) {
this.remapOptions.add(action);
}
public RemapJarTask classpath(FileCollection collection) {
if (this.classpath == null) {
this.classpath = collection;
} else {
this.classpath = this.classpath.plus(collection);
JarNester.nestJars(nestedJars.getFiles(), outputFile.toFile(), LOGGER);
}
return this;
}
private void modifyJarManifest() throws IOException {
int count = ZipUtils.transform(outputFile, Map.of(MANIFEST_PATH, bytes -> {
var manifest = new Manifest(new ByteArrayInputStream(bytes));
@ApiStatus.Experimental
// This only allows mod jars, proceed with care when trying to pass in configurations with projects, or something that depends on a task.
public RemapJarTask include(Object... paths) {
Collections.addAll(nestedPaths, paths);
this.addNestedDependencies.set(true);
getParameters().getJarManifestService().get().apply(manifest);
manifest.getMainAttributes().putValue("Fabric-Mapping-Namespace", getParameters().getTargetNamespace().get());
return this;
ByteArrayOutputStream out = new ByteArrayOutputStream();
manifest.write(out);
return out.toByteArray();
}));
Preconditions.checkState(count > 0, "Did not transform any jar manifest");
}
private void addRefmaps() throws IOException {
if (getParameters().getUseMixinExtension().get()) {
return;
}
for (RemapParams.RefmapData refmapData : getParameters().getMixinData().get()) {
int transformed = ZipUtils.transformJson(JsonObject.class, outputFile, refmapData.mixinConfigs().stream().collect(Collectors.toMap(s -> s, s -> json -> {
if (!json.has("refmap")) {
json.addProperty("refmap", refmapData.refmapName());
}
return json;
})));
}
}
private TinyRemapper createTinyRemapper() {
TinyRemapper.Builder builder = TinyRemapper.newRemapper();
for (Provider<MappingsService> provider : getParameters().getMappings().get()) {
builder.withMappings(provider.get().getMappingsProvider());
}
for (File mixinMapping : getParameters().getMixinMappings()) {
builder.withMappings(TinyUtils.createTinyMappingProvider(mixinMapping.toPath(), getParameters().getSourceNamespace().get(), getParameters().getTargetNamespace().get()));
}
if (getParameters().getUseMixinExtension().get()) {
builder.extension(new net.fabricmc.tinyremapper.extension.mixin.MixinExtension());
}
TinyRemapper remapper = builder.build();
// Apply classpath
for (File file : getParameters().getRemapClasspath()) {
remapper.readClassPathAsync(file.toPath());
}
return remapper;
}
}
}

View file

@ -1,7 +1,7 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2016-2021 FabricMC
* Copyright (c) 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
@ -24,70 +24,65 @@
package net.fabricmc.loom.task;
import org.gradle.api.file.RegularFileProperty;
import java.io.IOException;
import java.nio.file.Files;
import javax.inject.Inject;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.provider.Property;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.InputFile;
import org.gradle.api.tasks.Internal;
import org.gradle.api.tasks.OutputFile;
import org.gradle.api.tasks.TaskAction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.fabricmc.loom.api.mappings.layered.MappingsNamespace;
import net.fabricmc.loom.util.SourceRemapper;
public class RemapSourcesJarTask extends AbstractLoomTask {
private final RegularFileProperty input = getProject().getObjects().fileProperty();
private final RegularFileProperty output = getProject().getObjects().fileProperty().convention(input);
private final Property<String> targetNamespace = getProject().getObjects().property(String.class).convention(MappingsNamespace.INTERMEDIARY.toString());
private SourceRemapper sourceRemapper = null;
private final Property<Boolean> preserveFileTimestamps = getProject().getObjects().property(Boolean.class).convention(true);
private final Property<Boolean> reproducibleFileOrder = getProject().getObjects().property(Boolean.class).convention(false);
import net.fabricmc.loom.task.service.MappingsService;
import net.fabricmc.loom.task.service.SourceRemapperService;
public abstract class RemapSourcesJarTask extends AbstractRemapJarTask {
@Inject
public RemapSourcesJarTask() {
super();
getClasspath().from(getProject().getConfigurations().getByName(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME));
}
@TaskAction
public void remap() throws Exception {
if (sourceRemapper == null) {
String direction = targetNamespace.get();
SourceRemapper.remapSources(getProject(), input.get().getAsFile(), output.get().getAsFile(), direction.equals(MappingsNamespace.NAMED.toString()), reproducibleFileOrder.get(), preserveFileTimestamps.get());
} else {
sourceRemapper.scheduleRemapSources(input.get().getAsFile(), output.get().getAsFile(), reproducibleFileOrder.get(), preserveFileTimestamps.get());
public void run() {
submitWork(RemapSourcesAction.class, params -> {
params.getSourcesRemapperService().set(SourceRemapperService.create(getProject(), MappingsService.createDefault(getProject(), getSourceNamespace().get(), getTargetNamespace().get()), getClasspath()));
});
}
public interface RemapSourcesParams extends AbstractRemapParams {
Property<SourceRemapperService> getSourcesRemapperService();
}
public abstract static class RemapSourcesAction extends AbstractRemapAction<RemapSourcesParams> {
private static final Logger LOGGER = LoggerFactory.getLogger(RemapSourcesAction.class);
private final SourceRemapperService sourceRemapperService;
public RemapSourcesAction() {
super();
sourceRemapperService = getParameters().getSourcesRemapperService().get();
}
@Override
public void execute() {
try {
sourceRemapperService.remapSourcesJar(inputFile, outputFile);
rewriteJar();
} catch (Exception e) {
try {
Files.deleteIfExists(outputFile);
} catch (IOException ex) {
LOGGER.error("Failed to delete output file", ex);
}
throw new RuntimeException("Failed to remap sources", e);
}
}
}
@Internal
public SourceRemapper getSourceRemapper() {
return sourceRemapper;
}
public RemapSourcesJarTask setSourceRemapper(SourceRemapper sourceRemapper) {
this.sourceRemapper = sourceRemapper;
return this;
}
@InputFile
public RegularFileProperty getInput() {
return input;
}
@OutputFile
public RegularFileProperty getOutput() {
return output;
}
@Input
public Property<String> getTargetNamespace() {
return targetNamespace;
}
@Input
public Property<Boolean> getPreserveFileTimestamps() {
return preserveFileTimestamps;
}
@Input
public Property<Boolean> getReproducibleFileOrder() {
return reproducibleFileOrder;
}
}

View file

@ -0,0 +1,138 @@
/*
* 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.task;
import java.io.File;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.plugins.BasePlugin;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.plugins.JavaPluginExtension;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.TaskContainer;
import org.gradle.api.tasks.TaskProvider;
import org.gradle.api.tasks.bundling.AbstractArchiveTask;
import org.gradle.api.tasks.bundling.Jar;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.util.Constants;
public class RemapTaskConfiguration {
public static final String REMAP_JAR_TASK_NAME = "remapJar";
public static final String REMAP_SOURCES_JAR_TASK_NAME = "remapSourcesJar";
public static void setupRemap(Project project) {
final TaskContainer tasks = project.getTasks();
final LoomGradleExtension extension = LoomGradleExtension.get(project);
if (!extension.getRemapArchives().get()) {
extension.getUnmappedModCollection().from(project.getTasks().getByName(JavaPlugin.JAR_TASK_NAME));
return;
}
// Register the default remap jar task
TaskProvider<RemapJarTask> remapJarTaskProvider = tasks.register(REMAP_JAR_TASK_NAME, RemapJarTask.class, task -> {
final AbstractArchiveTask jarTask = tasks.named(JavaPlugin.JAR_TASK_NAME, AbstractArchiveTask.class).get();
// Basic task setup
task.dependsOn(jarTask);
task.setDescription("Remaps the built project jar to intermediary mappings.");
task.setGroup(Constants.TaskGroup.FABRIC);
project.getArtifacts().add(JavaPlugin.API_ELEMENTS_CONFIGURATION_NAME, task);
project.getArtifacts().add(JavaPlugin.RUNTIME_ELEMENTS_CONFIGURATION_NAME, task);
// Setup the input file and the nested deps
task.getInputFile().convention(jarTask.getArchiveFile());
task.dependsOn(tasks.named(JavaPlugin.JAR_TASK_NAME));
});
// Configure the default jar task
tasks.named(JavaPlugin.JAR_TASK_NAME, AbstractArchiveTask.class).configure(task -> {
task.getArchiveClassifier().convention("dev");
task.getDestinationDirectory().set(new File(project.getBuildDir(), "devlibs"));
});
tasks.named(BasePlugin.ASSEMBLE_TASK_NAME).configure(task -> task.dependsOn(remapJarTaskProvider));
trySetupSourceRemapping(project);
if (extension.getSetupRemappedVariants().get()) {
// 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);
configuration.getArtifacts().removeIf(artifact -> {
Task jarTask = project.getTasks().getByName(JavaPlugin.JAR_TASK_NAME);
// if the artifact is a -dev jar and "builtBy jar"
return "dev".equals(artifact.getClassifier()) && artifact.getBuildDependencies().getDependencies(null).contains(jarTask);
});
}
}
}
private static void trySetupSourceRemapping(Project project) {
final TaskContainer tasks = project.getTasks();
final LoomGradleExtension extension = LoomGradleExtension.get(project);
TaskProvider<RemapSourcesJarTask> remapSourcesTask = tasks.register(REMAP_SOURCES_JAR_TASK_NAME, RemapSourcesJarTask.class, task -> {
task.setDescription("Remaps the default sources jar to intermediary mappings.");
task.setGroup(Constants.TaskGroup.FABRIC);
final JavaPluginExtension javaExtension = project.getExtensions().getByType(JavaPluginExtension.class);
final String sourcesJarTaskName = javaExtension.getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME).getSourcesJarTaskName();
final Task sourcesTask = project.getTasks().findByName(sourcesJarTaskName);
if (sourcesTask == null) {
project.getLogger().info("{} task was not found, not remapping sources", sourcesJarTaskName);
task.setEnabled(false);
return;
}
if (!(sourcesTask instanceof Jar sourcesJarTask)) {
project.getLogger().info("{} task is not a Jar task, not remapping sources", sourcesJarTaskName);
task.setEnabled(false);
return;
}
sourcesJarTask.getArchiveClassifier().convention("dev-sources");
sourcesJarTask.getDestinationDirectory().set(new File(project.getBuildDir(), "devlibs"));
task.getArchiveClassifier().convention("sources");
task.dependsOn(sourcesJarTask);
task.getInputFile().convention(sourcesJarTask.getArchiveFile());
if (extension.getSetupRemappedVariants().get()) {
project.getArtifacts().add(JavaPlugin.SOURCES_ELEMENTS_CONFIGURATION_NAME, task);
// Remove the dev sources artifact
Configuration configuration = project.getConfigurations().getByName(JavaPlugin.SOURCES_ELEMENTS_CONFIGURATION_NAME);
configuration.getArtifacts().removeIf(a -> a.getFile().equals(sourcesJarTask.getArchiveFile().get().getAsFile()));
}
});
tasks.named(BasePlugin.ASSEMBLE_TASK_NAME).configure(task -> task.dependsOn(remapSourcesTask));
}
}

View file

@ -0,0 +1,123 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 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.task.service;
import java.io.Serializable;
import java.util.Optional;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Dependency;
import org.gradle.api.provider.Property;
import org.gradle.api.provider.Provider;
import org.gradle.api.services.BuildService;
import org.gradle.api.services.BuildServiceParameters;
import org.gradle.util.GradleVersion;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.LoomGradlePlugin;
import net.fabricmc.loom.util.Constants;
import net.fabricmc.tinyremapper.TinyRemapper;
public abstract class JarManifestService implements BuildService<JarManifestService.Params> {
interface Params extends BuildServiceParameters {
Property<String> getGradleVersion();
Property<String> getLoomVersion();
Property<String> getMCEVersion();
Property<String> getMinecraftVersion();
Property<String> getTinyRemapperVersion();
Property<String> getFabricLoaderVersion();
Property<MixinVersion> getMixinVersion();
}
public static Provider<JarManifestService> get(Project project) {
return project.getGradle().getSharedServices().registerIfAbsent("LoomJarManifestService:" + project.getName(), JarManifestService.class, spec -> {
spec.parameters(params -> {
LoomGradleExtension extension = LoomGradleExtension.get(project);
Optional<String> tinyRemapperVersion = Optional.ofNullable(TinyRemapper.class.getPackage().getImplementationVersion());
params.getGradleVersion().set(GradleVersion.current().getVersion());
params.getLoomVersion().set(LoomGradlePlugin.LOOM_VERSION);
params.getMCEVersion().set(Constants.Dependencies.Versions.MIXIN_COMPILE_EXTENSIONS);
params.getMinecraftVersion().set(extension.getMinecraftProvider().minecraftVersion());
params.getTinyRemapperVersion().set(tinyRemapperVersion.orElse("unknown"));
params.getFabricLoaderVersion().set(getLoaderVersion(project).orElse("unknown"));
params.getMixinVersion().set(getMixinVersion(project).orElse(new MixinVersion("unknown", "unknown")));
});
});
}
public void apply(Manifest manifest) {
// Don't set when running the reproducible build tests as it will break them when anything updates
if (Boolean.getBoolean("loom.test.reproducible")) {
return;
}
Attributes attributes = manifest.getMainAttributes();
Params p = getParameters();
attributes.putValue("Fabric-Gradle-Version", p.getGradleVersion().get());
attributes.putValue("Fabric-Loom-Version", p.getLoomVersion().get());
attributes.putValue("Fabric-Mixin-Compile-Extensions-Version", p.getMCEVersion().get());
attributes.putValue("Fabric-Minecraft-Version", p.getMinecraftVersion().get());
attributes.putValue("Fabric-Tiny-Remapper-Version", p.getTinyRemapperVersion().get());
attributes.putValue("Fabric-Loader-Version", p.getFabricLoaderVersion().get());
// This can be overridden by mods if required
if (!attributes.containsKey("Fabric-Mixin-Version")) {
attributes.putValue("Fabric-Mixin-Version", p.getMixinVersion().get().version());
attributes.putValue("Fabric-Mixin-Group", p.getMixinVersion().get().group());
}
}
private static Optional<String> getLoaderVersion(Project project) {
LoomGradleExtension extension = LoomGradleExtension.get(project);
if (extension.getInstallerData() == null) {
project.getLogger().warn("Could not determine fabric loader version for jar manifest");
return Optional.empty();
}
return Optional.of(extension.getInstallerData().version());
}
private record MixinVersion(String group, String version) implements Serializable { }
private static Optional<MixinVersion> getMixinVersion(Project project) {
// Not super ideal that this uses the mod compile classpath, should prob look into making this not a thing at somepoint
Optional<Dependency> dependency = project.getConfigurations().getByName(Constants.Configurations.LOADER_DEPENDENCIES)
.getDependencies()
.stream()
.filter(dep -> "sponge-mixin".equals(dep.getName()))
.findFirst();
if (dependency.isEmpty()) {
project.getLogger().warn("Could not determine Mixin version for jar manifest");
}
return dependency.map(d -> new MixinVersion(d.getGroup(), d.getVersion()));
}
}

View file

@ -0,0 +1,118 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 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.task.service;
import java.io.File;
import java.io.IOException;
import java.io.UncheckedIOException;
import org.gradle.api.Project;
import org.gradle.api.file.RegularFileProperty;
import org.gradle.api.provider.Property;
import org.gradle.api.provider.Provider;
import org.gradle.api.services.BuildService;
import org.gradle.api.services.BuildServiceParameters;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl;
import net.fabricmc.loom.util.TinyRemapperHelper;
import net.fabricmc.mappingio.MappingReader;
import net.fabricmc.mappingio.tree.MemoryMappingTree;
import net.fabricmc.tinyremapper.IMappingProvider;
public abstract class MappingsService implements BuildService<MappingsService.Params>, AutoCloseable {
interface Params extends BuildServiceParameters {
RegularFileProperty getMappingsFile();
Property<String> getFromNamespace();
Property<String> getToNamespace();
Property<Boolean> getRemapLocals();
}
public static synchronized Provider<MappingsService> create(Project project, String name, File mappingsFile, String from, String to, boolean remapLocals) {
return project.getGradle().getSharedServices().registerIfAbsent(name, MappingsService.class, spec -> {
spec.parameters(params -> {
params.getMappingsFile().set(mappingsFile);
params.getFromNamespace().set(from);
params.getToNamespace().set(to);
params.getRemapLocals().set(remapLocals);
});
});
}
public static Provider<MappingsService> createDefault(Project project, String from, String to) {
final MappingsProviderImpl mappingsProvider = LoomGradleExtension.get(project).getMappingsProvider();
final String name = mappingsProvider.getBuildServiceName("mappingsProvider", from, to);
return MappingsService.create(project, name, mappingsProvider.tinyMappings.toFile(), from, to, false);
}
private IMappingProvider mappingProvider = null;
private MemoryMappingTree memoryMappingTree = null;
public synchronized IMappingProvider getMappingsProvider() {
if (mappingProvider == null) {
try {
mappingProvider = TinyRemapperHelper.create(
getParameters().getMappingsFile().get().getAsFile().toPath(),
getParameters().getFromNamespace().get(),
getParameters().getToNamespace().get(),
getParameters().getRemapLocals().get()
);
} catch (IOException e) {
throw new UncheckedIOException("Failed to read mappings from: " + getParameters().getMappingsFile().get().getAsFile().getAbsolutePath(), e);
}
}
return mappingProvider;
}
public synchronized MemoryMappingTree getMemoryMappingTree() {
if (memoryMappingTree == null) {
memoryMappingTree = new MemoryMappingTree();
try {
MappingReader.read(getParameters().getMappingsFile().get().getAsFile().toPath(), memoryMappingTree);
} catch (IOException e) {
throw new UncheckedIOException("Failed to read mappings from: " + getParameters().getMappingsFile().get().getAsFile().getAbsolutePath(), e);
}
}
return memoryMappingTree;
}
public String getFromNamespace() {
return getParameters().getFromNamespace().get();
}
public String getToNamespace() {
return getParameters().getToNamespace().get();
}
@Override
public void close() {
mappingProvider = null;
}
}

View file

@ -0,0 +1,133 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 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.task.service;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import org.cadixdev.lorenz.MappingSet;
import org.cadixdev.mercury.Mercury;
import org.cadixdev.mercury.remapper.MercuryRemapper;
import org.gradle.api.Project;
import org.gradle.api.file.ConfigurableFileCollection;
import org.gradle.api.file.FileCollection;
import org.gradle.api.provider.Property;
import org.gradle.api.provider.Provider;
import org.gradle.api.services.BuildService;
import org.gradle.api.services.BuildServiceParameters;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.fabricmc.loom.util.DeletingFileVisitor;
import net.fabricmc.loom.util.FileSystemUtil;
import net.fabricmc.loom.util.SourceRemapper;
import net.fabricmc.loom.util.ZipUtils;
import net.fabricmc.lorenztiny.TinyMappingsReader;
public abstract class SourceRemapperService implements BuildService<SourceRemapperService.Params>, AutoCloseable {
public interface Params extends BuildServiceParameters {
Property<Provider<MappingsService>> getMappings();
ConfigurableFileCollection getClasspath();
}
public static synchronized Provider<SourceRemapperService> create(Project project, Provider<MappingsService> mappings, FileCollection classpath) {
// TODO may need a better name, im not too sure
return project.getGradle().getSharedServices().registerIfAbsent("sourceremapper", SourceRemapperService.class, spec ->
spec.parameters(params -> {
params.getMappings().set(mappings);
params.getClasspath().from(classpath);
}
));
}
private static final Logger LOGGER = LoggerFactory.getLogger(SourceRemapperService.class);
private Mercury mercury;
public void remapSourcesJar(Path source, Path destination) throws IOException {
if (source.equals(destination)) {
throw new UnsupportedOperationException("Cannot remap in place");
}
Path srcPath = source;
boolean isSrcTmp = false;
// Create a temp directory with all of the sources
if (!Files.isDirectory(source)) {
isSrcTmp = true;
srcPath = Files.createTempDirectory("fabric-loom-src");
ZipUtils.unpackAll(source, srcPath);
}
if (!Files.isDirectory(destination) && Files.exists(destination)) {
Files.delete(destination);
}
try (FileSystemUtil.Delegate dstFs = Files.isDirectory(destination) ? null : FileSystemUtil.getJarFileSystem(destination, true)) {
Path dstPath = dstFs != null ? dstFs.get().getPath("/") : destination;
doRemap(srcPath, dstPath, source);
SourceRemapper.copyNonJavaFiles(srcPath, dstPath, LOGGER, source);
} finally {
if (isSrcTmp) {
Files.walkFileTree(srcPath, new DeletingFileVisitor());
}
}
}
private synchronized void doRemap(Path srcPath, Path dstPath, Path source) throws IOException {
if (mercury == null) {
mercury = new Mercury();
mercury.setGracefulClasspathChecks(true);
mercury.getProcessors().add(MercuryRemapper.create(getMappings()));
getParameters().getClasspath().forEach(file -> mercury.getClassPath().add(file.toPath()));
}
try {
// Not thread safe!!
mercury.rewrite(srcPath, dstPath);
} catch (Exception e) {
LOGGER.warn("Could not remap " + source + " fully!", e);
}
}
private MappingSet getMappings() throws IOException {
return new TinyMappingsReader(mappingsService().getMemoryMappingTree(), mappingsService().getFromNamespace(), mappingsService().getToNamespace()).read();
}
private MappingsService mappingsService() {
return getParameters().getMappings().get().get();
}
@Override
public void close() throws Exception {
mercury = null;
// This is required (:
System.gc();
}
}

View file

@ -37,6 +37,7 @@ import org.cadixdev.lorenz.MappingSet;
import org.cadixdev.mercury.Mercury;
import org.cadixdev.mercury.remapper.MercuryRemapper;
import org.gradle.api.Project;
import org.slf4j.Logger;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.api.mappings.layered.MappingsNamespace;
@ -142,7 +143,7 @@ public class SourceRemapper {
project.getLogger().warn("Could not remap " + source.getName() + " fully!", e);
}
copyNonJavaFiles(srcPath, dstPath, project, source);
copyNonJavaFiles(srcPath, dstPath, project.getLogger(), source.toPath());
if (dstFs != null) {
dstFs.close();
@ -202,7 +203,7 @@ public class SourceRemapper {
return mercury;
}
private static void copyNonJavaFiles(Path from, Path to, Project project, File source) throws IOException {
public static void copyNonJavaFiles(Path from, Path to, Logger logger, Path source) throws IOException {
Files.walk(from).forEach(path -> {
Path targetPath = to.resolve(from.relativize(path).toString());
@ -210,7 +211,7 @@ public class SourceRemapper {
try {
Files.copy(path, targetPath);
} catch (IOException e) {
project.getLogger().warn("Could not copy non-java sources '" + source.getName() + "' fully!", e);
logger.warn("Could not copy non-java sources '" + source + "' fully!", e);
}
}
});

View file

@ -36,6 +36,7 @@ import org.gradle.api.Project;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.api.mappings.layered.MappingsNamespace;
import net.fabricmc.mappingio.MappingReader;
import net.fabricmc.mappingio.tree.MappingTree;
import net.fabricmc.mappingio.tree.MemoryMappingTree;
import net.fabricmc.tinyremapper.IMappingProvider;
@ -101,6 +102,12 @@ public final class TinyRemapperHelper {
return new IMappingProvider.Member(className, memberName, descriptor);
}
public static IMappingProvider create(Path mappings, String from, String to, boolean remapLocalVariables) throws IOException {
MemoryMappingTree mappingTree = new MemoryMappingTree();
MappingReader.read(mappings, mappingTree);
return create(mappingTree, from, to, remapLocalVariables);
}
public static IMappingProvider create(MappingTree mappings, String from, String to, boolean remapLocalVariables) {
return (acceptor) -> {
for (MappingTree.ClassMapping classDef : mappings.getClasses()) {

View file

@ -28,7 +28,7 @@ import org.gradle.util.GradleVersion
class LoomTestConstants {
public final static String DEFAULT_GRADLE = GradleVersion.current().getVersion()
public final static String PRE_RELEASE_GRADLE = "7.4-20211216231505+0000"
public final static String PRE_RELEASE_GRADLE = "7.4-20211219231013+0000"
public final static String[] STANDARD_TEST_VERSIONS = [DEFAULT_GRADLE, PRE_RELEASE_GRADLE]
}

View file

@ -44,7 +44,7 @@ class FabricAPITest extends Specification implements GradleProjectTestTrait {
setup:
def gradle = gradleProject(
repo: "https://github.com/FabricMC/fabric.git",
commit: "ce6198f63bbe0e17ba631420e9186fb72cc8b2af",
commit: "71b634e5b7845296b11be3fa6545f4fbfacc017f",
version: version,
patch: "fabric_api"
)

View file

@ -47,7 +47,7 @@ class MixinApSimpleTest extends Specification implements GradleProjectTestTrait
result.task(":build").outcome == SUCCESS
// verify the ref-map name is correctly generated
def main = new JarFile(gradle.getOutputFile("fabric-example-mod-1.0.0-dev.jar").absoluteFile)
def main = new JarFile(new File(gradle.projectDir, "build/devlibs/fabric-example-mod-1.0.0-dev.jar").absoluteFile)
main.getEntry("main-refmap0000.json") != null
def mixin = new JarFile(gradle.getOutputFile("fabric-example-mod-1.0.0-mixin.jar").absoluteFile)
mixin.getEntry("default-refmap0000.json") != null

View file

@ -45,9 +45,6 @@ class MultiProjectTest extends Specification implements GradleProjectTestTrait {
result.task(":core:build").outcome == SUCCESS
result.task(":example:build").outcome == SUCCESS
result.task(":remapAllJars").outcome == SUCCESS
result.task(":remapAllSources").outcome == SUCCESS
gradle.hasOutputZipEntry("multiproject-1.0.0.jar", "META-INF/jars/example-1.0.0.jar")
gradle.hasOutputZipEntry("multiproject-1.0.0.jar", "META-INF/jars/core-1.0.0.jar")
gradle.hasOutputZipEntry("multiproject-1.0.0.jar", "META-INF/jars/fabric-api-base-0.2.1+9354966b7d.jar")

View file

@ -51,6 +51,7 @@ class SimpleProjectTest extends Specification implements GradleProjectTestTrait
then:
result.task(":build").outcome == SUCCESS
gradle.getOutputZipEntry("fabric-example-mod-1.0.0.jar", "META-INF/MANIFEST.MF").contains("Fabric-Loom-Version: 0.0.0+unknown")
gradle.getOutputZipEntry("fabric-example-mod-1.0.0-sources.jar", "net/fabricmc/example/mixin/ExampleMixin.java").contains("class_442") // Very basic test to ensure sources got remapped
serverResult.successful()
serverResult.output.contains("Hello simple Fabric mod") // A check to ensure our mod init was actually called

View file

@ -1,55 +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.test.unit
import net.fabricmc.loom.build.nesting.MergedNestedJarProvider
import net.fabricmc.loom.build.nesting.NestedJarProvider
import org.gradle.api.Project
import spock.lang.Specification
class MergedNestedJarProviderTest extends Specification {
def "empty test"() {
given:
def nestedJarProvider = new MergedNestedJarProvider(new TestNestedJarProvider())
when:
nestedJarProvider.prepare(null)
then:
nestedJarProvider.provide() != null
}
private class TestNestedJarProvider implements NestedJarProvider {
private Collection<File> files = null
@Override
Collection<File> provide() {
return files
}
@Override
void prepare(Project project) {
files = []
}
}
}

View file

@ -1,6 +1,6 @@
diff --git a/build.gradle b/build.gradle
--- a/build.gradle (revision ce6198f63bbe0e17ba631420e9186fb72cc8b2af)
+++ b/build.gradle (date 1637848132986)
--- a/build.gradle (revision 71b634e5b7845296b11be3fa6545f4fbfacc017f)
+++ b/build.gradle (date 1638654919842)
@@ -31,17 +31,7 @@
throw new NullPointerException("Could not find version for " + project.name)
}
@ -20,43 +20,3 @@ diff --git a/build.gradle b/build.gradle
}
def getBranch() {
@@ -132,9 +122,8 @@
include "**/*.java"
}
- task sourcesJar(type: Jar, dependsOn: classes) {
- archiveClassifier = "sources"
- from sourceSets.main.allSource
+ java {
+ withSourcesJar()
}
checkstyle {
@@ -229,12 +218,16 @@
publications {
mavenJava(MavenPublication) {
from components.java
+
+ artifact javadocJar
}
}
setupRepositories(repositories)
}
+ loom.disableDeprecatedPomGeneration(publishing.publications.mavenJava)
+
javadoc.enabled = false
afterEvaluate {
@@ -242,10 +235,6 @@
genSourcesWithFernFlower.enabled = false
genSourcesWithCfr.enabled = false
unpickJar.enabled = false
-
- // Work around a loom bug causing empty jars to be pushed to maven local.
- publishMavenJavaPublicationToMavenLocal.dependsOn rootProject.tasks.getByName("remapAllJars")
- publishMavenJavaPublicationToMavenLocal.dependsOn rootProject.tasks.getByName("remapAllSources")
}
}

View file

@ -29,6 +29,9 @@ dependencies {
// PSA: Some older mods, compiled on Loom 0.2.1, might have outdated Maven POMs.
// You may need to force-disable transitiveness on them.
// Example include
include "org.xerial:sqlite-jdbc:3.36.0.3"
}
tasks.withType(JavaCompile).configureEach {