中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

gradle中增量構建的意思是什么

發布時間:2021-02-18 13:56:55 來源:億速云 閱讀:282 作者:Leah 欄目:開發技術

本篇文章給大家分享的是有關gradle中增量構建的意思是什么,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。

增量構建

gradle為了提升構建的效率,提出了增量構建的概念,為了實現增量構建,gradle將每一個task都分成了三部分,分別是input輸入,任務本身和output輸出。

自定義inputs和outputs

既然task中的input和output在增量編譯中這么重要,本章將會給大家講解一下怎么才能夠在task中定義input和output。

如果我們自定義一個task類型,那么滿足下面兩點就可以使用上增量構建了:

第一點,需要為task中的inputs和outputs添加必要的getter方法。

第二點,為getter方法添加對應的注解。

gradle支持三種主要的inputs和outputs類型:

  1. 簡單類型:簡單類型就是所有實現了Serializable接口的類型,比如說string和數字。

  2. 文件類型:文件類型就是 File 或者 FileCollection 的衍生類型,或者其他可以作為參數傳遞給 Project.file(java.lang.Object) 和 Project.files(java.lang.Object…) 的類型。

  3. 嵌套類型:有些自定義類型,本身不屬于前面的1,2兩種類型,但是它內部含有嵌套的inputs和outputs屬性,這樣的類型叫做嵌套類型。

接下來,我們來舉個例子,假如我們有一個類似于FreeMarker和Velocity這樣的模板引擎,負責將模板源文件,要傳遞的數據最后生成對應的填充文件,我們考慮一下他的輸入和輸出是什么。

輸入:模板源文件,模型數據和模板引擎。

輸出:要輸出的文件。

如果我們要編寫一個適用于模板轉換的task,我們可以這樣寫:

import java.io.File;
import java.util.HashMap;
import org.gradle.api.*;
import org.gradle.api.file.*;
import org.gradle.api.tasks.*;

public class ProcessTemplates extends DefaultTask {
  private TemplateEngineType templateEngine;
  private FileCollection sourceFiles;
  private TemplateData templateData;
  private File outputDir;

  @Input
  public TemplateEngineType getTemplateEngine() {
    return this.templateEngine;
  }

  @InputFiles
  public FileCollection getSourceFiles() {
    return this.sourceFiles;
  }

  @Nested
  public TemplateData getTemplateData() {
    return this.templateData;
  }

  @OutputDirectory
  public File getOutputDir() { return this.outputDir; }

  // 上面四個屬性的setter方法

  @TaskAction
  public void processTemplates() {
    // ...
  }
}

上面的例子中,我們定義了4個屬性,分別是TemplateEngineType,FileCollection,TemplateData和File。前面三個屬性是輸入,后面一個屬性是輸出。

除了getter和setter方法之外,我們還需要在getter方法中添加相應的注釋: @Input , @InputFiles ,@Nested 和 @OutputDirectory, 除此之外,我們還定義了一個 @TaskAction 表示這個task要做的工作。

TemplateEngineType表示的是模板引擎的類型,比如FreeMarker或者Velocity等。我們也可以用String來表示模板引擎的名字。但是為了安全起見,這里我們自定義了一個枚舉類型,在枚舉類型內部我們可以安全的定義各種支持的模板引擎類型。

因為enum默認是實現Serializable的,所以這里可以作為@Input使用。

sourceFiles使用的是FileCollection,表示的是一系列文件的集合,所以可以使用@InputFiles。

為什么TemplateData是@Nested類型的呢?TemplateData表示的是我們要填充的數據,我們看下它的實現:

import java.util.HashMap;
import java.util.Map;
import org.gradle.api.tasks.Input;

public class TemplateData {
  private String name;
  private Map<String, String> variables;

  public TemplateData(String name, Map<String, String> variables) {
    this.name = name;
    this.variables = new HashMap<>(variables);
  }

  @Input
  public String getName() { return this.name; }

  @Input
  public Map<String, String> getVariables() {
    return this.variables;
  }
}

可以看到,雖然TemplateData本身不是File或者簡單類型,但是它內部的屬性是簡單類型的,所以TemplateData本身可以看做是@Nested的。

outputDir表示的是一個輸出文件目錄,所以使用的是@OutputDirectory。

使用了這些注解之后,gradle在構建的時候就會檢測和上一次構建相比,這些屬性有沒有發送變化,如果沒有發送變化,那么gradle將會直接使用上一次構建生成的緩存。

注意,上面的例子中我們使用了FileCollection作為輸入的文件集合,考慮一種情況,假如只有文件集合中的某一個文件發送變化,那么gradle是會重新構建所有的文件,還是只重構這個被修改的文件呢?
留給大家討論

除了上講到的4個注解之外,gradle還提供了其他的幾個有用的注解:

@InputFile: 相當于File,表示單個input文件。

@InputDirectory: 相當于File,表示單個input目錄。

@Classpath: 相當于Iterable,表示的是類路徑上的文件,對于類路徑上的文件需要考慮文件的順序。如果類路徑上的文件是jar的話,jar中的文件創建時間戳的修改,并不會影響input。

@CompileClasspath:相當于Iterable,表示的是類路徑上的java文件,會忽略類路徑上的非java文件。

@OutputFile: 相當于File,表示輸出文件。

@OutputFiles: 相當于Map<String, File> 或者 Iterable,表示輸出文件。

@OutputDirectories: 相當于Map<String, File> 或者 Iterable,表示輸出文件。

@Destroys: 相當于File 或者 Iterable,表示這個task將會刪除的文件。

@LocalState: 相當于File 或者 Iterable,表示task的本地狀態。

@Console: 表示屬性不是input也不是output,但是會影響console的輸出。

@Internal: 內部屬性,不是input也不是output。

@ReplacedBy: 屬性被其他的屬性替換了,不能算在input和output中。

@SkipWhenEmpty: 和@InputFiles 跟 @InputDirectory一起使用,如果相應的文件或者目錄為空的話,將會跳過task的執行。

@Incremental: 和@InputFiles 跟 @InputDirectory一起使用,用來跟蹤文件的變化。

@Optional: 忽略屬性的驗證。

@PathSensitive: 表示需要考慮paths中的哪一部分作為增量的依據。

運行時API

自定義task當然是一個非常好的辦法來使用增量構建。但是自定義task類型需要我們編寫新的class文件。有沒有什么辦法可以不用修改task的源代碼,就可以使用增量構建呢?

答案是使用Runtime API。

gradle提供了三個API,用來對input,output和Destroyables進行獲取:

  • Task.getInputs() of type TaskInputs

  • Task.getOutputs() of type TaskOutputs

  • Task.getDestroyables() of type TaskDestroyables

獲取到input和output之后,我們就是可以其進行操作了,我們看下怎么用runtime API來實現之前的自定義task:

task processTemplatesAdHoc {
  inputs.property("engine", TemplateEngineType.FREEMARKER)
  inputs.files(fileTree("src/templates"))
    .withPropertyName("sourceFiles")
    .withPathSensitivity(PathSensitivity.RELATIVE)
  inputs.property("templateData.name", "docs")
  inputs.property("templateData.variables", [year: 2013])
  outputs.dir("$buildDir/genOutput2")
    .withPropertyName("outputDir")

  doLast {
    // Process the templates here
  }
}

上面例子中,inputs.property() 相當于 @Input ,而outputs.dir() 相當于@OutputDirectory。

Runtime API還可以和自定義類型一起使用:

task processTemplatesWithExtraInputs(type: ProcessTemplates) {
  // ...

  inputs.file("src/headers/headers.txt")
    .withPropertyName("headers")
    .withPathSensitivity(PathSensitivity.NONE)
}

上面的例子為ProcessTemplates添加了一個input。

隱式依賴

除了直接使用dependsOn之外,我們還可以使用隱式依賴:

task packageFiles(type: Zip) {
  from processTemplates.outputs
}

上面的例子中,packageFiles 使用了from,隱式依賴了processTemplates的outputs。

gradle足夠智能,可以檢測到這種依賴關系。

上面的例子還可以簡寫為:

task packageFiles2(type: Zip) {
  from processTemplates
}

我們看一個錯誤的隱式依賴的例子:

plugins {
  id 'java'
}

task badInstrumentClasses(type: Instrument) {
  classFiles = fileTree(compileJava.destinationDir)
  destinationDir = file("$buildDir/instrumented")
}

這個例子的本意是執行compileJava任務,然后將其輸出的destinationDir作為classFiles的值。

但是因為fileTree本身并不包含依賴關系,所以上面的執行的結果并不會執行compileJava任務。

我們可以這樣改寫:

task instrumentClasses(type: Instrument) {
  classFiles = compileJava.outputs.files
  destinationDir = file("$buildDir/instrumented")
}

或者使用layout:

task instrumentClasses2(type: Instrument) {
  classFiles = layout.files(compileJava)
  destinationDir = file("$buildDir/instrumented")
}

或者使用buildBy:

task instrumentClassesBuiltBy(type: Instrument) {
  classFiles = fileTree(compileJava.destinationDir) {
    builtBy compileJava
  }
  destinationDir = file("$buildDir/instrumented")
}

輸入校驗

gradle會默認對@InputFile ,@InputDirectory 和 @OutputDirectory 進行參數校驗。

如果你覺得這些參數是可選的,那么可以使用@Optional。

自定義緩存方法

上面的例子中,我們使用from來進行增量構建,但是from并沒有添加@InputFiles, 那么它的增量緩存是怎么實現的呢?

我們看一個例子:

public class ProcessTemplates extends DefaultTask {
  // ...
  private FileCollection sourceFiles = getProject().getLayout().files();

  @SkipWhenEmpty
  @InputFiles
  @PathSensitive(PathSensitivity.NONE)
  public FileCollection getSourceFiles() {
    return this.sourceFiles;
  }

  public void sources(FileCollection sourceFiles) {
    this.sourceFiles = this.sourceFiles.plus(sourceFiles);
  }

  // ...
}

上面的例子中,我們將sourceFiles定義為可緩存的input,然后又定義了一個sources方法,可以將新的文件加入到sourceFiles中,從而改變sourceFile input,也就達到了自定義修改input緩存的目的。

我們看下怎么使用:

task processTemplates(type: ProcessTemplates) {
  templateEngine = TemplateEngineType.FREEMARKER
  templateData = new TemplateData("test", [year: 2012])
  outputDir = file("$buildDir/genOutput")

  sources fileTree("src/templates")
}

我們還可以使用project.layout.files()將一個task的輸出作為輸入,可以這樣做:

 public void sources(Task inputTask) {
    this.sourceFiles = this.sourceFiles.plus(getProject().getLayout().files(inputTask));
  }

這個方法傳入一個task,然后使用project.layout.files()將task的輸出作為輸入。

看下怎么使用:

task copyTemplates(type: Copy) {
  into "$buildDir/tmp"
  from "src/templates"
}

task processTemplates2(type: ProcessTemplates) {
  // ...
  sources copyTemplates
}

非常的方便。

如果你不想使用gradle的緩存功能,那么可以使用upToDateWhen()來手動控制:

task alwaysInstrumentClasses(type: Instrument) {
  classFiles = layout.files(compileJava)
  destinationDir = file("$buildDir/instrumented")
  outputs.upToDateWhen { false }
}

上面使用false,表示alwaysInstrumentClasses這個task將會一直被執行,并不會使用到緩存。

輸入歸一化

要想比較gradle的輸入是否是一樣的,gradle需要對input進行歸一化處理,然后才進行比較。

我們可以自定義gradle的runtime classpath 。

normalization {
  runtimeClasspath {
    ignore 'build-info.properties'
  }
}

上面的例子中,我們忽略了classpath中的一個文件。

我們還可以忽略META-INF中的manifest文件的屬性:

normalization {
  runtimeClasspath {
    metaInf {
      ignoreAttribute("Implementation-Version")
    }
  }
}

忽略META-INF/MANIFEST.MF :

normalization {
  runtimeClasspath {
    metaInf {
      ignoreManifest()
    }
  }
}

忽略META-INF中所有的文件和目錄:

normalization {
  runtimeClasspath {
    metaInf {
      ignoreCompletely()
    }
  }
}

以上就是gradle中增量構建的意思是什么,小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

建德市| 宣威市| 卓资县| 蕉岭县| 怀化市| 综艺| 吉安市| 佛坪县| 洛阳市| 广安市| 米脂县| 区。| 若尔盖县| 肇庆市| 新昌县| 西城区| 客服| 云安县| 伊吾县| 郑州市| 桂阳县| 侯马市| 上饶市| 云南省| 彰化市| 吴桥县| 横峰县| 饶平县| 天等县| 武汉市| 永胜县| 江陵县| 叙永县| 峨眉山市| 宜兰县| 体育| 客服| 墨竹工卡县| 彩票| 德江县| 华容县|