5
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Gradle の公式ドキュメントの javax.inject.Inject を使ったサンプルを動かす方法(Project.exec のマイグレーション)

Last updated at Posted at 2024-12-01

この記事は ビジネスエンジニアリング株式会社(B-EN-G)アドベントカレンダー2024 の記事です。

TL;DR

Gradle Version 8.11.1 (本記事 執筆時点で最新バージョン)の公式ドキュメントで javax.inject.Inject を使ったサンプルが複数掲載されている。しかし、掲載箇所のスクリプトだけでは動作しない。
例えば、ExecOperations を使ったサンプル(リンク )を動かすには、以下のように記述を追加する必要がある。

build.gradle
import javax.inject.Inject

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'javax.inject:javax.inject:1'
    }
}

plugins {
    id 'java'
}
// ここより↑が追記部分
// ここから↓はサンプルスクリプトそのまま
abstract class MyExecOperationsTask extends DefaultTask {
    private ExecOperations execOperations

    @Inject //@javax.inject.Inject
    MyExecOperationsTask(ExecOperations execOperations) {
        this.execOperations = execOperations
    }

    @TaskAction
    void doTaskAction() {
        execOperations.exec {
            commandLine 'ls', '-la'
        }
    }
}

tasks.register("myInjectedExecOperationsTask", MyExecOperationsTask) {}

実行結果
image.png

動作確認バージョン

Gradle 8.11
openjdk 21.0.5 2024-10-15 LTS
OpenJDK Runtime Environment Temurin-21.0.5+11 (build 21.0.5+11-LTS)
OpenJDK 64-Bit Server VM Temurin-21.0.5+11 (build 21.0.5+11-LTS, mixed mode, sharing)

詳細:ここに至る経緯と得た知見

きっかけ

Gradle において従来の外部コマンドを実行するタスクは以下である。

build.gradle
tasks.register('simpleCommandExec') {
    exec {
        commandLine 'ls', '-la'
    }
}

しかしながら、 exec は 非推奨 deprecated である。
image.png

非推奨ではない外部コマンドの実行方法が知りたかった。

公式ドキュメントの記載

公式ドキュメントには以下のように記載されている( 出典へのリンク

Deprecated Project#exec and Project#javaexec
The Project#exec(Closure), Project#exec(Action), Project#javaexec(Closure), Project#javaexec(Action) methods have been deprecated and will be removed in Gradle 9.0.

These methods are scheduled for removal as part of the ongoing effort to make writing configuration-cache-compatible code easier. There is no way to use these methods without breaking configuration cache requirements so it is recommended to migrate to a compatible alternative. The appropriate replacement for your use case depends on the context in which the method was previously called.

At execution time, for example in @TaskAction or doFirst/doLast callbacks, the use of Project instance is not allowed when the configuration cache is enabled. To run external processes, tasks should use an injected ExecOperation service, which has the same API and can act as a drop-in replacement. The standard Java/Groovy/Kotlin process APIs, like java.lang.ProcessBuilder can be used as well.

At configuration time, only special Provider-based APIs must be used to run external processes when the configuration cache is enabled. You can use ProviderFactory.exec and ProviderFactory.javaexec to obtain the output of the process. A custom ValueSource implementation can be used for more sophisticated scenarios. The configuration cache guide has a more elaborate example of using these APIs.

非推奨になった経緯の説明とともに、Gradle 9.0 で削除される。
代替手段は以下の 5個であることが示されている。

代替手段の 1つ目で出てくる インジェクションされた ExecOperation service
のリンク先に掲載されているサンプルコードは以下のとおりです。

build.gradle
abstract class MyObjectFactoryTask extends DefaultTask {
    private ObjectFactory objectFactory

    @Inject //@javax.inject.Inject
    MyObjectFactoryTask(ObjectFactory objectFactory) {
        this.objectFactory = objectFactory
    }

    @TaskAction
    void doTaskAction() {
        var outputDirectory = objectFactory.directoryProperty()
        outputDirectory.convention(project.layout.projectDirectory)
        println(outputDirectory.get())
    }
}

tasks.register("myInjectedObjectFactoryTask",MyObjectFactoryTask) {}

しかし、上記のソースコードを build.gradle に張り付けるだけでは、@Inject の部分でコンパイルエラーが出て実行できません。解決するためには、冒頭で示したソースコード のようにする必要があります。

感想文

  • gradle を雰囲気で使っている勢としては動くようにするまで時間がかかった
  • jakarta.inject.Inject ではなく javax.inject.Inject なんだね

おまけ:他の実装方法

ProviderFactory.exec

build.gradle
tasks.register("sampleProvidersExec") {
    def result = providers.exec {
        commandLine 'ls', '-la'
    }.standardOutput.asText.get()
    print result
}
build.gradle
def gitVersion = providers.exec {
    commandLine("git", "--version")
}.standardOutput.asText.get()

tasks.register("printGitVersion") {
    doLast {
        println gitVersion
    }
}

カスタム ValueSource 実装

build.gradle
import org.gradle.api.provider.ValueSourceParameters

// >8 >8 snip >8 >8 -----

abstract class GitVersionValueSource implements ValueSource<String, ValueSourceParameters.None> {
    @Inject
    abstract ExecOperations getExecOperations()

    String obtain() {
        ByteArrayOutputStream output = new ByteArrayOutputStream()
        execOperations.exec {
            it.commandLine "git", "--version"
            it.standardOutput = output
        }
        return new String(output.toByteArray())
    }
}
def gitVersionProvider = providers.of(GitVersionValueSource.class) {}
def gitVersion = gitVersionProvider.get()

tasks.register("printGitVersion") {
    doLast {
        println gitVersion
    }
}
5
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?