mirror of
https://github.com/architectury/architectury-loom.git
synced 2026-03-28 04:07:01 -05:00
Merge remote-tracking branch 'FabricMC/dev/1.6' into dev/1.6
This commit is contained in:
@@ -12,7 +12,7 @@ mapping-io = "0.5.1"
|
||||
lorenz-tiny = "4.0.2"
|
||||
mercury = "0.1.4.17"
|
||||
kotlinx-metadata = "0.9.0"
|
||||
loom-native = "0.1.1"
|
||||
loom-native = "0.2.0"
|
||||
|
||||
# Plugins
|
||||
spotless = "6.25.0"
|
||||
|
||||
@@ -126,15 +126,16 @@ public abstract class CompileConfiguration implements Runnable {
|
||||
|
||||
try {
|
||||
setupMinecraft(configContext);
|
||||
|
||||
LoomDependencyManager dependencyManager = new LoomDependencyManager();
|
||||
extension.setDependencyManager(dependencyManager);
|
||||
dependencyManager.handleDependencies(getProject(), serviceManager);
|
||||
} catch (Exception e) {
|
||||
ExceptionUtil.printFileLocks(e, getProject());
|
||||
disownLock();
|
||||
throw ExceptionUtil.createDescriptiveWrapper(RuntimeException::new, "Failed to setup Minecraft", e);
|
||||
}
|
||||
|
||||
LoomDependencyManager dependencyManager = new LoomDependencyManager();
|
||||
extension.setDependencyManager(dependencyManager);
|
||||
dependencyManager.handleDependencies(getProject(), serviceManager);
|
||||
|
||||
releaseLock();
|
||||
extension.setRefreshDeps(previousRefreshDeps);
|
||||
|
||||
@@ -364,12 +365,14 @@ public abstract class CompileConfiguration implements Runnable {
|
||||
// already owned by current pid
|
||||
ACQUIRED_ALREADY_OWNED,
|
||||
// acquired due to current owner not existing
|
||||
ACQUIRED_PREVIOUS_OWNER_MISSING
|
||||
ACQUIRED_PREVIOUS_OWNER_MISSING,
|
||||
// acquired due to previous owner disowning the lock
|
||||
ACQUIRED_PREVIOUS_OWNER_DISOWNED
|
||||
}
|
||||
|
||||
private LockResult acquireProcessLockWaiting(LockFile lockFile) {
|
||||
// one hour
|
||||
return this.acquireProcessLockWaiting(lockFile, Duration.ofHours(1));
|
||||
return this.acquireProcessLockWaiting(lockFile, getDefaultTimeout());
|
||||
}
|
||||
|
||||
private LockResult acquireProcessLockWaiting(LockFile lockFile, Duration timeout) {
|
||||
@@ -387,25 +390,34 @@ public abstract class CompileConfiguration implements Runnable {
|
||||
final Logger logger = Logging.getLogger("loom_acquireProcessLockWaiting");
|
||||
final long currentPid = ProcessHandle.current().pid();
|
||||
boolean abrupt = false;
|
||||
boolean disowned = false;
|
||||
|
||||
if (Files.exists(lockFile.file)) {
|
||||
long lockingProcessId;
|
||||
long lockingProcessId = -1;
|
||||
|
||||
try {
|
||||
lockingProcessId = Long.parseLong(Files.readString(lockFile.file));
|
||||
} catch (final Exception e) {
|
||||
lockingProcessId = -1;
|
||||
String lockValue = Files.readString(lockFile.file);
|
||||
|
||||
if ("disowned".equals(lockValue)) {
|
||||
disowned = true;
|
||||
} else {
|
||||
lockingProcessId = Long.parseLong(lockValue);
|
||||
logger.lifecycle("\"{}\" is currently held by pid '{}'.", lockFile, lockingProcessId);
|
||||
}
|
||||
} catch (final Exception ignored) {
|
||||
// ignored
|
||||
}
|
||||
|
||||
if (lockingProcessId == currentPid) {
|
||||
return LockResult.ACQUIRED_ALREADY_OWNED;
|
||||
}
|
||||
|
||||
logger.lifecycle("\"{}\" is currently held by pid '{}'.", lockFile, lockingProcessId);
|
||||
|
||||
Optional<ProcessHandle> handle = ProcessHandle.of(lockingProcessId);
|
||||
|
||||
if (handle.isEmpty()) {
|
||||
if (disowned) {
|
||||
logger.lifecycle("Previous process has disowned the lock due to abrupt termination.");
|
||||
Files.deleteIfExists(lockFile.file);
|
||||
} else if (handle.isEmpty()) {
|
||||
logger.lifecycle("Locking process does not exist, assuming abrupt termination and deleting lock file.");
|
||||
Files.deleteIfExists(lockFile.file);
|
||||
abrupt = true;
|
||||
@@ -446,7 +458,35 @@ public abstract class CompileConfiguration implements Runnable {
|
||||
}
|
||||
|
||||
Files.writeString(lockFile.file, String.valueOf(currentPid));
|
||||
return abrupt ? LockResult.ACQUIRED_PREVIOUS_OWNER_MISSING : LockResult.ACQUIRED_CLEAN;
|
||||
|
||||
if (disowned) {
|
||||
return LockResult.ACQUIRED_PREVIOUS_OWNER_DISOWNED;
|
||||
} else if (abrupt) {
|
||||
return LockResult.ACQUIRED_PREVIOUS_OWNER_MISSING;
|
||||
}
|
||||
|
||||
return LockResult.ACQUIRED_CLEAN;
|
||||
}
|
||||
|
||||
private static Duration getDefaultTimeout() {
|
||||
if (System.getenv("CI") != null) {
|
||||
// Set a small timeout on CI, as it's unlikely going to unlock.
|
||||
return Duration.ofMinutes(1);
|
||||
}
|
||||
|
||||
return Duration.ofHours(1);
|
||||
}
|
||||
|
||||
// When we fail to configure, write "disowned" to the lock file to release it from this process
|
||||
// This allows the next run to rebuild without waiting for this process to exit
|
||||
private void disownLock() {
|
||||
final Path lock = getLockFile().file;
|
||||
|
||||
try {
|
||||
Files.writeString(lock, "disowned");
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void releaseLock() {
|
||||
|
||||
@@ -46,6 +46,11 @@ public record ClassLineNumbers(Map<String, ClassLineNumbers.Entry> lineMap) {
|
||||
if (lineMap.isEmpty()) {
|
||||
throw new IllegalArgumentException("lineMap is empty");
|
||||
}
|
||||
|
||||
for (Map.Entry<String, Entry> entry : lineMap.entrySet()) {
|
||||
Objects.requireNonNull(entry.getKey(), "lineMap key");
|
||||
Objects.requireNonNull(entry.getValue(), "lineMap value");
|
||||
}
|
||||
}
|
||||
|
||||
public static ClassLineNumbers readMappings(Path lineMappingsPath) {
|
||||
|
||||
@@ -60,12 +60,11 @@ public record LineNumberRemapper(ClassLineNumbers lineNumbers) {
|
||||
}
|
||||
}
|
||||
|
||||
String fileName = file.getFileName().toString();
|
||||
String fileName = file.toAbsolutePath().toString();
|
||||
|
||||
if (fileName.endsWith(".class")) {
|
||||
String idx = fileName.substring(0, fileName.length() - 6);
|
||||
|
||||
LOGGER.debug("Remapping line numbers for class: " + idx);
|
||||
// Strip the leading slash and the .class extension
|
||||
String idx = fileName.substring(1, fileName.length() - 6);
|
||||
|
||||
int dollarPos = idx.indexOf('$'); //This makes the assumption that only Java classes are to be remapped.
|
||||
|
||||
@@ -74,6 +73,8 @@ public record LineNumberRemapper(ClassLineNumbers lineNumbers) {
|
||||
}
|
||||
|
||||
if (lineNumbers.lineMap().containsKey(idx)) {
|
||||
LOGGER.debug("Remapping line numbers for class: {}", idx);
|
||||
|
||||
try (InputStream is = Files.newInputStream(file)) {
|
||||
ClassReader reader = new ClassReader(is);
|
||||
ClassWriter writer = new ClassWriter(0);
|
||||
@@ -82,6 +83,8 @@ public record LineNumberRemapper(ClassLineNumbers lineNumbers) {
|
||||
Files.write(dst, writer.toByteArray());
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
LOGGER.debug("No linemap found for: {}", idx);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -85,7 +85,13 @@ public record CachedJarProcessor(CachedFileStore<CachedData> fileStore, String b
|
||||
final Path outputPath = existingFs.getPath(outputFileName);
|
||||
Files.createDirectories(outputPath.getParent());
|
||||
Files.writeString(outputPath, entryData.sources());
|
||||
lineNumbersMap.put(entryData.className(), entryData.lineNumbers());
|
||||
|
||||
if (entryData.lineNumbers() != null) {
|
||||
lineNumbersMap.put(entryData.className(), entryData.lineNumbers());
|
||||
} else {
|
||||
LOGGER.info("Cached entry ({}) does not have line numbers", outputFileName);
|
||||
}
|
||||
|
||||
hasSomeExisting = true;
|
||||
|
||||
LOGGER.debug("Cached entry ({}) found: {}", fullHash, outputFileName);
|
||||
@@ -166,6 +172,10 @@ public record CachedJarProcessor(CachedFileStore<CachedData> fileStore, String b
|
||||
lineMapEntry = lineNumbers.lineMap().get(className);
|
||||
}
|
||||
|
||||
if (lineMapEntry == null) {
|
||||
LOGGER.info("No line numbers generated for class: {}", className);
|
||||
}
|
||||
|
||||
final var cachedData = new CachedData(className, sources, lineMapEntry);
|
||||
fileStore.putEntry(hash, cachedData);
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
package net.fabricmc.loom.task;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@@ -204,6 +205,9 @@ public abstract class GenerateSourcesTask extends AbstractLoomTask {
|
||||
if (!getUseCache().get()) {
|
||||
try (var timer = new Timer("Decompiled sources")) {
|
||||
runWithoutCache();
|
||||
} catch (Exception e) {
|
||||
ExceptionUtil.printFileLocks(e, getProject());
|
||||
throw ExceptionUtil.createDescriptiveWrapper(RuntimeException::new, "Failed to decompile", e);
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -221,6 +225,9 @@ public abstract class GenerateSourcesTask extends AbstractLoomTask {
|
||||
try (FileSystemUtil.Delegate fs = FileSystemUtil.getJarFileSystem(cacheFile, true)) {
|
||||
runWithCache(fs.getRoot());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
ExceptionUtil.printFileLocks(e, getProject());
|
||||
throw ExceptionUtil.createDescriptiveWrapper(RuntimeException::new, "Failed to decompile", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -492,7 +499,6 @@ public abstract class GenerateSourcesTask extends AbstractLoomTask {
|
||||
final var provideContext = new AbstractMappedMinecraftProvider.ProvideContext(false, true, configContext);
|
||||
minecraftJars = getExtension().getNamedMinecraftProvider().provide(provideContext);
|
||||
} catch (Exception e) {
|
||||
ExceptionUtil.printFileLocks(e, getProject());
|
||||
throw ExceptionUtil.createDescriptiveWrapper(RuntimeException::new, "Failed to rebuild input jars", e);
|
||||
}
|
||||
|
||||
@@ -567,6 +573,14 @@ public abstract class GenerateSourcesTask extends AbstractLoomTask {
|
||||
Objects.requireNonNull(lineNumbers, "lineNumbers");
|
||||
final var remapper = new LineNumberRemapper(lineNumbers);
|
||||
remapper.process(inputJar, outputJar);
|
||||
|
||||
final Path lineMap = inputJar.resolveSibling(inputJar.getFileName() + ".linemap.txt");
|
||||
|
||||
try (BufferedWriter writer = Files.newBufferedWriter(lineMap)) {
|
||||
lineNumbers.write(writer);
|
||||
}
|
||||
|
||||
LOGGER.info("Wrote linemap to {}", lineMap);
|
||||
}
|
||||
|
||||
private void doWork(@Nullable IPCServer ipcServer, Path inputJar, Path outputJar, Path linemapFile, @Nullable Path existingJar) {
|
||||
|
||||
@@ -143,45 +143,50 @@ public abstract class RemapTaskConfiguration implements Runnable {
|
||||
|
||||
private void trySetupSourceRemapping() {
|
||||
final LoomGradleExtension extension = LoomGradleExtension.get(getProject());
|
||||
final String sourcesJarTaskName = SourceSetHelper.getMainSourceSet(getProject()).getSourcesJarTaskName();
|
||||
|
||||
TaskProvider<RemapSourcesJarTask> remapSourcesTask = getTasks().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 Task sourcesTask = getTasks().findByName(sourcesJarTaskName);
|
||||
|
||||
if (sourcesTask == null) {
|
||||
getProject().getLogger().info("{} task was not found, not remapping sources", sourcesJarTaskName);
|
||||
task.setEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(sourcesTask instanceof Jar sourcesJarTask)) {
|
||||
getProject().getLogger().info("{} task is not a Jar task, not remapping sources", sourcesJarTaskName);
|
||||
task.setEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
sourcesJarTask.getArchiveClassifier().convention("dev-sources");
|
||||
sourcesJarTask.getDestinationDirectory().set(getProject().getLayout().getBuildDirectory().map(directory -> directory.dir("devlibs")));
|
||||
task.getArchiveClassifier().convention("sources");
|
||||
|
||||
task.dependsOn(sourcesJarTask);
|
||||
task.getInputFile().convention(sourcesJarTask.getArchiveFile());
|
||||
task.getIncludesClientOnlyClasses().set(getProject().provider(extension::areEnvironmentSourceSetsSplit));
|
||||
});
|
||||
|
||||
getTasks().named(BasePlugin.ASSEMBLE_TASK_NAME).configure(task -> task.dependsOn(remapSourcesTask));
|
||||
|
||||
if (GradleUtils.getBooleanProperty(getProject(), "fabric.loom.disableRemappedVariants")) {
|
||||
return;
|
||||
}
|
||||
|
||||
GradleUtils.afterSuccessfulEvaluation(getProject(), () -> {
|
||||
final String sourcesJarTaskName = SourceSetHelper.getMainSourceSet(getProject()).getSourcesJarTaskName();
|
||||
final Task sourcesTask = getTasks().findByName(sourcesJarTaskName);
|
||||
|
||||
if (!(sourcesTask instanceof Jar sourcesJarTask)) {
|
||||
boolean canRemap = true;
|
||||
|
||||
if (sourcesTask == null) {
|
||||
getProject().getLogger().info("{} task was not found, not remapping sources", sourcesJarTaskName);
|
||||
canRemap = false;
|
||||
}
|
||||
|
||||
if (canRemap && !(sourcesTask instanceof Jar)) {
|
||||
getProject().getLogger().info("{} task is not a Jar task, not remapping sources", sourcesJarTaskName);
|
||||
canRemap = false;
|
||||
}
|
||||
|
||||
boolean finalCanRemap = canRemap;
|
||||
|
||||
remapSourcesTask.configure(task -> {
|
||||
if (!finalCanRemap) {
|
||||
task.setEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
final Jar sourcesJarTask = (Jar) sourcesTask;
|
||||
|
||||
sourcesJarTask.getArchiveClassifier().convention("dev-sources");
|
||||
sourcesJarTask.getDestinationDirectory().set(getProject().getLayout().getBuildDirectory().map(directory -> directory.dir("devlibs")));
|
||||
task.getArchiveClassifier().convention("sources");
|
||||
|
||||
task.dependsOn(sourcesJarTask);
|
||||
task.getInputFile().convention(sourcesJarTask.getArchiveFile());
|
||||
});
|
||||
|
||||
if (GradleUtils.getBooleanProperty(getProject(), "fabric.loom.disableRemappedVariants")) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,10 +32,15 @@ import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import org.gradle.api.Project;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import net.fabricmc.loom.nativeplatform.LoomNativePlatform;
|
||||
import net.fabricmc.loom.nativeplatform.LoomNativePlatformException;
|
||||
|
||||
public final class ExceptionUtil {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ExceptionUtil.class);
|
||||
|
||||
/**
|
||||
* Creates a descriptive user-facing wrapper exception for an underlying cause.
|
||||
*
|
||||
@@ -74,7 +79,14 @@ public final class ExceptionUtil {
|
||||
return;
|
||||
}
|
||||
|
||||
final List<ProcessHandle> processes = LoomNativePlatform.getProcessesWithLockOn(path);
|
||||
final List<ProcessHandle> processes;
|
||||
|
||||
try {
|
||||
processes = LoomNativePlatform.getProcessesWithLockOn(path);
|
||||
} catch (LoomNativePlatformException e) {
|
||||
LOGGER.error("{}, Failed to query processes holding a lock on {}", e.getMessage(), path);
|
||||
return;
|
||||
}
|
||||
|
||||
if (processes.isEmpty()) {
|
||||
return;
|
||||
|
||||
@@ -31,11 +31,15 @@ import java.util.StringJoiner;
|
||||
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.logging.LogLevel;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import net.fabricmc.loom.nativeplatform.LoomNativePlatform;
|
||||
import net.fabricmc.loom.nativeplatform.LoomNativePlatformException;
|
||||
|
||||
public record ProcessUtil(LogLevel logLevel) {
|
||||
private static final String EXPLORER_COMMAND = "C:\\Windows\\explorer.exe";
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ProcessUtil.class);
|
||||
|
||||
public static ProcessUtil create(Project project) {
|
||||
return new ProcessUtil(project.getGradle().getStartParameter().getLogLevel());
|
||||
@@ -92,7 +96,14 @@ public record ProcessUtil(LogLevel logLevel) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
List<String> titles = LoomNativePlatform.getWindowTitlesForPid(processHandle.pid());
|
||||
List<String> titles;
|
||||
|
||||
try {
|
||||
titles = LoomNativePlatform.getWindowTitlesForPid(processHandle.pid());
|
||||
} catch (LoomNativePlatformException e) {
|
||||
LOGGER.error("{}, Failed to query window title for pid {}", e.getMessage(), processHandle.pid());
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
if (titles.isEmpty()) {
|
||||
return Optional.empty();
|
||||
|
||||
@@ -122,6 +122,8 @@ class DebugLineNumbersTest extends Specification implements GradleProjectTestTra
|
||||
def result = it.get()
|
||||
println("Breakpoint triggered: ${result.location()}")
|
||||
}
|
||||
|
||||
println("All breakpoints triggered")
|
||||
} finally {
|
||||
// Close the debugger and target process
|
||||
debugger.close()
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* This file is part of fabric-loom, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) 2024 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 java.nio.file.Files
|
||||
|
||||
import org.objectweb.asm.ClassReader
|
||||
import org.objectweb.asm.ClassVisitor
|
||||
import org.objectweb.asm.Label
|
||||
import org.objectweb.asm.MethodVisitor
|
||||
import spock.lang.Specification
|
||||
|
||||
import net.fabricmc.loom.decompilers.ClassLineNumbers
|
||||
import net.fabricmc.loom.decompilers.LineNumberRemapper
|
||||
import net.fabricmc.loom.test.util.ZipTestUtils
|
||||
import net.fabricmc.loom.util.Constants
|
||||
import net.fabricmc.loom.util.ZipUtils
|
||||
|
||||
class LineNumberRemapperTests extends Specification {
|
||||
def "remapLinenumbers"() {
|
||||
given:
|
||||
def className = LineNumberSource.class.name.replace('.', '/')
|
||||
def input = ZipTestUtils.createZipFromBytes([(className + ".class"): getClassBytes(LineNumberSource.class)])
|
||||
|
||||
// + 10 to each line number
|
||||
def entry = new ClassLineNumbers.Entry(className, 30, 40, [
|
||||
27: 37,
|
||||
29: 39,
|
||||
30: 40
|
||||
])
|
||||
def lineNumbers = new ClassLineNumbers([(className): entry])
|
||||
|
||||
def outputJar = Files.createTempDirectory("loom").resolve("output.jar")
|
||||
|
||||
when:
|
||||
def remapper = new LineNumberRemapper(lineNumbers)
|
||||
remapper.process(input, outputJar)
|
||||
|
||||
def unpacked = ZipUtils.unpack(outputJar, className + ".class")
|
||||
|
||||
then:
|
||||
readLineNumbers(getClassBytes(LineNumberSource.class)) == [27, 29, 30]
|
||||
readLineNumbers(unpacked) == [37, 39, 40]
|
||||
}
|
||||
|
||||
static byte[] getClassBytes(Class<?> clazz) {
|
||||
return clazz.classLoader.getResourceAsStream(clazz.name.replace('.', '/') + ".class").withCloseable {
|
||||
it.bytes
|
||||
}
|
||||
}
|
||||
|
||||
static List<Integer> readLineNumbers(byte[] clazzBytes) {
|
||||
def reader = new ClassReader(clazzBytes)
|
||||
def visitor = new LineNumberCollectorClassVisitor()
|
||||
reader.accept(visitor, 0)
|
||||
return visitor.lineNumbers
|
||||
}
|
||||
|
||||
private static class LineNumberCollectorClassVisitor extends ClassVisitor {
|
||||
final List<Integer> lineNumbers = []
|
||||
|
||||
protected LineNumberCollectorClassVisitor() {
|
||||
super(Constants.ASM_VERSION)
|
||||
}
|
||||
|
||||
@Override
|
||||
MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
|
||||
return new LineNumberCollectorMethodVisitor(super.visitMethod(access, name, descriptor, signature, exceptions), lineNumbers)
|
||||
}
|
||||
}
|
||||
|
||||
private static class LineNumberCollectorMethodVisitor extends MethodVisitor {
|
||||
final List<Integer> lineNumbers
|
||||
|
||||
protected LineNumberCollectorMethodVisitor(MethodVisitor methodVisitor, List<Integer> lineNumbers) {
|
||||
super(Constants.ASM_VERSION, methodVisitor)
|
||||
this.lineNumbers = lineNumbers
|
||||
}
|
||||
|
||||
@Override
|
||||
void visitLineNumber(int line, Label start) {
|
||||
super.visitLineNumber(line, start)
|
||||
lineNumbers.add(line)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,15 @@ import net.fabricmc.loom.util.FileSystemUtil
|
||||
|
||||
class ZipTestUtils {
|
||||
static Path createZip(Map<String, String> entries) {
|
||||
return createZipFromBytes(entries.collectEntries { k, v ->
|
||||
[
|
||||
k,
|
||||
v.getBytes(StandardCharsets.UTF_8)
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
static Path createZipFromBytes(Map<String, byte[]> entries) {
|
||||
def file = Files.createTempFile("loom-test", ".zip")
|
||||
Files.delete(file)
|
||||
|
||||
@@ -42,7 +51,7 @@ class ZipTestUtils {
|
||||
def fsPath = zip.getPath(path)
|
||||
def fsPathParent = fsPath.getParent()
|
||||
if (fsPathParent != null) Files.createDirectories(fsPathParent)
|
||||
Files.writeString(fsPath, value, StandardCharsets.UTF_8)
|
||||
Files.write(fsPath, value)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* This file is part of fabric-loom, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) 2024 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;
|
||||
|
||||
public class LineNumberSource {
|
||||
public static void main(String[] args) {
|
||||
System.out.println("Hello World!");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user