hello, I'm writing a plugin to build, expose and c...
# dependency-management
s
hello, I'm writing a plugin to build, expose and consume python packages. I'm modeling this using variant-aware resolution: specifically, I want to expose an artifact with a specific usage attribute, and have a corresponding resolvable configuration that resolves to the path of the package. Everything seems to work correctly, i.e. I can add an artifact to the consumable configuration, declare a dependency on the subproject using a dependency scope dependency, and read the path of the package from a resolvable configuration. The only thing that doesn't seem to be working is automatically triggering the task that builds the artifact just by declaring the dependency on the subproject. In other words, given the following:
Copy code
// plugin
val declarable = project.configurations.dependencyScope("pythonScope") {
    it.attributes {
        it.attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage::class.java, "python"))
    }
}

val consumable = project.configurations.consumable("pythonConsumable") {
    it.attributes {
        it.attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage::class.java, "python"))
    }
}

val resolvable = project.configurations.resolvable("pythonResolvable") {
    it.extendsFrom(declarable.get())

    it.attributes {
        it.attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage::class.java, "python"))
    }
}

// producer project
artifacts {
    add("pythonConsumable", pythonPackage) {
        builtBy(buildPythonPackage)
    }
}

// consumer project
dependencies {
    pythonScope(projects.grpc.stub.python)
}

...

// within the `testResolution` task
val res = project.configurations.getByName("pythonResolvable")
res2.resolvedConfiguration.resolvedArtifacts.forEach {
    println(it.file.path)
    println(it.file.isFile)
}
I can run
buildPythonPackage
and
"testResolution"
tasks and everything works, but from a clean build, if I only invoke
"testResolution"
,
buildPythonPackage
is not invoked. Am I missing something, or why is not the package being built since it's been explicitly declared which task generates the artifact?
p
Nope, it should work, I have a similar plugin and it does work, I also use the same code like you. Do you create just a single OutputFile in buildPythonPackage?
j
A couple things here: Putting attributes on a dependency scope configuration does nothing. We should just deprecate being able to. You say “within the test resolution task”. Can you be more specific there in how you link the configuration to the task? Your task needs to depend on the configuration in order for it to build the tasks that the configuration depends on (via its dependencies) Something like tasks.testResolution.dependsOn(configurations.pythonResolvable)
Also don’t use resolvedConfiguration, use configuration.incoming.files or configuration.incoming.artifacts
s
@Philip W I also declare a directory as the task output, but that’s sort of legacy so I’ll see what happens if I just declare a file
p
And how do you add the file/files as input?
s
@Justin Van Dort thank you for the extensive feedback. Probably the thing that’s missing is the explicit dependency on the configuration. I thought that simply triggering the resolution process would suffice. In other words, all I do in the task is what I wrote in the example and I thought that would be sufficient to trigger the package build task. I’ll remove the attributes from the dependency scope, I suspected it would do nothing but wasn’t sure
@Philip W what do you mean? As input to what?
p
As an input to your task
s
@Philip W it’s the last two lines in the sample I sent. I don’t exactly specify it as an input to the task but rather resolve the configuration and get the artifacts in the task implementation code
p
Okay, that won’t work, you should wire the configuration as inputfiles to your task using configurablefilecollection.
s
Do you have a short example of what you’re suggesting that uses the artifacts coming from a resolvable configuration? I get your suggestion in principle but I’m not sure how to do it with a configuration as a dependency
p
Copy code
val sapCIFlowEntrypoints = configurations.resolvable("sapCIFlowEntrypoints") {
    extendsFrom(configurations.named("sapCIInfrastructure").get())     // dependencyScope
    attributes {
        attribute(SAPCI.attribute, objects.named(SAPCI.jsonEntrypoints))
    }
}

val generateKtorServerApi by tasks.registering(GenerateKtorServerAPI::class) {
    this.entrypoints.from(sapCIFlowEntrypoints)
    this.apis.from(configurations.named("sapCIInfrastructureApi"))
    workerClasspath.from(configurations.named("ktorClientWorkerClasspath"))
}
and:
Copy code
@CacheableTask
abstract class GenerateKtorServerAPI : DefaultTask() {
    init {
        group = "sapci"
    }

    @get:InputFiles
    @get:SkipWhenEmpty
    @get:IgnoreEmptyDirectories
    @get:NormalizeLineEndings
    @get:PathSensitive(PathSensitivity.NONE)
    public abstract val apis: ConfigurableFileCollection
s
Cool many thanks, I’ll try that out too!
thanks @Justin Van Dort and @Philip W, both solutions seems to work as expected 👍