How to set subproject artifact as task input?

My gradle build has a subproject with a task that produces a file

$ ./gradlew :strings:tokenizeStrings # creates strings/string_tokens.csv

then in my root project I have a task which consumes that file

tasks.generateLocalizationFiles {
    inputTokensCsvFile.set(layout.projectDirectory.file("strings/string_tokens.csv"))
}

this works, but since gradle doesn’t know about the dependency, it only works if I run the two tasks manually in the right order

$ ./gradlew :strings:tokenizeStrings
$ ./gradlew :generateLocalizationFiles

I want to add the proper dependency to gradle so that I can run just :generateLocalizationFiles and it will go into the subproject and do whatever it needs to. But I can’t figure out the right way to do it.

What I’ve tried:

Following Simple sharing of artifacts between projects, I tried adding a consumable configuration to the suproject build script

val localizationData by configurations.creating {
    isCanBeConsumed = true
    isCanBeResolved = false
}

tasks.tokenizeStrings {
    artifacts {
        add("localizationData", outputTokensCsvFile) {
            builtBy(this)
        }
    }
}

and then a resolvable configuration plus the dependency to the root project build script

val localizedStringData by configurations.creating {
    isCanBeConsumed = false
    isCanBeResolved = true
}

// hook up our resolvable configuration to the strings' consumable configuration
dependencies {
    localizedStringData(project(mapOf(
        "path" to ":strings",
        "configuration" to "localizationData")
    ))
}

tasks.generateLocalizationFiles {
    dependsOn(localizedStringData)
    inputTokensCsvFile.set(localizedStringData.singleFile)
}

but that fails, seemingly because the consumable configuration is not populated?

Caused by: java.lang.IllegalStateException: Expected configuration ':localizedStringData' to contain exactly one file, however, it contains no files.

You need to add the outgoing artifact directly in the subproject build script, not inside the task configuration (which is only run lazily). You also don’t need builtBy if you’re using a RegularFileProperty for the artifact.

val localizationData by configurations.creating {
    isCanBeConsumed = true
    isCanBeResolved = false
}

artifacts {
    add("localizationData", tasks.tokenizeStrings.get().outputTokensCsvFile)
}