この記事は ビジネスエンジニアリング株式会社(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#exec
andProject#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
ordoFirst
/doLast
callbacks, the use of Project instance is not allowed when the configuration cache is enabled. To run external processes, tasks should use an injectedExecOperation
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
andProviderFactory.javaexec
to obtain the output of the process. A customValueSource
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個であることが示されている。
- インジェクションされた ExecOperation service
- 標準の Java/Groovy/Kotlin プロセス API(例:
java.lang.ProcessBuilder
) ProviderFactory.exec
ProviderFactory.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
}
}