mirror of
https://github.com/architectury/architectury-loom.git
synced 2026-03-28 04:07:01 -05:00
Rework how unpick and linenumber maps are applied (#907)
This should hopefully vastly improve debugging, and more imporantly not work in a consistant manner, making debugging issues a lot easier. This commit contains an intergration test that uses a real debugger to check that breakpoints are being fired as expected.
This commit is contained in:
@@ -95,7 +95,7 @@ dependencies {
|
||||
|
||||
// decompilers
|
||||
implementation ('net.fabricmc:fabric-fernflower:2.0.0')
|
||||
implementation ('net.fabricmc:cfr:0.2.0')
|
||||
implementation ('net.fabricmc:cfr:0.2.1')
|
||||
|
||||
// source code remapping
|
||||
implementation ('net.fabricmc:mercury:0.3.0')
|
||||
@@ -118,6 +118,7 @@ dependencies {
|
||||
exclude group: 'org.jetbrains.kotlin'
|
||||
}
|
||||
testImplementation 'org.mockito:mockito-core:5.2.0'
|
||||
testImplementation 'com.microsoft.java:com.microsoft.java.debug.core:0.46.0'
|
||||
|
||||
compileOnly 'org.jetbrains:annotations:24.0.1'
|
||||
testCompileOnly 'org.jetbrains:annotations:24.0.1'
|
||||
|
||||
@@ -57,6 +57,7 @@ import net.fabricmc.loom.configuration.providers.mappings.MappingConfiguration;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftJarConfiguration;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftSourceSets;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.mapped.AbstractMappedMinecraftProvider;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.mapped.IntermediaryMinecraftProvider;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.mapped.NamedMinecraftProvider;
|
||||
import net.fabricmc.loom.extension.MixinExtension;
|
||||
@@ -153,8 +154,8 @@ public abstract class CompileConfiguration implements Runnable {
|
||||
mappingConfiguration.applyToProject(getProject(), mappingsDep);
|
||||
|
||||
// Provide the remapped mc jars
|
||||
final IntermediaryMinecraftProvider<?> intermediaryMinecraftProvider = jarConfiguration.getIntermediaryMinecraftProviderBiFunction().apply(configContext, minecraftProvider);
|
||||
NamedMinecraftProvider<?> namedMinecraftProvider = jarConfiguration.getNamedMinecraftProviderBiFunction().apply(configContext, minecraftProvider);
|
||||
final IntermediaryMinecraftProvider<?> intermediaryMinecraftProvider = jarConfiguration.getIntermediaryMinecraftProviderBiFunction().apply(project, minecraftProvider);
|
||||
NamedMinecraftProvider<?> namedMinecraftProvider = jarConfiguration.getNamedMinecraftProviderBiFunction().apply(project, minecraftProvider);
|
||||
|
||||
registerGameProcessors(configContext);
|
||||
MinecraftJarProcessorManager minecraftJarProcessorManager = MinecraftJarProcessorManager.create(getProject());
|
||||
@@ -164,11 +165,13 @@ public abstract class CompileConfiguration implements Runnable {
|
||||
namedMinecraftProvider = jarConfiguration.getProcessedNamedMinecraftProviderBiFunction().apply(namedMinecraftProvider, minecraftJarProcessorManager);
|
||||
}
|
||||
|
||||
final var provideContext = new AbstractMappedMinecraftProvider.ProvideContext(true, extension.refreshDeps(), configContext);
|
||||
|
||||
extension.setIntermediaryMinecraftProvider(intermediaryMinecraftProvider);
|
||||
intermediaryMinecraftProvider.provide(true);
|
||||
intermediaryMinecraftProvider.provide(provideContext);
|
||||
|
||||
extension.setNamedMinecraftProvider(namedMinecraftProvider);
|
||||
namedMinecraftProvider.provide(true);
|
||||
namedMinecraftProvider.provide(provideContext);
|
||||
}
|
||||
|
||||
private void registerGameProcessors(ConfigContext configContext) {
|
||||
|
||||
@@ -27,13 +27,14 @@ package net.fabricmc.loom.configuration.decompile;
|
||||
import java.io.File;
|
||||
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.tasks.TaskProvider;
|
||||
import org.gradle.api.artifacts.ConfigurationContainer;
|
||||
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.configuration.ConfigContext;
|
||||
import net.fabricmc.loom.configuration.providers.mappings.MappingConfiguration;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.mapped.MappedMinecraftProvider;
|
||||
import net.fabricmc.loom.task.UnpickJarTask;
|
||||
import net.fabricmc.loom.task.GenerateSourcesTask;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
|
||||
public abstract class DecompileConfiguration<T extends MappedMinecraftProvider> {
|
||||
protected final Project project;
|
||||
@@ -50,11 +51,13 @@ public abstract class DecompileConfiguration<T extends MappedMinecraftProvider>
|
||||
|
||||
public abstract void afterEvaluation();
|
||||
|
||||
protected final TaskProvider<UnpickJarTask> createUnpickJarTask(String name, File inputJar, File outputJar) {
|
||||
return project.getTasks().register(name, UnpickJarTask.class, unpickJarTask -> {
|
||||
unpickJarTask.getUnpickDefinitions().set(mappingConfiguration.getUnpickDefinitionsFile());
|
||||
unpickJarTask.getInputJar().set(inputJar);
|
||||
unpickJarTask.getOutputJar().set(outputJar);
|
||||
});
|
||||
protected final void configureUnpick(GenerateSourcesTask task, File unpickOutputJar) {
|
||||
final ConfigurationContainer configurations = task.getProject().getConfigurations();
|
||||
|
||||
task.getUnpickDefinitions().set(mappingConfiguration.getUnpickDefinitionsFile());
|
||||
task.getUnpickOutputJar().set(unpickOutputJar);
|
||||
task.getUnpickConstantJar().setFrom(configurations.getByName(Constants.Configurations.MAPPING_CONSTANTS));
|
||||
task.getUnpickClasspath().setFrom(configurations.getByName(Constants.Configurations.MINECRAFT_COMPILE_LIBRARIES));
|
||||
task.getUnpickClasspath().from(configurations.getByName(Constants.Configurations.MOD_COMPILE_CLASSPATH_MAPPED));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,11 +25,11 @@
|
||||
package net.fabricmc.loom.configuration.decompile;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.configuration.ConfigContext;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftJar;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.mapped.MappedMinecraftProvider;
|
||||
import net.fabricmc.loom.task.GenerateSourcesTask;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
@@ -41,36 +41,25 @@ public class SingleJarDecompileConfiguration extends DecompileConfiguration<Mapp
|
||||
|
||||
@Override
|
||||
public final void afterEvaluation() {
|
||||
List<Path> minecraftJars = minecraftProvider.getMinecraftJarPaths();
|
||||
final List<MinecraftJar> minecraftJars = minecraftProvider.getMinecraftJars();
|
||||
assert minecraftJars.size() == 1;
|
||||
|
||||
final File namedJar = minecraftJars.get(0).toFile();
|
||||
|
||||
File mappedJar = namedJar;
|
||||
|
||||
if (mappingConfiguration.hasUnpickDefinitions()) {
|
||||
File outputJar = new File(extension.getMappingConfiguration().mappingsWorkingDir().toFile(), "minecraft-unpicked.jar");
|
||||
createUnpickJarTask("unpickJar", namedJar, outputJar);
|
||||
|
||||
mappedJar = outputJar;
|
||||
}
|
||||
|
||||
final File inputJar = mappedJar;
|
||||
final MinecraftJar minecraftJar = minecraftJars.get(0);
|
||||
|
||||
LoomGradleExtension.get(project).getDecompilerOptions().forEach(options -> {
|
||||
final String decompilerName = options.getFormattedName();
|
||||
String taskName = "genSourcesWith" + decompilerName;
|
||||
// Decompiler will be passed to the constructor of GenerateSourcesTask
|
||||
project.getTasks().register(taskName, GenerateSourcesTask.class, options).configure(task -> {
|
||||
task.getInputJar().set(inputJar);
|
||||
task.getRuntimeJar().set(namedJar);
|
||||
task.getInputJarName().set(minecraftJar.getName());
|
||||
task.getOutputJar().fileValue(GenerateSourcesTask.getMappedJarFileWithSuffix("-sources.jar", minecraftJar.getPath()));
|
||||
|
||||
task.dependsOn(project.getTasks().named("validateAccessWidener"));
|
||||
task.setDescription("Decompile minecraft using %s.".formatted(decompilerName));
|
||||
task.setGroup(Constants.TaskGroup.FABRIC);
|
||||
|
||||
if (mappingConfiguration.hasUnpickDefinitions()) {
|
||||
task.dependsOn(project.getTasks().named("unpickJar"));
|
||||
final File outputJar = new File(extension.getMappingConfiguration().mappingsWorkingDir().toFile(), "minecraft-unpicked.jar");
|
||||
configureUnpick(task, outputJar);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -32,9 +32,9 @@ import org.gradle.api.tasks.TaskProvider;
|
||||
|
||||
import net.fabricmc.loom.api.decompilers.DecompilerOptions;
|
||||
import net.fabricmc.loom.configuration.ConfigContext;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftJar;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.mapped.MappedMinecraftProvider;
|
||||
import net.fabricmc.loom.task.GenerateSourcesTask;
|
||||
import net.fabricmc.loom.task.UnpickJarTask;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
|
||||
public final class SplitDecompileConfiguration extends DecompileConfiguration<MappedMinecraftProvider.Split> {
|
||||
@@ -44,41 +44,26 @@ public final class SplitDecompileConfiguration extends DecompileConfiguration<Ma
|
||||
|
||||
@Override
|
||||
public void afterEvaluation() {
|
||||
File commonJarToDecompile = minecraftProvider.getCommonJar().toFile();
|
||||
File clientOnlyJarToDecompile = minecraftProvider.getClientOnlyJar().toFile();
|
||||
|
||||
TaskProvider<UnpickJarTask> unpickCommonJar = null;
|
||||
TaskProvider<UnpickJarTask> unpickClientOnlyJar = null;
|
||||
|
||||
if (mappingConfiguration.hasUnpickDefinitions()) {
|
||||
commonJarToDecompile = new File(extension.getMappingConfiguration().mappingsWorkingDir().toFile(), "minecraft-common-unpicked.jar");
|
||||
clientOnlyJarToDecompile = new File(extension.getMappingConfiguration().mappingsWorkingDir().toFile(), "minecraft-clientonly-unpicked.jar");
|
||||
|
||||
unpickCommonJar = createUnpickJarTask("unpickCommonJar", minecraftProvider.getCommonJar().toFile(), commonJarToDecompile);
|
||||
unpickClientOnlyJar = createUnpickJarTask("unpickClientOnlyJar", minecraftProvider.getClientOnlyJar().toFile(), clientOnlyJarToDecompile);
|
||||
}
|
||||
|
||||
// Need to re-declare them as final to access them from the lambada
|
||||
final File commonJar = commonJarToDecompile;
|
||||
final File clientOnlyJar = clientOnlyJarToDecompile;
|
||||
final TaskProvider<UnpickJarTask> unpickCommonJarTask = unpickCommonJar;
|
||||
final TaskProvider<UnpickJarTask> unpickClientOnlyJarTask = unpickClientOnlyJar;
|
||||
final MinecraftJar commonJar = minecraftProvider.getCommonJar();
|
||||
final MinecraftJar clientOnlyJar = minecraftProvider.getClientOnlyJar();
|
||||
|
||||
final TaskProvider<Task> commonDecompileTask = createDecompileTasks("Common", task -> {
|
||||
task.getInputJar().set(commonJar);
|
||||
task.getRuntimeJar().set(minecraftProvider.getCommonJar().toFile());
|
||||
task.getInputJarName().set(commonJar.getName());
|
||||
task.getOutputJar().fileValue(GenerateSourcesTask.getMappedJarFileWithSuffix("-sources.jar", commonJar.getPath()));
|
||||
|
||||
if (unpickCommonJarTask != null) {
|
||||
task.dependsOn(unpickCommonJarTask);
|
||||
if (mappingConfiguration.hasUnpickDefinitions()) {
|
||||
File unpickJar = new File(extension.getMappingConfiguration().mappingsWorkingDir().toFile(), "minecraft-common-unpicked.jar");
|
||||
configureUnpick(task, unpickJar);
|
||||
}
|
||||
});
|
||||
|
||||
final TaskProvider<Task> clientOnlyDecompileTask = createDecompileTasks("ClientOnly", task -> {
|
||||
task.getInputJar().set(clientOnlyJar);
|
||||
task.getRuntimeJar().set(minecraftProvider.getClientOnlyJar().toFile());
|
||||
task.getInputJarName().set(clientOnlyJar.getName());
|
||||
task.getOutputJar().fileValue(GenerateSourcesTask.getMappedJarFileWithSuffix("-sources.jar", clientOnlyJar.getPath()));
|
||||
|
||||
if (unpickCommonJarTask != null) {
|
||||
task.dependsOn(unpickClientOnlyJarTask);
|
||||
if (mappingConfiguration.hasUnpickDefinitions()) {
|
||||
File unpickJar = new File(extension.getMappingConfiguration().mappingsWorkingDir().toFile(), "minecraft-clientonly-unpicked.jar");
|
||||
configureUnpick(task, unpickJar);
|
||||
}
|
||||
|
||||
// Don't allow them to run at the same time.
|
||||
|
||||
@@ -28,6 +28,8 @@ import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.gradle.api.Project;
|
||||
|
||||
import net.fabricmc.loom.configuration.ConfigContext;
|
||||
import net.fabricmc.loom.configuration.decompile.DecompileConfiguration;
|
||||
import net.fabricmc.loom.configuration.decompile.SingleJarDecompileConfiguration;
|
||||
@@ -73,8 +75,8 @@ public enum MinecraftJarConfiguration {
|
||||
);
|
||||
|
||||
private final Function<ConfigContext, MinecraftProvider> minecraftProviderFunction;
|
||||
private final BiFunction<ConfigContext, MinecraftProvider, IntermediaryMinecraftProvider<?>> intermediaryMinecraftProviderBiFunction;
|
||||
private final BiFunction<ConfigContext, MinecraftProvider, NamedMinecraftProvider<?>> namedMinecraftProviderBiFunction;
|
||||
private final BiFunction<Project, MinecraftProvider, IntermediaryMinecraftProvider<?>> intermediaryMinecraftProviderBiFunction;
|
||||
private final BiFunction<Project, MinecraftProvider, NamedMinecraftProvider<?>> namedMinecraftProviderBiFunction;
|
||||
private final BiFunction<NamedMinecraftProvider<?>, MinecraftJarProcessorManager, ProcessedNamedMinecraftProvider<?, ?>> processedNamedMinecraftProviderBiFunction;
|
||||
private final BiFunction<ConfigContext, MappedMinecraftProvider, DecompileConfiguration<?>> decompileConfigurationBiFunction;
|
||||
private final List<String> supportedEnvironments;
|
||||
@@ -82,15 +84,15 @@ public enum MinecraftJarConfiguration {
|
||||
@SuppressWarnings("unchecked") // Just a bit of a generic mess :)
|
||||
<M extends MinecraftProvider, P extends NamedMinecraftProvider<M>, Q extends MappedMinecraftProvider> MinecraftJarConfiguration(
|
||||
Function<ConfigContext, M> minecraftProviderFunction,
|
||||
BiFunction<ConfigContext, M, IntermediaryMinecraftProvider<M>> intermediaryMinecraftProviderBiFunction,
|
||||
BiFunction<ConfigContext, M, P> namedMinecraftProviderBiFunction,
|
||||
BiFunction<Project, M, IntermediaryMinecraftProvider<M>> intermediaryMinecraftProviderBiFunction,
|
||||
BiFunction<Project, M, P> namedMinecraftProviderBiFunction,
|
||||
BiFunction<P, MinecraftJarProcessorManager, ProcessedNamedMinecraftProvider<M, P>> processedNamedMinecraftProviderBiFunction,
|
||||
BiFunction<ConfigContext, Q, DecompileConfiguration<?>> decompileConfigurationBiFunction,
|
||||
List<String> supportedEnvironments
|
||||
) {
|
||||
this.minecraftProviderFunction = (Function<ConfigContext, MinecraftProvider>) minecraftProviderFunction;
|
||||
this.intermediaryMinecraftProviderBiFunction = (BiFunction<ConfigContext, MinecraftProvider, IntermediaryMinecraftProvider<?>>) (Object) intermediaryMinecraftProviderBiFunction;
|
||||
this.namedMinecraftProviderBiFunction = (BiFunction<ConfigContext, MinecraftProvider, NamedMinecraftProvider<?>>) namedMinecraftProviderBiFunction;
|
||||
this.intermediaryMinecraftProviderBiFunction = (BiFunction<Project, MinecraftProvider, IntermediaryMinecraftProvider<?>>) (Object) intermediaryMinecraftProviderBiFunction;
|
||||
this.namedMinecraftProviderBiFunction = (BiFunction<Project, MinecraftProvider, NamedMinecraftProvider<?>>) namedMinecraftProviderBiFunction;
|
||||
this.processedNamedMinecraftProviderBiFunction = (BiFunction<NamedMinecraftProvider<?>, MinecraftJarProcessorManager, ProcessedNamedMinecraftProvider<?, ?>>) (Object) processedNamedMinecraftProviderBiFunction;
|
||||
this.decompileConfigurationBiFunction = (BiFunction<ConfigContext, MappedMinecraftProvider, DecompileConfiguration<?>>) decompileConfigurationBiFunction;
|
||||
this.supportedEnvironments = supportedEnvironments;
|
||||
@@ -100,11 +102,11 @@ public enum MinecraftJarConfiguration {
|
||||
return minecraftProviderFunction;
|
||||
}
|
||||
|
||||
public BiFunction<ConfigContext, MinecraftProvider, IntermediaryMinecraftProvider<?>> getIntermediaryMinecraftProviderBiFunction() {
|
||||
public BiFunction<Project, MinecraftProvider, IntermediaryMinecraftProvider<?>> getIntermediaryMinecraftProviderBiFunction() {
|
||||
return intermediaryMinecraftProviderBiFunction;
|
||||
}
|
||||
|
||||
public BiFunction<ConfigContext, MinecraftProvider, NamedMinecraftProvider<?>> getNamedMinecraftProviderBiFunction() {
|
||||
public BiFunction<Project, MinecraftProvider, NamedMinecraftProvider<?>> getNamedMinecraftProviderBiFunction() {
|
||||
return namedMinecraftProviderBiFunction;
|
||||
}
|
||||
|
||||
|
||||
@@ -53,13 +53,13 @@ import net.fabricmc.tinyremapper.TinyRemapper;
|
||||
|
||||
public abstract class AbstractMappedMinecraftProvider<M extends MinecraftProvider> implements MappedMinecraftProvider.ProviderImpl {
|
||||
protected final M minecraftProvider;
|
||||
protected final ConfigContext configContext;
|
||||
private final Project project;
|
||||
protected final LoomGradleExtension extension;
|
||||
|
||||
public AbstractMappedMinecraftProvider(ConfigContext configContext, M minecraftProvider) {
|
||||
this.configContext = configContext;
|
||||
public AbstractMappedMinecraftProvider(Project project, M minecraftProvider) {
|
||||
this.minecraftProvider = minecraftProvider;
|
||||
this.extension = configContext.extension();
|
||||
this.project = project;
|
||||
this.extension = LoomGradleExtension.get(project);
|
||||
}
|
||||
|
||||
public abstract MappingsNamespace getTargetNamespace();
|
||||
@@ -70,13 +70,13 @@ public abstract class AbstractMappedMinecraftProvider<M extends MinecraftProvide
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
public void provide(boolean applyDependencies) throws Exception {
|
||||
public List<MinecraftJar> provide(ProvideContext context) throws Exception {
|
||||
final List<RemappedJars> remappedJars = getRemappedJars();
|
||||
assert !remappedJars.isEmpty();
|
||||
|
||||
if (!areOutputsValid(remappedJars) || extension.refreshDeps()) {
|
||||
if (!areOutputsValid(remappedJars) || context.refreshOutputs()) {
|
||||
try {
|
||||
remapInputs(remappedJars);
|
||||
remapInputs(remappedJars, context.configContext());
|
||||
} catch (Throwable t) {
|
||||
cleanOutputs(remappedJars);
|
||||
|
||||
@@ -84,17 +84,25 @@ public abstract class AbstractMappedMinecraftProvider<M extends MinecraftProvide
|
||||
}
|
||||
}
|
||||
|
||||
if (applyDependencies) {
|
||||
if (context.applyDependencies()) {
|
||||
final List<String> dependencyTargets = getDependencyTargets();
|
||||
|
||||
if (dependencyTargets.isEmpty()) {
|
||||
return;
|
||||
if (!dependencyTargets.isEmpty()) {
|
||||
MinecraftSourceSets.get(getProject()).applyDependencies(
|
||||
(configuration, name) -> getProject().getDependencies().add(configuration, getDependencyNotation(name)),
|
||||
dependencyTargets
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
MinecraftSourceSets.get(getProject()).applyDependencies(
|
||||
(configuration, name) -> getProject().getDependencies().add(configuration, getDependencyNotation(name)),
|
||||
dependencyTargets
|
||||
);
|
||||
return remappedJars.stream()
|
||||
.map(RemappedJars::outputJar)
|
||||
.toList();
|
||||
}
|
||||
|
||||
public record ProvideContext(boolean applyDependencies, boolean refreshOutputs, ConfigContext configContext) {
|
||||
ProvideContext withApplyDependencies(boolean applyDependencies) {
|
||||
return new ProvideContext(applyDependencies, refreshOutputs(), configContext());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,15 +162,15 @@ public abstract class AbstractMappedMinecraftProvider<M extends MinecraftProvide
|
||||
return true;
|
||||
}
|
||||
|
||||
private void remapInputs(List<RemappedJars> remappedJars) throws IOException {
|
||||
private void remapInputs(List<RemappedJars> remappedJars, ConfigContext configContext) throws IOException {
|
||||
cleanOutputs(remappedJars);
|
||||
|
||||
for (RemappedJars remappedJar : remappedJars) {
|
||||
remapJar(remappedJar);
|
||||
remapJar(remappedJar, configContext);
|
||||
}
|
||||
}
|
||||
|
||||
private void remapJar(RemappedJars remappedJars) throws IOException {
|
||||
private void remapJar(RemappedJars remappedJars, ConfigContext configContext) throws IOException {
|
||||
final MappingConfiguration mappingConfiguration = extension.getMappingConfiguration();
|
||||
final String fromM = remappedJars.sourceNamespace().toString();
|
||||
final String toM = getTargetNamespace().toString();
|
||||
@@ -214,12 +222,8 @@ public abstract class AbstractMappedMinecraftProvider<M extends MinecraftProvide
|
||||
}
|
||||
}
|
||||
|
||||
public ConfigContext getConfigContext() {
|
||||
return configContext;
|
||||
}
|
||||
|
||||
public Project getProject() {
|
||||
return getConfigContext().project();
|
||||
return project;
|
||||
}
|
||||
|
||||
public M getMinecraftProvider() {
|
||||
|
||||
@@ -26,8 +26,9 @@ package net.fabricmc.loom.configuration.providers.minecraft.mapped;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.gradle.api.Project;
|
||||
|
||||
import net.fabricmc.loom.api.mappings.layered.MappingsNamespace;
|
||||
import net.fabricmc.loom.configuration.ConfigContext;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.MergedMinecraftProvider;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.SingleJarEnvType;
|
||||
@@ -36,8 +37,8 @@ import net.fabricmc.loom.configuration.providers.minecraft.SplitMinecraftProvide
|
||||
import net.fabricmc.tinyremapper.TinyRemapper;
|
||||
|
||||
public abstract sealed class IntermediaryMinecraftProvider<M extends MinecraftProvider> extends AbstractMappedMinecraftProvider<M> permits IntermediaryMinecraftProvider.MergedImpl, IntermediaryMinecraftProvider.SingleJarImpl, IntermediaryMinecraftProvider.SplitImpl {
|
||||
public IntermediaryMinecraftProvider(ConfigContext configContext, M minecraftProvider) {
|
||||
super(configContext, minecraftProvider);
|
||||
public IntermediaryMinecraftProvider(Project project, M minecraftProvider) {
|
||||
super(project, minecraftProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -51,8 +52,8 @@ public abstract sealed class IntermediaryMinecraftProvider<M extends MinecraftPr
|
||||
}
|
||||
|
||||
public static final class MergedImpl extends IntermediaryMinecraftProvider<MergedMinecraftProvider> implements Merged {
|
||||
public MergedImpl(ConfigContext configContext, MergedMinecraftProvider minecraftProvider) {
|
||||
super(configContext, minecraftProvider);
|
||||
public MergedImpl(Project project, MergedMinecraftProvider minecraftProvider) {
|
||||
super(project, minecraftProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -64,8 +65,8 @@ public abstract sealed class IntermediaryMinecraftProvider<M extends MinecraftPr
|
||||
}
|
||||
|
||||
public static final class SplitImpl extends IntermediaryMinecraftProvider<SplitMinecraftProvider> implements Split {
|
||||
public SplitImpl(ConfigContext configContext, SplitMinecraftProvider minecraftProvider) {
|
||||
super(configContext, minecraftProvider);
|
||||
public SplitImpl(Project project, SplitMinecraftProvider minecraftProvider) {
|
||||
super(project, minecraftProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -85,17 +86,17 @@ public abstract sealed class IntermediaryMinecraftProvider<M extends MinecraftPr
|
||||
public static final class SingleJarImpl extends IntermediaryMinecraftProvider<SingleJarMinecraftProvider> implements SingleJar {
|
||||
private final SingleJarEnvType env;
|
||||
|
||||
private SingleJarImpl(ConfigContext configContext, SingleJarMinecraftProvider minecraftProvider, SingleJarEnvType env) {
|
||||
super(configContext, minecraftProvider);
|
||||
private SingleJarImpl(Project project, SingleJarMinecraftProvider minecraftProvider, SingleJarEnvType env) {
|
||||
super(project, minecraftProvider);
|
||||
this.env = env;
|
||||
}
|
||||
|
||||
public static SingleJarImpl server(ConfigContext configContext, SingleJarMinecraftProvider minecraftProvider) {
|
||||
return new SingleJarImpl(configContext, minecraftProvider, SingleJarEnvType.SERVER);
|
||||
public static SingleJarImpl server(Project project, SingleJarMinecraftProvider minecraftProvider) {
|
||||
return new SingleJarImpl(project, minecraftProvider, SingleJarEnvType.SERVER);
|
||||
}
|
||||
|
||||
public static SingleJarImpl client(ConfigContext configContext, SingleJarMinecraftProvider minecraftProvider) {
|
||||
return new SingleJarImpl(configContext, minecraftProvider, SingleJarEnvType.CLIENT);
|
||||
public static SingleJarImpl client(Project project, SingleJarMinecraftProvider minecraftProvider) {
|
||||
return new SingleJarImpl(project, minecraftProvider, SingleJarEnvType.CLIENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -26,8 +26,9 @@ package net.fabricmc.loom.configuration.providers.minecraft.mapped;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.gradle.api.Project;
|
||||
|
||||
import net.fabricmc.loom.api.mappings.layered.MappingsNamespace;
|
||||
import net.fabricmc.loom.configuration.ConfigContext;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.MergedMinecraftProvider;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.SingleJarEnvType;
|
||||
@@ -36,8 +37,8 @@ import net.fabricmc.loom.configuration.providers.minecraft.SplitMinecraftProvide
|
||||
import net.fabricmc.tinyremapper.TinyRemapper;
|
||||
|
||||
public abstract class NamedMinecraftProvider<M extends MinecraftProvider> extends AbstractMappedMinecraftProvider<M> {
|
||||
public NamedMinecraftProvider(ConfigContext configContext, M minecraftProvider) {
|
||||
super(configContext, minecraftProvider);
|
||||
public NamedMinecraftProvider(Project project, M minecraftProvider) {
|
||||
super(project, minecraftProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -51,8 +52,8 @@ public abstract class NamedMinecraftProvider<M extends MinecraftProvider> extend
|
||||
}
|
||||
|
||||
public static final class MergedImpl extends NamedMinecraftProvider<MergedMinecraftProvider> implements Merged {
|
||||
public MergedImpl(ConfigContext configContext, MergedMinecraftProvider minecraftProvider) {
|
||||
super(configContext, minecraftProvider);
|
||||
public MergedImpl(Project project, MergedMinecraftProvider minecraftProvider) {
|
||||
super(project, minecraftProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -69,8 +70,8 @@ public abstract class NamedMinecraftProvider<M extends MinecraftProvider> extend
|
||||
}
|
||||
|
||||
public static final class SplitImpl extends NamedMinecraftProvider<SplitMinecraftProvider> implements Split {
|
||||
public SplitImpl(ConfigContext configContext, SplitMinecraftProvider minecraftProvider) {
|
||||
super(configContext, minecraftProvider);
|
||||
public SplitImpl(Project project, SplitMinecraftProvider minecraftProvider) {
|
||||
super(project, minecraftProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -95,17 +96,17 @@ public abstract class NamedMinecraftProvider<M extends MinecraftProvider> extend
|
||||
public static final class SingleJarImpl extends NamedMinecraftProvider<SingleJarMinecraftProvider> implements SingleJar {
|
||||
private final SingleJarEnvType env;
|
||||
|
||||
private SingleJarImpl(ConfigContext configContext, SingleJarMinecraftProvider minecraftProvider, SingleJarEnvType env) {
|
||||
super(configContext, minecraftProvider);
|
||||
private SingleJarImpl(Project project, SingleJarMinecraftProvider minecraftProvider, SingleJarEnvType env) {
|
||||
super(project, minecraftProvider);
|
||||
this.env = env;
|
||||
}
|
||||
|
||||
public static SingleJarImpl server(ConfigContext configContext, SingleJarMinecraftProvider minecraftProvider) {
|
||||
return new SingleJarImpl(configContext, minecraftProvider, SingleJarEnvType.SERVER);
|
||||
public static SingleJarImpl server(Project project, SingleJarMinecraftProvider minecraftProvider) {
|
||||
return new SingleJarImpl(project, minecraftProvider, SingleJarEnvType.SERVER);
|
||||
}
|
||||
|
||||
public static SingleJarImpl client(ConfigContext configContext, SingleJarMinecraftProvider minecraftProvider) {
|
||||
return new SingleJarImpl(configContext, minecraftProvider, SingleJarEnvType.CLIENT);
|
||||
public static SingleJarImpl client(Project project, SingleJarMinecraftProvider minecraftProvider) {
|
||||
return new SingleJarImpl(project, minecraftProvider, SingleJarEnvType.CLIENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -29,10 +29,14 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.gradle.api.Project;
|
||||
|
||||
import net.fabricmc.loom.configuration.ConfigContext;
|
||||
import net.fabricmc.loom.configuration.mods.dependency.LocalMavenHelper;
|
||||
import net.fabricmc.loom.configuration.processors.MinecraftJarProcessorManager;
|
||||
import net.fabricmc.loom.configuration.processors.ProcessorContextImpl;
|
||||
@@ -49,26 +53,31 @@ public abstract class ProcessedNamedMinecraftProvider<M extends MinecraftProvide
|
||||
private final MinecraftJarProcessorManager jarProcessorManager;
|
||||
|
||||
public ProcessedNamedMinecraftProvider(P parentMinecraftProvide, MinecraftJarProcessorManager jarProcessorManager) {
|
||||
super(parentMinecraftProvide.getConfigContext(), parentMinecraftProvide.getMinecraftProvider());
|
||||
super(parentMinecraftProvide.getProject(), parentMinecraftProvide.getMinecraftProvider());
|
||||
this.parentMinecraftProvider = parentMinecraftProvide;
|
||||
this.jarProcessorManager = Objects.requireNonNull(jarProcessorManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void provide(boolean applyDependencies) throws Exception {
|
||||
parentMinecraftProvider.provide(false);
|
||||
public List<MinecraftJar> provide(ProvideContext context) throws Exception {
|
||||
parentMinecraftProvider.provide(context.withApplyDependencies(false));
|
||||
|
||||
boolean requiresProcessing = parentMinecraftProvider.getMinecraftJars().stream()
|
||||
boolean requiresProcessing = context.refreshOutputs() || parentMinecraftProvider.getMinecraftJars().stream()
|
||||
.map(this::getProcessedPath)
|
||||
.anyMatch(jarProcessorManager::requiresProcessingJar);
|
||||
|
||||
final Map<MinecraftJar, MinecraftJar> minecraftJarOutputMap = parentMinecraftProvider.getMinecraftJars().stream()
|
||||
.collect(Collectors.toMap(Function.identity(), this::getProcessedJar));
|
||||
|
||||
if (requiresProcessing) {
|
||||
processJars();
|
||||
processJars(minecraftJarOutputMap, context.configContext());
|
||||
}
|
||||
|
||||
if (applyDependencies) {
|
||||
if (context.applyDependencies()) {
|
||||
applyDependencies();
|
||||
}
|
||||
|
||||
return List.copyOf(minecraftJarOutputMap.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -76,14 +85,17 @@ public abstract class ProcessedNamedMinecraftProvider<M extends MinecraftProvide
|
||||
return MavenScope.LOCAL;
|
||||
}
|
||||
|
||||
private void processJars() throws IOException {
|
||||
for (MinecraftJar minecraftJar : parentMinecraftProvider.getMinecraftJars()) {
|
||||
final MinecraftJar outputJar = getProcessedJar(minecraftJar);
|
||||
private void processJars(Map<MinecraftJar, MinecraftJar> minecraftJarMap, ConfigContext configContext) throws IOException {
|
||||
for (Map.Entry<MinecraftJar, MinecraftJar> entry : minecraftJarMap.entrySet()) {
|
||||
final MinecraftJar minecraftJar = entry.getKey();
|
||||
final MinecraftJar outputJar = entry.getValue();
|
||||
deleteSimilarJars(outputJar.getPath());
|
||||
|
||||
final LocalMavenHelper mavenHelper = getMavenHelper(minecraftJar.getName());
|
||||
final Path outputPath = mavenHelper.copyToMaven(minecraftJar.getPath(), null);
|
||||
|
||||
assert outputJar.getPath().equals(outputPath);
|
||||
|
||||
jarProcessorManager.processJar(outputPath, new ProcessorContextImpl(configContext, minecraftJar));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ package net.fabricmc.loom.task;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.io.Writer;
|
||||
@@ -49,10 +50,13 @@ import org.gradle.api.file.ConfigurableFileCollection;
|
||||
import org.gradle.api.file.RegularFileProperty;
|
||||
import org.gradle.api.provider.Property;
|
||||
import org.gradle.api.services.ServiceReference;
|
||||
import org.gradle.api.tasks.Input;
|
||||
import org.gradle.api.tasks.InputFile;
|
||||
import org.gradle.api.tasks.InputFiles;
|
||||
import org.gradle.api.tasks.OutputFile;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
import org.gradle.process.ExecOperations;
|
||||
import org.gradle.process.ExecResult;
|
||||
import org.gradle.work.DisableCachingByDefault;
|
||||
import org.gradle.workers.WorkAction;
|
||||
import org.gradle.workers.WorkParameters;
|
||||
@@ -68,6 +72,8 @@ import net.fabricmc.loom.api.mappings.layered.MappingsNamespace;
|
||||
import net.fabricmc.loom.configuration.ConfigContextImpl;
|
||||
import net.fabricmc.loom.configuration.processors.MappingProcessorContextImpl;
|
||||
import net.fabricmc.loom.configuration.processors.MinecraftJarProcessorManager;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftJar;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.mapped.AbstractMappedMinecraftProvider;
|
||||
import net.fabricmc.loom.decompilers.LineNumberRemapper;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
import net.fabricmc.loom.util.FileSystemUtil;
|
||||
@@ -90,16 +96,10 @@ public abstract class GenerateSourcesTask extends AbstractLoomTask {
|
||||
private final DecompilerOptions decompilerOptions;
|
||||
|
||||
/**
|
||||
* The jar to decompile, can be the unpick jar.
|
||||
* The jar name to decompile, {@link MinecraftJar#getName()}.
|
||||
*/
|
||||
@InputFile
|
||||
public abstract RegularFileProperty getInputJar();
|
||||
|
||||
/**
|
||||
* The jar used at runtime.
|
||||
*/
|
||||
@InputFile
|
||||
public abstract RegularFileProperty getRuntimeJar();
|
||||
@Input
|
||||
public abstract Property<String> getInputJarName();
|
||||
|
||||
@InputFiles
|
||||
public abstract ConfigurableFileCollection getClasspath();
|
||||
@@ -107,9 +107,26 @@ public abstract class GenerateSourcesTask extends AbstractLoomTask {
|
||||
@OutputFile
|
||||
public abstract RegularFileProperty getOutputJar();
|
||||
|
||||
// Unpick
|
||||
@InputFile
|
||||
public abstract RegularFileProperty getUnpickDefinitions();
|
||||
|
||||
@InputFiles
|
||||
public abstract ConfigurableFileCollection getUnpickConstantJar();
|
||||
|
||||
@InputFiles
|
||||
public abstract ConfigurableFileCollection getUnpickClasspath();
|
||||
|
||||
@OutputFile
|
||||
public abstract RegularFileProperty getUnpickOutputJar();
|
||||
|
||||
// Injects
|
||||
@Inject
|
||||
public abstract WorkerExecutor getWorkerExecutor();
|
||||
|
||||
@Inject
|
||||
public abstract ExecOperations getExecOperations();
|
||||
|
||||
@Inject
|
||||
public abstract WorkerDaemonClientsManager getWorkerDaemonClientsManager();
|
||||
|
||||
@@ -124,8 +141,6 @@ public abstract class GenerateSourcesTask extends AbstractLoomTask {
|
||||
getOutputs().upToDateWhen((o) -> false);
|
||||
getClasspath().from(decompilerOptions.getClasspath()).finalizeValueOnRead();
|
||||
dependsOn(decompilerOptions.getClasspath().getBuiltBy());
|
||||
|
||||
getOutputJar().fileProvider(getProject().provider(() -> getMappedJarFileWithSuffix("-sources.jar")));
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
@@ -136,10 +151,20 @@ public abstract class GenerateSourcesTask extends AbstractLoomTask {
|
||||
throw new UnsupportedOperationException("GenSources task requires a 64bit JVM to run due to the memory requirements.");
|
||||
}
|
||||
|
||||
final MinecraftJar minecraftJar = rebuildInputJar();
|
||||
// Input jar is the jar to decompile, this may be unpicked.
|
||||
Path inputJar = minecraftJar.getPath();
|
||||
// Runtime jar is the jar used to run the game
|
||||
final Path runtimeJar = inputJar;
|
||||
|
||||
if (getUnpickDefinitions().isPresent()) {
|
||||
inputJar = unpickJar(inputJar);
|
||||
}
|
||||
|
||||
if (!platform.supportsUnixDomainSockets()) {
|
||||
getProject().getLogger().warn("Decompile worker logging disabled as Unix Domain Sockets is not supported on your operating system.");
|
||||
|
||||
doWork(null);
|
||||
doWork(null, inputJar, runtimeJar);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -149,7 +174,7 @@ public abstract class GenerateSourcesTask extends AbstractLoomTask {
|
||||
|
||||
try (ThreadedProgressLoggerConsumer loggerConsumer = new ThreadedProgressLoggerConsumer(getProject(), decompilerOptions.getName(), "Decompiling minecraft sources");
|
||||
IPCServer logReceiver = new IPCServer(ipcPath, loggerConsumer)) {
|
||||
doWork(logReceiver);
|
||||
doWork(logReceiver, inputJar, runtimeJar);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException("Failed to shutdown log receiver", e);
|
||||
} finally {
|
||||
@@ -157,18 +182,94 @@ public abstract class GenerateSourcesTask extends AbstractLoomTask {
|
||||
}
|
||||
}
|
||||
|
||||
private void doWork(@Nullable IPCServer ipcServer) {
|
||||
// Re-run the named minecraft provider to give us a fresh jar to decompile.
|
||||
// This prevents re-applying line maps on an existing jar.
|
||||
private MinecraftJar rebuildInputJar() {
|
||||
final List<MinecraftJar> minecraftJars;
|
||||
|
||||
try (var serviceManager = new ScopedSharedServiceManager()) {
|
||||
final var configContext = new ConfigContextImpl(getProject(), serviceManager, getExtension());
|
||||
final var provideContext = new AbstractMappedMinecraftProvider.ProvideContext(false, true, configContext);
|
||||
minecraftJars = getExtension().getNamedMinecraftProvider().provide(provideContext);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to rebuild input jars", e);
|
||||
}
|
||||
|
||||
for (MinecraftJar minecraftJar : minecraftJars) {
|
||||
if (minecraftJar.getName().equals(getInputJarName().get())) {
|
||||
return minecraftJar;
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Could not find minecraft jar (%s) but got (%s)".formatted(
|
||||
getInputJarName().get(),
|
||||
minecraftJars.stream().map(MinecraftJar::getName).collect(Collectors.joining(", ")))
|
||||
);
|
||||
}
|
||||
|
||||
private Path unpickJar(Path inputJar) {
|
||||
final Path outputJar = getUnpickOutputJar().get().getAsFile().toPath();
|
||||
final List<String> args = getUnpickArgs(inputJar, outputJar);
|
||||
|
||||
ExecResult result = getExecOperations().javaexec(spec -> {
|
||||
spec.getMainClass().set("daomephsta.unpick.cli.Main");
|
||||
spec.classpath(getProject().getConfigurations().getByName(Constants.Configurations.UNPICK_CLASSPATH));
|
||||
spec.args(args);
|
||||
spec.systemProperty("java.util.logging.config.file", writeUnpickLogConfig().getAbsolutePath());
|
||||
});
|
||||
|
||||
result.rethrowFailure();
|
||||
|
||||
return outputJar;
|
||||
}
|
||||
|
||||
private List<String> getUnpickArgs(Path inputJar, Path outputJar) {
|
||||
var fileArgs = new ArrayList<File>();
|
||||
|
||||
fileArgs.add(inputJar.toFile());
|
||||
fileArgs.add(outputJar.toFile());
|
||||
fileArgs.add(getUnpickDefinitions().get().getAsFile());
|
||||
fileArgs.add(getUnpickConstantJar().getSingleFile());
|
||||
|
||||
// Classpath
|
||||
for (Path minecraftJar : getExtension().getMinecraftJars(MappingsNamespace.NAMED)) {
|
||||
fileArgs.add(minecraftJar.toFile());
|
||||
}
|
||||
|
||||
for (File file : getUnpickClasspath()) {
|
||||
fileArgs.add(file);
|
||||
}
|
||||
|
||||
return fileArgs.stream()
|
||||
.map(File::getAbsolutePath)
|
||||
.toList();
|
||||
}
|
||||
|
||||
private File writeUnpickLogConfig() {
|
||||
final File unpickLoggingConfigFile = getExtension().getFiles().getUnpickLoggingConfigFile();
|
||||
|
||||
try (InputStream is = GenerateSourcesTask.class.getClassLoader().getResourceAsStream("unpick-logging.properties")) {
|
||||
Files.deleteIfExists(unpickLoggingConfigFile.toPath());
|
||||
Files.copy(Objects.requireNonNull(is), unpickLoggingConfigFile.toPath());
|
||||
} catch (IOException e) {
|
||||
throw new org.gradle.api.UncheckedIOException("Failed to copy unpick logging config", e);
|
||||
}
|
||||
|
||||
return unpickLoggingConfigFile;
|
||||
}
|
||||
|
||||
private void doWork(@Nullable IPCServer ipcServer, Path inputJar, Path runtimeJar) {
|
||||
final String jvmMarkerValue = UUID.randomUUID().toString();
|
||||
final WorkQueue workQueue = createWorkQueue(jvmMarkerValue);
|
||||
|
||||
workQueue.submit(DecompileAction.class, params -> {
|
||||
params.getDecompilerOptions().set(decompilerOptions.toDto());
|
||||
|
||||
params.getInputJar().set(getInputJar());
|
||||
params.getRuntimeJar().set(getRuntimeJar());
|
||||
params.getInputJar().set(inputJar.toFile());
|
||||
params.getRuntimeJar().set(runtimeJar.toFile());
|
||||
params.getSourcesDestinationJar().set(getOutputJar());
|
||||
params.getLinemap().set(getMappedJarFileWithSuffix("-sources.lmap"));
|
||||
params.getLinemapJar().set(getMappedJarFileWithSuffix("-linemapped.jar"));
|
||||
params.getLinemap().set(getMappedJarFileWithSuffix("-sources.lmap", runtimeJar));
|
||||
params.getLinemapJar().set(getMappedJarFileWithSuffix("-linemapped.jar", runtimeJar));
|
||||
params.getMappings().set(getMappings().toFile());
|
||||
|
||||
if (ipcServer != null) {
|
||||
@@ -317,8 +418,8 @@ public abstract class GenerateSourcesTask extends AbstractLoomTask {
|
||||
}
|
||||
}
|
||||
|
||||
private File getMappedJarFileWithSuffix(String suffix) {
|
||||
String path = getRuntimeJar().get().getAsFile().getAbsolutePath();
|
||||
public static File getMappedJarFileWithSuffix(String suffix, Path runtimeJar) {
|
||||
final String path = runtimeJar.toFile().getAbsolutePath();
|
||||
|
||||
if (!path.toLowerCase(Locale.ROOT).endsWith(".jar")) {
|
||||
throw new RuntimeException("Invalid mapped JAR path: " + path);
|
||||
|
||||
@@ -1,118 +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.task;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.gradle.api.file.ConfigurableFileCollection;
|
||||
import org.gradle.api.file.RegularFileProperty;
|
||||
import org.gradle.api.tasks.InputFile;
|
||||
import org.gradle.api.tasks.InputFiles;
|
||||
import org.gradle.api.tasks.Internal;
|
||||
import org.gradle.api.tasks.JavaExec;
|
||||
import org.gradle.api.tasks.OutputFile;
|
||||
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.api.mappings.layered.MappingsNamespace;
|
||||
import net.fabricmc.loom.extension.LoomFiles;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
|
||||
public abstract class UnpickJarTask extends JavaExec {
|
||||
@InputFile
|
||||
public abstract RegularFileProperty getInputJar();
|
||||
|
||||
@InputFile
|
||||
public abstract RegularFileProperty getUnpickDefinitions();
|
||||
|
||||
@InputFiles
|
||||
// Only 1 file, but it comes from a configuration
|
||||
public abstract ConfigurableFileCollection getConstantJar();
|
||||
|
||||
@InputFiles
|
||||
public abstract ConfigurableFileCollection getUnpickClasspath();
|
||||
|
||||
@OutputFile
|
||||
public abstract RegularFileProperty getOutputJar();
|
||||
|
||||
@Inject
|
||||
public UnpickJarTask() {
|
||||
classpath(getProject().getConfigurations().getByName(Constants.Configurations.UNPICK_CLASSPATH));
|
||||
getMainClass().set("daomephsta.unpick.cli.Main");
|
||||
|
||||
getConstantJar().setFrom(getProject().getConfigurations().getByName(Constants.Configurations.MAPPING_CONSTANTS));
|
||||
getUnpickClasspath().setFrom(getProject().getConfigurations().getByName(Constants.Configurations.MINECRAFT_COMPILE_LIBRARIES));
|
||||
getUnpickClasspath().from(getProject().getConfigurations().getByName(Constants.Configurations.MOD_COMPILE_CLASSPATH_MAPPED));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exec() {
|
||||
fileArg(getInputJar().get().getAsFile(), getOutputJar().get().getAsFile(), getUnpickDefinitions().get().getAsFile());
|
||||
fileArg(getConstantJar().getSingleFile());
|
||||
|
||||
// Classpath
|
||||
for (Path minecraftJar : getExtension().getMinecraftJars(MappingsNamespace.NAMED)) {
|
||||
fileArg(minecraftJar.toFile());
|
||||
}
|
||||
|
||||
for (File file : getUnpickClasspath()) {
|
||||
fileArg(file);
|
||||
}
|
||||
|
||||
writeUnpickLogConfig();
|
||||
systemProperty("java.util.logging.config.file", getDirectories().getUnpickLoggingConfigFile().getAbsolutePath());
|
||||
|
||||
super.exec();
|
||||
}
|
||||
|
||||
private void writeUnpickLogConfig() {
|
||||
try (InputStream is = UnpickJarTask.class.getClassLoader().getResourceAsStream("unpick-logging.properties")) {
|
||||
Files.deleteIfExists(getDirectories().getUnpickLoggingConfigFile().toPath());
|
||||
Files.copy(is, getDirectories().getUnpickLoggingConfigFile().toPath());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to copy unpick logging config", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void fileArg(File... files) {
|
||||
for (File file : files) {
|
||||
args(file.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
@Internal
|
||||
protected LoomGradleExtension getExtension() {
|
||||
return LoomGradleExtension.get(getProject());
|
||||
}
|
||||
|
||||
private LoomFiles getDirectories() {
|
||||
return getExtension().getFiles();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
/*
|
||||
* This file is part of fabric-loom, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) 2023 FabricMC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package net.fabricmc.loom.test.integration
|
||||
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import java.util.concurrent.CompletionStage
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
import com.microsoft.java.debug.core.DebugUtility
|
||||
import com.microsoft.java.debug.core.IDebugSession
|
||||
import com.sun.jdi.Bootstrap
|
||||
import com.sun.jdi.event.BreakpointEvent
|
||||
import groovy.transform.CompileStatic
|
||||
import io.reactivex.Maybe
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.functions.Function
|
||||
import spock.lang.Specification
|
||||
import spock.lang.Timeout
|
||||
|
||||
import net.fabricmc.loom.test.util.GradleProjectTestTrait
|
||||
import net.fabricmc.loom.util.ZipUtils
|
||||
|
||||
import static net.fabricmc.loom.test.LoomTestConstants.PRE_RELEASE_GRADLE
|
||||
import static org.gradle.testkit.runner.TaskOutcome.SUCCESS
|
||||
|
||||
@Timeout(value = 30, unit = TimeUnit.MINUTES)
|
||||
class DebugLineNumbersTest extends Specification implements GradleProjectTestTrait {
|
||||
static final String MAPPINGS = "1.20.1-net.fabricmc.yarn.1_20_1.1.20.1+build.1-v2"
|
||||
static final Map<String, Integer> BREAKPOINTS = [
|
||||
"net.minecraft.server.dedicated.ServerPropertiesLoader": 16,
|
||||
"net.minecraft.server.dedicated.MinecraftDedicatedServer": 107,
|
||||
"net.minecraft.registry.RegistryOps": 67
|
||||
]
|
||||
|
||||
def "Debug test"() {
|
||||
setup:
|
||||
def gradle = gradleProject(project: "minimalBase", version: PRE_RELEASE_GRADLE)
|
||||
gradle.buildGradle << '''
|
||||
loom {
|
||||
// Just test with the server, no need to also decompile the client
|
||||
serverOnlyMinecraftJar()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
minecraft "com.mojang:minecraft:1.20.1"
|
||||
mappings "net.fabricmc:yarn:1.20.1+build.1:v2"
|
||||
modImplementation 'net.fabricmc:fabric-loader:0.14.21'
|
||||
}
|
||||
|
||||
runServer {
|
||||
debugOptions {
|
||||
enabled = true
|
||||
port = 8050
|
||||
host = "*"
|
||||
server = true
|
||||
suspend = true
|
||||
}
|
||||
}
|
||||
'''
|
||||
when:
|
||||
// First generate sources
|
||||
def genSources = gradle.run(task: "genSources")
|
||||
genSources.task(":genSources").outcome == SUCCESS
|
||||
|
||||
// Print out the source of the file
|
||||
def lines = getClassSource(gradle, "net/minecraft/server/dedicated/ServerPropertiesLoader.java").lines().toList()
|
||||
int l = 1
|
||||
for (final def line in lines) {
|
||||
//println(l++ + ": " + line)
|
||||
}
|
||||
|
||||
// I agree
|
||||
def runDir = new File(gradle.projectDir, "run")
|
||||
runDir.mkdirs()
|
||||
new File(runDir, "eula.txt") << "eula=true"
|
||||
new File(runDir, "server.properties") << ""
|
||||
|
||||
// Run the gradle task off thread
|
||||
def executor = Executors.newSingleThreadExecutor()
|
||||
def resultCF = CompletableFuture.supplyAsync({
|
||||
gradle.run(task: "runServer")
|
||||
}, executor)
|
||||
|
||||
Map<String, CompletableFuture<BreakpointEvent>> futures
|
||||
def debugger = new Debugger(openDebugSession())
|
||||
|
||||
try {
|
||||
futures = BREAKPOINTS.collectEntries { className, line ->
|
||||
[(className): debugger.addBreakpoint(className, line)]
|
||||
}
|
||||
|
||||
// Start running the game, the process has been suspended until this point.
|
||||
debugger.start()
|
||||
|
||||
// Wait for all of the breakpoints
|
||||
futures.values().forEach {
|
||||
def result = it.get()
|
||||
println("Breakpoint triggered: ${result.location()}")
|
||||
}
|
||||
} finally {
|
||||
// Close the debugger and target process
|
||||
debugger.close()
|
||||
}
|
||||
|
||||
def result = resultCF.get()
|
||||
executor.shutdown()
|
||||
|
||||
then:
|
||||
result.task(":runServer").outcome == SUCCESS
|
||||
|
||||
BREAKPOINTS.forEach { className, line ->
|
||||
futures[className].get().location().lineNumber() == line
|
||||
}
|
||||
}
|
||||
|
||||
private static String getClassSource(GradleProject gradle, String classname, String mappings = MAPPINGS) {
|
||||
File sourcesJar = gradle.getGeneratedSources(mappings, "serveronly")
|
||||
return new String(ZipUtils.unpack(sourcesJar.toPath(), classname), StandardCharsets.UTF_8)
|
||||
}
|
||||
|
||||
private static IDebugSession openDebugSession() {
|
||||
int timeout = 5
|
||||
int maxTimeout = 120 / timeout
|
||||
|
||||
for (i in 0..maxTimeout) {
|
||||
try {
|
||||
return DebugUtility.attach(
|
||||
Bootstrap.virtualMachineManager(),
|
||||
"127.0.0.1",
|
||||
8050,
|
||||
timeout
|
||||
)
|
||||
} catch (ConnectException e) {
|
||||
Thread.sleep(timeout * 1000)
|
||||
if (i == maxTimeout) {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalStateException()
|
||||
}
|
||||
|
||||
@CompileStatic // Makes RxJava somewhat usable in Groovy
|
||||
class Debugger implements AutoCloseable {
|
||||
final IDebugSession debugSession
|
||||
|
||||
Debugger(IDebugSession debugSession) {
|
||||
this.debugSession = debugSession
|
||||
|
||||
debugSession.eventHub.events().subscribe({ }) {
|
||||
// Manually bail out, as it seems this can be called after close()
|
||||
it.printStackTrace()
|
||||
System.exit(-1)
|
||||
}
|
||||
}
|
||||
|
||||
CompletableFuture<BreakpointEvent> addBreakpoint(String className, int lineNumber) {
|
||||
def breakpoint = debugSession.createBreakpoint(
|
||||
className,
|
||||
lineNumber,
|
||||
0,
|
||||
null,
|
||||
null
|
||||
)
|
||||
|
||||
// Wait for the breakpoint to be installed
|
||||
return breakpoint.install().thenCompose {
|
||||
// Then compose with the first result
|
||||
return breakpointEvents()
|
||||
.filter { event ->
|
||||
event.location().sourcePath().replaceAll("[\\\\/]", ".") == className + ".java" &&
|
||||
event.location().lineNumber() == lineNumber
|
||||
}
|
||||
.firstElement()
|
||||
.to(toCompletionStage())
|
||||
}
|
||||
}
|
||||
|
||||
private static <T> Function<Maybe<T>, CompletionStage<T>> toCompletionStage() {
|
||||
return { Maybe<T> m ->
|
||||
CompletableFuture<T> cf = new CompletableFuture<>()
|
||||
m.subscribe(cf.&complete, cf.&completeExceptionally) { cf.complete(null) }
|
||||
return cf
|
||||
}
|
||||
}
|
||||
|
||||
Observable<BreakpointEvent> breakpointEvents() {
|
||||
return debugSession.getEventHub().breakpointEvents()
|
||||
.map {
|
||||
it.shouldResume = true
|
||||
it.event as BreakpointEvent
|
||||
}
|
||||
}
|
||||
|
||||
void start() {
|
||||
debugSession.start()
|
||||
}
|
||||
|
||||
@Override
|
||||
void close() throws Exception {
|
||||
debugSession.terminate()
|
||||
debugSession.eventHub.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -236,8 +236,8 @@ trait GradleProjectTestTrait {
|
||||
return ZipUtils.unpackNullable(file.toPath(), entryName) != null
|
||||
}
|
||||
|
||||
File getGeneratedSources(String mappings) {
|
||||
return new File(getGradleHomeDir(), "caches/fabric-loom/minecraftMaven/net/minecraft/minecraft-merged/${mappings}/minecraft-merged-${mappings}-sources.jar")
|
||||
File getGeneratedSources(String mappings, String jarType = "merged") {
|
||||
return new File(getGradleHomeDir(), "caches/fabric-loom/minecraftMaven/net/minecraft/minecraft-${jarType}/${mappings}/minecraft-${jarType}-${mappings}-sources.jar")
|
||||
}
|
||||
|
||||
File getGeneratedLocalSources(String mappings) {
|
||||
|
||||
Reference in New Issue
Block a user