diff --git a/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java b/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java index fff381f6..72ee99eb 100644 --- a/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java +++ b/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java @@ -109,15 +109,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); @@ -274,12 +275,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) { @@ -297,25 +300,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 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; @@ -356,7 +368,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() { diff --git a/src/main/java/net/fabricmc/loom/decompilers/ClassLineNumbers.java b/src/main/java/net/fabricmc/loom/decompilers/ClassLineNumbers.java index 2c872406..120972fa 100644 --- a/src/main/java/net/fabricmc/loom/decompilers/ClassLineNumbers.java +++ b/src/main/java/net/fabricmc/loom/decompilers/ClassLineNumbers.java @@ -46,6 +46,11 @@ public record ClassLineNumbers(Map lineMap) { if (lineMap.isEmpty()) { throw new IllegalArgumentException("lineMap is empty"); } + + for (Map.Entry entry : lineMap.entrySet()) { + Objects.requireNonNull(entry.getKey(), "lineMap key"); + Objects.requireNonNull(entry.getValue(), "lineMap value"); + } } public static ClassLineNumbers readMappings(Path lineMappingsPath) { diff --git a/src/main/java/net/fabricmc/loom/decompilers/cache/CachedJarProcessor.java b/src/main/java/net/fabricmc/loom/decompilers/cache/CachedJarProcessor.java index 82952210..e89d035e 100644 --- a/src/main/java/net/fabricmc/loom/decompilers/cache/CachedJarProcessor.java +++ b/src/main/java/net/fabricmc/loom/decompilers/cache/CachedJarProcessor.java @@ -85,7 +85,13 @@ public record CachedJarProcessor(CachedFileStore 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 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); diff --git a/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java b/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java index acf71a2e..44c20751 100644 --- a/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java +++ b/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java @@ -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; @@ -489,6 +490,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) {