Gradle에서 build를 실행해보면 단순히 build라는 Task가 실행되는 것이 아니라, compileJava -> classes -> jar -> 등등.. 여러가지가 연결되어 실행되는 것을 보신적이 있으실 거에요! 각 Task들은 의존관계를 가질 수 있고, 어떤 Task가 실행되면 그 Task가 의존하는 Task가 실행되는 식으로 연결을 할 수가 있어요. 그것과 관련된 것들에 대해 글을 적어보려고 해요.
참고로 build task와 같은 정의된 Task를 LifeCycle Task 라고 해요. build라는 Task는 별도의 역할은 없지만, 빌드하는것과 관련된 Task들은 build라는 Task에 연결하자! 라는 느낌으로 java 플러그인이든, 다른 플러그인들이던간에 빌드와 관련된 것은 build에서 의존하도록 하고, 사용하는 사용자 입장에서는 build만 돌리면 되는 느낌으로 가는 것 같아요.
명시적으로 dependsOn을 설정하여 build 태스크에 연결하기
val packageApp = tasks.register<Zip>("packageApp") {
from(layout.projectDirectory.file("run.sh"))
from(tasks.jar) {
into("libs")
}
from(configurations.runtimeClasspath) {
into("libs")
}
destinationDirectory.set(layout.buildDirectory.dir("dist"))
archiveFileName.set("myApplication.zip")
}
tasks.build {
dependsOn(packageApp)
}
tasks.build에 dependsOn을 통해 제가 정의한 packageApp을 의존관계로 추가 했어요. 그러면 이제 build를 실행하면 packageApp도 실행이 될거에요. dependsOn은 여러개를 입력할 수도 있어요.
build 실행을 통해 의존관계에 있는 packageApp이 실행 되었어요. dependsOn은 Task의 전이가 발생해요. 즉 build를 실행하면 build가 의존을 하고 있는 task들이 실행되는 식이죠. 하지만 전이는 하지 않고 순서만 설정하고 싶은 경우도 있어요.
전이 없이 순서만 설정
의존성을 생성하지 않고 단순히 순서만 정의 할때는 mustRunAfter, shouldRunAfter를 사용해요.
mustRunAfter
tasks.register("taskB") {
mustRunAfter("taskA")
doLast {
println("Executing Task B")
}
}
지정된 태스크가 반드시 실행되야 함을 지정해요. 위 코드 예시를 들자면 taskA와 taskB를 같이 실행하는 task를 실행하는 경우, taskA가 먼저 실행되고, 그다음 taskB가 실행돼요.
shouldRunAfter
tasks.register("taskC") {
shouldRunAfter("taskA")
doLast {
println("Executing Task C")
}
}
지정된 태스크가 먼저 실행됨을 선호 하지만, 순서가 완전히 강제되는 것은 아니에요. 다른 조건에 의해 순서가 변경될 수 있어요.
task간 의존관계 뿐만 아니라 어떤 task의 output이 다른 task의 input이 되어야 하는 경우가 있어요. 이렇게 output 과 input을 연결하면 명시적으로 dependsOn을 설정해주지 않아도 의존관계가 생기게 돼요.
Task간 데이터 연결
public abstract class Producer extends DefaultTask {
@OutputFile
public abstract RegularFileProperty getOutputFile();
@TaskAction
public void produce() throws IOException {
String message = "Hello, World!";
File output = getOutputFile().get().getAsFile();
Files.writeString(output.toPath(), message, StandardOpenOption.CREATE);
getLogger().quiet("Wrote " + message + "to " + output);
}
}
public abstract class Consumer extends DefaultTask {
@InputFile
public abstract RegularFileProperty getInputFile();
@TaskAction
void consume() throws IOException {
File input = getInputFile().get().getAsFile();
String message = Files.readString(input.toPath());
getLogger().quiet("Read " + message + " from " + input);
}
}
어떤 Task의 Output이 다른 Task의 Input으로 연결되게끔 할 거에요. 위 예시와 같이 Producer task는 file을 output으로 내보내고, 그 파일을 Consumer태스크가 input으로 받도록 설정 할거에요.
val producer = tasks.register<Producer>("producer")
val consumer = tasks.register<Consumer>("consumer")
consumer {
inputFile = producer.flatMap { it.outputFile }
}
producer {
outputFile = layout.buildDirectory.file("file.txt")
}
위와 같이 설정하면, producer의 파라미터로 file.txt로 받아 실행되고, 그 결과 파일이 consumer의 inputFile 파라미터로 연결돼요. 따라서 consumer Task를 실행하면 자동으로 Producer도 실행되게 된답니다.
참고: https://docs.gradle.org/current/userguide/more_about_tasks.html
'GRADLE' 카테고리의 다른 글
Gradle 커스텀 Task 만들기 (2) | 2024.10.03 |
---|