この記事は ビジネスエンジニアリング株式会社(B-EN-G)アドベントカレンダー2024 の記事です。
TL;DR
Gradle Version 8.11.1 (本記事 執筆時点で最新バージョン)の公式ドキュメントで javax.inject.Inject を使ったサンプルが複数掲載されている。しかし、掲載箇所のスクリプトだけでは動作しない。
例えば、ExecOperations を使ったサンプル(リンク )を動かすには、以下のように記述を追加する必要がある。
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) {}
動作確認バージョン
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 において従来の外部コマンドを実行するタスクは以下である。
tasks.register('simpleCommandExec') {
exec {
commandLine 'ls', '-la'
}
}
しかしながら、 exec は 非推奨 deprecated である。

非推奨ではない外部コマンドの実行方法が知りたかった。
公式ドキュメントの記載
公式ドキュメントには以下のように記載されている( 出典へのリンク )
Deprecated
Project#execandProject#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
@TaskActionordoFirst/doLastcallbacks, the use of Project instance is not allowed when the configuration cache is enabled. To run external processes, tasks should use an injectedExecOperationservice, 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.execandProviderFactory.javaexecto obtain the output of the process. A customValueSourceimplementation can be used for more sophisticated scenarios. The configuration cache guide has a more elaborate example of using these APIs.
非推奨になった経緯の説明とともに、Gradle 9.0 で削除される。
代替手段は以下の 5個であることが示されている。
- インジェクションされた ExecOperation service
- 標準の Java/Groovy/Kotlin プロセス API(例:
java.lang.ProcessBuilder) ProviderFactory.execProviderFactory.javaexec- カスタム
ValueSource実装
代替手段の 1つ目で出てくる インジェクションされた ExecOperation service
のリンク先に掲載されているサンプルコードは以下のとおりです。
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なんだね- Issue は上がっている https://github.com/gradle/gradle/issues/20183
おまけ:他の実装方法
ProviderFactory.exec
tasks.register("sampleProvidersExec") {
def result = providers.exec {
commandLine 'ls', '-la'
}.standardOutput.asText.get()
print result
}
def gitVersion = providers.exec {
commandLine("git", "--version")
}.standardOutput.asText.get()
tasks.register("printGitVersion") {
doLast {
println gitVersion
}
}
カスタム ValueSource 実装
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
}
}
