mirror of
https://github.com/architectury/architectury-loom.git
synced 2026-03-28 04:07:01 -05:00
Merge branch '0.11.0-interf' into dev/0.11.0
# Conflicts: # .github/workflows/test.yml # build.gradle # src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java # src/main/java/net/fabricmc/loom/build/JarRemapper.java # src/main/java/net/fabricmc/loom/build/MixinRefmapHelper.java # src/main/java/net/fabricmc/loom/build/ModCompileRemapper.java # src/main/java/net/fabricmc/loom/build/mixin/AnnotationProcessorInvoker.java # src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java # src/main/java/net/fabricmc/loom/configuration/JarManifestConfiguration.java # src/main/java/net/fabricmc/loom/configuration/RemapConfiguration.java # src/main/java/net/fabricmc/loom/configuration/ide/RunConfig.java # src/main/java/net/fabricmc/loom/configuration/ide/SetupIntelijRunConfigs.java # src/main/java/net/fabricmc/loom/configuration/providers/LaunchProvider.java # src/main/java/net/fabricmc/loom/configuration/providers/MinecraftProviderImpl.java # src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftMappedProvider.java # src/main/java/net/fabricmc/loom/extension/MixinExtensionImpl.java # src/main/java/net/fabricmc/loom/task/LoomTasks.java # src/main/java/net/fabricmc/loom/task/RemapJarTask.java # src/main/java/net/fabricmc/loom/task/RemapSourcesJarTask.java # src/main/java/net/fabricmc/loom/util/Constants.java # src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/LayeredMappingSpecBuilderTest.groovy
This commit is contained in:
8
.github/workflows/test.yml
vendored
8
.github/workflows/test.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: gradle/wrapper-validation-action@v1
|
||||
- run: gradle build check -x test --stacktrace
|
||||
- run: gradle build check -x test --stacktrace --warning-mode fail
|
||||
|
||||
# This job is used to feed the test matrix of next job to allow the tests to run in parallel
|
||||
prepare_test_matrix:
|
||||
@@ -27,7 +27,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: gradle writeActionsTestMatrix --stacktrace
|
||||
- run: gradle writeActionsTestMatrix --stacktrace --warning-mode fail
|
||||
- id: set-matrix
|
||||
run: echo "::set-output name=matrix::$(cat build/test_matrix.json)"
|
||||
|
||||
@@ -50,7 +50,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: gradle test --tests ${{ matrix.test }} --stacktrace
|
||||
- run: gradle test --tests ${{ matrix.test }} --stacktrace --warning-mode fail
|
||||
env:
|
||||
TEST_WARNING_MODE: fail
|
||||
|
||||
@@ -77,7 +77,7 @@ jobs:
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: ${{ matrix.java }}
|
||||
- run: ./gradlew test --tests ${{ matrix.test }} --stacktrace
|
||||
- run: ./gradlew test --tests ${{ matrix.test }} --stacktrace --warning-mode fail
|
||||
env:
|
||||
TEST_WARNING_MODE: fail
|
||||
|
||||
|
||||
26
build.gradle
26
build.gradle
@@ -207,8 +207,8 @@ jacoco {
|
||||
jacocoTestReport {
|
||||
dependsOn test
|
||||
reports {
|
||||
xml.enabled false
|
||||
csv.enabled false
|
||||
xml.required = false
|
||||
csv.required = false
|
||||
html.destination file("${buildDir}/jacocoHtml")
|
||||
}
|
||||
}
|
||||
@@ -220,6 +220,7 @@ test {
|
||||
}
|
||||
|
||||
import me.shedaniel.javaversionbridge.BridgeTransformingTask
|
||||
import org.gradle.util.GradleVersion
|
||||
import org.w3c.dom.Document
|
||||
import org.w3c.dom.Element
|
||||
import org.w3c.dom.Node
|
||||
@@ -338,6 +339,27 @@ tasks.named('wrapper') {
|
||||
distributionType = Wrapper.DistributionType.ALL
|
||||
}
|
||||
|
||||
/**
|
||||
* Run this task to download the gradle sources next to the api jar, you may need to manually attach the sources jar
|
||||
*/
|
||||
task downloadGradleSources() {
|
||||
doLast {
|
||||
// Awful hack to find the gradle api location
|
||||
def gradleApiFile = project.configurations.detachedConfiguration(dependencies.gradleApi()).files.stream()
|
||||
.filter {
|
||||
it.name.startsWith("gradle-api")
|
||||
}.findFirst().orElseThrow()
|
||||
|
||||
def gradleApiSources = new File(gradleApiFile.absolutePath.replace(".jar", "-sources.jar"))
|
||||
def url = "https://services.gradle.org/distributions/gradle-${GradleVersion.current().getVersion()}-src.zip"
|
||||
|
||||
gradleApiSources.delete()
|
||||
|
||||
println("Downloading (${url}) to (${gradleApiSources})")
|
||||
gradleApiSources << new URL(url).newInputStream()
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType(GenerateModuleMetadata) {
|
||||
enabled = false
|
||||
}
|
||||
|
||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.1-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.2-all.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
@@ -26,15 +26,17 @@ 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;
|
||||
import org.cadixdev.mercury.Mercury;
|
||||
import org.gradle.api.Action;
|
||||
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;
|
||||
@@ -61,7 +63,12 @@ public interface LoomGradleExtension extends LoomGradleExtensionAPI {
|
||||
|
||||
LoomFiles getFiles();
|
||||
|
||||
NamedDomainObjectProvider<Configuration> createLazyConfiguration(String name);
|
||||
default NamedDomainObjectProvider<Configuration> createLazyConfiguration(String name) {
|
||||
return createLazyConfiguration(name, config -> {
|
||||
});
|
||||
}
|
||||
|
||||
NamedDomainObjectProvider<Configuration> createLazyConfiguration(String name, Action<? super Configuration> configurationAction);
|
||||
|
||||
NamedDomainObjectProvider<Configuration> getLazyConfigurationProvider(String name);
|
||||
|
||||
@@ -95,16 +102,12 @@ public interface LoomGradleExtension extends LoomGradleExtensionAPI {
|
||||
return getMappingsProvider().mappedProvider;
|
||||
}
|
||||
|
||||
File getNextMixinMappings();
|
||||
File getMixinMappings(SourceSet sourceSet);
|
||||
|
||||
Set<File> getAllMixinMappings();
|
||||
FileCollection getAllMixinMappings();
|
||||
|
||||
boolean isRootProject();
|
||||
|
||||
default boolean ideSync() {
|
||||
return Boolean.parseBoolean(System.getProperty("idea.sync.active", "false"));
|
||||
}
|
||||
|
||||
default String getIntermediaryUrl(String minecraftVersion) {
|
||||
return String.format(this.getIntermediaryUrl().get(), minecraftVersion);
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ import net.fabricmc.loom.configuration.CompileConfiguration;
|
||||
import net.fabricmc.loom.configuration.FabricApiExtension;
|
||||
import net.fabricmc.loom.configuration.MavenPublication;
|
||||
import net.fabricmc.loom.configuration.ide.IdeConfiguration;
|
||||
import net.fabricmc.loom.configuration.ide.idea.IdeaConfiguration;
|
||||
import net.fabricmc.loom.decompilers.DecompilerConfiguration;
|
||||
import net.fabricmc.loom.extension.LoomFiles;
|
||||
import net.fabricmc.loom.extension.LoomGradleExtensionImpl;
|
||||
@@ -93,5 +94,6 @@ public class LoomGradlePlugin implements BootstrappedPlugin {
|
||||
MavenPublication.configure(project);
|
||||
LoomTasks.registerTasks(project);
|
||||
DecompilerConfiguration.setup(project);
|
||||
IdeaConfiguration.setup(project);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.gradle.api.Action;
|
||||
import org.gradle.api.DomainObjectCollection;
|
||||
import org.gradle.api.NamedDomainObjectContainer;
|
||||
import org.gradle.api.artifacts.Dependency;
|
||||
import org.gradle.api.file.ConfigurableFileCollection;
|
||||
@@ -63,7 +64,7 @@ public interface LoomGradleExtensionAPI {
|
||||
getShareRemapCaches().set(true);
|
||||
}
|
||||
|
||||
ListProperty<LoomDecompiler> getGameDecompilers();
|
||||
DomainObjectCollection<LoomDecompiler> getGameDecompilers();
|
||||
|
||||
default void addDecompiler(LoomDecompiler decompiler) {
|
||||
getGameDecompilers().add(decompiler);
|
||||
@@ -140,6 +141,15 @@ public interface LoomGradleExtensionAPI {
|
||||
*/
|
||||
Property<String> getIntermediaryUrl();
|
||||
|
||||
/**
|
||||
* When true loom will inject interfaces declared in mod manifests into the minecraft jar file.
|
||||
* This is used to expose interfaces that are implemented on Minecraft classes by mixins at runtime
|
||||
* in the dev environment.
|
||||
*
|
||||
* @return the property controlling interface injection.
|
||||
*/
|
||||
Property<Boolean> getEnableInterfaceInjection();
|
||||
|
||||
// ===================
|
||||
// Architectury Loom
|
||||
// ===================
|
||||
|
||||
@@ -26,6 +26,10 @@ package net.fabricmc.loom.api.decompilers;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.file.FileCollection;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public interface LoomDecompiler {
|
||||
String name();
|
||||
|
||||
@@ -37,4 +41,12 @@ public interface LoomDecompiler {
|
||||
* @param metaData Additional information that may or may not be needed while decompiling
|
||||
*/
|
||||
void decompile(Path compiledJar, Path sourcesDestination, Path linemapDestination, DecompilationMetadata metaData);
|
||||
|
||||
/**
|
||||
* Add additional classpath entries to the decompiler classpath, can return a configuration.
|
||||
*/
|
||||
@Nullable
|
||||
default FileCollection getBootstrapClasspath(Project project) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,9 +27,10 @@ package net.fabricmc.loom.api.mappings.layered.spec;
|
||||
import groovy.lang.Closure;
|
||||
import groovy.lang.DelegatesTo;
|
||||
import org.gradle.api.Action;
|
||||
import org.gradle.util.ConfigureUtil;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import net.fabricmc.loom.util.ClosureAction;
|
||||
|
||||
/**
|
||||
* Used to configure a layered mapping spec.
|
||||
*/
|
||||
@@ -52,7 +53,7 @@ public interface LayeredMappingSpecBuilder {
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
default LayeredMappingSpecBuilder officialMojangMappings(@DelegatesTo(value = MojangMappingsSpecBuilder.class, strategy = Closure.DELEGATE_FIRST) Closure closure) {
|
||||
return officialMojangMappings(mojangMappingsSpecBuilder -> ConfigureUtil.configure(closure, mojangMappingsSpecBuilder));
|
||||
return officialMojangMappings(new ClosureAction<>(closure));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,7 +67,7 @@ public interface LayeredMappingSpecBuilder {
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
default LayeredMappingSpecBuilder parchment(Object object, @DelegatesTo(value = ParchmentMappingsSpecBuilder.class, strategy = Closure.DELEGATE_FIRST) Closure closure) {
|
||||
return parchment(object, parchmentMappingsSpecBuilder -> ConfigureUtil.configure(closure, parchmentMappingsSpecBuilder));
|
||||
return parchment(object, new ClosureAction<>(closure));
|
||||
}
|
||||
|
||||
LayeredMappingSpecBuilder parchment(Object object, Action<ParchmentMappingsSpecBuilder> action);
|
||||
|
||||
@@ -1,179 +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 dev.architectury.tinyremapper.IMappingProvider;
|
||||
import dev.architectury.tinyremapper.InputTag;
|
||||
import dev.architectury.tinyremapper.OutputConsumerPath;
|
||||
import dev.architectury.tinyremapper.TinyRemapper;
|
||||
import org.gradle.api.Action;
|
||||
import org.gradle.api.Project;
|
||||
import org.objectweb.asm.commons.Remapper;
|
||||
|
||||
import net.fabricmc.loom.util.CloseableList;
|
||||
import net.fabricmc.loom.util.LoggerFilter;
|
||||
import net.fabricmc.stitch.util.Pair;
|
||||
|
||||
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(Project project) throws IOException {
|
||||
LoggerFilter.replaceSystemOut();
|
||||
TinyRemapper.Builder remapperBuilder = TinyRemapper.newRemapper();
|
||||
remapperBuilder.logger(project.getLogger()::lifecycle);
|
||||
remapperBuilder.logUnknownInvokeDynamic(false);
|
||||
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.toString().equals(path.toString()))
|
||||
)
|
||||
.toArray(Path[]::new);
|
||||
|
||||
remapper.readClassPathAsync(remapClasspath);
|
||||
|
||||
for (RemapData data : remapData) {
|
||||
InputTag tag = remapper.createInputTag();
|
||||
data.tag = tag;
|
||||
project.getLogger().info(":remapper input -> " + data.input.getFileName().toString());
|
||||
|
||||
try {
|
||||
remapper.readInputsAsync(tag, data.input);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to read remapper input " + data.input.getFileName().toString(), e);
|
||||
}
|
||||
}
|
||||
|
||||
//noinspection MismatchedQueryAndUpdateOfCollection
|
||||
try (CloseableList<OutputConsumerPath> outputConsumers = new CloseableList<>()) {
|
||||
for (RemapData data : remapData) {
|
||||
OutputConsumerPath outputConsumer;
|
||||
project.getLogger().info(":remapper output -> " + data.output.getFileName().toString());
|
||||
|
||||
try {
|
||||
Files.deleteIfExists(data.output);
|
||||
outputConsumer = new OutputConsumerPath.Builder(data.output).build();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to create remapper output " + data.output.getFileName().toString(), e);
|
||||
}
|
||||
|
||||
outputConsumers.add(outputConsumer);
|
||||
|
||||
outputConsumer.addNonClassFiles(data.input);
|
||||
|
||||
data.processAccessWidener(remapper.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 = LoomGradleExtension.get(project).isForge() ? LoomGradleExtension.get(project).getForge().getMixinConfigs().get() : 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) {
|
||||
|
||||
@@ -29,7 +29,6 @@ import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
import com.google.common.base.Suppliers;
|
||||
import com.google.common.io.Files;
|
||||
@@ -59,6 +58,7 @@ import net.fabricmc.loom.configuration.processors.dependency.ModDependencyInfo;
|
||||
import net.fabricmc.loom.configuration.processors.dependency.RemapData;
|
||||
import net.fabricmc.loom.util.Checksum;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
import net.fabricmc.loom.util.ModUtils;
|
||||
import net.fabricmc.loom.util.OperatingSystem;
|
||||
import net.fabricmc.loom.util.SourceRemapper;
|
||||
|
||||
@@ -94,7 +94,7 @@ public class ModCompileRemapper {
|
||||
String name = extension.isForgeAndOfficial() ? "B" + checksum.get() : artifact.getModuleVersion().getId().getName();
|
||||
String version = extension.isForgeAndOfficial() ? "B" + checksum.get() : replaceIfNullOrEmpty(artifact.getModuleVersion().getId().getVersion(), () -> Checksum.truncatedSha256(artifact.getFile()));
|
||||
|
||||
if (!shouldRemapMod(logger, artifact.getFile(), artifact.getId(), extension.isForge(), sourceConfig.getName())) {
|
||||
if (!ModUtils.shouldRemapMod(artifact.getFile(), artifact.getId(), extension.isForge(), sourceConfig.getName())) {
|
||||
addToRegularCompile(project, regularConfig, artifact);
|
||||
continue;
|
||||
}
|
||||
@@ -121,7 +121,7 @@ public class ModCompileRemapper {
|
||||
|
||||
// Create a mod dependency for each file in the file collection
|
||||
for (File artifact : files) {
|
||||
if (!shouldRemapMod(logger, artifact, artifact.getName(), extension.isForge(), sourceConfig.getName())) {
|
||||
if (!ModUtils.shouldRemapMod(artifact, artifact.getName(), extension.isForge(), sourceConfig.getName())) {
|
||||
dependencies.add(regularConfig.getName(), project.files(artifact));
|
||||
continue;
|
||||
}
|
||||
@@ -165,37 +165,6 @@ public class ModCompileRemapper {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an artifact is a fabric mod, according to the presence of a fabric.mod.json.
|
||||
*/
|
||||
private static boolean shouldRemapMod(Logger logger, File artifact, Object id, boolean forge, String config) {
|
||||
try (ZipFile zipFile = new ZipFile(artifact)) {
|
||||
if (zipFile.getEntry("architectury.common.marker") != null) {
|
||||
logger.info("Found architectury common mod in " + config + ": {}", id);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (forge) {
|
||||
if (zipFile.getEntry("META-INF/mods.toml") != null) {
|
||||
logger.info("Found Forge mod in " + config + ": {}", id);
|
||||
return true;
|
||||
}
|
||||
|
||||
logger.lifecycle(":could not find forge mod in " + config + " but forcing: {}", id);
|
||||
return true;
|
||||
} else {
|
||||
if (zipFile.getEntry("fabric.mod.json") != null) {
|
||||
logger.info("Found Fabric mod in " + config + ": {}", id);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static void addToRegularCompile(Project project, Configuration regularCompile, ResolvedArtifact artifact) {
|
||||
project.getLogger().info(":providing " + artifact);
|
||||
DependencyHandler dependencies = project.getDependencies();
|
||||
|
||||
@@ -40,8 +40,9 @@ import org.gradle.api.artifacts.Configuration;
|
||||
import org.gradle.api.artifacts.ConfigurationContainer;
|
||||
import org.gradle.api.tasks.SourceSet;
|
||||
|
||||
import net.fabricmc.loom.extension.MixinExtension;
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.configuration.ide.idea.IdeaUtils;
|
||||
import net.fabricmc.loom.extension.MixinExtension;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
|
||||
/**
|
||||
@@ -85,7 +86,7 @@ public abstract class AnnotationProcessorInvoker<T extends Task> {
|
||||
Path mappings = loom.isForge() ? loom.getMappingsProvider().mixinTinyMappingsWithSrg : loom.getMappingsProvider().tinyMappings;
|
||||
Map<String, String> args = new HashMap<>() {{
|
||||
put(Constants.MixinArguments.IN_MAP_FILE_NAMED_INTERMEDIARY, mappings.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");
|
||||
}};
|
||||
@@ -99,9 +100,8 @@ public abstract class AnnotationProcessorInvoker<T extends Task> {
|
||||
|
||||
public void configureMixin() {
|
||||
ConfigurationContainer configs = project.getConfigurations();
|
||||
LoomGradleExtension extension = LoomGradleExtension.get(project);
|
||||
|
||||
if (!extension.ideSync()) {
|
||||
if (!IdeaUtils.isIdeaSync()) {
|
||||
for (Configuration processorConfig : apConfigurations) {
|
||||
project.getLogger().info("Adding mixin to classpath of AP config: " + processorConfig.getName());
|
||||
// Pass named MC classpath to mixin AP classpath
|
||||
|
||||
@@ -58,7 +58,7 @@ public class JavaApInvoker extends AnnotationProcessorInvoker<JavaCompile> {
|
||||
|
||||
@Override
|
||||
protected File getRefmapDestinationDir(JavaCompile task) {
|
||||
return task.getDestinationDir();
|
||||
return task.getDestinationDirectory().getAsFile().get();
|
||||
}
|
||||
|
||||
private static String getAptConfigurationName(String sourceSet) {
|
||||
|
||||
@@ -84,7 +84,7 @@ public class KaptApInvoker extends AnnotationProcessorInvoker<JavaCompile> {
|
||||
try {
|
||||
String refmapName = Objects.requireNonNull(MixinExtension.getMixinInformationContainer(sourceSet)).refmapNameProvider().get();
|
||||
Path src = Paths.get(getRefmapDestination(task, refmapName));
|
||||
Path dest = Paths.get(task.getDestinationDir().toString(), refmapName);
|
||||
Path dest = Paths.get(task.getDestinationDirectory().get().getAsFile().toString(), refmapName);
|
||||
|
||||
// Possible that no mixin annotations exist
|
||||
if (Files.exists(src)) {
|
||||
|
||||
@@ -59,6 +59,6 @@ public class ScalaApInvoker extends AnnotationProcessorInvoker<ScalaCompile> {
|
||||
|
||||
@Override
|
||||
protected File getRefmapDestinationDir(ScalaCompile task) {
|
||||
return task.getDestinationDir();
|
||||
return task.getDestinationDirectory().get().getAsFile();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
* 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.common.hash.Hashing;
|
||||
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) {
|
||||
String modId = (metadata.group() + "_" + metadata.name() + metadata.classifier())
|
||||
.replaceAll("\\.", "_")
|
||||
.toLowerCase(Locale.ENGLISH);
|
||||
|
||||
// Fabric Loader can't handle modIds longer than 64 characters
|
||||
if (modId.length() > 64) {
|
||||
String hash = Hashing.sha256()
|
||||
.hashString(modId, StandardCharsets.UTF_8)
|
||||
.toString();
|
||||
modId = modId.substring(0, 50) + hash.substring(0, 14);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -1,281 +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.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 (!ZipUtils.contains(file.toPath(), "fabric.mod.json")) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,22 +24,24 @@
|
||||
|
||||
package net.fabricmc.loom.configuration;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import org.gradle.api.NamedDomainObjectProvider;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.artifacts.Configuration;
|
||||
import org.gradle.api.plugins.JavaPlugin;
|
||||
import org.gradle.api.plugins.JavaPluginConvention;
|
||||
import org.gradle.api.plugins.JavaPluginExtension;
|
||||
import org.gradle.api.tasks.AbstractCopyTask;
|
||||
import org.gradle.api.tasks.SourceSet;
|
||||
import org.gradle.api.tasks.TaskContainer;
|
||||
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;
|
||||
import net.fabricmc.loom.build.mixin.KaptApInvoker;
|
||||
import net.fabricmc.loom.build.mixin.ScalaApInvoker;
|
||||
import net.fabricmc.loom.configuration.ide.SetupIntelijRunConfigs;
|
||||
import net.fabricmc.loom.configuration.providers.LaunchProvider;
|
||||
import net.fabricmc.loom.configuration.providers.MinecraftProviderImpl;
|
||||
import net.fabricmc.loom.configuration.providers.forge.FieldMigratedMappingsProvider;
|
||||
@@ -51,7 +53,9 @@ import net.fabricmc.loom.configuration.providers.forge.PatchProvider;
|
||||
import net.fabricmc.loom.configuration.providers.forge.SrgProvider;
|
||||
import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl;
|
||||
import net.fabricmc.loom.extension.MixinExtension;
|
||||
import net.fabricmc.loom.task.GenerateSourcesTask;
|
||||
import net.fabricmc.loom.task.GenVsCodeProjectTask;
|
||||
import net.fabricmc.loom.task.UnpickJarTask;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
|
||||
public final class CompileConfiguration {
|
||||
@@ -73,12 +77,17 @@ public final class CompileConfiguration {
|
||||
}
|
||||
});
|
||||
|
||||
extension.createLazyConfiguration(Constants.Configurations.MOD_COMPILE_CLASSPATH).configure(configuration -> configuration.setTransitive(true));
|
||||
extension.createLazyConfiguration(Constants.Configurations.MOD_COMPILE_CLASSPATH_MAPPED).configure(configuration -> configuration.setTransitive(false));
|
||||
extension.createLazyConfiguration(Constants.Configurations.MINECRAFT_NAMED).configure(configuration -> configuration.setTransitive(false)); // The launchers do not recurse dependencies
|
||||
extension.createLazyConfiguration(Constants.Configurations.MINECRAFT_DEPENDENCIES).configure(configuration -> configuration.setTransitive(false));
|
||||
extension.createLazyConfiguration(Constants.Configurations.LOADER_DEPENDENCIES).configure(configuration -> configuration.setTransitive(false));
|
||||
extension.createLazyConfiguration(Constants.Configurations.MINECRAFT).configure(configuration -> configuration.setTransitive(false));
|
||||
extension.createLazyConfiguration(Constants.Configurations.MOD_COMPILE_CLASSPATH, configuration -> configuration.setTransitive(true));
|
||||
extension.createLazyConfiguration(Constants.Configurations.MOD_COMPILE_CLASSPATH_MAPPED, configuration -> configuration.setTransitive(false));
|
||||
extension.createLazyConfiguration(Constants.Configurations.MINECRAFT_NAMED, configuration -> configuration.setTransitive(false)); // The launchers do not recurse dependencies
|
||||
NamedDomainObjectProvider<Configuration> serverDeps = extension.createLazyConfiguration(Constants.Configurations.MINECRAFT_SERVER_DEPENDENCIES, configuration -> configuration.setTransitive(false));
|
||||
extension.createLazyConfiguration(Constants.Configurations.MINECRAFT_DEPENDENCIES, configuration -> {
|
||||
configuration.extendsFrom(serverDeps.get());
|
||||
configuration.setTransitive(false);
|
||||
});
|
||||
extension.createLazyConfiguration(Constants.Configurations.MINECRAFT_NATIVES, configuration -> configuration.setTransitive(false));
|
||||
extension.createLazyConfiguration(Constants.Configurations.LOADER_DEPENDENCIES, configuration -> configuration.setTransitive(false));
|
||||
extension.createLazyConfiguration(Constants.Configurations.MINECRAFT, configuration -> configuration.setTransitive(false));
|
||||
|
||||
if (extension.isForge()) {
|
||||
extension.createLazyConfiguration(Constants.Configurations.FORGE).configure(configuration -> configuration.setTransitive(false));
|
||||
@@ -113,11 +122,10 @@ public final class CompileConfiguration {
|
||||
}
|
||||
|
||||
if (extension.supportsInclude()) {
|
||||
extension.createLazyConfiguration(Constants.Configurations.INCLUDE).configure(configuration -> configuration.setTransitive(false)); // Dont get transitive deps
|
||||
extension.createLazyConfiguration(Constants.Configurations.INCLUDE, configuration -> configuration.setTransitive(false)); // Dont get transitive deps
|
||||
}
|
||||
|
||||
extension.createLazyConfiguration(Constants.Configurations.MAPPING_CONSTANTS);
|
||||
extension.createLazyConfiguration(Constants.Configurations.NAMED_ELEMENTS).configure(configuration -> {
|
||||
extension.createLazyConfiguration(Constants.Configurations.NAMED_ELEMENTS, configuration -> {
|
||||
configuration.setCanBeConsumed(true);
|
||||
configuration.setCanBeResolved(false);
|
||||
configuration.extendsFrom(project.getConfigurations().getByName(JavaPlugin.API_CONFIGURATION_NAME));
|
||||
@@ -173,16 +181,15 @@ public final class CompileConfiguration {
|
||||
}
|
||||
|
||||
public static void configureCompile(Project p) {
|
||||
JavaPluginConvention javaModule = (JavaPluginConvention) p.getConvention().getPlugins().get("java");
|
||||
final JavaPluginExtension javaPluginExtension = p.getExtensions().getByType(JavaPluginExtension.class);
|
||||
LoomGradleExtension extension = LoomGradleExtension.get(p);
|
||||
|
||||
SourceSet main = javaModule.getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME);
|
||||
|
||||
Javadoc javadoc = (Javadoc) p.getTasks().getByName(JavaPlugin.JAVADOC_TASK_NAME);
|
||||
javadoc.setClasspath(main.getOutput().plus(main.getCompileClasspath()));
|
||||
p.getTasks().named(JavaPlugin.JAVADOC_TASK_NAME, Javadoc.class).configure(javadoc -> {
|
||||
final SourceSet main = javaPluginExtension.getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME);
|
||||
javadoc.setClasspath(main.getOutput().plus(main.getCompileClasspath()));
|
||||
});
|
||||
|
||||
p.afterEvaluate(project -> {
|
||||
LoomGradleExtension extension = LoomGradleExtension.get(project);
|
||||
|
||||
LoomDependencyManager dependencyManager = new LoomDependencyManager();
|
||||
extension.setDependencyManager(dependencyManager);
|
||||
|
||||
@@ -208,31 +215,23 @@ public final class CompileConfiguration {
|
||||
|
||||
dependencyManager.handleDependencies(project);
|
||||
|
||||
project.getTasks().getByName("idea").finalizedBy(project.getTasks().getByName("genIdeaWorkspace"));
|
||||
project.getTasks().getByName("eclipse").finalizedBy(project.getTasks().getByName("genEclipseRuns"));
|
||||
project.getTasks().getByName("cleanEclipse").finalizedBy(project.getTasks().getByName("cleanEclipseRuns"));
|
||||
|
||||
SetupIntelijRunConfigs.setup(project);
|
||||
GenVsCodeProjectTask.generate(project);
|
||||
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()) {
|
||||
setupMixinAp(project, mixin);
|
||||
}
|
||||
|
||||
configureDecompileTasks(project);
|
||||
});
|
||||
|
||||
finalizedBy(p, "idea", "genIdeaWorkspace");
|
||||
finalizedBy(p, "eclipse", "genEclipseRuns");
|
||||
finalizedBy(p, "cleanEclipse", "cleanEclipseRuns");
|
||||
|
||||
// Add the "dev" jar to the "namedElements" configuration
|
||||
p.artifacts(artifactHandler -> artifactHandler.add(Constants.Configurations.NAMED_ELEMENTS, p.getTasks().getByName("jar")));
|
||||
p.artifacts(artifactHandler -> artifactHandler.add(Constants.Configurations.NAMED_ELEMENTS, p.getTasks().named("jar")));
|
||||
|
||||
// Ensure that the encoding is set to UTF-8, no matter what the system default is
|
||||
// this fixes some edge cases with special characters not displaying correctly
|
||||
@@ -269,7 +268,47 @@ public final class CompileConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
private static void configureDecompileTasks(Project project) {
|
||||
final TaskContainer tasks = project.getTasks();
|
||||
final LoomGradleExtension extension = LoomGradleExtension.get(project);
|
||||
|
||||
MappingsProviderImpl mappingsProvider = extension.getMappingsProvider();
|
||||
|
||||
File mappedJar = mappingsProvider.mappedProvider.getMappedJar();
|
||||
|
||||
if (mappingsProvider.hasUnpickDefinitions()) {
|
||||
File outputJar = mappingsProvider.mappedProvider.getUnpickedJar();
|
||||
|
||||
tasks.register("unpickJar", UnpickJarTask.class, unpickJarTask -> {
|
||||
unpickJarTask.getUnpickDefinitions().set(mappingsProvider.getUnpickDefinitionsFile());
|
||||
unpickJarTask.getInputJar().set(mappingsProvider.mappedProvider.getMappedJar());
|
||||
unpickJarTask.getOutputJar().set(outputJar);
|
||||
});
|
||||
|
||||
mappedJar = outputJar;
|
||||
}
|
||||
|
||||
final File inputJar = mappedJar;
|
||||
|
||||
extension.getGameDecompilers().configureEach(decompiler -> {
|
||||
String taskName = "genSourcesWith" + decompiler.name();
|
||||
|
||||
// Set the input jar for the task after evaluation has occurred.
|
||||
tasks.named(taskName, GenerateSourcesTask.class).configure(task -> {
|
||||
task.getInputJar().set(inputJar);
|
||||
|
||||
if (mappingsProvider.hasUnpickDefinitions()) {
|
||||
task.dependsOn(tasks.named("unpickJar"));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private static void extendsFrom(String a, String b, Project project) {
|
||||
project.getConfigurations().getByName(a, configuration -> configuration.extendsFrom(project.getConfigurations().getByName(b)));
|
||||
}
|
||||
|
||||
private static void finalizedBy(Project project, String a, String b) {
|
||||
project.getTasks().named(a).configure(task -> task.finalizedBy(project.getTasks().named(b)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 dev.architectury.tinyremapper.TinyRemapper;
|
||||
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;
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
@@ -46,6 +46,7 @@ import net.fabricmc.loom.LoomGradlePlugin;
|
||||
import net.fabricmc.loom.LoomRepositoryPlugin;
|
||||
import net.fabricmc.loom.build.ModCompileRemapper;
|
||||
import net.fabricmc.loom.configuration.DependencyProvider.DependencyInfo;
|
||||
import net.fabricmc.loom.configuration.ide.idea.IdeaUtils;
|
||||
import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
import net.fabricmc.loom.util.SourceRemapper;
|
||||
@@ -210,7 +211,7 @@ public class LoomDependencyManager {
|
||||
loaderDepsConfig.getDependencies().add(modDep);
|
||||
|
||||
// TODO: work around until https://github.com/FabricMC/Mixin/pull/60 and https://github.com/FabricMC/fabric-mixin-compile-extensions/issues/14 is fixed.
|
||||
if (!extension.ideSync() && extension.getMixin().getUseLegacyMixinAp().get()) {
|
||||
if (!IdeaUtils.isIdeaSync() && extension.getMixin().getUseLegacyMixinAp().get()) {
|
||||
apDepsConfig.getDependencies().add(modDep);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,249 +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 java.util.Set;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
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.gradle.api.tasks.bundling.Jar;
|
||||
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.PropertyUtil;
|
||||
import net.fabricmc.loom.util.SourceRemapper;
|
||||
import net.fabricmc.loom.util.aw2at.Aw2At;
|
||||
|
||||
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()));
|
||||
// Architectury Loom Patch
|
||||
// Since we make sourcesJar and remapSourcesJar output files separate, we have to remove the dev sources jar file here
|
||||
configuration.getArtifacts().removeIf(a -> a != artifact && task.getInput().get().getAsFile().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 (extension.isForge()) {
|
||||
Set<String> mixinConfigs = PropertyUtil.getAndFinalize(extension.getForge().getMixinConfigs());
|
||||
|
||||
if (!mixinConfigs.isEmpty()) {
|
||||
((Jar) jarTask).manifest(manifest -> {
|
||||
manifest.attributes(ImmutableMap.of("MixinConfigs", String.join(",", mixinConfigs)));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (isDefaultRemap) {
|
||||
extension.getUnmappedModCollection().from(jarTask);
|
||||
remapJarTask.getAddNestedDependencies().convention(true);
|
||||
remapJarTask.getRemapAccessWidener().convention(true);
|
||||
|
||||
project.getArtifacts().add("archives", remapJarTask);
|
||||
|
||||
if (extension.isForge()) {
|
||||
boolean convertAws = PropertyUtil.getAndFinalize(extension.getForge().getConvertAccessWideners());
|
||||
|
||||
if (convertAws) {
|
||||
Aw2At.setup(project, remapJarTask);
|
||||
remapJarTask.getRemapAccessWidener().set(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
remapJarTask.dependsOn(jarTask);
|
||||
project.getTasks().getByName("build").dependsOn(remapJarTask);
|
||||
|
||||
// TODO this might be wrong?
|
||||
project.getTasks().withType(RemapJarTask.class).forEach(task -> {
|
||||
if (extension.supportsInclude() && 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(project);
|
||||
} 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.getOutput().convention(sourcesTask.getArchiveFile().get());
|
||||
String sourcesTaskClassifer = sourcesTask.getArchiveClassifier().get();
|
||||
sourcesTask.getArchiveClassifier().set(sourcesTaskClassifer == null ? "dev" : sourcesTaskClassifer + "-dev");
|
||||
remapSourcesJarTask.getInput().convention(sourcesTask.getArchiveFile());
|
||||
remapSourcesJarTask.dependsOn(sourcesTask);
|
||||
|
||||
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);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
) {
|
||||
@@ -121,7 +121,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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -152,7 +152,7 @@ public class TransitiveAccessWidenerJarProcessor implements JarProcessor {
|
||||
try {
|
||||
AccessWidenerRemapper remappingVisitor = new AccessWidenerRemapper(
|
||||
accessWidener,
|
||||
tinyRemapper.getRemapper(),
|
||||
tinyRemapper.getEnvironment().getRemapper(),
|
||||
MappingsNamespace.INTERMEDIARY.toString(),
|
||||
MappingsNamespace.NAMED.toString()
|
||||
);
|
||||
|
||||
@@ -24,15 +24,19 @@
|
||||
|
||||
package net.fabricmc.loom.configuration.ide;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
@@ -48,6 +52,9 @@ import org.w3c.dom.Node;
|
||||
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.configuration.InstallerData;
|
||||
import net.fabricmc.loom.configuration.ide.idea.IdeaSyncTask;
|
||||
import net.fabricmc.loom.configuration.providers.BundleMetadata;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
|
||||
public class RunConfig {
|
||||
public String configName;
|
||||
@@ -57,6 +64,7 @@ public class RunConfig {
|
||||
public String mainClass;
|
||||
public String runDirIdeaUrl;
|
||||
public String runDir;
|
||||
public String environment;
|
||||
public List<String> vmArgs = new ArrayList<>();
|
||||
public List<String> programArgs = new ArrayList<>();
|
||||
public List<String> vscodeBeforeRun = new ArrayList<>();
|
||||
@@ -179,6 +187,7 @@ public class RunConfig {
|
||||
runConfig.runDirIdeaUrl = "file://$PROJECT_DIR$/" + runDir;
|
||||
runConfig.runDir = runDir;
|
||||
runConfig.sourceSet = sourceSet;
|
||||
runConfig.environment = environment;
|
||||
|
||||
// Custom parameters
|
||||
runConfig.programArgs.addAll(settings.getProgramArgs());
|
||||
@@ -195,7 +204,7 @@ public class RunConfig {
|
||||
public String fromDummy(String dummy, boolean relativeDir, Project project) throws IOException {
|
||||
String dummyConfig;
|
||||
|
||||
try (InputStream input = SetupIntelijRunConfigs.class.getClassLoader().getResourceAsStream(dummy)) {
|
||||
try (InputStream input = IdeaSyncTask.class.getClassLoader().getResourceAsStream(dummy)) {
|
||||
dummyConfig = new String(input.readAllBytes(), StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@@ -249,7 +258,12 @@ public class RunConfig {
|
||||
}
|
||||
|
||||
first = false;
|
||||
sb.append("\"").append(arg).append("\"");
|
||||
|
||||
if (arg.contains(" ")) {
|
||||
sb.append("\"").append(arg).append("\"");
|
||||
} else {
|
||||
sb.append(arg);
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
@@ -285,6 +299,31 @@ public class RunConfig {
|
||||
return defaultMainClass;
|
||||
}
|
||||
|
||||
public List<String> getExcludedLibraryPaths(Project project) {
|
||||
if (!environment.equals("server")) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
final BundleMetadata bundleMetadata = LoomGradleExtension.get(project).getMinecraftProvider().getServerBundleMetadata();
|
||||
|
||||
if (bundleMetadata == null) {
|
||||
// Legacy version
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
final Set<File> allLibraries = project.getConfigurations().getByName(Constants.Configurations.MINECRAFT_DEPENDENCIES).getFiles();
|
||||
final Set<File> serverLibraries = project.getConfigurations().getByName(Constants.Configurations.MINECRAFT_SERVER_DEPENDENCIES).getFiles();
|
||||
final List<String> clientOnlyLibraries = new LinkedList<>();
|
||||
|
||||
for (File commonLibrary : allLibraries) {
|
||||
if (!serverLibraries.contains(commonLibrary)) {
|
||||
clientOnlyLibraries.add(commonLibrary.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
return clientOnlyLibraries;
|
||||
}
|
||||
|
||||
private static String encodeEscaped(String s) {
|
||||
StringBuilder ret = new StringBuilder();
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ import java.util.function.Function;
|
||||
|
||||
import org.gradle.api.Named;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.plugins.JavaPluginConvention;
|
||||
import org.gradle.api.plugins.JavaPluginExtension;
|
||||
import org.gradle.api.tasks.SourceSet;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
@@ -247,10 +247,7 @@ public final class RunConfigSettings implements Named {
|
||||
}
|
||||
|
||||
public void source(String source) {
|
||||
setSource(proj -> {
|
||||
JavaPluginConvention conv = proj.getConvention().getPlugin(JavaPluginConvention.class);
|
||||
return conv.getSourceSets().getByName(source);
|
||||
});
|
||||
setSource(proj -> project.getExtensions().getByType(JavaPluginExtension.class).getSourceSets().getByName(source));
|
||||
}
|
||||
|
||||
public void ideConfigGenerated(boolean ideConfigGenerated) {
|
||||
@@ -261,7 +258,7 @@ public final class RunConfigSettings implements Named {
|
||||
* Add the {@code -XstartOnFirstThread} JVM argument when on OSX.
|
||||
*/
|
||||
public void startFirstThread() {
|
||||
if (OperatingSystem.getOS().equalsIgnoreCase("osx")) {
|
||||
if (OperatingSystem.CURRENT_OS.equals(OperatingSystem.MAC_OS)) {
|
||||
vmArg("-XstartOnFirstThread");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
/*
|
||||
* This file is part of fabric-loom, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) 2016-2021 FabricMC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package net.fabricmc.loom.configuration.ide;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.gradle.api.Project;
|
||||
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftNativesProvider;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.assets.MinecraftAssetsProvider;
|
||||
|
||||
public class SetupIntelijRunConfigs {
|
||||
public static void setup(Project project) {
|
||||
File projectDir = project.getRootProject().file(".idea");
|
||||
|
||||
if (!projectDir.exists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
generate(project, false);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to generate run configs", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void generate(Project project, boolean override) throws IOException {
|
||||
Project rootProject = project.getRootProject();
|
||||
LoomGradleExtension extension = LoomGradleExtension.get(project);
|
||||
|
||||
if (extension.ideSync()) {
|
||||
//Ensures the assets are downloaded when idea is syncing a project
|
||||
MinecraftAssetsProvider.provide(extension.getMinecraftProvider(), project);
|
||||
MinecraftNativesProvider.provide(project);
|
||||
}
|
||||
|
||||
String projectPath = project == rootProject ? "" : project.getPath().replace(':', '_');
|
||||
|
||||
File projectDir = rootProject.file(".idea");
|
||||
File runConfigsDir = new File(projectDir, "runConfigurations");
|
||||
|
||||
if (!runConfigsDir.exists()) {
|
||||
runConfigsDir.mkdirs();
|
||||
}
|
||||
|
||||
for (RunConfigSettings settings : extension.getRunConfigs()) {
|
||||
if (!settings.isIdeConfigGenerated()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
RunConfig config = RunConfig.runConfig(project, settings);
|
||||
String name = config.configName.replaceAll("[^a-zA-Z0-9$_]", "_");
|
||||
|
||||
File runConfigs = new File(runConfigsDir, name + projectPath + ".xml");
|
||||
String runConfigXml = config.fromDummy("idea_run_config_template.xml", true, project);
|
||||
|
||||
if (runConfigs.exists() && override) {
|
||||
runConfigs.delete();
|
||||
}
|
||||
|
||||
if (!runConfigs.exists()) {
|
||||
FileUtils.writeStringToFile(runConfigs, runConfigXml, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
settings.makeRunDir();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.ide.idea;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.tasks.TaskProvider;
|
||||
import org.gradle.execution.taskgraph.TaskExecutionGraphInternal;
|
||||
|
||||
public class IdeaConfiguration {
|
||||
public static void setup(Project project) {
|
||||
TaskProvider<IdeaSyncTask> ideaSyncTask = project.getTasks().register("ideaSyncTask", IdeaSyncTask.class, ideaSyncTask1 -> {
|
||||
ideaSyncTask1.dependsOn(project.getTasks().named("downloadAssets"));
|
||||
});
|
||||
|
||||
if (!IdeaUtils.isIdeaSync()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Run the idea sync task, is this exposed via the api?
|
||||
final TaskExecutionGraphInternal taskGraph = (TaskExecutionGraphInternal) project.getGradle().getTaskGraph();
|
||||
taskGraph.whenReady(taskExecutionGraph -> taskGraph.addEntryTasks(List.of(ideaSyncTask.get())));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* 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.ide.idea;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.io.StringWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.transform.OutputKeys;
|
||||
import javax.xml.transform.Transformer;
|
||||
import javax.xml.transform.TransformerFactory;
|
||||
import javax.xml.transform.dom.DOMSource;
|
||||
import javax.xml.transform.stream.StreamResult;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
import org.jetbrains.annotations.VisibleForTesting;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.configuration.ide.RunConfig;
|
||||
import net.fabricmc.loom.configuration.ide.RunConfigSettings;
|
||||
import net.fabricmc.loom.task.AbstractLoomTask;
|
||||
|
||||
public abstract class IdeaSyncTask extends AbstractLoomTask {
|
||||
@Inject
|
||||
public IdeaSyncTask() {
|
||||
// Always re-run this task.
|
||||
getOutputs().upToDateWhen(element -> false);
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
public void runTask() throws IOException {
|
||||
File projectDir = getProject().getRootProject().file(".idea");
|
||||
|
||||
if (!projectDir.exists()) {
|
||||
throw new RuntimeException("No .idea directory found");
|
||||
}
|
||||
|
||||
generateRunConfigs();
|
||||
}
|
||||
|
||||
// See: https://github.com/FabricMC/fabric-loom/pull/206#issuecomment-986054254 for the reason why XML's are still used to provide the run configs
|
||||
private void generateRunConfigs() throws IOException {
|
||||
Project rootProject = getProject().getRootProject();
|
||||
LoomGradleExtension extension = LoomGradleExtension.get(getProject());
|
||||
String projectPath = getProject() == rootProject ? "" : getProject().getPath().replace(':', '_');
|
||||
File runConfigsDir = new File(rootProject.file(".idea"), "runConfigurations");
|
||||
|
||||
if (!runConfigsDir.exists()) {
|
||||
runConfigsDir.mkdirs();
|
||||
}
|
||||
|
||||
for (RunConfigSettings settings : extension.getRunConfigs()) {
|
||||
if (!settings.isIdeConfigGenerated()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
RunConfig config = RunConfig.runConfig(getProject(), settings);
|
||||
String name = config.configName.replaceAll("[^a-zA-Z0-9$_]", "_");
|
||||
|
||||
File runConfigs = new File(runConfigsDir, name + projectPath + ".xml");
|
||||
String runConfigXml = config.fromDummy("idea_run_config_template.xml", true, getProject());
|
||||
|
||||
if (!runConfigs.exists()) {
|
||||
FileUtils.writeStringToFile(runConfigs, runConfigXml, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
settings.makeRunDir();
|
||||
|
||||
final List<String> excludedLibraryPaths = config.getExcludedLibraryPaths(getProject());
|
||||
|
||||
if (!excludedLibraryPaths.isEmpty()) {
|
||||
try {
|
||||
setClasspathModifications(runConfigs.toPath(), excludedLibraryPaths);
|
||||
} catch (Exception e) {
|
||||
getProject().getLogger().error("Failed to modify run configuration xml", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setClasspathModifications(Path runConfig, List<String> exclusions) throws IOException {
|
||||
if (!IdeaUtils.supportsCustomizableClasspath()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final String inputXml = Files.readString(runConfig, StandardCharsets.UTF_8);
|
||||
final String outputXml;
|
||||
|
||||
try {
|
||||
outputXml = setClasspathModificationsInXml(inputXml, exclusions);
|
||||
} catch (Exception e) {
|
||||
getLogger().error("Failed to modify idea xml", e);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!inputXml.equals(outputXml)) {
|
||||
Files.writeString(runConfig, outputXml, StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static String setClasspathModificationsInXml(String input, List<String> exclusions) throws Exception {
|
||||
final DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
|
||||
final DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
|
||||
final Document document = documentBuilder.parse(new InputSource(new StringReader(input)));
|
||||
final Element root = document.getDocumentElement();
|
||||
|
||||
final NodeList nodeList = root.getElementsByTagName("configuration");
|
||||
assert nodeList.getLength() == 1;
|
||||
|
||||
final Element configuration = (Element) nodeList.item(0);
|
||||
final NodeList classpathModificationsList = configuration.getElementsByTagName("classpathModifications");
|
||||
|
||||
// Remove all the existing exclusions
|
||||
for (int i = 0; i < classpathModificationsList.getLength(); i++) {
|
||||
configuration.removeChild(classpathModificationsList.item(i));
|
||||
}
|
||||
|
||||
final Element classpathModifications = document.createElement("classpathModifications");
|
||||
|
||||
for (String exclusionPath : exclusions) {
|
||||
final Element exclusion = document.createElement("entry");
|
||||
|
||||
exclusion.setAttribute("exclude", "true");
|
||||
exclusion.setAttribute("path", exclusionPath);
|
||||
|
||||
classpathModifications.appendChild(exclusion);
|
||||
}
|
||||
|
||||
configuration.appendChild(classpathModifications);
|
||||
|
||||
final TransformerFactory transformerFactory = TransformerFactory.newInstance();
|
||||
final Transformer transformer = transformerFactory.newTransformer();
|
||||
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
|
||||
|
||||
final DOMSource source = new DOMSource(document);
|
||||
|
||||
final StringWriter writer = new StringWriter();
|
||||
transformer.transform(source, new StreamResult(writer));
|
||||
|
||||
return writer.toString().replace("\r", "");
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -22,26 +22,24 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package net.fabricmc.loom.build.nesting;
|
||||
package net.fabricmc.loom.configuration.ide.idea;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.Objects;
|
||||
|
||||
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());
|
||||
public class IdeaUtils {
|
||||
public static boolean isIdeaSync() {
|
||||
return Boolean.parseBoolean(System.getProperty("idea.sync.active", "false"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepare(Project project) {
|
||||
Arrays.stream(children).forEach(nestedJarProvider -> nestedJarProvider.prepare(project));
|
||||
public static String getIdeaVersion() {
|
||||
return Objects.requireNonNull(System.getProperty("idea.version"), "Could not get idea version");
|
||||
}
|
||||
|
||||
// 2021.3 or newer
|
||||
public static boolean supportsCustomizableClasspath() {
|
||||
final String[] split = getIdeaVersion().split("\\.");
|
||||
final int major = Integer.parseInt(split[0]);
|
||||
final int minor = Integer.parseInt(split[1]);
|
||||
return major > 2021 || (major == 2021 && minor >= 3);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,299 @@
|
||||
/*
|
||||
* 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.ifaceinject;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.google.common.hash.Hasher;
|
||||
import com.google.common.hash.Hashing;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import org.gradle.api.Project;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.commons.Remapper;
|
||||
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.configuration.RemappedConfigurationEntry;
|
||||
import net.fabricmc.loom.configuration.processors.JarProcessor;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
import net.fabricmc.loom.util.Pair;
|
||||
import net.fabricmc.loom.util.TinyRemapperHelper;
|
||||
import net.fabricmc.loom.util.ZipUtils;
|
||||
import net.fabricmc.tinyremapper.TinyRemapper;
|
||||
|
||||
public class InterfaceInjectionProcessor implements JarProcessor {
|
||||
// Filename used to store hash of injected interfaces in processed jar file
|
||||
private static final String HASH_FILENAME = "injected_interfaces.sha256";
|
||||
|
||||
private final Map<String, List<InjectedInterface>> injectedInterfaces;
|
||||
private final Project project;
|
||||
private final LoomGradleExtension extension;
|
||||
private final byte[] inputHash;
|
||||
private Map<String, List<InjectedInterface>> remappedInjectedInterfaces;
|
||||
|
||||
public InterfaceInjectionProcessor(Project project) {
|
||||
this.project = project;
|
||||
this.extension = LoomGradleExtension.get(project);
|
||||
this.injectedInterfaces = getInjectedInterfaces().stream()
|
||||
.collect(Collectors.groupingBy(InjectedInterface::className));
|
||||
|
||||
this.inputHash = hashInjectedInterfaces();
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return injectedInterfaces.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setup() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(File jarFile) {
|
||||
// Lazily remap from intermediary->named
|
||||
if (remappedInjectedInterfaces == null) {
|
||||
TinyRemapper tinyRemapper = createTinyRemapper();
|
||||
Remapper remapper = tinyRemapper.getEnvironment().getRemapper();
|
||||
|
||||
try {
|
||||
remappedInjectedInterfaces = new HashMap<>(injectedInterfaces.size());
|
||||
|
||||
for (Map.Entry<String, List<InjectedInterface>> entry : injectedInterfaces.entrySet()) {
|
||||
String namedClassName = remapper.map(entry.getKey());
|
||||
remappedInjectedInterfaces.put(
|
||||
namedClassName,
|
||||
entry.getValue().stream()
|
||||
.map(injectedInterface ->
|
||||
new InjectedInterface(
|
||||
injectedInterface.modId(),
|
||||
namedClassName,
|
||||
remapper.map(injectedInterface.ifaceName())
|
||||
))
|
||||
.toList()
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
tinyRemapper.finish();
|
||||
}
|
||||
}
|
||||
|
||||
project.getLogger().lifecycle("Processing file: " + jarFile.getName());
|
||||
|
||||
try {
|
||||
ZipUtils.transform(jarFile.toPath(), getTransformers());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to apply interface injections to " + jarFile, e);
|
||||
}
|
||||
}
|
||||
|
||||
private List<Pair<String, ZipUtils.UnsafeUnaryOperator<byte[]>>> getTransformers() {
|
||||
return remappedInjectedInterfaces.keySet().stream()
|
||||
.map(string -> new Pair<>(string.replaceAll("\\.", "/") + ".class", getTransformer(string)))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private ZipUtils.UnsafeUnaryOperator<byte[]> getTransformer(String className) {
|
||||
return input -> {
|
||||
ClassReader reader = new ClassReader(input);
|
||||
ClassWriter writer = new ClassWriter(0);
|
||||
List<InjectedInterface> ifaces = remappedInjectedInterfaces.get(className);
|
||||
ClassVisitor classVisitor = new InjectingClassVisitor(Constants.ASM_VERSION, writer, ifaces);
|
||||
|
||||
// Log which mods add which interface to the class
|
||||
project.getLogger().info("Injecting interfaces into " + className + ": "
|
||||
+ ifaces.stream().map(i -> i.ifaceName() + " [" + i.modId() + "]"
|
||||
).collect(Collectors.joining(", ")));
|
||||
|
||||
reader.accept(classVisitor, 0);
|
||||
return writer.toByteArray();
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInvalid(File file) {
|
||||
byte[] hash;
|
||||
|
||||
try {
|
||||
hash = ZipUtils.unpackNullable(file.toPath(), HASH_FILENAME);
|
||||
} catch (IOException e) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (hash == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return !Arrays.equals(inputHash, hash);
|
||||
}
|
||||
|
||||
private List<InjectedInterface> getInjectedInterfaces() {
|
||||
List<InjectedInterface> result = new ArrayList<>();
|
||||
|
||||
for (RemappedConfigurationEntry entry : Constants.MOD_COMPILE_ENTRIES) {
|
||||
// Only apply injected interfaces from mods that are part of the compile classpath
|
||||
if (!entry.compileClasspath()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Set<File> artifacts = extension.getLazyConfigurationProvider(entry.sourceConfiguration())
|
||||
.get()
|
||||
.resolve();
|
||||
|
||||
for (File artifact : artifacts) {
|
||||
result.addAll(InjectedInterface.fromModJar(artifact.toPath()));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private record InjectedInterface(String modId, String className, String ifaceName) {
|
||||
/**
|
||||
* Reads the injected interfaces contained in a mod jar, or returns null if there is none.
|
||||
*/
|
||||
public static List<InjectedInterface> fromModJar(Path modJarPath) {
|
||||
byte[] modJsonBytes;
|
||||
|
||||
try {
|
||||
modJsonBytes = ZipUtils.unpackNullable(modJarPath, "fabric.mod.json");
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to extract fabric.mod.json from " + modJarPath);
|
||||
}
|
||||
|
||||
if (modJsonBytes == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
JsonObject jsonObject = new Gson().fromJson(new String(modJsonBytes, StandardCharsets.UTF_8), JsonObject.class);
|
||||
|
||||
String modId = jsonObject.get("id").getAsString();
|
||||
|
||||
if (!jsonObject.has("custom")) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
JsonObject custom = jsonObject.getAsJsonObject("custom");
|
||||
|
||||
if (!custom.has("loom:injected_interfaces")) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
JsonObject addedIfaces = custom.getAsJsonObject("loom:injected_interfaces");
|
||||
|
||||
List<InjectedInterface> result = new ArrayList<>();
|
||||
|
||||
for (String className : addedIfaces.keySet()) {
|
||||
JsonArray ifaceNames = addedIfaces.getAsJsonArray(className);
|
||||
|
||||
for (JsonElement ifaceName : ifaceNames) {
|
||||
result.add(new InjectedInterface(modId, className, ifaceName.getAsString()));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private static class InjectingClassVisitor extends ClassVisitor {
|
||||
private final List<InjectedInterface> injectedInterfaces;
|
||||
|
||||
InjectingClassVisitor(int asmVersion, ClassWriter writer, List<InjectedInterface> injectedInterfaces) {
|
||||
super(asmVersion, writer);
|
||||
this.injectedInterfaces = injectedInterfaces;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
|
||||
Set<String> modifiedInterfaces = new LinkedHashSet<>(interfaces.length + injectedInterfaces.size());
|
||||
Collections.addAll(modifiedInterfaces, interfaces);
|
||||
|
||||
for (InjectedInterface injectedInterface : injectedInterfaces) {
|
||||
modifiedInterfaces.add(injectedInterface.ifaceName());
|
||||
}
|
||||
|
||||
// See JVMS: https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-4.html#jvms-ClassSignature
|
||||
if (signature != null) {
|
||||
var resultingSignature = new StringBuilder(signature);
|
||||
|
||||
for (InjectedInterface injectedInterface : injectedInterfaces) {
|
||||
String superinterfaceSignature = "L" + injectedInterface.ifaceName() + ";";
|
||||
|
||||
if (resultingSignature.indexOf(superinterfaceSignature) == -1) {
|
||||
resultingSignature.append(superinterfaceSignature);
|
||||
}
|
||||
}
|
||||
|
||||
signature = resultingSignature.toString();
|
||||
}
|
||||
|
||||
super.visit(version, access, name, signature, superName, modifiedInterfaces.toArray(new String[0]));
|
||||
}
|
||||
}
|
||||
|
||||
private TinyRemapper createTinyRemapper() {
|
||||
try {
|
||||
TinyRemapper tinyRemapper = TinyRemapperHelper.getTinyRemapper(project, "intermediary", "named");
|
||||
tinyRemapper.readClassPath(TinyRemapperHelper.getMinecraftDependencies(project));
|
||||
tinyRemapper.readClassPath(extension.getMinecraftMappedProvider().getIntermediaryJar().toPath());
|
||||
|
||||
return tinyRemapper;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to create tiny remapper for intermediary->named", e);
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] hashInjectedInterfaces() {
|
||||
// Hash the interfaces we're about to inject to not have to repeat this everytime
|
||||
Hasher hasher = Hashing.sha256().newHasher();
|
||||
|
||||
for (Map.Entry<String, List<InjectedInterface>> entry : injectedInterfaces.entrySet()) {
|
||||
hasher.putString("class:", StandardCharsets.UTF_8);
|
||||
hasher.putString(entry.getKey(), StandardCharsets.UTF_8);
|
||||
|
||||
for (InjectedInterface ifaceName : entry.getValue()) {
|
||||
hasher.putString("iface:", StandardCharsets.UTF_8);
|
||||
hasher.putString(ifaceName.ifaceName(), StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
|
||||
return hasher.hash().asBytes();
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ import java.io.IOException;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.plugins.JavaPluginConvention;
|
||||
import org.gradle.api.plugins.JavaPluginExtension;
|
||||
|
||||
import net.fabricmc.loom.LoomGradlePlugin;
|
||||
|
||||
@@ -67,7 +67,7 @@ public class ModVersionParser {
|
||||
}
|
||||
|
||||
private File locateModJsonFile() {
|
||||
return project.getConvention().getPlugin(JavaPluginConvention.class).getSourceSets()
|
||||
return project.getExtensions().getByType(JavaPluginExtension.class).getSourceSets()
|
||||
.getByName("main")
|
||||
.getResources()
|
||||
.matching(patternFilterable -> patternFilterable.include("fabric.mod.json"))
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* 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.providers;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.fabricmc.loom.util.FileSystemUtil;
|
||||
|
||||
public record BundleMetadata(List<Entry> libraries, List<Entry> versions, String mainClass) {
|
||||
private static final String LIBRARIES_LIST_PATH = "META-INF/libraries.list";
|
||||
private static final String VERSIONS_LIST_PATH = "META-INF/versions.list";
|
||||
private static final String MAINCLASS_PATH = "META-INF/main-class";
|
||||
|
||||
@Nullable
|
||||
public static BundleMetadata fromJar(Path jar) throws IOException {
|
||||
final List<Entry> libraries;
|
||||
final List<Entry> versions;
|
||||
final String mainClass;
|
||||
|
||||
try (FileSystemUtil.Delegate fs = FileSystemUtil.getJarFileSystem(jar)) {
|
||||
if (!Files.exists(fs.get().getPath(VERSIONS_LIST_PATH))) {
|
||||
// Legacy jar
|
||||
return null;
|
||||
}
|
||||
|
||||
libraries = readEntries(fs.readString(LIBRARIES_LIST_PATH), "META-INF/libraries/");
|
||||
versions = readEntries(fs.readString(VERSIONS_LIST_PATH), "META-INF/versions/");
|
||||
mainClass = fs.readString(MAINCLASS_PATH).trim();
|
||||
}
|
||||
|
||||
return new BundleMetadata(libraries, versions, mainClass);
|
||||
}
|
||||
|
||||
private static List<Entry> readEntries(String content, String pathPrefix) {
|
||||
List<Entry> entries = new ArrayList<>();
|
||||
|
||||
for (String entry : content.split("\n")) {
|
||||
if (entry.isBlank()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String[] split = entry.split("\t");
|
||||
|
||||
if (split.length != 3) {
|
||||
continue;
|
||||
}
|
||||
|
||||
entries.add(new Entry(split[0], split[1], pathPrefix + split[2]));
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(entries);
|
||||
}
|
||||
|
||||
public record Entry(String sha1, String name, String path) {
|
||||
public void unpackEntry(Path jar, Path dest) throws IOException {
|
||||
try (FileSystemUtil.Delegate fs = FileSystemUtil.getJarFileSystem(jar);
|
||||
InputStream is = Files.newInputStream(fs.get().getPath(path()))) {
|
||||
Files.copy(is, dest, StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -57,14 +57,16 @@ public class LaunchProvider extends DependencyProvider {
|
||||
|
||||
@Override
|
||||
public void provide(DependencyInfo dependency, Consumer<Runnable> postPopulationScheduler) throws IOException {
|
||||
final String nativesPath = getExtension().getFiles().getNativesDirectory(getProject()).getAbsolutePath();
|
||||
|
||||
final LaunchConfig launchConfig = new LaunchConfig()
|
||||
.property("fabric.development", "true")
|
||||
.property("fabric.remapClasspathFile", getRemapClasspathFile().getAbsolutePath())
|
||||
.property("log4j.configurationFile", getAllLog4JConfigFiles())
|
||||
.property("log4j2.formatMsgNoLookups", "true")
|
||||
|
||||
.property("client", "java.library.path", getExtension().getMinecraftProvider().nativesDir().getAbsolutePath())
|
||||
.property("client", "org.lwjgl.librarypath", getExtension().getMinecraftProvider().nativesDir().getAbsolutePath());
|
||||
.property("client", "java.library.path", nativesPath)
|
||||
.property("client", "org.lwjgl.librarypath", nativesPath);
|
||||
|
||||
if (!getExtension().isForge()) {
|
||||
launchConfig
|
||||
|
||||
@@ -31,10 +31,6 @@ import net.fabricmc.loom.configuration.providers.minecraft.MinecraftVersionMeta;
|
||||
public interface MinecraftProvider {
|
||||
File workingDir();
|
||||
|
||||
boolean hasCustomNatives();
|
||||
|
||||
File nativesDir();
|
||||
|
||||
File dir(String path);
|
||||
|
||||
File file(String path);
|
||||
|
||||
@@ -27,21 +27,17 @@ package net.fabricmc.loom.configuration.providers;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
import com.google.common.base.Stopwatch;
|
||||
import com.google.common.io.Files;
|
||||
import org.gradle.api.GradleException;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.logging.Logger;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.fabricmc.loom.LoomGradlePlugin;
|
||||
import net.fabricmc.loom.configuration.DependencyProvider;
|
||||
@@ -49,10 +45,9 @@ import net.fabricmc.loom.configuration.providers.minecraft.ManifestVersion;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftLibraryProvider;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftVersionMeta;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
import net.fabricmc.loom.util.MirrorUtil;
|
||||
import net.fabricmc.loom.util.DownloadUtil;
|
||||
import net.fabricmc.loom.util.HashedDownloadUtil;
|
||||
import net.fabricmc.loom.util.ZipUtils;
|
||||
import net.fabricmc.loom.util.MirrorUtil;
|
||||
import net.fabricmc.stitch.merge.JarMerger;
|
||||
|
||||
public class MinecraftProviderImpl extends DependencyProvider implements MinecraftProvider {
|
||||
@@ -68,6 +63,8 @@ public class MinecraftProviderImpl extends DependencyProvider implements Minecra
|
||||
public File minecraftServerJar;
|
||||
// The extracted server jar from the boostrap, only exists in >=21w39a
|
||||
public File minecraftExtractedServerJar;
|
||||
@Nullable
|
||||
private BundleMetadata serverBundleMetadata;
|
||||
private Boolean isNewerThan21w39a;
|
||||
private File minecraftMergedJar;
|
||||
private File versionManifestJson;
|
||||
@@ -109,6 +106,8 @@ public class MinecraftProviderImpl extends DependencyProvider implements Minecra
|
||||
downloadJars(getProject().getLogger());
|
||||
}
|
||||
|
||||
serverBundleMetadata = BundleMetadata.fromJar(minecraftServerJar.toPath());
|
||||
|
||||
libraryProvider = new MinecraftLibraryProvider();
|
||||
libraryProvider.provide(this, getProject());
|
||||
|
||||
@@ -274,7 +273,20 @@ public class MinecraftProviderImpl extends DependencyProvider implements Minecra
|
||||
logger.info(":merging jars");
|
||||
Stopwatch stopwatch = Stopwatch.createStarted();
|
||||
|
||||
try (JarMerger jarMerger = new JarMerger(minecraftClientJar, getServerJarToMerge(logger), minecraftMergedJar)) {
|
||||
File jarToMerge = minecraftServerJar;
|
||||
|
||||
if (serverBundleMetadata != null) {
|
||||
logger.info(":Extracting server jar from bootstrap");
|
||||
|
||||
if (serverBundleMetadata.versions().size() != 1) {
|
||||
throw new UnsupportedOperationException("Expected only 1 version in META-INF/versions.list, but got %d".formatted(serverBundleMetadata.versions().size()));
|
||||
}
|
||||
|
||||
serverBundleMetadata.versions().get(0).unpackEntry(minecraftServerJar.toPath(), minecraftExtractedServerJar.toPath());
|
||||
jarToMerge = minecraftExtractedServerJar;
|
||||
}
|
||||
|
||||
try (JarMerger jarMerger = new JarMerger(minecraftClientJar, jarToMerge, minecraftMergedJar)) {
|
||||
jarMerger.enableSyntheticParamsOffset();
|
||||
jarMerger.merge();
|
||||
}
|
||||
@@ -282,78 +294,6 @@ public class MinecraftProviderImpl extends DependencyProvider implements Minecra
|
||||
logger.info(":merged jars in " + stopwatch);
|
||||
}
|
||||
|
||||
private File getServerJarToMerge(Logger logger) throws IOException {
|
||||
try (ZipFile zipFile = new ZipFile(minecraftServerJar)) {
|
||||
ZipEntry versionsListEntry = zipFile.getEntry("META-INF/versions.list");
|
||||
|
||||
if (versionsListEntry == null) {
|
||||
// Legacy pre 21w38a jar
|
||||
return minecraftServerJar;
|
||||
}
|
||||
|
||||
logger.info(":Extracting server jar from bootstrap");
|
||||
|
||||
String versionsList;
|
||||
|
||||
try (InputStream is = zipFile.getInputStream(versionsListEntry)) {
|
||||
versionsList = new String(is.readAllBytes(), StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
String jarPath = null;
|
||||
String[] versions = versionsList.split("\n");
|
||||
|
||||
if (versions.length != 1) {
|
||||
throw new UnsupportedOperationException("Expected only 1 version in META-INF/versions.list, but got %d".formatted(versions.length));
|
||||
}
|
||||
|
||||
for (String version : versions) {
|
||||
if (version.isBlank()) continue;
|
||||
|
||||
String[] split = version.split("\t");
|
||||
|
||||
if (split.length != 3) continue;
|
||||
|
||||
final String hash = split[0];
|
||||
final String id = split[1];
|
||||
final String path = split[2];
|
||||
|
||||
// Take the first (only) version we find.
|
||||
jarPath = path;
|
||||
break;
|
||||
}
|
||||
|
||||
Objects.requireNonNull(jarPath, "Could not find minecraft server jar for " + minecraftVersion());
|
||||
ZipEntry serverJarEntry = zipFile.getEntry("META-INF/versions/" + jarPath);
|
||||
Objects.requireNonNull(serverJarEntry, "Could not find server jar in boostrap@ " + jarPath);
|
||||
|
||||
try (InputStream is = zipFile.getInputStream(serverJarEntry)) {
|
||||
java.nio.file.Files.copy(is, minecraftExtractedServerJar.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
|
||||
return minecraftExtractedServerJar;
|
||||
}
|
||||
}
|
||||
|
||||
public File getMinecraftServerJar() {
|
||||
if (isNewerThan21w39a()) {
|
||||
try {
|
||||
return getServerJarToMerge(getProject().getLogger());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
return minecraftServerJar;
|
||||
}
|
||||
|
||||
public boolean isNewerThan21w39a() {
|
||||
if (isNewerThan21w39a != null) {
|
||||
return isNewerThan21w39a;
|
||||
}
|
||||
|
||||
return isNewerThan21w39a = ZipUtils.contains(minecraftServerJar.toPath(), "META-INF/versions.list");
|
||||
}
|
||||
|
||||
public File getMergedJar() {
|
||||
return minecraftMergedJar;
|
||||
}
|
||||
@@ -363,20 +303,6 @@ public class MinecraftProviderImpl extends DependencyProvider implements Minecra
|
||||
return workingDir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasCustomNatives() {
|
||||
return getProject().getProperties().get("fabric.loom.natives.dir") != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File nativesDir() {
|
||||
if (hasCustomNatives()) {
|
||||
return new File((String) getProject().property("fabric.loom.natives.dir"));
|
||||
}
|
||||
|
||||
return dir("natives");
|
||||
}
|
||||
|
||||
@Override
|
||||
public File dir(String path) {
|
||||
File dir = file(path);
|
||||
@@ -415,4 +341,9 @@ public class MinecraftProviderImpl extends DependencyProvider implements Minecra
|
||||
public String getTargetConfig() {
|
||||
return Constants.Configurations.MINECRAFT;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public BundleMetadata getServerBundleMetadata() {
|
||||
return serverBundleMetadata;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ import net.fabricmc.loom.api.mappings.layered.MappingsNamespace;
|
||||
import net.fabricmc.loom.configuration.DependencyProvider;
|
||||
import net.fabricmc.loom.configuration.accesswidener.AccessWidenerJarProcessor;
|
||||
import net.fabricmc.loom.configuration.accesswidener.TransitiveAccessWidenerJarProcessor;
|
||||
import net.fabricmc.loom.configuration.ifaceinject.InterfaceInjectionProcessor;
|
||||
import net.fabricmc.loom.configuration.processors.JarProcessorManager;
|
||||
import net.fabricmc.loom.configuration.processors.MinecraftProcessedProvider;
|
||||
import net.fabricmc.loom.configuration.providers.MinecraftProviderImpl;
|
||||
@@ -212,6 +213,14 @@ public class MappingsProviderImpl extends DependencyProvider implements Mappings
|
||||
}
|
||||
}
|
||||
|
||||
if (extension.getEnableInterfaceInjection().get()) {
|
||||
InterfaceInjectionProcessor jarProcessor = new InterfaceInjectionProcessor(getProject());
|
||||
|
||||
if (!jarProcessor.isEmpty()) {
|
||||
extension.getGameJarProcessors().add(jarProcessor);
|
||||
}
|
||||
}
|
||||
|
||||
extension.getAccessWidenerPath().finalizeValue();
|
||||
extension.getGameJarProcessors().finalizeValue();
|
||||
JarProcessorManager processorManager = new JarProcessorManager(extension.getGameJarProcessors().get());
|
||||
@@ -275,7 +284,7 @@ public class MappingsProviderImpl extends DependencyProvider implements Mappings
|
||||
String yarnMinecraftVersion = yarnVersion.substring(0, yarnVersion.lastIndexOf(separator));
|
||||
|
||||
if (!yarnMinecraftVersion.equalsIgnoreCase(minecraftVersion)) {
|
||||
throw new RuntimeException(String.format("Minecraft Version (%s) does not match yarn's minecraft version (%s)", minecraftVersion, yarnMinecraftVersion));
|
||||
getProject().getLogger().warn("Minecraft Version ({}) does not match yarn's minecraft version ({})", minecraftVersion, yarnMinecraftVersion);
|
||||
}
|
||||
|
||||
// We can save reading the zip file + header by checking the file name
|
||||
@@ -625,6 +634,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) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* 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.providers.minecraft;
|
||||
|
||||
import static net.fabricmc.loom.util.OperatingSystem.LINUX;
|
||||
import static net.fabricmc.loom.util.OperatingSystem.MAC_OS;
|
||||
import static net.fabricmc.loom.util.OperatingSystem.WINDOWS;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.gradle.api.Project;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.fabricmc.loom.util.Architecture;
|
||||
import net.fabricmc.loom.util.OperatingSystem;
|
||||
|
||||
public class LWJGLVersionOverride {
|
||||
public static final String LWJGL_VERSION = "3.3.0";
|
||||
@Nullable
|
||||
public static final String NATIVE_CLASSIFIER = getNativesClassifier();
|
||||
|
||||
public static final List<String> DEPENDENCIES = List.of(
|
||||
"org.lwjgl:lwjgl:" + LWJGL_VERSION,
|
||||
"org.lwjgl:lwjgl-glfw:" + LWJGL_VERSION,
|
||||
"org.lwjgl:lwjgl-jemalloc:" + LWJGL_VERSION,
|
||||
"org.lwjgl:lwjgl-openal:" + LWJGL_VERSION,
|
||||
"org.lwjgl:lwjgl-opengl:" + LWJGL_VERSION,
|
||||
"org.lwjgl:lwjgl-stb:" + LWJGL_VERSION,
|
||||
"org.lwjgl:lwjgl-tinyfd:" + LWJGL_VERSION
|
||||
);
|
||||
public static final List<String> NATIVES = DEPENDENCIES.stream().map(s -> s + ":" + NATIVE_CLASSIFIER).toList();
|
||||
|
||||
/**
|
||||
* Update lwjgl by default when running on arm and a supported configuration.
|
||||
*/
|
||||
public static boolean overrideByDefault() {
|
||||
return NATIVE_CLASSIFIER != null && Architecture.CURRENT.isArm();
|
||||
}
|
||||
|
||||
public static boolean forceOverride(Project project) {
|
||||
return project.getProperties().get("fabric.loom.override-lwjgl") != null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getNativesClassifier() {
|
||||
return switch (OperatingSystem.CURRENT_OS) {
|
||||
case WINDOWS -> getWindowsClassifier();
|
||||
case MAC_OS -> getMacOSClassifier();
|
||||
case LINUX -> getLinuxClassifier();
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
private static String getWindowsClassifier() {
|
||||
if (Architecture.CURRENT.is64Bit()) {
|
||||
if (Architecture.CURRENT.isArm()) {
|
||||
// Arm 64 bit
|
||||
return "natives-windows-arm64";
|
||||
}
|
||||
|
||||
// None arm 64bit
|
||||
return "natives-windows";
|
||||
}
|
||||
|
||||
// All 32bit, including arm
|
||||
return "natives-windows-x86";
|
||||
}
|
||||
|
||||
private static String getMacOSClassifier() {
|
||||
if (Architecture.CURRENT.isArm()) {
|
||||
// Apple silicone arm
|
||||
return "natives-macos-arm64";
|
||||
}
|
||||
|
||||
// Intel 64bit.
|
||||
return "natives-macos";
|
||||
}
|
||||
|
||||
private static String getLinuxClassifier() {
|
||||
if (Architecture.CURRENT.isArm()) {
|
||||
return Architecture.CURRENT.is64Bit() ? "natives-linux-arm64" : "natives-linux-arm32";
|
||||
}
|
||||
|
||||
return "natives-linux";
|
||||
}
|
||||
}
|
||||
@@ -24,31 +24,77 @@
|
||||
|
||||
package net.fabricmc.loom.configuration.providers.minecraft;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.artifacts.ExternalModuleDependency;
|
||||
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.configuration.providers.BundleMetadata;
|
||||
import net.fabricmc.loom.configuration.providers.MinecraftProviderImpl;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
|
||||
public class MinecraftLibraryProvider {
|
||||
public File MINECRAFT_LIBS;
|
||||
private static final Pattern NATIVES_PATTERN = Pattern.compile("^(?<group>.*)/(.*?)/(?<version>.*)/((?<name>.*?)-([0-9].*?)-)(?<classifier>.*).jar$");
|
||||
|
||||
public void provide(MinecraftProviderImpl minecraftProvider, Project project) {
|
||||
MinecraftVersionMeta versionInfo = minecraftProvider.getVersionInfo();
|
||||
BundleMetadata serverBundleMetadata = minecraftProvider.getServerBundleMetadata();
|
||||
|
||||
initFiles(project, minecraftProvider);
|
||||
final boolean overrideLWJGL = LWJGLVersionOverride.overrideByDefault() || LWJGLVersionOverride.forceOverride(project) || Boolean.getBoolean("loom.test.lwjgloverride");
|
||||
|
||||
if (overrideLWJGL) {
|
||||
project.getLogger().warn("Loom is upgrading Minecraft's LWJGL version to {}", LWJGLVersionOverride.LWJGL_VERSION);
|
||||
}
|
||||
|
||||
for (MinecraftVersionMeta.Library library : versionInfo.libraries()) {
|
||||
if (library.isValidForOS() && !library.hasNatives() && library.artifact() != null) {
|
||||
project.getDependencies().add(Constants.Configurations.MINECRAFT_DEPENDENCIES, project.getDependencies().module(library.name()));
|
||||
if (overrideLWJGL && library.name().startsWith("org.lwjgl")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (library.isValidForOS() && !library.hasNatives() && library.artifact() != null) {
|
||||
if (serverBundleMetadata != null && isLibraryInBundle(serverBundleMetadata, library)) {
|
||||
project.getDependencies().add(Constants.Configurations.MINECRAFT_SERVER_DEPENDENCIES, library.name());
|
||||
} else {
|
||||
// Client only library, or legacy version
|
||||
project.getDependencies().add(Constants.Configurations.MINECRAFT_DEPENDENCIES, library.name());
|
||||
}
|
||||
}
|
||||
|
||||
if (library.hasNativesForOS()) {
|
||||
MinecraftVersionMeta.Download nativeDownload = library.classifierForOS();
|
||||
|
||||
Matcher matcher = NATIVES_PATTERN.matcher(nativeDownload.path());
|
||||
|
||||
if (!matcher.find()) {
|
||||
project.getLogger().warn("Failed to match regex for natives path : " + nativeDownload.path());
|
||||
continue;
|
||||
}
|
||||
|
||||
final String group = matcher.group("group").replace("/", ".");
|
||||
final String name = matcher.group("name");
|
||||
final String version = matcher.group("version");
|
||||
final String classifier = matcher.group("classifier");
|
||||
|
||||
final String dependencyNotation = "%s:%s:%s:%s".formatted(group, name, version, classifier);
|
||||
|
||||
project.getLogger().debug("Add native dependency '{}'", dependencyNotation);
|
||||
project.getDependencies().add(Constants.Configurations.MINECRAFT_NATIVES, dependencyNotation);
|
||||
}
|
||||
}
|
||||
|
||||
if (overrideLWJGL) {
|
||||
LWJGLVersionOverride.DEPENDENCIES.forEach(s -> project.getDependencies().add(Constants.Configurations.MINECRAFT_DEPENDENCIES, s));
|
||||
LWJGLVersionOverride.NATIVES.forEach(s -> project.getDependencies().add(Constants.Configurations.MINECRAFT_NATIVES, s));
|
||||
|
||||
// Add the native support mod that fixes a handful of issues related to the LWJGL update at runtime.
|
||||
ExternalModuleDependency dependency = (ExternalModuleDependency) project.getDependencies().create(Constants.Dependencies.NATIVE_SUPPORT + Constants.Dependencies.Versions.NATIVE_SUPPORT_VERSION);
|
||||
dependency.setTransitive(false);
|
||||
project.getDependencies().add("modLocalRuntime", dependency);
|
||||
}
|
||||
}
|
||||
|
||||
private void initFiles(Project project, MinecraftProviderImpl minecraftProvider) {
|
||||
LoomGradleExtension extension = LoomGradleExtension.get(project);
|
||||
MINECRAFT_LIBS = new File(extension.getFiles().getUserCache(), "libraries");
|
||||
private static boolean isLibraryInBundle(BundleMetadata bundleMetadata, MinecraftVersionMeta.Library library) {
|
||||
return bundleMetadata.libraries().stream().anyMatch(entry -> entry.name().equals(library.name()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,150 +0,0 @@
|
||||
/*
|
||||
* This file is part of fabric-loom, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) 2016-2021 FabricMC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package net.fabricmc.loom.configuration.providers.minecraft;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.gradle.api.GradleException;
|
||||
import org.gradle.api.Project;
|
||||
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.LoomGradlePlugin;
|
||||
import net.fabricmc.loom.util.HashedDownloadUtil;
|
||||
import net.fabricmc.loom.util.MirrorUtil;
|
||||
import net.fabricmc.loom.util.ZipUtils;
|
||||
|
||||
public class MinecraftNativesProvider {
|
||||
private final Project project;
|
||||
private final LoomGradleExtension extension;
|
||||
private final File nativesDir;
|
||||
private final File jarStore;
|
||||
|
||||
public MinecraftNativesProvider(Project project) {
|
||||
this.project = project;
|
||||
extension = LoomGradleExtension.get(project);
|
||||
|
||||
nativesDir = extension.getMinecraftProvider().nativesDir();
|
||||
jarStore = extension.getFiles().getNativesJarStore();
|
||||
}
|
||||
|
||||
public static void provide(Project project) throws IOException {
|
||||
new MinecraftNativesProvider(project).provide();
|
||||
}
|
||||
|
||||
private void provide() throws IOException {
|
||||
if (extension.getMinecraftProvider().hasCustomNatives()) {
|
||||
if (!nativesDir.exists()) {
|
||||
throw new RuntimeException("Could no find custom natives directory at " + nativesDir.getAbsolutePath());
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!LoomGradlePlugin.refreshDeps && !requiresExtract()) {
|
||||
project.getLogger().info("Natives do no need extracting, skipping");
|
||||
return;
|
||||
}
|
||||
|
||||
extractNatives();
|
||||
}
|
||||
|
||||
private void extractNatives() throws IOException {
|
||||
boolean offline = project.getGradle().getStartParameter().isOffline();
|
||||
|
||||
if (nativesDir.exists()) {
|
||||
try {
|
||||
FileUtils.deleteDirectory(nativesDir);
|
||||
} catch (IOException e) {
|
||||
throw new IOException("Failed to delete the natives directory, is the game running?", e);
|
||||
}
|
||||
}
|
||||
|
||||
nativesDir.mkdirs();
|
||||
|
||||
for (MinecraftVersionMeta.Download library : getNatives()) {
|
||||
File libJarFile = library.relativeFile(jarStore);
|
||||
|
||||
if (!offline) {
|
||||
HashedDownloadUtil.downloadIfInvalid(new URL(MirrorUtil.getLibrariesBase(project) + library.path()), libJarFile, library.sha1(), project.getLogger(), false);
|
||||
}
|
||||
|
||||
if (!libJarFile.exists()) {
|
||||
throw new GradleException("Native jar not found at " + libJarFile.getAbsolutePath());
|
||||
}
|
||||
|
||||
ZipUtils.unpackAll(libJarFile.toPath(), nativesDir.toPath());
|
||||
|
||||
// Store a file containing the hash of the extracted natives, used on subsequent runs to skip extracting all the natives if they haven't changed
|
||||
File libSha1File = new File(nativesDir, libJarFile.getName() + ".sha1");
|
||||
FileUtils.writeStringToFile(libSha1File, library.sha1(), StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean requiresExtract() {
|
||||
List<MinecraftVersionMeta.Download> natives = getNatives();
|
||||
|
||||
if (natives.isEmpty()) {
|
||||
throw new IllegalStateException("No natives found for the current system");
|
||||
}
|
||||
|
||||
for (MinecraftVersionMeta.Download library : natives) {
|
||||
File libJarFile = library.relativeFile(jarStore);
|
||||
File libSha1File = new File(nativesDir, libJarFile.getName() + ".sha1");
|
||||
|
||||
if (!libSha1File.exists()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
String sha1 = FileUtils.readFileToString(libSha1File, StandardCharsets.UTF_8);
|
||||
|
||||
if (!sha1.equalsIgnoreCase(library.sha1())) {
|
||||
return true;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
project.getLogger().error("Failed to read " + libSha1File.getAbsolutePath(), e);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// All looks good, no need to re-extract
|
||||
return false;
|
||||
}
|
||||
|
||||
private List<MinecraftVersionMeta.Download> getNatives() {
|
||||
return extension.getMinecraftProvider().getVersionInfo().libraries().stream()
|
||||
.filter((MinecraftVersionMeta.Library::hasNativesForOS))
|
||||
.map(MinecraftVersionMeta.Library::classifierForOS)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
@@ -81,7 +81,7 @@ public record MinecraftVersionMeta(
|
||||
return false;
|
||||
}
|
||||
|
||||
if (natives.get(OperatingSystem.getOS()) == null) {
|
||||
if (natives.get(OperatingSystem.CURRENT_OS) == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ public record MinecraftVersionMeta(
|
||||
}
|
||||
|
||||
public Download classifierForOS() {
|
||||
return downloads().classifier(natives.get(OperatingSystem.getOS()));
|
||||
return downloads().classifier(natives.get(OperatingSystem.CURRENT_OS));
|
||||
}
|
||||
|
||||
public Download artifact() {
|
||||
@@ -119,7 +119,7 @@ public record MinecraftVersionMeta(
|
||||
|
||||
public record OS(String name) {
|
||||
public boolean isValidForOS() {
|
||||
return name() == null || name().equalsIgnoreCase(OperatingSystem.getOS());
|
||||
return name() == null || name().equalsIgnoreCase(OperatingSystem.CURRENT_OS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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.providers.minecraft;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.gradle.api.Project;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.commons.Remapper;
|
||||
|
||||
import net.fabricmc.loom.api.mappings.layered.MappingsNamespace;
|
||||
import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
import net.fabricmc.loom.util.TinyRemapperHelper;
|
||||
import net.fabricmc.tinyremapper.TinyRemapper;
|
||||
import net.fabricmc.tinyremapper.api.TrClass;
|
||||
|
||||
public record SignatureFixerApplyVisitor(Map<String, String> signatureFixes) implements TinyRemapper.ApplyVisitorProvider {
|
||||
@Override
|
||||
public ClassVisitor insertApplyVisitor(TrClass cls, ClassVisitor next) {
|
||||
return new ClassVisitor(Constants.ASM_VERSION, next) {
|
||||
@Override
|
||||
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
|
||||
if (signature == null) {
|
||||
signature = signatureFixes.getOrDefault(name, null);
|
||||
}
|
||||
|
||||
super.visit(version, access, name, signature, superName, interfaces);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static Map<String, String> getRemappedSignatures(boolean toIntermediary, MappingsProviderImpl mappingsProvider, Project project, String targetNamespace) throws IOException {
|
||||
if (mappingsProvider.getSignatureFixes() == null) {
|
||||
// No fixes
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
if (toIntermediary) {
|
||||
// No need to remap, as these are already intermediary
|
||||
return mappingsProvider.getSignatureFixes();
|
||||
}
|
||||
|
||||
// Remap the sig fixes from intermediary to the target namespace
|
||||
final Map<String, String> remapped = new HashMap<>();
|
||||
final TinyRemapper sigTinyRemapper = TinyRemapperHelper.getTinyRemapper(project, MappingsNamespace.INTERMEDIARY.toString(), targetNamespace);
|
||||
final Remapper sigAsmRemapper = sigTinyRemapper.getEnvironment().getRemapper();
|
||||
|
||||
// Remap the class names and the signatures using a new tiny remapper instance.
|
||||
for (Map.Entry<String, String> entry : mappingsProvider.getSignatureFixes().entrySet()) {
|
||||
remapped.put(
|
||||
sigAsmRemapper.map(entry.getKey()),
|
||||
sigAsmRemapper.mapSignature(entry.getValue(), false)
|
||||
);
|
||||
}
|
||||
|
||||
sigTinyRemapper.finish();
|
||||
return remapped;
|
||||
}
|
||||
}
|
||||
@@ -43,7 +43,7 @@ public interface LoomFiles {
|
||||
File getProjectPersistentCache();
|
||||
File getProjectBuildCache();
|
||||
File getRemappedModCache();
|
||||
File getNativesJarStore();
|
||||
File getNativesDirectory(Project project);
|
||||
File getDefaultLog4jConfigFile();
|
||||
File getDevLauncherConfig();
|
||||
File getUnpickLoggingConfigFile();
|
||||
|
||||
@@ -26,6 +26,10 @@ package net.fabricmc.loom.extension;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.gradle.api.Project;
|
||||
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
|
||||
public abstract class LoomFilesBaseImpl implements LoomFiles {
|
||||
protected abstract File getGradleUserHomeDir();
|
||||
protected abstract File getRootDir();
|
||||
@@ -70,8 +74,8 @@ public abstract class LoomFilesBaseImpl implements LoomFiles {
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getNativesJarStore() {
|
||||
return createFile(getUserCache(), "natives/jars");
|
||||
public File getNativesDirectory(Project project) {
|
||||
return createFile(getRootProjectPersistentCache(), "natives/" + LoomGradleExtension.get(project).getMinecraftProvider().minecraftVersion());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -33,6 +33,7 @@ import java.util.function.Consumer;
|
||||
|
||||
import com.google.common.base.Suppliers;
|
||||
import org.gradle.api.Action;
|
||||
import org.gradle.api.DomainObjectCollection;
|
||||
import org.gradle.api.NamedDomainObjectContainer;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.artifacts.Dependency;
|
||||
@@ -69,7 +70,7 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA
|
||||
private static final String PLATFORM_PROPERTY = "loom.platform";
|
||||
|
||||
protected final DeprecationHelper deprecationHelper;
|
||||
protected final ListProperty<LoomDecompiler> decompilers;
|
||||
protected final DomainObjectCollection<LoomDecompiler> decompilers;
|
||||
protected final ListProperty<JarProcessor> jarProcessors;
|
||||
protected final ConfigurableFileCollection log4jConfigs;
|
||||
protected final RegularFileProperty accessWidener;
|
||||
@@ -79,6 +80,7 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA
|
||||
protected final Property<Boolean> setupRemappedVariants;
|
||||
protected final Property<Boolean> transitiveAccessWideners;
|
||||
protected final Property<String> intermediary;
|
||||
protected final Property<Boolean> enableInterfaceInjection;
|
||||
|
||||
private final ModVersionParser versionParser;
|
||||
|
||||
@@ -98,8 +100,7 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA
|
||||
protected LoomGradleExtensionApiImpl(Project project, LoomFiles directories) {
|
||||
this.runConfigs = project.container(RunConfigSettings.class,
|
||||
baseName -> new RunConfigSettings(project, baseName));
|
||||
this.decompilers = project.getObjects().listProperty(LoomDecompiler.class)
|
||||
.empty();
|
||||
this.decompilers = project.getObjects().domainObjectSet(LoomDecompiler.class);
|
||||
this.jarProcessors = project.getObjects().listProperty(JarProcessor.class)
|
||||
.empty();
|
||||
this.log4jConfigs = project.files(directories.getDefaultLog4jConfigFile());
|
||||
@@ -116,6 +117,9 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA
|
||||
this.transitiveAccessWideners.finalizeValueOnRead();
|
||||
this.intermediary = project.getObjects().property(String.class)
|
||||
.convention("https://maven.fabricmc.net/net/fabricmc/intermediary/%1$s/intermediary-%1$s-v2.jar");
|
||||
this.enableInterfaceInjection = project.getObjects().property(Boolean.class)
|
||||
.convention(true);
|
||||
this.enableInterfaceInjection.finalizeValueOnRead();
|
||||
|
||||
this.versionParser = new ModVersionParser(project);
|
||||
|
||||
@@ -158,7 +162,7 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListProperty<LoomDecompiler> getGameDecompilers() {
|
||||
public DomainObjectCollection<LoomDecompiler> getGameDecompilers() {
|
||||
return decompilers;
|
||||
}
|
||||
|
||||
@@ -231,6 +235,11 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA
|
||||
return intermediary;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Property<Boolean> getEnableInterfaceInjection() {
|
||||
return enableInterfaceInjection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableDeprecatedPomGeneration(MavenPublication publication) {
|
||||
net.fabricmc.loom.configuration.MavenPublication.excludePublication(publication);
|
||||
|
||||
@@ -26,22 +26,22 @@ 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 com.google.common.base.Suppliers;
|
||||
import org.cadixdev.lorenz.MappingSet;
|
||||
import org.cadixdev.mercury.Mercury;
|
||||
import org.gradle.api.Action;
|
||||
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.api.ForgeExtensionAPI;
|
||||
@@ -59,7 +59,7 @@ public class LoomGradleExtensionImpl extends LoomGradleExtensionApiImpl implemen
|
||||
private final ConfigurableFileCollection unmappedMods;
|
||||
private final Supplier<ForgeExtensionAPI> forgeExtension;
|
||||
|
||||
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<>();
|
||||
@@ -80,6 +80,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();
|
||||
this.forgeExtension = Suppliers.memoize(() -> isForge() ? project.getObjects().newInstance(ForgeExtensionImpl.class, project, this) : null);
|
||||
@@ -97,15 +98,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
|
||||
@@ -160,8 +161,8 @@ public class LoomGradleExtensionImpl extends LoomGradleExtensionApiImpl implemen
|
||||
}
|
||||
|
||||
@Override
|
||||
public NamedDomainObjectProvider<Configuration> createLazyConfiguration(String name) {
|
||||
NamedDomainObjectProvider<Configuration> provider = project.getConfigurations().register(name);
|
||||
public NamedDomainObjectProvider<Configuration> createLazyConfiguration(String name, Action<? super Configuration> configurationAction) {
|
||||
NamedDomainObjectProvider<Configuration> provider = project.getConfigurations().register(name, configurationAction);
|
||||
|
||||
if (lazyConfigurations.containsKey(name)) {
|
||||
throw new IllegalStateException("Duplicate configuration name" + name);
|
||||
|
||||
@@ -29,7 +29,7 @@ import java.util.Objects;
|
||||
import org.gradle.api.Action;
|
||||
import org.gradle.api.InvalidUserDataException;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.plugins.JavaPluginConvention;
|
||||
import org.gradle.api.plugins.JavaPluginExtension;
|
||||
import org.gradle.api.provider.Property;
|
||||
import org.gradle.api.provider.Provider;
|
||||
import org.gradle.api.tasks.SourceSet;
|
||||
@@ -111,8 +111,7 @@ public abstract class MixinExtensionApiImpl implements MixinExtensionAPI {
|
||||
|
||||
private SourceSet resolveSourceSet(String sourceSetName) {
|
||||
// try to find sourceSet with name sourceSetName in this project
|
||||
SourceSet sourceSet = project.getConvention().getPlugin(JavaPluginConvention.class)
|
||||
.getSourceSets().findByName(sourceSetName);
|
||||
SourceSet sourceSet = project.getExtensions().getByType(JavaPluginExtension.class).getSourceSets().findByName(sourceSetName);
|
||||
|
||||
if (sourceSet == null) {
|
||||
throw new InvalidUserDataException("No sourceSet " + sourceSetName + " was found");
|
||||
|
||||
@@ -38,8 +38,8 @@ import org.gradle.api.Project;
|
||||
import org.gradle.api.Task;
|
||||
import org.gradle.api.UnknownTaskException;
|
||||
import org.gradle.api.artifacts.Configuration;
|
||||
import org.gradle.api.plugins.BasePluginConvention;
|
||||
import org.gradle.api.plugins.JavaPluginConvention;
|
||||
import org.gradle.api.plugins.BasePluginExtension;
|
||||
import org.gradle.api.plugins.JavaPluginExtension;
|
||||
import org.gradle.api.provider.Property;
|
||||
import org.gradle.api.provider.Provider;
|
||||
import org.gradle.api.tasks.Input;
|
||||
@@ -67,12 +67,11 @@ public class MixinExtensionImpl extends MixinExtensionApiImpl implements MixinEx
|
||||
}
|
||||
|
||||
private String getDefaultMixinRefmapName() {
|
||||
String defaultRefmapName = project.getConvention().getPlugin(BasePluginConvention.class).getArchivesBaseName() + "-refmap.json";
|
||||
String defaultRefmapName = project.getExtensions().getByType(BasePluginExtension.class).getArchivesName().get() + "-refmap.json";
|
||||
|
||||
if (project.getRootProject() != project) {
|
||||
defaultRefmapName = project.getConvention().getPlugin(BasePluginConvention.class).getArchivesBaseName() + "-" + project.getPath().replaceFirst(":", "").replace(':', '_') + "-refmap.json";
|
||||
}
|
||||
|
||||
project.getLogger().info("Could not find refmap definition, will be using default name: " + defaultRefmapName);
|
||||
return defaultRefmapName;
|
||||
}
|
||||
@@ -81,7 +80,7 @@ public class MixinExtensionImpl extends MixinExtensionApiImpl implements MixinEx
|
||||
protected PatternSet add0(SourceSet sourceSet, Provider<String> refmapName) {
|
||||
if (!super.getUseLegacyMixinAp().get()) throw new IllegalStateException("You need to set useLegacyMixinAp = true to configure Mixin annotation processor.");
|
||||
|
||||
PatternSet pattern = new PatternSet().setIncludes(Collections.singletonList("*.json"));
|
||||
PatternSet pattern = new PatternSet().setIncludes(Collections.singletonList("**/*.json"));
|
||||
MixinExtension.setMixinInformationContainer(sourceSet, new MixinExtension.MixinInformationContainer(sourceSet, refmapName, pattern));
|
||||
|
||||
isDefault = false;
|
||||
@@ -92,7 +91,7 @@ public class MixinExtensionImpl extends MixinExtensionApiImpl implements MixinEx
|
||||
@Override
|
||||
@NotNull
|
||||
public Stream<SourceSet> getMixinSourceSetsStream() {
|
||||
return project.getConvention().getPlugin(JavaPluginConvention.class).getSourceSets().stream()
|
||||
return project.getExtensions().getByType(JavaPluginExtension.class).getSourceSets().stream()
|
||||
.filter(sourceSet -> MixinExtension.getMixinInformationContainer(sourceSet) != null);
|
||||
}
|
||||
|
||||
@@ -127,7 +126,7 @@ public class MixinExtensionImpl extends MixinExtensionApiImpl implements MixinEx
|
||||
@Override
|
||||
public void init() {
|
||||
if (isDefault) {
|
||||
project.getConvention().getPlugin(JavaPluginConvention.class).getSourceSets().forEach(this::add);
|
||||
project.getExtensions().getByType(JavaPluginExtension.class).getSourceSets().forEach(this::add);
|
||||
}
|
||||
|
||||
isDefault = false;
|
||||
|
||||
123
src/main/java/net/fabricmc/loom/task/AbstractRemapJarTask.java
Normal file
123
src/main/java/net/fabricmc/loom/task/AbstractRemapJarTask.java
Normal 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();
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,7 @@ import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.specs.Spec;
|
||||
import org.gradle.api.tasks.JavaExec;
|
||||
|
||||
import net.fabricmc.loom.configuration.ide.RunConfig;
|
||||
@@ -44,8 +45,9 @@ public abstract class AbstractRunTask extends JavaExec {
|
||||
setGroup(Constants.TaskGroup.FABRIC);
|
||||
this.config = configProvider.apply(getProject());
|
||||
|
||||
setClasspath(config.sourceSet.getRuntimeClasspath());
|
||||
setClasspath(config.sourceSet.getRuntimeClasspath().filter(new LibraryFilter()));
|
||||
args(config.programArgs);
|
||||
getMainClass().set(config.mainClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -65,11 +67,6 @@ public abstract class AbstractRunTask extends JavaExec {
|
||||
super.setWorkingDir(dir);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMain() {
|
||||
return config.mainClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getJvmArgs() {
|
||||
List<String> superArgs = super.getJvmArgs();
|
||||
@@ -77,4 +74,22 @@ public abstract class AbstractRunTask extends JavaExec {
|
||||
args.addAll(config.vmArgs);
|
||||
return args;
|
||||
}
|
||||
|
||||
private class LibraryFilter implements Spec<File> {
|
||||
private List<String> excludedLibraryPaths = null;
|
||||
|
||||
@Override
|
||||
public boolean isSatisfiedBy(File element) {
|
||||
if (excludedLibraryPaths == null) {
|
||||
excludedLibraryPaths = config.getExcludedLibraryPaths(getProject());
|
||||
}
|
||||
|
||||
if (excludedLibraryPaths.contains(element.getAbsolutePath())) {
|
||||
getProject().getLogger().debug("Excluding library {} from {} run config", element.getName(), config.configName);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ import org.gradle.api.Project;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftNativesProvider;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.assets.MinecraftAssetsProvider;
|
||||
|
||||
public class DownloadAssetsTask extends AbstractLoomTask {
|
||||
@@ -40,6 +39,5 @@ public class DownloadAssetsTask extends AbstractLoomTask {
|
||||
LoomGradleExtension extension = getExtension();
|
||||
|
||||
MinecraftAssetsProvider.provide(extension.getMinecraftProvider(), project);
|
||||
MinecraftNativesProvider.provide(project);
|
||||
}
|
||||
}
|
||||
|
||||
50
src/main/java/net/fabricmc/loom/task/ExtractNativesTask.java
Normal file
50
src/main/java/net/fabricmc/loom/task/ExtractNativesTask.java
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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.File;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.gradle.api.tasks.Sync;
|
||||
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
|
||||
public abstract class ExtractNativesTask extends Sync {
|
||||
@Inject
|
||||
public ExtractNativesTask() {
|
||||
// Is there a lazy way to do this for many files? - Doesnt seem there is...
|
||||
for (File nativeFile : getProject().getConfigurations().getByName(Constants.Configurations.MINECRAFT_NATIVES).getFiles()) {
|
||||
from(getProject().zipTree(nativeFile), copySpec -> {
|
||||
copySpec.exclude("META-INF/**");
|
||||
});
|
||||
}
|
||||
|
||||
into(LoomGradleExtension.get(getProject()).getFiles().getNativesDirectory(getProject()));
|
||||
|
||||
setDescription("Downloads and extracts the minecraft natives");
|
||||
}
|
||||
}
|
||||
@@ -49,6 +49,7 @@ import org.gradle.api.provider.MapProperty;
|
||||
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.api.tasks.TaskAction;
|
||||
import org.gradle.workers.WorkAction;
|
||||
import org.gradle.workers.WorkParameters;
|
||||
@@ -89,6 +90,9 @@ public abstract class GenerateSourcesTask extends AbstractLoomTask {
|
||||
@Input
|
||||
public abstract MapProperty<String, String> getOptions();
|
||||
|
||||
@InputFiles
|
||||
public abstract ConfigurableFileCollection getClasspath();
|
||||
|
||||
@Inject
|
||||
public abstract WorkerExecutor getWorkerExecutor();
|
||||
|
||||
@@ -102,6 +106,12 @@ public abstract class GenerateSourcesTask extends AbstractLoomTask {
|
||||
Objects.requireNonNull(getDecompilerConstructor(this.decompiler.getClass().getCanonicalName()),
|
||||
"%s must have a no args constructor".formatted(this.decompiler.getClass().getCanonicalName()));
|
||||
|
||||
FileCollection decompilerClasspath = decompiler.getBootstrapClasspath(getProject());
|
||||
|
||||
if (decompilerClasspath != null) {
|
||||
getClasspath().from(decompilerClasspath);
|
||||
}
|
||||
|
||||
getOutputs().upToDateWhen((o) -> false);
|
||||
getMaxMemory().convention(4096L).finalizeValueOnRead();
|
||||
getOptions().finalizeValueOnRead();
|
||||
@@ -179,6 +189,7 @@ public abstract class GenerateSourcesTask extends AbstractLoomTask {
|
||||
spec.forkOptions(forkOptions -> {
|
||||
forkOptions.setMaxHeapSize("%dm".formatted(getMaxMemory().get()));
|
||||
forkOptions.systemProperty(WorkerDaemonClientsManagerHelper.MARKER_PROP, jvmMarkerValue);
|
||||
forkOptions.bootstrapClasspath(getClasspath());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -24,21 +24,13 @@
|
||||
|
||||
package net.fabricmc.loom.task;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.tasks.TaskContainer;
|
||||
import org.gradle.api.tasks.TaskProvider;
|
||||
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.api.decompilers.LoomDecompiler;
|
||||
import net.fabricmc.loom.api.decompilers.architectury.ArchitecturyLoomDecompiler;
|
||||
import net.fabricmc.loom.configuration.ide.RunConfigSettings;
|
||||
import net.fabricmc.loom.configuration.ide.SetupIntelijRunConfigs;
|
||||
import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
|
||||
public final class LoomTasks {
|
||||
@@ -53,13 +45,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);
|
||||
|
||||
tasks.register("downloadAssets", DownloadAssetsTask.class, t -> t.setDescription("Downloads required assets for Fabric."));
|
||||
tasks.register("remapSourcesJar", RemapSourcesJarTask.class, t -> t.setDescription("Remaps the project sources jar to intermediary names."));
|
||||
TaskProvider<ExtractNativesTask> extractNatives = tasks.register("extractNatives", ExtractNativesTask.class);
|
||||
tasks.register("downloadAssets", DownloadAssetsTask.class, t -> {
|
||||
t.dependsOn(extractNatives);
|
||||
t.setDescription("Downloads required assets for Fabric.");
|
||||
});
|
||||
|
||||
TaskProvider<ValidateAccessWidenerTask> validateAccessWidener = tasks.register("validateAccessWidener", ValidateAccessWidenerTask.class, t -> {
|
||||
t.setDescription("Validate all the rules in the access widener against the Minecraft jar");
|
||||
@@ -146,51 +138,15 @@ public final class LoomTasks {
|
||||
}
|
||||
|
||||
private static void registerDecompileTasks(TaskContainer tasks, Project project) {
|
||||
LoomGradleExtension extension = LoomGradleExtension.get(project);
|
||||
|
||||
project.afterEvaluate(p -> {
|
||||
MappingsProviderImpl mappingsProvider = extension.getMappingsProvider();
|
||||
|
||||
if (mappingsProvider.mappedProvider == null) {
|
||||
// If this is ever null something has gone badly wrong,
|
||||
// for some reason for another this afterEvaluate still gets called when something has gone badly
|
||||
// wrong, returning here seems to produce nicer errors.
|
||||
return;
|
||||
}
|
||||
|
||||
File mappedJar = mappingsProvider.mappedProvider.getMappedJar();
|
||||
|
||||
if (mappingsProvider.hasUnpickDefinitions()) {
|
||||
File outputJar = mappingsProvider.mappedProvider.getUnpickedJar();
|
||||
|
||||
tasks.register("unpickJar", UnpickJarTask.class, unpickJarTask -> {
|
||||
unpickJarTask.getUnpickDefinitions().set(mappingsProvider.getUnpickDefinitionsFile());
|
||||
unpickJarTask.getInputJar().set(mappingsProvider.mappedProvider.getMappedJar());
|
||||
unpickJarTask.getOutputJar().set(outputJar);
|
||||
});
|
||||
|
||||
mappedJar = outputJar;
|
||||
}
|
||||
|
||||
final File inputJar = mappedJar;
|
||||
|
||||
extension.getGameDecompilers().finalizeValue();
|
||||
|
||||
for (LoomDecompiler decompiler : extension.getGameDecompilers().get()) {
|
||||
String taskName = "genSourcesWith" + decompiler.name();
|
||||
// Decompiler will be passed to the constructor of GenerateSourcesTask
|
||||
tasks.register(taskName, GenerateSourcesTask.class, decompiler).configure(task -> {
|
||||
task.setDescription("Decompile minecraft using %s.".formatted(decompiler.name()));
|
||||
task.setGroup(Constants.TaskGroup.FABRIC);
|
||||
task.getInputJar().set(inputJar);
|
||||
|
||||
if (mappingsProvider.hasUnpickDefinitions()) {
|
||||
task.dependsOn(tasks.named("unpickJar"));
|
||||
}
|
||||
|
||||
task.dependsOn(tasks.named("validateAccessWidener"));
|
||||
});
|
||||
}
|
||||
LoomGradleExtension.get(project).getGameDecompilers().configureEach(decompiler -> {
|
||||
String taskName = "genSourcesWith" + decompiler.name();
|
||||
// Decompiler will be passed to the constructor of GenerateSourcesTask
|
||||
tasks.register(taskName, GenerateSourcesTask.class, decompiler).configure(task -> {
|
||||
task.setDescription("Decompile minecraft using %s.".formatted(decompiler.name()));
|
||||
task.setGroup(Constants.TaskGroup.FABRIC);
|
||||
task.dependsOn(tasks.named("validateAccessWidener"));
|
||||
});
|
||||
});
|
||||
|
||||
for (ArchitecturyLoomDecompiler decompiler : extension.getArchGameDecompilers().get()) {
|
||||
String taskName = "genSourcesWith" + decompiler.name();
|
||||
@@ -206,12 +162,11 @@ public final class LoomTasks {
|
||||
});
|
||||
}
|
||||
|
||||
tasks.register("genSources", task -> {
|
||||
task.setDescription("Decompile minecraft using the default decompiler.");
|
||||
task.setGroup(Constants.TaskGroup.FABRIC);
|
||||
tasks.register("genSources", task -> {
|
||||
task.setDescription("Decompile minecraft using the default decompiler.");
|
||||
task.setGroup(Constants.TaskGroup.FABRIC);
|
||||
|
||||
task.dependsOn(project.getTasks().named("genSourcesWithCfr"));
|
||||
});
|
||||
task.dependsOn(project.getTasks().named("genSourcesWithCfr"));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ import org.gradle.api.IllegalDependencyNotation;
|
||||
import org.gradle.api.JavaVersion;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.artifacts.Dependency;
|
||||
import org.gradle.api.plugins.JavaPluginConvention;
|
||||
import org.gradle.api.plugins.JavaPluginExtension;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
import org.gradle.api.tasks.options.Option;
|
||||
|
||||
@@ -171,12 +171,7 @@ public class MigrateMappingsTask extends AbstractLoomTask {
|
||||
project.getLogger().lifecycle(":remapping");
|
||||
Mercury mercury = SourceRemapper.createMercuryWithClassPath(project, false);
|
||||
|
||||
final JavaPluginConvention convention = project.getConvention().findPlugin(JavaPluginConvention.class);
|
||||
final JavaVersion javaVersion = convention != null
|
||||
?
|
||||
convention.getSourceCompatibility()
|
||||
:
|
||||
JavaVersion.current();
|
||||
final JavaVersion javaVersion = project.getExtensions().getByType(JavaPluginExtension.class).getSourceCompatibility();
|
||||
mercury.setSourceCompatibility(javaVersion.toString());
|
||||
|
||||
mercury.getClassPath().add(minecraftMappedProvider.getMappedJar().toPath());
|
||||
|
||||
@@ -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,443 +24,306 @@
|
||||
|
||||
package net.fabricmc.loom.task;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
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.Writer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.FileAlreadyExistsException;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.io.Serializable;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.NoSuchFileException;
|
||||
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.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.jar.Manifest;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import dev.architectury.tinyremapper.IMappingProvider;
|
||||
import dev.architectury.tinyremapper.TinyRemapper;
|
||||
import dev.architectury.tinyremapper.TinyUtils;
|
||||
import dev.architectury.tinyremapper.extension.mixin.MixinExtension;
|
||||
import org.cadixdev.at.AccessTransformSet;
|
||||
import org.cadixdev.at.io.AccessTransformFormats;
|
||||
import org.cadixdev.lorenz.MappingSet;
|
||||
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.SetProperty;
|
||||
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.build.JarRemapper;
|
||||
import net.fabricmc.loom.api.mappings.layered.MappingsNamespace;
|
||||
import net.fabricmc.loom.build.MixinRefmapHelper;
|
||||
import net.fabricmc.loom.build.nesting.EmptyNestedJarProvider;
|
||||
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.FileSystemUtil;
|
||||
import net.fabricmc.loom.util.LfWriter;
|
||||
import net.fabricmc.loom.util.SourceRemapper;
|
||||
import net.fabricmc.loom.util.TinyRemapperHelper;
|
||||
import net.fabricmc.loom.util.ZipReprocessorUtil;
|
||||
import net.fabricmc.loom.util.ZipUtils;
|
||||
import net.fabricmc.loom.util.aw2at.Aw2At;
|
||||
import net.fabricmc.lorenztiny.TinyMappingsReader;
|
||||
import net.fabricmc.mappingio.tree.MappingTree;
|
||||
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;
|
||||
|
||||
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<>();
|
||||
private final Property<String> fromM;
|
||||
private final Property<String> toM;
|
||||
private final SetProperty<String> atAccessWideners;
|
||||
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);
|
||||
fromM = getProject().getObjects().property(String.class)
|
||||
.convention("named");
|
||||
toM = getProject().getObjects().property(String.class)
|
||||
.convention(SourceRemapper.intermediary(getProject()));
|
||||
atAccessWideners = getProject().getObjects().setProperty(String.class)
|
||||
.empty();
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
scheduleRemap(singleRemap || LoomGradleExtension.get(getProject()).isRootProject());
|
||||
|
||||
if (singleRemap) {
|
||||
jarRemapper.remap(getProject());
|
||||
}
|
||||
|
||||
convertAwToAt();
|
||||
}
|
||||
|
||||
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.getArchivePath().toPath();
|
||||
|
||||
if (!Files.exists(input)) {
|
||||
throw new FileNotFoundException(input.toString());
|
||||
}
|
||||
|
||||
MappingsProviderImpl mappingsProvider = extension.getMappingsProvider();
|
||||
|
||||
String fromM = this.fromM.get();
|
||||
String toM = this.toM.get();
|
||||
|
||||
if (isMainRemapTask) {
|
||||
jarRemapper.addToClasspath(getRemapClasspath());
|
||||
|
||||
jarRemapper.addMappings(TinyRemapperHelper.create((fromM.equals("srg") || toM.equals("srg")) && extension.shouldGenerateSrgTiny() ? mappingsProvider.getMappingsWithSrg() : mappingsProvider.getMappings(), fromM, toM, false));
|
||||
}
|
||||
|
||||
for (File mixinMapFile : extension.getAllMixinMappings()) {
|
||||
if (mixinMapFile.exists()) {
|
||||
IMappingProvider provider = TinyUtils.createTinyMappingProvider(mixinMapFile.toPath(), fromM, "intermediary");
|
||||
jarRemapper.addMappings(!toM.equals("intermediary") ? remapToSrg(extension, provider, "intermediary", toM) : provider);
|
||||
submitWork(RemapAction.class, params -> {
|
||||
if (getAddNestedDependencies().get()) {
|
||||
params.getNestedJars().from(getNestedJars());
|
||||
}
|
||||
}
|
||||
|
||||
// Add remap options to the jar remapper
|
||||
jarRemapper.addOptions(this.remapOptions);
|
||||
params.getJarManifestService().set(JarManifestService.get(getProject()));
|
||||
params.getRemapClasspath().from(getClasspath());
|
||||
params.getMappings().add(MappingsService.createDefault(getProject(), getSourceNamespace().get(), getTargetNamespace().get()));
|
||||
|
||||
project.getLogger().info(":scheduling remap " + input.getFileName() + " from " + fromM + " to " + toM);
|
||||
final boolean legacyMixin = extension.getMixin().getUseLegacyMixinAp().get();
|
||||
params.getUseMixinExtension().set(!legacyMixin);
|
||||
|
||||
NestedJarProvider nestedJarProvider = getNestedJarProvider();
|
||||
nestedJarProvider.prepare(getProject());
|
||||
|
||||
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 / architectury.common.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!");
|
||||
}
|
||||
} else if (extension.isForge()) {
|
||||
throw new RuntimeException("Forge must have useLegacyMixinAp enabled");
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
if (!extension.isForge()) {
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (legacyMixin) {
|
||||
params.getMixinMappings().from(extension.getAllMixinMappings());
|
||||
setupLegacyMixinRefmapRemapping(params);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private NestedJarProvider getNestedJarProvider() {
|
||||
if (!LoomGradleExtension.get(getProject()).supportsInclude()) {
|
||||
return EmptyNestedJarProvider.INSTANCE;
|
||||
}
|
||||
private void setupLegacyMixinRefmapRemapping(RemapParams params) {
|
||||
final LoomGradleExtension extension = LoomGradleExtension.get(getProject());
|
||||
final MixinExtension mixinExtension = extension.getMixin();
|
||||
|
||||
Configuration includeConfiguration = getProject().getConfigurations().getByName(Constants.Configurations.INCLUDE);
|
||||
final JsonObject fabricModJson = MixinRefmapHelper.readFabricModJson(getInputFile().getAsFile().get());
|
||||
|
||||
if (!addDefaultNestedDependencies.getOrElse(true)) {
|
||||
return new NestedJarPathProvider(nestedPaths);
|
||||
}
|
||||
|
||||
NestedJarProvider baseProvider = NestedDependencyProvider.createNestedDependencyProviderFromConfiguration(getProject(), includeConfiguration);
|
||||
|
||||
if (nestedPaths.isEmpty()) {
|
||||
return baseProvider;
|
||||
}
|
||||
|
||||
return new MergedNestedJarProvider(
|
||||
baseProvider,
|
||||
new NestedJarPathProvider(nestedPaths)
|
||||
);
|
||||
}
|
||||
|
||||
private IMappingProvider remapToSrg(LoomGradleExtension extension, IMappingProvider parent, String from, String to) throws IOException {
|
||||
MappingTree mappings = (from.equals("srg") || to.equals("srg")) && extension.shouldGenerateSrgTiny() ? extension.getMappingsProvider().getMappingsWithSrg() : extension.getMappingsProvider().getMappings();
|
||||
|
||||
return sink -> {
|
||||
parent.load(new IMappingProvider.MappingAcceptor() {
|
||||
@Override
|
||||
public void acceptClass(String srcName, String dstName) {
|
||||
String srgName = mappings.getClasses()
|
||||
.stream()
|
||||
.filter(it -> Objects.equals(it.getName(from), dstName))
|
||||
.findFirst()
|
||||
.map(it -> it.getName(to))
|
||||
.orElse(dstName);
|
||||
sink.acceptClass(srcName, srgName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void acceptMethod(IMappingProvider.Member method, String dstName) {
|
||||
String srgName = mappings.getClasses()
|
||||
.stream()
|
||||
.flatMap(it -> it.getMethods().stream())
|
||||
.filter(it -> Objects.equals(it.getName(from), dstName))
|
||||
.findFirst()
|
||||
.map(it -> it.getName(to))
|
||||
.orElse(dstName);
|
||||
sink.acceptMethod(method, srgName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void acceptField(IMappingProvider.Member field, String dstName) {
|
||||
String srgName = mappings.getClasses()
|
||||
.stream()
|
||||
.flatMap(it -> it.getFields().stream())
|
||||
.filter(it -> Objects.equals(it.getName(from), dstName))
|
||||
.findFirst()
|
||||
.map(it -> it.getName(to))
|
||||
.orElse(dstName);
|
||||
sink.acceptField(field, srgName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void acceptMethodArg(IMappingProvider.Member method, int lvIndex, String dstName) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void acceptMethodVar(IMappingProvider.Member method, int lvIndex, int startOpIdx, int asmIndex, String dstName) {
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
private Path[] getRemapClasspath() {
|
||||
FileCollection files = this.classpath;
|
||||
|
||||
if (files == null) {
|
||||
files = getProject().getConfigurations().getByName(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME);
|
||||
}
|
||||
|
||||
return files.getFiles().stream()
|
||||
.map(File::toPath)
|
||||
.filter(Files::exists)
|
||||
.toArray(Path[]::new);
|
||||
}
|
||||
|
||||
private void convertAwToAt() throws IOException {
|
||||
if (!this.atAccessWideners.isPresent()) {
|
||||
if (fabricModJson == null) {
|
||||
getProject().getLogger().warn("Could not find fabric.mod.json file in: " + getInputFile().getAsFile().get().getName());
|
||||
return;
|
||||
}
|
||||
|
||||
Set<String> atAccessWideners = this.atAccessWideners.get();
|
||||
final Collection<String> allMixinConfigs = MixinRefmapHelper.getMixinConfigurationFiles(fabricModJson);
|
||||
|
||||
if (atAccessWideners.isEmpty()) {
|
||||
return;
|
||||
for (SourceSet sourceSet : mixinExtension.getMixinSourceSets()) {
|
||||
MixinExtension.MixinInformationContainer container = Objects.requireNonNull(
|
||||
MixinExtension.getMixinInformationContainer(sourceSet)
|
||||
);
|
||||
|
||||
String[] rootPaths = sourceSet.getResources().getSrcDirs().stream()
|
||||
.map(root -> {
|
||||
String rootPath = root.getAbsolutePath().replace("\\", "/");
|
||||
|
||||
if (rootPath.charAt(rootPath.length() - 1) != '/') {
|
||||
rootPath += '/';
|
||||
}
|
||||
|
||||
return rootPath;
|
||||
})
|
||||
.toArray(String[]::new);
|
||||
|
||||
final String refmapName = container.refmapNameProvider().get();
|
||||
final List<String> mixinConfigs = container.sourceSet().getResources()
|
||||
.matching(container.mixinConfigPattern())
|
||||
.getFiles()
|
||||
.stream()
|
||||
.map(file -> {
|
||||
String s = file.getAbsolutePath().replace("\\", "/");
|
||||
|
||||
for (String rootPath : rootPaths) {
|
||||
if (s.startsWith(rootPath)) {
|
||||
s = s.substring(rootPath.length());
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
})
|
||||
.filter(allMixinConfigs::contains)
|
||||
.toList();
|
||||
|
||||
params.getMixinData().add(new RemapParams.RefmapData(mixinConfigs, refmapName));
|
||||
}
|
||||
}
|
||||
|
||||
AccessTransformSet at = AccessTransformSet.create();
|
||||
File jar = getArchiveFile().get().getAsFile();
|
||||
public interface RemapParams extends AbstractRemapParams {
|
||||
ConfigurableFileCollection getNestedJars();
|
||||
ConfigurableFileCollection getRemapClasspath();
|
||||
ConfigurableFileCollection getMixinMappings();
|
||||
ListProperty<Provider<MappingsService>> getMappings();
|
||||
|
||||
try (FileSystemUtil.Delegate fileSystem = FileSystemUtil.getJarFileSystem(jar, false)) {
|
||||
FileSystem fs = fileSystem.get();
|
||||
Path atPath = fs.getPath(Constants.Forge.ACCESS_TRANSFORMER_PATH);
|
||||
Property<Boolean> getUseMixinExtension();
|
||||
|
||||
if (Files.exists(atPath)) {
|
||||
throw new FileAlreadyExistsException("Jar " + jar + " already contains an access transformer - cannot convert AWs!");
|
||||
}
|
||||
record RefmapData(List<String> mixinConfigs, String refmapName) implements Serializable { }
|
||||
ListProperty<RefmapData> getMixinData();
|
||||
|
||||
for (String aw : atAccessWideners) {
|
||||
Path awPath = fs.getPath(aw);
|
||||
Property<JarManifestService> getJarManifestService();
|
||||
}
|
||||
|
||||
if (Files.notExists(awPath)) {
|
||||
throw new NoSuchFileException("Could not find AW '" + aw + "' to convert into AT!");
|
||||
public abstract static class RemapAction extends AbstractRemapAction<RemapParams> {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(RemapAction.class);
|
||||
|
||||
private TinyRemapper tinyRemapper;
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
try (BufferedReader reader = Files.newBufferedReader(awPath, StandardCharsets.UTF_8)) {
|
||||
at.merge(Aw2At.toAccessTransformSet(reader));
|
||||
}
|
||||
|
||||
Files.delete(awPath);
|
||||
}
|
||||
|
||||
LoomGradleExtension extension = LoomGradleExtension.get(getProject());
|
||||
MappingTree mappings = (fromM.get().equals("srg") || toM.get().equals("srg")) && extension.shouldGenerateSrgTiny() ? extension.getMappingsProvider().getMappingsWithSrg() : extension.getMappingsProvider().getMappings();
|
||||
|
||||
try (TinyMappingsReader reader = new TinyMappingsReader(mappings, fromM.get(), toM.get())) {
|
||||
MappingSet mappingSet = reader.read();
|
||||
at = at.remap(mappingSet);
|
||||
}
|
||||
|
||||
try (Writer writer = new LfWriter(Files.newBufferedWriter(atPath))) {
|
||||
AccessTransformFormats.FML.write(writer, at);
|
||||
throw new RuntimeException("Failed to remap", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@InputFile
|
||||
public RegularFileProperty getInput() {
|
||||
return input;
|
||||
}
|
||||
private void remap() throws IOException {
|
||||
final InputTag inputTag = tinyRemapper.createInputTag();
|
||||
|
||||
@Input
|
||||
public Property<Boolean> getAddNestedDependencies() {
|
||||
return addNestedDependencies;
|
||||
}
|
||||
tinyRemapper.readInputsAsync(inputTag, inputFile);
|
||||
|
||||
@Input
|
||||
public Property<Boolean> getAddDefaultNestedDependencies() {
|
||||
return addDefaultNestedDependencies;
|
||||
}
|
||||
|
||||
@Input
|
||||
public Property<Boolean> getRemapAccessWidener() {
|
||||
return remapAccessWidener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the jar paths to the access wideners that will be converted to ATs for Forge runtime.
|
||||
* If you specify multiple files, they will be merged into one.
|
||||
*
|
||||
* <p>The specified files will be converted and removed from the final jar.
|
||||
*
|
||||
* @return the property containing access widener paths in the final jar
|
||||
*/
|
||||
@Input
|
||||
public SetProperty<String> getAtAccessWideners() {
|
||||
return atAccessWideners;
|
||||
}
|
||||
|
||||
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);
|
||||
try (OutputConsumerPath outputConsumer = new OutputConsumerPath.Builder(outputFile).build()) {
|
||||
outputConsumer.addNonClassFiles(inputFile);
|
||||
tinyRemapper.apply(outputConsumer, inputTag);
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
private void remapAccessWidener() throws IOException {
|
||||
final AccessWidenerFile accessWidenerFile = AccessWidenerFile.fromModJar(inputFile);
|
||||
|
||||
@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);
|
||||
if (accessWidenerFile == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
byte[] remapped = remapAccessWidener(accessWidenerFile.content(), tinyRemapper.getEnvironment().getRemapper(), MappingsNamespace.INTERMEDIARY.toString());
|
||||
|
||||
@Input
|
||||
public Property<String> getFromM() {
|
||||
return fromM;
|
||||
}
|
||||
// Finally, replace the output with the remaped aw
|
||||
ZipUtils.replace(outputFile, accessWidenerFile.path(), remapped);
|
||||
}
|
||||
|
||||
@Input
|
||||
public Property<String> getToM() {
|
||||
return toM;
|
||||
private static byte[] remapAccessWidener(byte[] input, Remapper asmRemapper, String targetNamespace) {
|
||||
int version = AccessWidenerReader.readVersion(input);
|
||||
|
||||
AccessWidenerWriter writer = new AccessWidenerWriter(version);
|
||||
AccessWidenerRemapper remapper = new AccessWidenerRemapper(
|
||||
writer,
|
||||
asmRemapper,
|
||||
MappingsNamespace.NAMED.toString(),
|
||||
targetNamespace
|
||||
);
|
||||
AccessWidenerReader reader = new AccessWidenerReader(remapper);
|
||||
reader.read(input);
|
||||
|
||||
return writer.write();
|
||||
}
|
||||
|
||||
private void addNestedJars() {
|
||||
FileCollection nestedJars = getParameters().getNestedJars();
|
||||
|
||||
if (nestedJars.isEmpty()) {
|
||||
LOGGER.info("No jars to nest");
|
||||
return;
|
||||
}
|
||||
|
||||
JarNester.nestJars(nestedJars.getFiles(), outputFile.toFile(), LOGGER);
|
||||
}
|
||||
|
||||
private void modifyJarManifest() throws IOException {
|
||||
int count = ZipUtils.transform(outputFile, Map.of(MANIFEST_PATH, bytes -> {
|
||||
var manifest = new Manifest(new ByteArrayInputStream(bytes));
|
||||
|
||||
getParameters().getJarManifestService().get().apply(manifest);
|
||||
manifest.getMainAttributes().putValue("Fabric-Mapping-Namespace", getParameters().getTargetNamespace().get());
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,82 +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> sourceNamespace;
|
||||
private final Property<String> targetNamespace;
|
||||
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() {
|
||||
this.sourceNamespace = getProject().getObjects().property(String.class).convention("named");
|
||||
this.targetNamespace = getProject().getObjects().property(String.class).convention(SourceRemapper.intermediary(getProject()));
|
||||
super();
|
||||
|
||||
getClasspath().from(getProject().getConfigurations().getByName(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME));
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
public void remap() throws Exception {
|
||||
if (sourceRemapper == null) {
|
||||
if (sourceNamespace.get().equals(targetNamespace.get())) {
|
||||
SourceRemapper.remapSources(getProject(), getInput().get().getAsFile(), getOutput().get().getAsFile(),
|
||||
targetNamespace.get().equals(MappingsNamespace.NAMED.toString()) ? SourceRemapper.intermediary(getProject()) : "named", targetNamespace.get(), reproducibleFileOrder.get(), preserveFileTimestamps.get());
|
||||
} else {
|
||||
SourceRemapper.remapSources(getProject(), getInput().get().getAsFile(), getOutput().get().getAsFile(), sourceNamespace.get(), targetNamespace.get(), 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);
|
||||
}
|
||||
} else {
|
||||
sourceRemapper.scheduleRemapSources(input.get().getAsFile(), output.get().getAsFile(), reproducibleFileOrder.get(), preserveFileTimestamps.get());
|
||||
}
|
||||
}
|
||||
|
||||
@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> getSourceNamespace() {
|
||||
return sourceNamespace;
|
||||
}
|
||||
|
||||
@Input
|
||||
public Property<String> getTargetNamespace() {
|
||||
return targetNamespace;
|
||||
}
|
||||
|
||||
@Input
|
||||
public Property<Boolean> getPreserveFileTimestamps() {
|
||||
return preserveFileTimestamps;
|
||||
}
|
||||
|
||||
@Input
|
||||
public Property<Boolean> getReproducibleFileOrder() {
|
||||
return reproducibleFileOrder;
|
||||
}
|
||||
}
|
||||
|
||||
138
src/main/java/net/fabricmc/loom/task/RemapTaskConfiguration.java
Normal file
138
src/main/java/net/fabricmc/loom/task/RemapTaskConfiguration.java
Normal 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));
|
||||
}
|
||||
}
|
||||
@@ -59,6 +59,9 @@ public abstract class ValidateAccessWidenerTask extends DefaultTask {
|
||||
|
||||
getAccessWidener().convention(extension.getAccessWidenerPath()).finalizeValueOnRead();
|
||||
getTargetJar().convention(getProject().getObjects().fileProperty().fileValue(extension.getMinecraftMappedProvider().getMappedJar())).finalizeValueOnRead();
|
||||
|
||||
// Ignore outputs for up-to-date checks as there aren't any (so only inputs are checked)
|
||||
getOutputs().upToDateWhen(task -> true);
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -22,19 +22,26 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package net.fabricmc.loom.build.nesting;
|
||||
package net.fabricmc.loom.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collection;
|
||||
public class Architecture {
|
||||
public static final Architecture CURRENT = new Architecture(System.getProperty("os.arch"));
|
||||
|
||||
import org.gradle.api.Project;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
private final String name;
|
||||
|
||||
@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();
|
||||
public Architecture(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
// Setup the files ready to be provided
|
||||
default void prepare(Project project) { }
|
||||
public boolean is64Bit() {
|
||||
return name.contains("64") || name.startsWith("armv8");
|
||||
}
|
||||
|
||||
public boolean isArm() {
|
||||
return name.startsWith("arm") || name.startsWith("aarch64");
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of fabric-loom, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) 2016-2020 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
|
||||
@@ -22,17 +22,15 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package net.fabricmc.loom.task;
|
||||
package net.fabricmc.loom.util;
|
||||
|
||||
import org.gradle.api.tasks.Internal;
|
||||
import groovy.lang.Closure;
|
||||
import org.gradle.api.Action;
|
||||
|
||||
import net.fabricmc.loom.util.SourceRemapper;
|
||||
|
||||
public class RemapAllSourcesTask extends AbstractLoomTask {
|
||||
public SourceRemapper sourceRemapper;
|
||||
|
||||
@Internal
|
||||
public SourceRemapper getSourceRemapper() {
|
||||
return sourceRemapper;
|
||||
public record ClosureAction<T>(Closure closure) implements Action<T> {
|
||||
@Override
|
||||
public void execute(T t) {
|
||||
closure.setDelegate(t);
|
||||
closure.call(t);
|
||||
}
|
||||
}
|
||||
@@ -41,8 +41,6 @@ public class Constants {
|
||||
public static final String EXPERIMENTAL_VERSIONS = "https://maven.fabricmc.net/net/minecraft/experimental_versions.json";
|
||||
public static final String FABRIC_REPOSITORY = "https://maven.fabricmc.net/";
|
||||
|
||||
public static final String SYSTEM_ARCH = System.getProperty("os.arch").equals("64") ? "64" : "32";
|
||||
|
||||
public static final int ASM_VERSION = Opcodes.ASM9;
|
||||
|
||||
public static final List<RemappedConfigurationEntry> MOD_COMPILE_ENTRIES = ImmutableList.of(
|
||||
@@ -65,8 +63,13 @@ public class Constants {
|
||||
public static final String MOD_COMPILE_CLASSPATH_MAPPED = "modCompileClasspathMapped";
|
||||
public static final String INCLUDE = "include";
|
||||
public static final String MINECRAFT = "minecraft";
|
||||
/**
|
||||
* The server specific configuration will be empty when using a legacy (pre 21w38a server jar)
|
||||
* find the client only dependencies on the "minecraftLibraries" config.
|
||||
*/
|
||||
public static final String MINECRAFT_SERVER_DEPENDENCIES = "minecraftServerLibraries";
|
||||
public static final String MINECRAFT_DEPENDENCIES = "minecraftLibraries";
|
||||
public static final String MINECRAFT_REMAP_CLASSPATH = "minecraftRemapClasspath";
|
||||
public static final String MINECRAFT_NATIVES = "minecraftNatives";
|
||||
public static final String MINECRAFT_NAMED = "minecraftNamed";
|
||||
public static final String MAPPINGS = "mappings";
|
||||
public static final String MAPPINGS_FINAL = "mappingsFinal";
|
||||
@@ -118,6 +121,7 @@ public class Constants {
|
||||
public static final String DEV_LAUNCH_INJECTOR = "net.fabricmc:dev-launch-injector:";
|
||||
public static final String TERMINAL_CONSOLE_APPENDER = "net.minecrell:terminalconsoleappender:";
|
||||
public static final String JETBRAINS_ANNOTATIONS = "org.jetbrains:annotations:";
|
||||
public static final String NATIVE_SUPPORT = "net.fabricmc:fabric-loom-native-support:";
|
||||
public static final String JAVAX_ANNOTATIONS = "com.google.code.findbugs:jsr305:"; // I hate that I have to add these.
|
||||
public static final String FORGE_RUNTIME = "dev.architectury:architectury-loom-runtime:";
|
||||
public static final String ACCESS_TRANSFORMERS = "net.minecraftforge:accesstransformers:";
|
||||
@@ -133,6 +137,7 @@ public class Constants {
|
||||
public static final String DEV_LAUNCH_INJECTOR = "0.2.1+build.8";
|
||||
public static final String TERMINAL_CONSOLE_APPENDER = "1.2.0";
|
||||
public static final String JETBRAINS_ANNOTATIONS = "23.0.0";
|
||||
public static final String NATIVE_SUPPORT_VERSION = "1.0.1";
|
||||
public static final String JAVAX_ANNOTATIONS = "3.0.2";
|
||||
public static final String FORGE_RUNTIME = "1.1.3";
|
||||
public static final String ACCESS_TRANSFORMERS = "3.0.1";
|
||||
|
||||
@@ -28,9 +28,12 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystemAlreadyExistsException;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.NoSuchFileException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
@@ -38,6 +41,20 @@ import java.util.function.Supplier;
|
||||
|
||||
public final class FileSystemUtil {
|
||||
public record Delegate(FileSystem fs, boolean owner) implements AutoCloseable, Supplier<FileSystem> {
|
||||
public byte[] readAllBytes(String path) throws IOException {
|
||||
Path fsPath = get().getPath(path);
|
||||
|
||||
if (Files.exists(fsPath)) {
|
||||
return Files.readAllBytes(fsPath);
|
||||
} else {
|
||||
throw new NoSuchFileException(fsPath.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public String readString(String path) throws IOException {
|
||||
return new String(readAllBytes(path), StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (owner) {
|
||||
@@ -65,6 +82,10 @@ public final class FileSystemUtil {
|
||||
return getJarFileSystem(path.toUri(), create);
|
||||
}
|
||||
|
||||
public static Delegate getJarFileSystem(Path path) throws IOException {
|
||||
return getJarFileSystem(path, false);
|
||||
}
|
||||
|
||||
public static Delegate getJarFileSystem(URI uri, boolean create) throws IOException {
|
||||
URI jarUri;
|
||||
|
||||
|
||||
@@ -25,18 +25,12 @@
|
||||
package net.fabricmc.loom.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
public final class ModUtils {
|
||||
private ModUtils() {
|
||||
}
|
||||
|
||||
public static boolean isMod(File input) {
|
||||
try (ZipFile zipFile = new ZipFile(input)) {
|
||||
return zipFile.getEntry("fabric.mod.json") != null;
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
return ZipUtils.contains(input.toPath(), "fabric.mod.json");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,23 +30,21 @@ import java.net.StandardProtocolFamily;
|
||||
import java.nio.channels.ServerSocketChannel;
|
||||
|
||||
public class OperatingSystem {
|
||||
public static String getOS() {
|
||||
public static final String WINDOWS = "windows";
|
||||
public static final String MAC_OS = "osx";
|
||||
public static final String LINUX = "linux";
|
||||
|
||||
public static final String CURRENT_OS = getOS();
|
||||
|
||||
private static String getOS() {
|
||||
String osName = System.getProperty("os.name").toLowerCase();
|
||||
|
||||
if (osName.contains("win")) {
|
||||
return "windows";
|
||||
return WINDOWS;
|
||||
} else if (osName.contains("mac")) {
|
||||
return "osx";
|
||||
return MAC_OS;
|
||||
} else {
|
||||
return "linux";
|
||||
}
|
||||
}
|
||||
|
||||
public static String getArch() {
|
||||
if (is64Bit()) {
|
||||
return "64";
|
||||
} else {
|
||||
return "32";
|
||||
return LINUX;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,10 +52,6 @@ public class OperatingSystem {
|
||||
return System.getProperty("sun.arch.data.model").contains("64");
|
||||
}
|
||||
|
||||
public static boolean isWindows() {
|
||||
return getOS().equals("windows");
|
||||
}
|
||||
|
||||
public static boolean isCIBuild() {
|
||||
String loomProperty = System.getProperty("fabric.loom.ci");
|
||||
|
||||
|
||||
@@ -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.configuration.RemappedConfigurationEntry;
|
||||
@@ -152,7 +153,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();
|
||||
@@ -231,7 +232,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());
|
||||
|
||||
@@ -239,7 +240,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);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -44,6 +44,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;
|
||||
|
||||
@@ -138,6 +139,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()) {
|
||||
|
||||
@@ -83,13 +83,7 @@ public class ZipUtils {
|
||||
|
||||
public static byte[] unpack(Path zip, String path) throws IOException {
|
||||
try (FileSystemUtil.Delegate fs = FileSystemUtil.getJarFileSystem(zip, false)) {
|
||||
Path fsPath = fs.get().getPath(path);
|
||||
|
||||
if (Files.exists(fsPath)) {
|
||||
return Files.readAllBytes(fsPath);
|
||||
} else {
|
||||
throw new NoSuchFileException(fsPath.toString());
|
||||
}
|
||||
return fs.readAllBytes(path);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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-20211203231050+0000"
|
||||
public final static String PRE_RELEASE_GRADLE = "7.5-20211228231407+0000"
|
||||
|
||||
public final static String[] STANDARD_TEST_VERSIONS = [DEFAULT_GRADLE, PRE_RELEASE_GRADLE]
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
@@ -22,34 +22,30 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package net.fabricmc.loom.test.unit
|
||||
package net.fabricmc.loom.test.integration
|
||||
|
||||
import net.fabricmc.loom.build.nesting.MergedNestedJarProvider
|
||||
import net.fabricmc.loom.build.nesting.NestedJarProvider
|
||||
import org.gradle.api.Project
|
||||
import net.fabricmc.loom.test.util.GradleProjectTestTrait
|
||||
import net.fabricmc.loom.util.ZipUtils
|
||||
import spock.lang.Specification
|
||||
import spock.lang.Unroll
|
||||
|
||||
class MergedNestedJarProviderTest extends Specification {
|
||||
def "empty test"() {
|
||||
given:
|
||||
def nestedJarProvider = new MergedNestedJarProvider(new TestNestedJarProvider())
|
||||
when:
|
||||
nestedJarProvider.prepare(null)
|
||||
then:
|
||||
nestedJarProvider.provide() != null
|
||||
}
|
||||
import static net.fabricmc.loom.test.LoomTestConstants.STANDARD_TEST_VERSIONS
|
||||
import static org.gradle.testkit.runner.TaskOutcome.SUCCESS
|
||||
|
||||
private class TestNestedJarProvider implements NestedJarProvider {
|
||||
private Collection<File> files = null
|
||||
class InterfaceInjectionTest extends Specification implements GradleProjectTestTrait {
|
||||
@Unroll
|
||||
def "interface injection (gradle #version)"() {
|
||||
setup:
|
||||
def gradle = gradleProject(project: "interfaceInjection", version: version)
|
||||
ZipUtils.pack(new File(gradle.projectDir, "dummyDependency").toPath(), new File(gradle.projectDir, "dummy.jar").toPath())
|
||||
|
||||
@Override
|
||||
Collection<File> provide() {
|
||||
return files
|
||||
}
|
||||
when:
|
||||
def result = gradle.run(task: "build")
|
||||
|
||||
@Override
|
||||
void prepare(Project project) {
|
||||
files = []
|
||||
}
|
||||
}
|
||||
then:
|
||||
result.task(":build").outcome == SUCCESS
|
||||
|
||||
where:
|
||||
version << STANDARD_TEST_VERSIONS
|
||||
}
|
||||
}
|
||||
@@ -67,6 +67,9 @@ class MixinApAutoRefmapTest extends Specification implements GradleProjectTestTr
|
||||
def j5 = JsonParser.parseReader(new InputStreamReader(jar.getInputStream(jar.getEntry("irrelevant.mixins.json"))))
|
||||
!j5.asJsonObject.has("refmap")
|
||||
|
||||
def j6 = JsonParser.parseReader(new InputStreamReader(jar.getInputStream(jar.getEntry("subfolder/subfolder.mixins.json"))))
|
||||
j6.asJsonObject.getAsJsonPrimitive("refmap").getAsString() == "refmap0001.json"
|
||||
|
||||
where:
|
||||
version << STANDARD_TEST_VERSIONS
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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.test.integration
|
||||
|
||||
import net.fabricmc.loom.test.util.GradleProjectTestTrait
|
||||
import spock.lang.Specification
|
||||
import spock.lang.Unroll
|
||||
import spock.util.environment.RestoreSystemProperties
|
||||
|
||||
import static net.fabricmc.loom.test.LoomTestConstants.STANDARD_TEST_VERSIONS
|
||||
import static org.gradle.testkit.runner.TaskOutcome.SUCCESS
|
||||
import static org.gradle.testkit.runner.TaskOutcome.UP_TO_DATE
|
||||
|
||||
class NativesTest extends Specification implements GradleProjectTestTrait {
|
||||
@Unroll
|
||||
def "Default natives for 1.18 (gradle #version)"() {
|
||||
setup:
|
||||
def gradle = gradleProject(project: "minimalBase", version: version)
|
||||
|
||||
gradle.buildGradle << '''
|
||||
dependencies {
|
||||
minecraft "com.mojang:minecraft:1.18"
|
||||
mappings loom.officialMojangMappings()
|
||||
}
|
||||
'''
|
||||
|
||||
when:
|
||||
// Run the task twice to ensure its up to date
|
||||
def result = gradle.run(task: "extractNatives")
|
||||
def result2 = gradle.run(task: "extractNatives")
|
||||
|
||||
then:
|
||||
result.task(":extractNatives").outcome == SUCCESS
|
||||
result2.task(":extractNatives").outcome == UP_TO_DATE
|
||||
|
||||
where:
|
||||
version << STANDARD_TEST_VERSIONS
|
||||
}
|
||||
|
||||
@RestoreSystemProperties
|
||||
@Unroll
|
||||
def "Overridden natives for 1.18 (gradle #version)"() {
|
||||
setup:
|
||||
System.setProperty('loom.test.lwjgloverride', "true")
|
||||
def gradle = gradleProject(project: "minimalBase", version: version)
|
||||
|
||||
gradle.buildGradle << '''
|
||||
dependencies {
|
||||
minecraft "com.mojang:minecraft:1.18"
|
||||
mappings loom.officialMojangMappings()
|
||||
}
|
||||
'''
|
||||
|
||||
when:
|
||||
// Run the task twice to ensure its up to date
|
||||
def result = gradle.run(task: "extractNatives")
|
||||
|
||||
then:
|
||||
result.task(":extractNatives").outcome == SUCCESS
|
||||
|
||||
where:
|
||||
version << STANDARD_TEST_VERSIONS
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.test.integration
|
||||
|
||||
import net.fabricmc.loom.test.util.GradleProjectTestTrait
|
||||
import spock.lang.Specification
|
||||
import spock.lang.Unroll
|
||||
|
||||
import static net.fabricmc.loom.test.LoomTestConstants.STANDARD_TEST_VERSIONS
|
||||
import static org.gradle.testkit.runner.TaskOutcome.SUCCESS
|
||||
|
||||
// Basic test to just ensure it builds.
|
||||
class SignatureFixesTest extends Specification implements GradleProjectTestTrait {
|
||||
@Unroll
|
||||
def "build (gradle #version)"() {
|
||||
setup:
|
||||
def gradle = gradleProject(project: "minimalBase", version: version)
|
||||
|
||||
gradle.buildGradle << '''
|
||||
dependencies {
|
||||
minecraft "com.mojang:minecraft:1.18-pre1"
|
||||
mappings "net.fabricmc:yarn:1.18-pre1+build.14:v2"
|
||||
}
|
||||
'''
|
||||
|
||||
when:
|
||||
def result = gradle.run(task: "build")
|
||||
|
||||
then:
|
||||
result.task(":build").outcome == SUCCESS
|
||||
|
||||
where:
|
||||
version << STANDARD_TEST_VERSIONS
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* 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.test.unit
|
||||
|
||||
import net.fabricmc.loom.configuration.ide.RunConfig
|
||||
import net.fabricmc.loom.configuration.ide.idea.IdeaSyncTask
|
||||
import org.intellij.lang.annotations.Language
|
||||
import spock.lang.Specification
|
||||
|
||||
import java.nio.charset.StandardCharsets
|
||||
|
||||
class IdeaClasspathModificationsTest extends Specification {
|
||||
|
||||
def "configure exclusions"() {
|
||||
when:
|
||||
def input = fromDummy()
|
||||
def output = IdeaSyncTask.setClasspathModificationsInXml(input, ["/path/to/file.jar"])
|
||||
|
||||
then:
|
||||
output == EXPECTED
|
||||
}
|
||||
|
||||
def "re-configure exclusions"() {
|
||||
when:
|
||||
def input = fromDummy()
|
||||
def output = IdeaSyncTask.setClasspathModificationsInXml(input, ["/path/to/file.jar"])
|
||||
output = IdeaSyncTask.setClasspathModificationsInXml(output, ["/path/to/file.jar", "/path/to/another.jar"])
|
||||
|
||||
then:
|
||||
output == EXPECTED2
|
||||
}
|
||||
|
||||
private String fromDummy() {
|
||||
String dummyConfig
|
||||
|
||||
IdeaSyncTask.class.getClassLoader().getResourceAsStream("idea_run_config_template.xml").withCloseable {
|
||||
dummyConfig = new String(it.readAllBytes(), StandardCharsets.UTF_8)
|
||||
}
|
||||
|
||||
dummyConfig = dummyConfig.replace("%NAME%", "Minecraft Client")
|
||||
dummyConfig = dummyConfig.replace("%MAIN_CLASS%", "net.minecraft.client.Main")
|
||||
dummyConfig = dummyConfig.replace("%IDEA_MODULE%", "main.test")
|
||||
dummyConfig = dummyConfig.replace("%RUN_DIRECTORY%", ".run")
|
||||
dummyConfig = dummyConfig.replace("%PROGRAM_ARGS%", RunConfig.joinArguments([]).replaceAll("\"", """))
|
||||
dummyConfig = dummyConfig.replace("%VM_ARGS%", RunConfig.joinArguments([]).replaceAll("\"", """))
|
||||
|
||||
return dummyConfig
|
||||
}
|
||||
|
||||
@Language("XML")
|
||||
private static final String EXPECTED = '''
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" factoryName="Application" name="Minecraft Client" type="Application">
|
||||
<option name="MAIN_CLASS_NAME" value="net.minecraft.client.Main"/>
|
||||
<module name="main.test"/>
|
||||
<option name="PROGRAM_PARAMETERS" value=""/>
|
||||
<option name="VM_PARAMETERS" value=""/>
|
||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/.run/"/>
|
||||
<method v="2">
|
||||
<option enabled="true" name="Make"/>
|
||||
</method>
|
||||
<classpathModifications><entry exclude="true" path="/path/to/file.jar"/></classpathModifications></configuration>
|
||||
</component>
|
||||
'''.trim()
|
||||
|
||||
@Language("XML")
|
||||
private static final String EXPECTED2 = '''
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" factoryName="Application" name="Minecraft Client" type="Application">
|
||||
<option name="MAIN_CLASS_NAME" value="net.minecraft.client.Main"/>
|
||||
<module name="main.test"/>
|
||||
<option name="PROGRAM_PARAMETERS" value=""/>
|
||||
<option name="VM_PARAMETERS" value=""/>
|
||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/.run/"/>
|
||||
<method v="2">
|
||||
<option enabled="true" name="Make"/>
|
||||
</method>
|
||||
<classpathModifications><entry exclude="true" path="/path/to/file.jar"/><entry exclude="true" path="/path/to/another.jar"/></classpathModifications></configuration>
|
||||
</component>
|
||||
'''.trim()
|
||||
|
||||
}
|
||||
@@ -33,6 +33,6 @@ class RunConfigUnitTest extends Specification {
|
||||
def args = RunConfig.joinArguments(["-Dfabric.test=123", "-Dfabric.test=abc 123"])
|
||||
|
||||
then:
|
||||
args == '"-Dfabric.test=123" "-Dfabric.test=abc 123"'
|
||||
args == '-Dfabric.test=123 "-Dfabric.test=abc 123"'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,28 +30,29 @@ import net.fabricmc.loom.configuration.providers.mappings.LayeredMappingSpecBuil
|
||||
import net.fabricmc.loom.configuration.providers.mappings.intermediary.IntermediaryMappingsSpec
|
||||
import net.fabricmc.loom.configuration.providers.mappings.mojmap.MojangMappingsSpec
|
||||
import net.fabricmc.loom.configuration.providers.mappings.parchment.ParchmentMappingsSpec
|
||||
import org.gradle.api.Action
|
||||
import org.gradle.util.ConfigureUtil
|
||||
import net.fabricmc.loom.util.ClosureAction
|
||||
import spock.lang.Specification
|
||||
|
||||
class LayeredMappingSpecBuilderTest extends LayeredMappingsSpecification {
|
||||
def "simple mojmap" () {
|
||||
when:
|
||||
def spec = layered() {
|
||||
def spec = layered {
|
||||
officialMojangMappings()
|
||||
}
|
||||
def layers = spec.layers()
|
||||
then:
|
||||
spec.version == "layered+hash.2198"
|
||||
layers.size() == 2
|
||||
spec.version == "layered+hash.2198"
|
||||
layers[0].class == IntermediaryMappingsSpec
|
||||
layers[1].class == MojangMappingsSpec
|
||||
}
|
||||
|
||||
def "simple mojmap with parchment" () {
|
||||
when:
|
||||
def dep = "I like cake"
|
||||
def spec = layered() {
|
||||
officialMojangMappings()
|
||||
parchment("I like cake")
|
||||
parchment(dep)
|
||||
}
|
||||
def layers = spec.layers()
|
||||
def parchment = layers[2] as ParchmentMappingsSpec
|
||||
@@ -87,7 +88,7 @@ class LayeredMappingSpecBuilderTest extends LayeredMappingsSpecification {
|
||||
|
||||
def "simple mojmap with parchment keep prefix alternate hash" () {
|
||||
when:
|
||||
def spec = layered() {
|
||||
def spec = layered {
|
||||
officialMojangMappings()
|
||||
parchment("I really like cake") {
|
||||
it.removePrefix = false
|
||||
@@ -105,14 +106,9 @@ class LayeredMappingSpecBuilderTest extends LayeredMappingsSpecification {
|
||||
parchment.removePrefix() == false
|
||||
}
|
||||
|
||||
// Gradle does this big of magic behind the scenes
|
||||
LayeredMappingSpec layered(@DelegatesTo(LayeredMappingSpecBuilderImpl) Closure cl) {
|
||||
return layeredAction(ConfigureUtil.configureUsing(cl))
|
||||
}
|
||||
|
||||
LayeredMappingSpec layeredAction(Action<LayeredMappingSpecBuilderImpl> action) {
|
||||
LayeredMappingSpecBuilderImpl builder = new LayeredMappingSpecBuilderImpl(null)
|
||||
action.execute(builder)
|
||||
new ClosureAction(cl).execute(builder)
|
||||
return builder.build()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,3 +7,7 @@ dependencies {
|
||||
mappings "net.fabricmc:yarn:21w38a+build.11:v2"
|
||||
modImplementation "net.fabricmc:fabric-loader:0.11.7"
|
||||
}
|
||||
|
||||
tasks.named("genSourcesWithCfr") {
|
||||
options.put("test", "value")
|
||||
}
|
||||
18
src/test/resources/projects/interfaceInjection/build.gradle
Normal file
18
src/test/resources/projects/interfaceInjection/build.gradle
Normal file
@@ -0,0 +1,18 @@
|
||||
// This is used by a range of tests that append to this file before running the gradle tasks.
|
||||
// Can be used for tests that require minimal custom setup
|
||||
plugins {
|
||||
id 'fabric-loom'
|
||||
id 'maven-publish'
|
||||
}
|
||||
|
||||
archivesBaseName = "fabric-example-mod"
|
||||
version = "1.0.0"
|
||||
group = "com.example"
|
||||
|
||||
dependencies {
|
||||
minecraft "com.mojang:minecraft:1.17.1"
|
||||
mappings "net.fabricmc:yarn:1.17.1+build.59:v2"
|
||||
modImplementation "net.fabricmc:fabric-loader:0.11.6"
|
||||
|
||||
modImplementation files("dummy.jar")
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "dummy",
|
||||
"version": "1",
|
||||
"name": "Dummy Mod",
|
||||
"custom": {
|
||||
"fabric-api:module-lifecycle": "stable",
|
||||
"loom:injected_interfaces": {
|
||||
"net/minecraft/class_2248": ["InjectedInterface"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import net.minecraft.block.Blocks;
|
||||
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
|
||||
public class ExampleMod implements ModInitializer {
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
Blocks.AIR.newMethodThatDidNotExist();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
|
||||
public interface InjectedInterface {
|
||||
default void newMethodThatDidNotExist() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package net.fabricmc.example.mixin;
|
||||
|
||||
import net.minecraft.client.gui.screen.ChatScreen;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(ChatScreen.class)
|
||||
public class ExampleMixinSubfolder {
|
||||
@Inject(at = @At("HEAD"), method = "init()V")
|
||||
private void init(CallbackInfo info) {
|
||||
System.out.println("This line is printed by an example mod mixin!");
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@
|
||||
},
|
||||
"mixins": [
|
||||
"main.mixins.json",
|
||||
"subfolder/subfolder.mixins.json",
|
||||
{
|
||||
"config": "blabla.json",
|
||||
"environment": "client"
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"required": true,
|
||||
"minVersion": "0.8",
|
||||
"package": "net.fabricmc.example.mixin",
|
||||
"compatibilityLevel": "JAVA_16",
|
||||
"mixins": [
|
||||
],
|
||||
"client": [
|
||||
"ExampleMixinSubfolder"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,7 @@ archivesBaseName = "fabric-example-mod"
|
||||
version = "1.0.0"
|
||||
|
||||
dependencies {
|
||||
minecraft "com.mojang:minecraft:1.16.5"
|
||||
mappings "net.fabricmc:yarn:1.16.5+build.5:v2"
|
||||
modImplementation "net.fabricmc:fabric-loader:0.11.2"
|
||||
minecraft "com.mojang:minecraft:1.18.1"
|
||||
mappings "net.fabricmc:yarn:1.18.1+build.12:v2"
|
||||
modImplementation "net.fabricmc:fabric-loader:0.12.12"
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user