如何强制选择变体 withResolvedConfiguration.getResolvedArtifacts?

How to force variant selection withResolvedConfiguration.getResolvedArtifacts?

我正在使用一个大型的多模块 Android 应用程序,我正在尝试定义一个 Gradle 任务来收集所有 运行 时间依赖项的 jar .我在 app/build.gradle:

中尝试这样的事情
task collectDeps {
    doLast {
        configurations.releaseRuntimeClasspath.resolvedConfiguration.resolvedArtifacts.each {
                // do stuff
        }
    }
}

我过去在其他 Java 项目中使用过这个片段,所以我知道它在概念上是有效的;这只是我第一次在具有多种构建类型 and/or 变体的项目上尝试它。

当 运行 在 Android 项目上时,执行此任务会引发变体解析错误:

Execution failed for task ':app:collectDeps'.
> Could not resolve all dependencies for configuration ':app:releaseRuntimeClasspath'.
   > The consumer was configured to find a runtime of a component, preferably optimized for Android, as well as attribute 'com.android.build.api.attributes.BuildTypeAttr' with value 'release', attribute 'com.android.build.api.attributes.AgpVersionAttr' with value '7.1.1', attribute 'org.jetbrains.kotlin.platform.type' with value 'androidJvm'. However we cannot choose between the following variants of project :myModule:
       - Configuration ':myModule:releaseRuntimeElements' variant android-aar-metadata declares a runtime of a component, preferably optimized for Android, as well as attribute 'com.android.build.api.attributes.AgpVersionAttr' with value '7.1.1', attribute 'com.android.build.api.attributes.BuildTypeAttr' with value 'release', attribute 'org.jetbrains.kotlin.platform.type' with value 'androidJvm':
           - Unmatched attributes:
               - Provides attribute 'artifactType' with value 'android-aar-metadata' but the consumer didn't ask for it
               - Provides attribute 'com.android.build.gradle.internal.attributes.VariantAttr' with value 'release' but the consumer didn't ask for it
               - Provides a library but the consumer didn't ask for it
       - Configuration ':myModule:releaseRuntimeElements' variant android-art-profile declares a runtime of a component, preferably optimized for Android, as well as attribute 'com.android.build.api.attributes.AgpVersionAttr' with value '7.1.1', attribute 'com.android.build.api.attributes.BuildTypeAttr' with value 'release', attribute 'org.jetbrains.kotlin.platform.type' with value 'androidJvm':
           - Unmatched attributes:
               - Provides attribute 'artifactType' with value 'android-art-profile' but the consumer didn't ask for it
               - Provides attribute 'com.android.build.gradle.internal.attributes.VariantAttr' with value 'release' but the consumer didn't ask for it
               - Provides a library but the consumer didn't ask for it

为简洁起见,我已经删除了错误;总共有大约 20 个变体。请注意,myModule 是顶级应用程序的项目依赖项;如果我删除该依赖项,错误是相同的,但来自不同的模块。

在这里我还应该注意,所有其他构建目标都可以正常工作;该应用程序非常成熟,我所做的唯一更改是将此新任务添加到 app/build.gradle。所以我假设 某些东西 关于我解决 Gradle 不喜欢的依赖关系的方式,但我正在努力弄清楚是什么,或者如何解决它.

用谷歌搜索这个错误不是很有用; Gradle 文档对如何 解决变体的确切说明非常模糊,所提供的解决方案似乎侧重于更改将依赖项添加到项目的方式;但我不一定要这样做,因为该构建适用于所有其他用例。

理想情况下,我希望能够在我的 collectDeps 任务中强制执行特定的分辨率变体(事实上,理想情况下 collectDeps 将在插件中定义)。这可能吗?

以防万一,构建使用 Gradle 7.2 和 Android Gradle 插件

的 v7.1.1

可能有更好的方法来处理这个问题,但我最终通过从 Sonatype's open source Nexus scanning plugin 获得灵感来解决我的问题。代码看起来像(这是在 Kotlin 中,但可以毫不费力地修改为 Groovy):

project.allprojects.forEach { project ->
    val cfg = project.configurations.releaseRuntimeClasspath
     try {
        cfg.resolvedConfiguration.resolvedArtifacts.forEach {
            // do stuff
        }
     } catch(e: Exception) {
        when(e) {
            is ResolveException, is AmbiguousVariantSelectionException -> {
                val copyConfiguration = createCopyConfiguration(project)

                cfg.allDependencies.forEach {
                    if(it is ProjectDependency) {
                        project.evaluationDependsOn(it.dependencyProject.path)
                    } else {
                        copyConfiguration.dependencies.add(it)
                    }
                }

                copyConfiguration.resolvedConfiguration.resolvedArtifacts.forEach {
                    // do stuff
                }
            }
            else -> throw(e)
        }
     }
}

private fun createCopyConfiguration(project: Project): Configuration {
    var configurationName = "myCopyConfiguration"

    var i = 0
    while(project.configurations.findByName(configurationName) != null) {
        configurationName += i
        i++
    }

    val copyConfiguration = project.configurations.create(configurationName)
    copyConfiguration.attributes {
        val factory = project.objects
        this.attribute(Usage.USAGE_ATTRIBUTE, factory.named(Usage::class.java, Usage.JAVA_RUNTIME))
    }

    return copyConfiguration
}

基本思想是,如果由于变体选择不明确而无法解析配置,我将创建并注入一个指定属性的新父配置 org.gradle.usage='java-runtime';这足以消除变体的歧义。

请注意,我没有使用任何其他属性对此进行测试,因此它可能可以通过设置 artifactType 属性来代替;但我的用例更具体地与运行时类路径相关,所以这对我有用