有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java如何将我的Gradle插件中的任务执行链接到另一个插件的输出文件?

我正在尝试为Gradle编写一个插件,该插件将使用执行另一个任务(在另一个插件中定义)的文件结果执行一些操作。我可以通过任务类中的this.getProject().getTasks().getByName("distZip").getOutputs().getFiles().getSingleFile()轻松获得所需的文件

但使用这种方法,无论文件在更高级别的任务中是否发生了更改,每次都会执行我的任务

我将getter方法注释为@InputFile。但不幸的是,格雷德尔仍然没有将这个问题标记为^{

public class YcfPlugin implements Plugin<Project> {
    @Override
    public void apply(Project project) {
        project.getExtensions().create("ycf", YcfPluginExtension.class);
        project.getPluginManager().apply("java-library-distribution");

        project.getTasks().register("ycfCreateVersion", YcfTaskCreateVersion.class);
    }
}
abstract class YcfTask extends DefaultTask {
    public static final String TASK_GROUP = "Some Description";
    YcfPluginExtension ycfExtension = this.getProject().getExtensions().getByType(YcfPluginExtension.class);
    Logger logger = this.getProject().getLogger();

    public YcfTask() {
        this.setGroup(TASK_GROUP);   
    }
}
public class YcfTaskCreateVersion extends YcfTask {
    private File fi;

    public YcfTaskCreateVersion() {
        this.setDescription("Some description");
        this.dependsOn(this.getProject().getTasks().getByName("distZip"));

    }

    @InputFile
    public File getFi() {
        return this.getProject().getTasks().getByName("distZip").getOutputs().getFiles().getSingleFile();
    }
    @TaskAction
    public void run() {
        byte[] bytes = Files.readAllBytes(getFi().toPath());
        //..do something with file content
    }
}

共 (1) 个答案

  1. # 1 楼答案

    你目前的做法有一些误解:

    1. 应该在getter方法上使用@InputFile注释。getter方法不仅仅是以前缀get开头的方法。通常,它是一个返回(私有)字段值的方法。在您的示例中,有一个名为fi的字段,因此相应的getter方法getFi应该只返回该字段的值。顺便说一下,在您当前的代码中,fi字段根本没有使用

    2. 很好,您知道应该使用什么值作为任务的输入(getProject().getTasks().getByName("distZip").getOutputs().getFiles().getSingleFile()),但这不应该是任务类型实现的一部分。相反,任务类型应该尽可能可配置。然后,您可以在插件代码中创建并配置此任务类型的一个实例:

      public class YcfTaskCreateVersion extends YcfTask {
          private File fi;
      
          @InputFile
          public File getFi() {
              return fi;
          }
      
          public void setFi(File fi) {
              this.fi = fi;
          }
      
          @TaskAction
          public void run() {
              // ...
          }
      }
      
      public class YcfPlugin implements Plugin<Project> {
          @Override
          public void apply(Project project) {
              // ...
      
              YcfTaskCreateVersion createVersionTask = project.getTasks()
                  .register("ycfCreateVersion", YcfTaskCreateVersion.class);
              createVersionTask.setFi(project.getTasks().getByName("distZip")
                  .getOutputs().getFiles().getSingleFile());
          }
      }
      
    3. 遗憾的是,上面的代码无法工作,因为在创建任务时(应用插件时),任务distZip(甚至可能是任务)的输出将不可用。但这不是一个大问题,因为Gradle支持这个用例。您可以将字段的类型更改为Object,这样不仅可以传入File类型的对象。任务执行时,将检查是否传递了文件(或可能转换为文件的内容)

      public class YcfTaskCreateVersion extends YcfTask {
          private Object fi;
      
          @InputFile
          public Object getFi() {
              return fi;
          }
      
          public void setFi(Object fi) {
              this.fi = fi;
          }
      
          @TaskAction
          public void run() {
              File file = getProject().files(fi).getSingleFile();
              // do something with file
          }
      }
      

      这个设置有一个很酷的地方:你可以直接传递任务distZip,Gradle会自动提取文件输出。它甚至会检测到您正在使用一个任务的输出作为另一个任务的输入,因此Gradle会自动设置两个任务之间的任务依赖关系,您不必再使用dependsOn

      createVersionTask.setFi(project.getTasks().getByName("distZip"));
      
    4. 让我们检查一下Gradle documentation on task outcomes。有一节是关于UP-TO-DATE

      Task’s outputs did not change.

      • Task has outputs and inputs and they have not changed. See Incremental Builds.
      • Task has actions, but the task tells Gradle it did not change its outputs.
      • Task has no actions and some dependencies, but all of the dependencies are up-to-date, skipped or from cache. See also Lifecycle Tasks.
      • Task has no actions and no dependencies.

      正如您所见,您的任务需要定义最新的输出。可以用与输入文件相同的方式定义输出文件(字段、getter with @OutputFile和setter)。它们应该是可配置的,默认为build目录中的文件。或者,您可以使用onlyIf来实现一个自定义检查,检查是否应该运行该任务。如果onlyIf内的谓词返回false,则任务将是SKIPPED