Class GenerateForgePatchedSourcesTask

java.lang.Object
org.gradle.api.internal.AbstractTask
org.gradle.api.DefaultTask
net.fabricmc.loom.task.AbstractLoomTask
net.fabricmc.loom.task.GenerateForgePatchedSourcesTask
All Implemented Interfaces:
Comparable<org.gradle.api.Task>, org.gradle.api.internal.DynamicObjectAware, org.gradle.api.internal.TaskInternal, org.gradle.api.Named, org.gradle.api.plugins.ExtensionAware, org.gradle.api.Task, org.gradle.util.Configurable<org.gradle.api.Task>

@CacheableTask public abstract class GenerateForgePatchedSourcesTask extends AbstractLoomTask
生成与打补丁后一致的 Forge 可调试源码(genForgePatchedSources).

以前为什么不能缓存

四件事各自都足以让缓存失效,且都是真问题:只删注解不但不会命中,还会掩盖正确性缺口。
  1. @DisableCachingByDefault 与构造里的 getOutputs().upToDateWhen($ -> false): 前者让 Gradle 既不查构建缓存也不写条目,后者让 up-to-date 判定永远失败。两者叠加, 结果就是每次构建重跑整条 MCP 链(含反编译,最贵的一步)。
  2. 声明的产物跑完就不存在:sideAnnotationStrippedMinecraftJar 是 @OutputFile, 却被放在构造期新开的随机临时目录里,并在执行结束时随 TempFiles#close 被删掉。
  3. 随机路径进了缓存键:同一个随机临时路径经 MCP 链 rename 步的 ConstantLogic (@Input Property<String>)成为任务输入。路径每次构建都变,键也跟着变, 即便标上 @CacheableTask 也永远撞不到同一个键。
  4. TempFiles 藏在任务状态里:@Internal Property<TempFiles> 是一个持有随机目录、 且在执行期自我删除的可变对象。它既不可序列化(配置缓存存不下条目),又把 AT 文件变成 「配置期抽进临时目录、执行期被删掉」的东西——配置缓存复用时的第二次构建会因此少传 --atFile,AT 静默失效。

现在的形状

  • 中间产物落在构建目录内的固定位置(WORK_DIRECTORY):AT+SAS 之后的中间 jar 因此 有了稳定路径,它既是真产物,也是 rename 步稳定的输入。
  • AT 文件改到执行期从 userdev jar 抽出(工具信息仍是配置期解析好的纯值), 配置期不再产生任何临时文件。
  • 随机临时目录只留给执行期内部的短命中间件,不再进入任何任务属性。

输入是否完整

逐项对应执行期真正读入的东西:pre-patch jar(inputJar)、named 运行时 jar(runtimeJar)、 AT 工具与规则位置、MCP 链的步骤与配置、Forge 工具设置、SAS 数据、补丁前缀,以及 Forge 源码注入与 源码重映射各自的 @Nested 选项。Forge 源码注入读的源码包、预解压目录与映射文件由 ForgeSourcesService#getSourcesCacheKey 单独覆盖(它自己的注释说明为什么必须计入)。

产物路径与 genSources 撞车(旧有问题,不是本改动引入)

输出源码包走的是「named MC jar 旁的 -sources.jar」这条约定,而 genSources (GenerateSourcesTask)用的是同一个约定和同一个 named jar(见 SingleJarDecompileConfiguration) ——两个任务写同一路径,内容不同。保留原语义:后跑者胜。对缓存的影响仅是「对方改写了产物 → 本任务判为过期 → 重新执行或由缓存恢复」;缓存里存的始终是本任务自己那份产物,不会把别人的内容当自己的。

NeoForge 缺口(调研结论,不是「顺手加一个分支」的量)

注册处 SingleJarDecompileConfiguration 用 extension.isForge()(platform == FORGE)把 NeoForge 挡在外面。要放 NeoForge 进来,机械改动只有三处(注册判据、跳过 SAS、源码重映射的源命名空间), 但其中后两处是语义问题,只能靠在 NeoForge 上真跑一遍并核对产出来定:
  1. SAS 是 Forge 独占的:sass 来自 Forge 的 userdev 配置,剥离工具是 net.minecraftforge:mergetool(见 ForgeTools#SIDE_STRIPPER)。NeoForge 的 neoform 没有这一步,所以「AT+SAS 后的 jar 冒充 rename 步产物」这个手法对它没有对应物,得改成 「AT 后的 jar 直接冒充」——本身是一行 if,但要连带确认补丁前缀与 AT 规则的命名空间仍对得上。
  2. 源码重映射的命名空间写死成 srg → named(见构造器里 getSourceRemapperOptions 的接线)。 NeoForge 的中间命名空间是 mojang(unobfuscated 形态是 official),见 MinecraftPatchedProvider 里 intermediateNamespace 的注释;写死 srg 会让 NeoForge 的源码重映射找不到 srg 列而整批失败。
  3. neoform 的 patch 步语义未验证:Forge 车道上本任务把 userdev 里的 Forge 补丁(srg)叠在 MCP 已打过补丁的源码上,再整体 srg→named;NeoForge 的 pre-patch jar 有 installer tools / rename / preProcessJar 三条分支(见 MinecraftPatchedProvider#registerPatchedChainTasks), 它们是否同样留下「未打 NeoForge 补丁的 mojang 源码」只能实测。
现有 neoforge 用例(NeoForge*Test)只跑 build,没有一条跑到 genSources 或本任务, 因此没有「产出的源码是对的」这条底线保障;在没有那套验证之前,这里只记录缺口而不硬接。

构建缓存何时真正生效(Gradle 的 unknown output 规则)

源码包产物落在跨项目共享的 loom 缓存里(named MC jar 旁的 -sources.jar)。对「执行前就已存在、 又不是本任务创建的」输出,Gradle 会直接关掉该任务的构建缓存,原文: Gradle does not know how file ... was created (output property ...). Task output caching requires exclusive access to output paths to guarantee correctness。所以:
  • up-to-date 判定不受影响(它只要求输入与输出都没变);
  • 构建缓存要等本任务在某个工程里成功跑过一次(Gradle 记下「这个文件是本任务造的」)才开始存条目, 此后输入变化、或产物被删,才会走缓存而不是重跑整条链;
  • 同一条规则也解释了 downloadMinecraftClientJar / generateForgePrePatchJar 等 「产物在共享缓存里」的任务的 Caching disabled 日志,不是本任务特有的问题。
集成用例因此只断言 up-to-date(SUCCESS + 重跑 UP_TO_DATE):FROM-CACHE 需要「先把产物预删干净」 的三段验法,而那个手法本地未跑通(原因未定位),故不在用例里断言,理由与实测证据记在 PatchedDecompileTest 的注释里。
  • Nested Class Summary

    Nested Classes
    Modifier and Type
    Class
    Description
    static interface 
     

    Nested classes/interfaces inherited from interface org.gradle.api.Named

    org.gradle.api.Named.Namer
  • Field Summary

    Fields
    Modifier and Type
    Field
    Description
    static final String
    中间产物目录(构建目录内的固定位置,见类注释).

    Fields inherited from interface org.gradle.api.Task

    TASK_ACTION, TASK_CONSTRUCTOR_ARGS, TASK_DEPENDS_ON, TASK_DESCRIPTION, TASK_GROUP, TASK_NAME, TASK_OVERWRITE, TASK_TYPE
  • Constructor Summary

    Constructors
    Constructor
    Description
     
  • Method Summary

    Modifier and Type
    Method
    Description
    protected abstract org.gradle.api.file.ConfigurableFileCollection
    AT 工具及其依赖的 jar.
    protected abstract org.gradle.api.provider.Property<String>
    AT 文件在 userdev jar 里的目录形态(ats 声明为目录时设置).
    protected abstract org.gradle.api.provider.Property<String>
    执行 AT 工具的 JVM;未设置表示沿用 Gradle 自身的 JVM.
    protected abstract org.gradle.api.provider.Property<String>
    AT 工具的主类.
    protected abstract org.gradle.api.provider.ListProperty<String>
    AT 文件在 userdev jar 里的清单形态(ats 声明为文件列表时设置).
    protected abstract org.gradle.api.provider.Property<Boolean>
    protected abstract org.gradle.api.provider.Property<Boolean>
    同 AccessTransformForgeJarTask:它只决定工具的标准输出接哪里,不影响产物字节.
    protected abstract org.gradle.api.provider.Property<ForgeSourcesService.Options>
     
    abstract org.gradle.api.file.RegularFileProperty
    The SRG Minecraft file produced by the MCP executor.
    protected abstract org.gradle.api.provider.Property<McpExecutor.Options>
     
    abstract org.gradle.api.model.ObjectFactory
    执行期实例化服务选项用(Project 在执行期不可用,见类注释).
    abstract org.gradle.api.file.RegularFileProperty
    The source jar.
    protected abstract org.gradle.api.provider.Property<String>
     
    protected abstract org.gradle.api.provider.Property<String>
     
    protected abstract org.gradle.api.provider.Property<String>
     
    abstract org.gradle.api.file.RegularFileProperty
    The runtime Minecraft file.
    protected abstract org.gradle.api.provider.Property<GenerateForgePatchedSourcesTask.SasOptions>
     
    protected abstract org.gradle.api.file.RegularFileProperty
    AT 与 SAS 之后的 Minecraft jar,同时也是 MCP 链 rename 步的输出.
    protected abstract org.gradle.api.provider.Property<SourceRemapperService.Options>
     
    protected abstract org.gradle.api.provider.Property<ForgeToolService.Options>
     
    protected abstract org.gradle.api.file.DirectoryProperty
    本任务的中间产物目录.
    void
    run()
     

    Methods inherited from class net.fabricmc.loom.task.AbstractLoomTask

    getExtension, getModPlatform

    Methods inherited from class org.gradle.api.DefaultTask

    compareTo, configure, dependsOn, doFirst, doFirst, doFirst, doLast, doLast, doLast, finalizedBy, getActions, getAnt, getDependsOn, getDescription, getDestroyables, getDidWork, getEnabled, getExtensions, getFinalizedBy, getGroup, getInputs, getLocalState, getLogger, getLogging, getMustRunAfter, getName, getOutputs, getPath, getProject, getShouldRunAfter, getState, getTaskDependencies, getTemporaryDir, getTimeout, hasProperty, mustRunAfter, onlyIf, onlyIf, onlyIf, property, setActions, setDependsOn, setDescription, setDidWork, setEnabled, setFinalizedBy, setGroup, setMustRunAfter, setOnlyIf, setOnlyIf, setOnlyIf, setProperty, setShouldRunAfter, shouldRunAfter, usesService

    Methods inherited from class org.gradle.api.internal.AbstractTask

    acceptServiceReferences, appendParallelSafeAction, doNotTrackState, doNotTrackStateIf, getAsDynamicObject, getIdentityPath, getImpliesSubProjects, getLifecycleDependencies, getOnlyIf, getReasonNotToTrackState, getReasonsNotToTrackState, getReasonTaskIsIncompatibleWithConfigurationCache, getRequiredServices, getServices, getSharedResources, getStandardOutputCapture, getTaskActions, getTaskIdentity, getTemporaryDirFactory, hasTaskActions, injectIntoNewInstance, isCompatibleWithConfigurationCache, isEnabled, isHasCustomActions, notCompatibleWithConfigurationCache, prependParallelSafeAction, restoreOnlyIf, restoreTaskActions, setImpliesSubProjects

    Methods inherited from class java.lang.Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

    Methods inherited from interface org.gradle.api.Task

    doNotTrackState, notCompatibleWithConfigurationCache
  • Field Details

    • WORK_DIRECTORY

      public static final String WORK_DIRECTORY
      中间产物目录(构建目录内的固定位置,见类注释).
      See Also:
  • Constructor Details

    • GenerateForgePatchedSourcesTask

      public GenerateForgePatchedSourcesTask()
  • Method Details

    • getInputJar

      @InputFile @PathSensitive(NONE) public abstract org.gradle.api.file.RegularFileProperty getInputJar()
      The SRG Minecraft file produced by the MCP executor.
    • getRuntimeJar

      @InputFile @PathSensitive(NONE) public abstract org.gradle.api.file.RegularFileProperty getRuntimeJar()
      The runtime Minecraft file.
    • getOutputJar

      @OutputFile public abstract org.gradle.api.file.RegularFileProperty getOutputJar()
      The source jar.
    • getSideAnnotationStrippedMinecraftJar

      @OutputFile protected abstract org.gradle.api.file.RegularFileProperty getSideAnnotationStrippedMinecraftJar()
      AT 与 SAS 之后的 Minecraft jar,同时也是 MCP 链 rename 步的输出.

      本任务自产自销(既是产物又是链路中间件),因此它的**路径**必须稳定:该路径会作为 rename 步 ConstantLogic 的字符串输入参与缓存键(见类注释第 3 条)。

    • getWorkDirectory

      @Internal protected abstract org.gradle.api.file.DirectoryProperty getWorkDirectory()
      本任务的中间产物目录.

      @Internal:它只决定中间产物放在哪里,不参与任何产物字节,因而不进缓存键。

    • getForgeSourcesOptions

      protected abstract org.gradle.api.provider.Property<ForgeSourcesService.Options> getForgeSourcesOptions()
    • getMcpExecutorOptions

      protected abstract org.gradle.api.provider.Property<McpExecutor.Options> getMcpExecutorOptions()
    • getToolServiceOptions

      protected abstract org.gradle.api.provider.Property<ForgeToolService.Options> getToolServiceOptions()
    • getSourceRemapperOptions

      protected abstract org.gradle.api.provider.Property<SourceRemapperService.Options> getSourceRemapperOptions()
    • getSasOptions

      protected abstract org.gradle.api.provider.Property<GenerateForgePatchedSourcesTask.SasOptions> getSasOptions()
    • getAtMainClass

      @Input protected abstract org.gradle.api.provider.Property<String> getAtMainClass()
      AT 工具的主类.

      AT 工具的信息(主类、classpath、JVM)只能在配置期取得(依赖解析 + Java 工具链),故解析成纯值后 逐项声明成输入;待应用的 AT 文件则在执行期从 userdev jar 抽出,见 createAccessTransformer(net.fabricmc.loom.util.service.ServiceFactory, dev.architectury.loom.util.TempFiles)。

    • getAtClasspath

      @Classpath protected abstract org.gradle.api.file.ConfigurableFileCollection getAtClasspath()
      AT 工具及其依赖的 jar.
    • getAtJavaExecutable

      @Input @Optional protected abstract org.gradle.api.provider.Property<String> getAtJavaExecutable()
      执行 AT 工具的 JVM;未设置表示沿用 Gradle 自身的 JVM.
    • getAtVerboseStdout

      @Internal protected abstract org.gradle.api.provider.Property<Boolean> getAtVerboseStdout()
      同 AccessTransformForgeJarTask:它只决定工具的标准输出接哪里,不影响产物字节.
    • getAtVerboseStderr

      @Internal protected abstract org.gradle.api.provider.Property<Boolean> getAtVerboseStderr()
    • getAtDirectory

      @Input @Optional protected abstract org.gradle.api.provider.Property<String> getAtDirectory()
      AT 文件在 userdev jar 里的目录形态(ats 声明为目录时设置).
    • getAtPaths

      @Input protected abstract org.gradle.api.provider.ListProperty<String> getAtPaths()
      AT 文件在 userdev jar 里的清单形态(ats 声明为文件列表时设置).
    • getPatchPathInZip

      @Input protected abstract org.gradle.api.provider.Property<String> getPatchPathInZip()
    • getPatchesOriginalPrefix

      @Input protected abstract org.gradle.api.provider.Property<String> getPatchesOriginalPrefix()
    • getPatchesModifiedPrefix

      @Input protected abstract org.gradle.api.provider.Property<String> getPatchesModifiedPrefix()
    • getObjects

      @Inject public abstract org.gradle.api.model.ObjectFactory getObjects()
      执行期实例化服务选项用(Project 在执行期不可用,见类注释).
    • run

      public void run() throws IOException
      Throws:
      IOException