成人午夜视频全免费观看高清-秋霞福利视频一区二区三区-国产精品久久久久电影小说-亚洲不卡区三一区三区一区

gradle中的增量構(gòu)建方式

本篇內(nèi)容主要講解“gradle中的增量構(gòu)建方式”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“gradle中的增量構(gòu)建方式”吧!

成都創(chuàng)新互聯(lián)公司堅(jiān)持“要么做到,要么別承諾”的工作理念,服務(wù)領(lǐng)域包括:成都網(wǎng)站設(shè)計(jì)、網(wǎng)站制作、外貿(mào)營銷網(wǎng)站建設(shè)、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣等服務(wù),滿足客戶于互聯(lián)網(wǎng)時(shí)代的藍(lán)山網(wǎng)站設(shè)計(jì)、移動媒體設(shè)計(jì)的需求,幫助企業(yè)找到有效的互聯(lián)網(wǎng)解決方案。努力成為您成熟可靠的網(wǎng)絡(luò)建設(shè)合作伙伴!

簡介

在我們使用的各種工具中,為了提升工作效率,總會使用到各種各樣的緩存技術(shù),比如說docker中的layer就是緩存了之前構(gòu)建的image。在gradle中這種以task組合起來的構(gòu)建工具也不例外,在gradle中,這種技術(shù)叫做增量構(gòu)建。

增量構(gòu)建

gradle為了提升構(gòu)建的效率,提出了增量構(gòu)建的概念,為了實(shí)現(xiàn)增量構(gòu)建,gradle將每一個(gè)task都分成了三部分,分別是input輸入,任務(wù)本身和output輸出。下圖是一個(gè)典型的java編譯的task。

gradle中的增量構(gòu)建方式

以上圖為例,input就是目標(biāo)jdk的版本,源代碼等,output就是編譯出來的class文件。

增量構(gòu)建的原理就是監(jiān)控input的變化,只有input發(fā)送變化了,才重新執(zhí)行task任務(wù),否則gradle認(rèn)為可以重用之前的執(zhí)行結(jié)果。

所以在編寫gradle的task的時(shí)候,需要指定task的輸入和輸出。

并且要注意只有會對輸出結(jié)果產(chǎn)生變化的才能被稱為輸入,如果你定義了對初始結(jié)果完全無關(guān)的變量作為輸入,則這些變量的變化會導(dǎo)致gradle重新執(zhí)行task,導(dǎo)致了不必要的性能的損耗。

還要注意不確定執(zhí)行結(jié)果的任務(wù),比如說同樣的輸入可能會得到不同的輸出結(jié)果,那么這樣的任務(wù)將不能夠被配置為增量構(gòu)建任務(wù)。

自定義inputs和outputs

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

如果我們自定義一個(gè)task類型,那么滿足下面兩點(diǎn)就可以使用上增量構(gòu)建了:

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

第二點(diǎn),為getter方法添加對應(yīng)的注解。

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

  1. 簡單類型:簡單類型就是所有實(shí)現(xiàn)了Serializable接口的類型,比如說string和數(shù)字。

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

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

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

輸入:模板源文件,模型數(shù)據(jù)和模板引擎。

輸出:要輸出的文件。

如果我們要編寫一個(gè)適用于模板轉(zhuǎn)換的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](https://my.oschina.net/InPuto)
    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; }

    // 上面四個(gè)屬性的setter方法

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

上面的例子中,我們定義了4個(gè)屬性,分別是TemplateEngineType,F(xiàn)ileCollection,TemplateData和File。前面三個(gè)屬性是輸入,后面一個(gè)屬性是輸出。

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

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

因?yàn)閑num默認(rèn)是實(shí)現(xiàn)Serializable的,所以這里可以作為@Input使用。

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

為什么TemplateData是@Nested類型的呢?TemplateData表示的是我們要填充的數(shù)據(jù),我們看下它的實(shí)現(xiàn):

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或者簡單類型,但是它內(nèi)部的屬性是簡單類型的,所以TemplateData本身可以看做是@Nested的。

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

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

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

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

  • @InputFile: 相當(dāng)于File,表示單個(gè)input文件。

  • @InputDirectory: 相當(dāng)于File,表示單個(gè)input目錄。

  • @Classpath: 相當(dāng)于Iterable<File>,表示的是類路徑上的文件,對于類路徑上的文件需要考慮文件的順序。如果類路徑上的文件是jar的話,jar中的文件創(chuàng)建時(shí)間戳的修改,并不會影響input。

  • @CompileClasspath:相當(dāng)于Iterable<File>,表示的是類路徑上的java文件,會忽略類路徑上的非java文件。

  • @OutputFile: 相當(dāng)于File,表示輸出文件。

  • @OutputFiles: 相當(dāng)于Map<String, File> 或者 Iterable<File>,表示輸出文件。

  • @OutputDirectories: 相當(dāng)于Map<String, File> 或者 Iterable<File>,表示輸出文件。

  • @Destroys: 相當(dāng)于File 或者 Iterable<File>,表示這個(gè)task將會刪除的文件。

  • @LocalState: 相當(dāng)于File 或者 Iterable<File>,表示task的本地狀態(tài)。

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

  • @Internal: 內(nèi)部屬性,不是input也不是output。

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

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

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

  • @Optional: 忽略屬性的驗(yàn)證。

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

運(yùn)行時(shí)API

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

答案是使用Runtime API。

gradle提供了三個(gè)API,用來對input,output和Destroyables進(jìn)行獲取:

  • Task.getInputs() of type TaskInputs

  • Task.getOutputs() of type TaskOutputs

  • Task.getDestroyables() of type TaskDestroyables

獲取到input和output之后,我們就是可以其進(jìn)行操作了,我們看下怎么用runtime API來實(shí)現(xiàn)之前的自定義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() 相當(dāng)于 @Input ,而outputs.dir() 相當(dāng)于@OutputDirectory。

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

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

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

上面的例子為ProcessTemplates添加了一個(gè)input。

隱式依賴

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

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

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

gradle足夠智能,可以檢測到這種依賴關(guān)系。

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

task packageFiles2(type: Zip) {
    from processTemplates
}

我們看一個(gè)錯誤的隱式依賴的例子:

plugins {
    id 'java'
}

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

這個(gè)例子的本意是執(zhí)行compileJava任務(wù),然后將其輸出的destinationDir作為classFiles的值。

但是因?yàn)閒ileTree本身并不包含依賴關(guān)系,所以上面的執(zhí)行的結(jié)果并不會執(zhí)行compileJava任務(wù)。

我們可以這樣改寫:

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")
}

輸入校驗(yàn)

gradle會默認(rèn)對@InputFile ,@InputDirectory 和 @OutputDirectory 進(jìn)行參數(shù)校驗(yàn)。

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

自定義緩存方法

上面的例子中,我們使用from來進(jìn)行增量構(gòu)建,但是from并沒有添加@InputFiles, 那么它的增量緩存是怎么實(shí)現(xiàn)的呢?

我們看一個(gè)例子:

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,然后又定義了一個(gè)sources方法,可以將新的文件加入到sourceFiles中,從而改變sourceFile input,也就達(dá)到了自定義修改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()將一個(gè)task的輸出作為輸入,可以這樣做:

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

這個(gè)方法傳入一個(gè)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這個(gè)task將會一直被執(zhí)行,并不會使用到緩存。

輸入歸一化

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

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

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

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

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

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

忽略META-INF/MANIFEST.MF :

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

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

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

其他使用技巧

如果你的gradle因?yàn)槟撤N原因暫停了,你可以送 --continuous 或者 -t 參數(shù),來重用之前的緩存,繼續(xù)構(gòu)建gradle項(xiàng)目。

你還可以使用 --parallel 來并行執(zhí)行task。

到此,相信大家對“gradle中的增量構(gòu)建方式”有了更深的了解,不妨來實(shí)際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

分享標(biāo)題:gradle中的增量構(gòu)建方式
URL鏈接:http://jinyejixie.com/article26/pphojg.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供服務(wù)器托管、網(wǎng)頁設(shè)計(jì)公司、微信小程序ChatGPT、商城網(wǎng)站品牌網(wǎng)站制作

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

商城網(wǎng)站建設(shè)
疏附县| 中牟县| 普宁市| 滨州市| 平邑县| 宝应县| 平山县| 北碚区| 郓城县| 工布江达县| 黄冈市| 大兴区| 潞西市| 昔阳县| 卫辉市| 涪陵区| 清苑县| 潍坊市| 泾川县| 军事| 隆昌县| 洪洞县| 台州市| 遂溪县| 巧家县| 漾濞| 晋中市| 繁峙县| 包头市| 兴隆县| 鄂托克旗| 徐闻县| 平武县| 咸丰县| 讷河市| 维西| 炎陵县| 双辽市| 兴化市| 新津县| 乌海市|