Class ProcessMinecraftJarTask

java.lang.Object
org.gradle.api.internal.AbstractTask
org.gradle.api.DefaultTask
net.fabricmc.loom.pipeline.ProcessMinecraftJarTask
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 ProcessMinecraftJarTask extends org.gradle.api.DefaultTask
L3 流水线:对「命名映射后的 Minecraft jar」执行 jar processor 链.

它取代 ProcessedNamedMinecraftProvider.provide() 里那段配置期同步执行的 「存在性检查 → 取跨进程锁 → copyToMaven 落位 → processJar」:是否需要重跑交给 Gradle 的 up-to-date 判定与构建缓存,跨进程互斥交给任务图。与 RemapMinecraftTask 串联—— 后者的产物是本任务的输入,故两者必须同批接线。

链的执行语义与配置期逐项对齐:先原子落位(对应 LocalMavenHelper.copyToMaven), 再由每个 processor 就地对产物路径做改写(processor 的契约就是「就地改 jar」), 失败时与 MinecraftJarProcessorManager.processJar 一样尽力删除半成品。

链的输入设计

链是有序的,且顺序变了结果就变,因此三个与链有关的输入都是有序 ListProperty (Gradle 对列表输入的指纹含顺序,故「换序」会正确地触发重跑),都不能退化为 SetProperty:
  • getProcessorDescriptors():@Input。执行期凭它重建 processor 实例; ProcessorDescriptor 按接口契约是 Serializable 的纯值,可以直接作为值输入。
  • getProcessorSpecs():@Internal。仅供执行期重建后的 processor 使用,不参与指纹。
  • getSpecFingerprints():@Input。spec 的指纹,与链一一对应,参与指纹。

为什么 spec 不是 @Input,而是 @Internal + 指纹

MinecraftJarProcessor.Spec 不继承 Serializable,因此 spec 不能作为值输入: 实测(Gradle 9.5,org.gradle.configuration-cache=true)把一个 record 实现的 spec 放进 @Input 会直接失败——Cannot fingerprint input property 'x': value '[Spec[...]]' cannot be serialized。 spec 的身份在 loom 既有协议里就是 spec.hashCode()(MinecraftJarProcessorManager.getCacheValue() 用它组成缓存值,产物目录名与 getJarHash() 均由该缓存值派生),故本任务沿用同一身份: 对象本体用 @Internal 带到执行期,指纹用 @Input 参与判定。 代价如实记下:hashCode 碰撞时任务不会重跑——这与既有协议同源(既有协议同样以该哈希命名产物), 不是本任务新引入的风险;但 hashCode 实现不稳定(例如用身份哈希)的类型会让任务每次都重跑。

为什么链不能用 @Nested

实测结论(同一环境):把 descriptor/spec 放进 @Nested 的 ListProperty 里, 由于 record 的访问器不带 get/is 前缀,Gradle 内省不出任何属性, 链的值就完全不进指纹——改了 descriptor 或 spec 的值,任务仍然报 UP-TO-DATE, 也就是「任务成功、产物是旧的」这种静默损坏。相比之下 @Nested 一旦能内省出属性 (带 get 前缀的访问器)就会要求每个子属性自带输入注解,对第三方 descriptor 同样不可行。

本任务不产出的东西

配置期那条路径在落位时还顺带产生了两个本任务不管的文件,接线时必须由别处继续产出, 否则会出现「任务成功但下游缺文件」:
  • <name>-<version>.pom:由 LocalMavenHelper.savePom() 写出(内容是坐标的纯函数, 与 jar 无关),缺失会让按坐标注入的依赖解析失败。
  • <jar>.backup:由 ProcessedNamedMinecraftProvider 在链跑完后复制,是 GenerateSourcesTask 的 @Classpath 输入,缺失会让 genSources 直接失败。 它必须在链跑完之后产生,因此不能留在配置期同步执行。
  • Nested Class Summary

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

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

    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
    abstract org.gradle.api.provider.Property<Boolean>
    是否禁用混淆(与配置期的 ProcessorContextImpl.disableObfuscation() 同源).
    abstract org.gradle.api.file.RegularFileProperty
    待处理的 jar(命名映射后的产物).
    abstract org.gradle.api.provider.Property<MappingsNamespace>
    平台默认命名空间(例如 Forge 的 srg),供 processor 使用.
    abstract org.gradle.api.provider.Property<ExecutionProcessorContext.JarConfigurationKind>
    本次构建的 jar 配置身份,供执行期上下文回答「是否 split 形态」.
    abstract org.gradle.api.provider.Property<MinecraftJar.Type>
    本任务处理的 jar 类型,供执行期上下文回答「是否 merged / 含客户端 / 含服务端」.
    abstract org.gradle.api.provider.Property<TinyMappingsService.Options>
    映射服务配置;执行期据此取出映射树.
    protected abstract org.gradle.api.model.ObjectFactory
    重建 processor 实例所需的 Gradle 工厂;执行期由 Gradle 注入,进不了序列化状态.
    abstract org.gradle.api.file.RegularFileProperty
    产物:processor 链就地对这个路径做改写.
    abstract org.gradle.api.provider.ListProperty<MinecraftJarProcessor.ProcessorDescriptor<?>>
    processor 链的描述符,顺序即处理顺序.
    abstract org.gradle.api.provider.ListProperty<MinecraftJarProcessor.Spec>
    与 getProcessorDescriptors() 一一对应的 spec,顺序一致.
    abstract org.gradle.api.provider.Property<MappingsNamespace>
    生产命名空间;processor 请求 remapper 时用的源命名空间.
    abstract org.gradle.api.provider.Property<TinyRemapperService.Options>
    重映射服务配置;执行期据此取出 remapper.
    abstract org.gradle.api.provider.ListProperty<Integer>
    与 getProcessorSpecs() 一一对应的指纹,顺序一致.
    void
     

    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
  • Constructor Details

    • ProcessMinecraftJarTask

      public ProcessMinecraftJarTask()
  • Method Details

    • getInputJar

      @InputFile @PathSensitive(NONE) public abstract org.gradle.api.file.RegularFileProperty getInputJar()
      待处理的 jar(命名映射后的产物).
    • getOutputJar

      @OutputFile public abstract org.gradle.api.file.RegularFileProperty getOutputJar()
      产物:processor 链就地对这个路径做改写.

      它的位置沿用 maven 仓库里的既有路径(消费侧按坐标找它),不由本任务决定。 任务先把它替换为输入的原子副本,再交给链改写——输入 jar 因此保持只读, 任务可重复执行,缓存与 up-to-date 判定不会因为「输入被自己改过」而失真。

    • getProcessorDescriptors

      @Input public abstract org.gradle.api.provider.ListProperty<MinecraftJarProcessor.ProcessorDescriptor<?>> getProcessorDescriptors()
      processor 链的描述符,顺序即处理顺序.

      @Input:ProcessorDescriptor 按契约是配置缓存可序列化的纯值, Gradle 对它的值指纹直接可用,无需额外指纹。

    • getProcessorSpecs

      @Internal public abstract org.gradle.api.provider.ListProperty<MinecraftJarProcessor.Spec> getProcessorSpecs()
      与 getProcessorDescriptors() 一一对应的 spec,顺序一致.

      @Internal:Spec 不保证可序列化(见类注释),故不参与指纹, 只随配置缓存带到执行期供重建后的 processor 使用。指纹由 getSpecFingerprints() 承担。

    • getSpecFingerprints

      @Input public abstract org.gradle.api.provider.ListProperty<Integer> getSpecFingerprints()
      与 getProcessorSpecs() 一一对应的指纹,顺序一致.

      取 spec.hashCode():与 loom 既有的 spec 身份一致(见类注释)。

    • getJarConfiguration

      @Input public abstract org.gradle.api.provider.Property<ExecutionProcessorContext.JarConfigurationKind> getJarConfiguration()
      本次构建的 jar 配置身份,供执行期上下文回答「是否 split 形态」.
    • getJarType

      @Input public abstract org.gradle.api.provider.Property<MinecraftJar.Type> getJarType()
      本任务处理的 jar 类型,供执行期上下文回答「是否 merged / 含客户端 / 含服务端」.
    • getDisableObfuscation

      @Input public abstract org.gradle.api.provider.Property<Boolean> getDisableObfuscation()
      是否禁用混淆(与配置期的 ProcessorContextImpl.disableObfuscation() 同源).
    • getProductionNamespace

      @Input public abstract org.gradle.api.provider.Property<MappingsNamespace> getProductionNamespace()
      生产命名空间;processor 请求 remapper 时用的源命名空间.
    • getIntermediaryNamespace

      @Input public abstract org.gradle.api.provider.Property<MappingsNamespace> getIntermediaryNamespace()
      平台默认命名空间(例如 Forge 的 srg),供 processor 使用.
    • getMappingsServiceOptions

      @Optional public abstract org.gradle.api.provider.Property<TinyMappingsService.Options> getMappingsServiceOptions()
      映射服务配置;执行期据此取出映射树.

      为何标为可选

      直通形态(disableObfuscation)没有 MappingConfiguration,构建里不存在 可供装配的映射树。而该形态下处理器拿到的 spec 来自 DeobfSpecContext, 那个上下文本身就不提供映射(实际也不读)。把它定为必填会让该形态永远进不了任务路径。

      故改为可选;真正读映射(getMappings())时才报错,由 ExecutionProcessorContext 负责给出可读的失败信息。

    • getRemapperServiceOptions

      public abstract org.gradle.api.provider.Property<TinyRemapperService.Options> getRemapperServiceOptions()
      重映射服务配置;执行期据此取出 remapper.

      processor 请求的是「生产命名空间 → named」那一对(context.createRemapper(context.getProductionNamespace(), MappingsNamespace.NAMED)), 装配时必须与它一致:ExecutionProcessorContext.createRemapper 对不匹配的请求直接抛异常, 而不是返回一个配置不同的 remapper。

    • getObjectFactory

      @Inject protected abstract org.gradle.api.model.ObjectFactory getObjectFactory()
      重建 processor 实例所需的 Gradle 工厂;执行期由 Gradle 注入,进不了序列化状态.
    • process

      public void process() throws IOException
      Throws:
      IOException