此頁面總結 Gradle 回報的不同工作 (或一般工作) 驗證問題,並提供修正這些問題的指南。

快取註解使用不當

此錯誤表示您已使用 @CacheableTransform 註解非人工製品轉換的類型,或使用 @CacheableTask 註解非 Task 的類型。

解決方案是移除註解。

對於工作,要使用的註解是 @CacheableTask。對於人工製品轉換,要使用的註解是 @CacheableTransform

缺少正規化註解

當工作或人工製品轉換可快取,而檔案或檔案集合輸入屬性未宣告正規化方式時,就會發生此錯誤。正規化會告知 Gradle,例如輸入檔案的絕對路徑是否重要,或只有內容相關。如果您未宣告正規化策略,則工作輸出無法在機器之間或同一台機器上的不同位置之間重複使用。簡而言之,沒有正規化,快取將非常低效。

若要修正此問題,您需要宣告正規化策略,套用下列其中一個註解

未設定必要值

此錯誤表示屬性預期有值,但未提供任何值。預設情況下,Gradle 屬性為必要,亦即如果輸入或輸出屬性未設定,無論是透過慣例值或明確地透過建置指令碼,Gradle 都會失敗,因為它不知道在執行工作時要使用什麼值。

若要修正問題,您必須

  • 明確提供此屬性的值 (例如透過在建置指令碼中設定工作)

  • 或透過使用 @Optional 註解屬性,使其為選用

不當使用絕對路徑敏感度作為人工製品轉換

此錯誤表示您已註解人工製品轉換的輸入,使其對絕對路徑敏感。然而,人工製品轉換會使用自己的工作區獨立執行,例如,能抵禦「清除」建置。即使無法透過建置快取分享人工製品轉換結果,使用絕對路徑敏感度也沒有意義。

若要修正此問題,您必須使用下列其中一個方式變更正規化策略

不當使用人工製品轉換的輸出屬性

此錯誤表示您已使用輸出註解註解人工製品轉換的屬性,這並非註冊人工製品轉換輸出的正確方式。

若要修正此問題,您必須移除該屬性,並改用 TransformOutputs 參數。

有關詳細資訊,請參閱 TransformAction#transform(TransformOutputs)

不當使用欄位註解

此錯誤表示您已註解欄位,但此欄位沒有 getter。只有當欄位有對應的 getter 時,Gradle 才會辨識欄位的註解。如果沒有這個 getter,註解將完全沒有作用。

若要修正此問題,您需要為此欄位建立一個 getter。我們也建議您註解 getter,而不是欄位。

如果您使用 Groovy,您可能不小心在屬性宣告中加入了 private 修飾詞

@InputFile
RegularFileProperty inputFile // this is a public property

@InputFile
private RegularFileProperty inputFile // this is a field, remove the `private` modifier

不當使用方法註解

此錯誤表示註解已放置在預期之外的方法中。一般來說,像 @InputFiles@OutputDirectory 這樣的註解需要放置在「屬性」方法中。屬性是透過 getter 定義的。

這會導致 Gradle 忽略註解,因此這些註解通常不會用於最新檢查或快取中。

若要修正此問題,您必須移除註解,為您要使用的屬性建立一個 getter,並註解 getter。

可變類型與 setter

此錯誤表示「可變」類型的屬性也提供 setter。Gradle 中的可變類型包括 PropertyConfigurableFileCollection

例如,您寫了

class MyTask extends DefaultTask {
    private Property<Integer> x

    @Input
    Property<Integer> getX() { this.x }

    void setX(Property<Integer> x) { this.x = x }
}

然而,像 Property 這樣的可變類型設計用於追蹤依賴項和提供給它們的值的來源。因此,讓它們可覆寫會造成錯誤,因為 Gradle 將無法得知屬性值從何而來。

若要修正此問題,您應讓您的屬性為 final 並移除 setter

class MyTask extends DefaultTask {
    @Input
    final Property<Integer> getX() = ...

您也可以依賴 Gradle 內建的注入 final 屬性的功能

abstract class MyTask {
    abstract Property<Integer> getX()
}

然後,屬性的值需要透過變異方法進行設定

myTask {
    x.set(123)
}

多餘的 getter

此錯誤表示 boolean 屬性同時具有 getis getter 方法。這是個問題,因為兩個 getter 可以有不同的註解,而 Gradle 無法知道要使用哪一個。

解決此問題的方法是移除其中一個 getter,或使用 @Internal 標記其中一個 getter

私人 getter 上的註解

此錯誤表示您已使用輸入或輸出註解註解 私人 getter。Gradle 不會將私人 getter 視為最新檢查的輸入,這表示您的註解實際上已被忽略。這很重要,因為您可能會認為您已宣告輸入,但事實並非如此。

若要修正此問題,請讓 getter 公開,或註解現有的 getter,或建立新的註解 getter。

已忽略屬性上的註解

此錯誤表示您有一個屬性,它已註解為告訴 Gradle 忽略它的註解(例如 @ReplacedBy),但它也已註解為輸入註解(例如 @InputFile)。

這是錯誤,因為 Gradle 無法判斷屬性是否應實際用於最新檢查,也就是說,它是否實際上是輸入。

若要修正此問題,您必須

  • 從屬性中移除輸入註解,或

  • 從屬性中移除忽略註解。

衝突註解

此錯誤表示屬性已註解有衝突註解,也就是說,具有不同且無法調和語意的註解。

例如,屬性無法同時註解有 @InputFile@OutputFile

若要修正此問題,您需要了解不同註解的語意,並僅選取一個。

註解在特定內容中無效

此錯誤表示屬性已註解有在特定內容中無效的註解。例如,通常可以註解 DirectoryProperty@OutputDirectory,但在人工製品轉換的內容中無效,因為人工製品轉換提供自己的工作區。

若要修正此問題,您必須移除屬性。

沒有註解的屬性

此錯誤表示屬性未註解有輸入或輸出註解。因此,Gradle 不知道此屬性表示輸入、輸出,或僅應忽略。結果,最新檢查和快取將無法運作。

若要修正此問題,您需要註解屬性有適當的註解,例如,對於表示輸入目錄的屬性,註解為 @InputDirectory,或對於表示輸出目錄的屬性,註解為 @OutputDirectory

或者,如果屬性是內部的,也就是說,它不應參與最新檢查(它不是輸入或輸出),則您需要註解它有 @Internal

註解與屬性類型不相容

此錯誤表示對於特定類型的屬性,修改器註解沒有意義。例如,如果在輸出屬性上使用 @SkipWhenEmpty,就會發生這種情況。由於此組合沒有關聯的語意,因此 Gradle 無法推論您的意圖。

要修正這個問題,你很可能需要移除衝突的修改器註解,或檢查實際的屬性類型是否是你想要的。

不正確使用 @Input 註解

這個錯誤表示一個屬性已註解為 @Input,但它應該註解為 @InputFile@InputDirectory

如果你在一個基於檔案的屬性上使用 @Input 註解,Gradle 就不會將檔案內容或目錄內容視為輸入,就像你預期的。

要修正這個問題,你需要告訴 Gradle 這個檔案屬性是否代表一個輸入檔案,如果是,你應該用 @InputFile 註解它,或是一個目錄,如果是,你應該用 @InputDirectory 註解它。如果你真正想要表達的是實際檔案路徑是一個輸入,那麼你應該回傳一個 String,它對應到檔案的絕對路徑。

註解為 @ServiceReference 的屬性不是一個 BuildService

這個錯誤表示一個註解為 @ServiceReference 的屬性有一個類型,它沒有實作 BuildService 介面。

註解為 @ServiceReference 的屬性,用來保存對 共用建置服務 的參照。

任務之間的隱式依賴性

這個錯誤表示你有一個任務依賴於另一個任務,但這兩個任務之間沒有宣告明確或隱式的依賴性。因此,建置的結果會依賴於任務執行的順序,這通常稱為「任務之間的意外依賴性」。通常,這是因為你直接參照另一個任務的輸出檔案,而不是直接使用該任務作為輸入。

例如,想像你有一個任務,它將 ConfigurableFileCollection 作為輸入,而且你已經使用以下方式宣告對 jar 任務的依賴性:

someTask {
    inputFile.from(jar.archivePath)
}

jar.archivePath 屬性為 File 類型,不具備任何工作任務相依性。這表示如果在呼叫 jar 之後 呼叫 someTask,工作任務會成功,但如果移除 jar,工作任務就會失敗。

若要修正此問題,您可以宣告一個 Property 作為輸入

someTask {
    inputFile.from(jar.archiveFile)
}

jar.archiveFile 屬性為 Provider<RegularFile> 類型,可適當地傳遞工作任務相依性:Gradle 將能夠知道檔案是由 jar 工作任務產生的。

實際上,為工作任務本身新增一個隱含相依性更為容易

someTask {
    inputFile.from(jar)
}

在某些情況下,對於不使用 組態迴避 API 的生產者工作任務,您可以宣告一個對工作任務的明確相依性

someTask {
    dependsOn(producer)
    inputFile.from(producer.someFile)
}

在某些情況下,不希望新增對產生工作任務的相依性,例如當使用者為可能的多個工作任務產生報告時。在此情況下,您可以使用 Task.mustRunAfter() 在兩個工作任務之間建立 排序

輸入檔案不存在

當檔案 (或目錄) 宣告為工作任務的輸入,但在執行工作任務時,檔案 (或目錄) 不存在時,就會發生此錯誤。

這通常表示缺少工作任務相依性:檔案應在執行工作任務之前 存在,這表示相依工作任務尚未執行。

症狀類似於 工作任務之間的隱含相依性,但在此情況下,建立檔案的工作任務尚未執行。

請參閱 工作任務之間的隱含相依性 區段以取得可能的解決方案。如果檔案不是由另一個工作任務產生的,您可能需要在呼叫工作任務之前確認檔案存在。如果您要宣告的是在執行工作任務時檔案存在與否並不重要,您可以使用 @InputFiles,這不會因為不存在的輸入而失敗

build.gradle.kts
abstract class GreetingFileTask : DefaultTask() {

    @get:InputFiles
    abstract val source: RegularFileProperty

    @get:OutputFile
    abstract val destination: RegularFileProperty

    @TaskAction
    fun greet() {
        val file = destination.get().asFile
        if (source.get().asFile.exists()) {
            file.writeText("Hello ${source.get().asFile.readText()}")
        } else {
            file.writeText("Hello missing file!")
        }
    }
}
build.gradle
abstract class GreetingFileTask extends DefaultTask {

    @InputFiles
    abstract RegularFileProperty getSource()

    @OutputFile
    abstract RegularFileProperty getDestination()

    @TaskAction
    def greet() {
        def file = getDestination().get().asFile
        if (source.get().asFile.exists()) {
            file.write("Hello ${source.get().asFile.text}!")
        } else {
            file.write 'Hello missing file!'
        }
    }
}

意外的輸入檔案或目錄

此錯誤表示屬性預期一個常規檔案作為輸入,但提供給它的卻是一個目錄 (或相反)。

例如,如果屬性加上 @InputFile 註解

@InputFile
File getInputFile()

然後 Gradle 預期輸入檔案為一般檔案。如果輸入為目錄,則驗證會失敗。

若要修正此問題,您有兩個選項

  • 您不是輸入檔案而是輸入目錄,這種情況下您只需要修正輸入

  • 或工作實際上應該使用目錄作為輸入,這種情況下您需要將屬性的類型變更為 @InputDirectory

無法寫入輸出檔案或目錄

此錯誤表示

  • 無法寫入輸出目錄,因為已設定的目錄屬性實際上是指一般檔案(或實際目錄以外的其他項目)。

  • 或無法寫入輸出檔案,因為已設定的檔案屬性實際上是指目錄。

  • 或輸出位置的父目錄存在且為檔案。

例如,您已將輸出目錄設定為 /some/path/file.txt 而不是 /some/path。您也可能已設定輸出目錄為 /some/path,但祖先 /some 為一般檔案。

若要修正此問題,請確定已設定的輸出為目錄(針對預期目錄的屬性)或檔案(針對預期檔案的工作)。

無法寫入保留位置

此錯誤表示您嘗試將檔案寫入僅由 Gradle 管理的位置。通常,這是當您嘗試將檔案直接寫入成品轉換輸出目錄時發生。如果您有心這麼做,這是錯誤的,因為這些目錄不應直接寫入:所有成品轉換寫入都應在成品轉換程式碼本身內執行。

如果您沒有打算寫入此目錄,您應僅設定工作以寫入不同位置。

檔案輸入中的不支援符號

此錯誤表示檔案、目錄、檔案集合或檔案的巢狀集合屬性,是指 Gradle 無法轉換為檔案的元素。

若要修正此問題,請查看錯誤訊息,其中指出支援的檔案符號清單,並確定選取其中一個符號。

在基本類型上使用 @Optional 註解無效

此錯誤表示基本類型的屬性也註解了 @Optional。這類似於在 Java 中 null 無法指定給基本類型。

若要修正此問題,您有兩個選項

  • 移除 @Optional 註解

  • 或如果你打算讓屬性為可為空,請使用封裝類型(例如 Integer 而不是 int)

無法使用具有未知實作的輸入

此錯誤表示任務使用類別作為輸入,而 Gradle 無法追蹤類別的實作。Gradle 將下列類別的實作視為任務的輸入

  • 任務類別,

  • 任務動作的類別,

  • 以及任務的巢狀輸入類別,即註解為 @Nested 的輸入。

Gradle 無法追蹤類別實作有兩個原因

  • 使用非序列化 Java lambda 實作類別,

  • 或類別已由 Gradle 未知的類別載入器載入。

使用 Java lambda 表示位元組碼使用 invoke-dynamic,而不是建立實際子類別。invoke-dynamic 指令的類別是在 JVM 執行階段產生的,如果目標函式介面不可序列化,Gradle 無法在不同的 JVM 中唯一識別此類別。對於任務動作來說這不是問題,因為 Gradle 以特殊方式處理它們。另一方面,對於由非序列化 lambda 實作的巢狀輸入類別,Gradle 不支援追蹤。作為解決方法,你可以將 lambda 轉換為匿名內部類別,或讓目標函式介面可序列化。

對於 Gradle 無法追蹤實作是因為它是由 Gradle 未知的類別載入器載入的情況,你可以使用 Gradle 內建的方式載入類別。

缺少不快取的原因

此警告表示任務或人工製品轉換動作未標記為可快取,儘管也沒有原因說明它不可快取。任務或人工製品轉換作者應始終使用 @DisableCachingByDefault 註解提供不可快取的原因。

要解決此問題,請使用 @CacheableTask/@CacheableTransform@DisableCachingByDefault(because = "…​") 註解工作類型。

不支援的值類型

此訊息表示任務宣告輸入屬性具有不支援類型的值。

要解決此問題,請查看訊息,其中指出可能的解決方案清單。

請在下方找到不支援值類型的清單

ResolvedArtifactResult

使用相依性解析結果作為工作輸入

java.net.URL

此類型不支援使用 @Input 註解的屬性,因為此類型的最新檢查可能不一致,導致不正確的建置結果。這是因為 Java 中已知的問題,其中 java.net.URL 的序列化不正確。請參閱 OpenJDK 錯誤報告 以取得更多詳細資訊。為了解決此問題,我們建議改用 java.net.URI。

不支援的巢狀映射鍵類型

此錯誤表示巢狀映射宣告不支援類型的鍵。Gradle 使用鍵為 (子) 屬性產生名稱。只允許 Enum、Integer 和 String 類型的鍵可保證這些名稱是唯一的且格式正確,而且比依賴 toString() 來產生此類名稱更好。

若要修正此問題,請將鍵的類型變更為 Enum、Integer 或 String。

不支援的巢狀類型

此錯誤表示不支援的類型被註解為巢狀。巢狀類型預期宣告一些註解屬性 (本身會檢查註解) 或一些條件式行為,其中擷取類型本身作為輸入很重要。Java SE API 的類型、Kotlin stdlib 的類型和 Groovy 的 GString 不受支援,因為它們不符合這些需求。

若要修正此問題,請宣告巢狀類型,例如 Provider<T>、Iterable<T> 或 <MapProperty<K, V>>,其中 T 和 V 有一些註解屬性或一些需要擷取類型作為輸入的行為。