Hi, I need to configure a `Copy` task with data fr...
# community-support
m
Hi, I need to configure a
Copy
task with data from a file produced by another task. Currently that's done by cross-configuring the second task from the first. I'm trying to find a better way, but so far have come up empty. Do you have any pointers for me, or am I stuck with using cross-configuration?
v
You really shouldn't change the configuration of a task at execution phase, especially not its inputs and outputs. How to properly do it depends on the concrete details. This for example works fine and without execution phase configuration or cross-configuration:
Copy code
abstract class Foo : DefaultTask() {
    @get:OutputFile
    abstract val output: RegularFileProperty

    @TaskAction
    fun execute() {
        output.get().asFile.writeText(
            """
                gradlew
                gradlew.bat
            """.trimIndent()
        )
    }
}

val foo by tasks.registering(Foo::class) {
    output = layout.buildDirectory.file("foo.txt")
}

val sync by tasks.registering(Sync::class) {
    from(foo.flatMap { it.output }.map { it.asFile.readLines() })
    into(layout.buildDirectory.dir("synced"))
}
m
So if I get this correctly, the magic is this line:
from(foo.flatMap { it.output }.map { it.asFile.readLines() })
. I was trying something similar, but couldn't get it to work, since I need to configure the `include`s
v
I think you need to use
include { ... }
, all others need a
String
eagerly:
Copy code
abstract class Foo : DefaultTask() {
    @get:OutputFile
    abstract val output: RegularFileProperty

    @TaskAction
    fun execute() {
        output.get().asFile.writeText(
            """
                gradlew
                gradlew.bat
            """.trimIndent()
        )
    }
}

val foo by tasks.registering(Foo::class) {
    output = layout.buildDirectory.file("foo.txt")
}

val sync by tasks.registering(Sync::class) {
    inputs.files(foo).withPropertyName("fooOutput")
    from(layout.projectDirectory)
    into(layout.buildDirectory.dir("synced"))
    include {
        it.file.name in foo.flatMap { it.output }.get().asFile.readLines()
    }
}
m
Thanks, I'll try that!
👌 1
Hmm, that closure is called for every file that is potentially copied, right? That might be a bit much overhead, if I need to parse my JSON file every time
v
Copy code
abstract class Foo : DefaultTask() {
    @get:OutputFile
    abstract val output: RegularFileProperty

    @TaskAction
    fun execute() {
        output.get().asFile.writeText(
            """
                gradlew
                gradlew.bat
            """.trimIndent()
        )
    }
}

val foo by tasks.registering(Foo::class) {
    output = layout.buildDirectory.file("foo.txt")
}

val sync by tasks.registering(Sync::class) {
    inputs.files(foo).withPropertyName("fooOutput")
    from(layout.projectDirectory)
    into(layout.buildDirectory.dir("synced"))
    val includes = objects
        .listProperty<String>()
        .value(foo.flatMap { it.output }.map { it.asFile.readLines() })
        .apply { finalizeValueOnRead() }
    include {
        it.file.name in includes.get()
    }
}
m
Nice!
v
Or actually better
Copy code
abstract class Foo : DefaultTask() {
    @get:OutputFile
    abstract val output: RegularFileProperty

    @TaskAction
    fun execute() {
        output.get().asFile.writeText(
            """
                gradlew
                gradlew.bat
            """.trimIndent()
        )
    }
}

val foo by tasks.registering(Foo::class) {
    output = layout.buildDirectory.file("foo.txt")
}

val sync by tasks.registering(Sync::class) {
    from(layout.projectDirectory)
    into(layout.buildDirectory.dir("synced"))
    val includes = objects
        .listProperty<String>()
        .value(foo.flatMap { it.output }.map { it.asFile.readLines() })
        .apply { finalizeValueOnRead() }
    inputs.property("includes", includes)
    include {
        it.file.name in includes.get()
    }
}
m
I'm still using groovy and neither I nor Gradle can find where
apply
comes from
Ah, seems to be kotlin internal
v
That's Kotlin syntax, yes, in Groovy it would be
tap
instead
Or you just do
includes.finalizeValueOnRead()
as separate statement
m
ah, because of the
void
return, now I get why this is useful
v
But I highly recommend switching to Kotlin DSL. 🙂 By now it is the default, you immediately get type-safe build scripts, actually helpful error messages if you mess up the syntax, and amazingly better IDE support if you use a good IDE like IntelliJ IDEA or Android Studio.
m
Believe me, I would love to, but that's going to take a while, yet
v
There is no need for a big-bang, you can switch over one build script after the other 🙂
m
I know, but that would require everyone who potentially needs to touch that file to know enough Kotlin, so it won't be that easy
v
Easier than to know enough Gradle and all the APIs with which the IDE can only partly help. 😄
But yeah, I know what you mean
Just tell them to not touch your precious build scripts 😄
m
This works beautifully now, and it's so much cleaner than before! Thank you for your help!