You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Gradle多模块自定义源集问题:依赖模块编译类路径缺失类

问题:Gradle多模块构建中Scala 2.13变体依赖缺失编译类

项目背景

我正在参与OpenLineage Spark集成项目,基于Java开发,使用Gradle 8.4构建,包含app、shared、spark2至spark35等多模块。

需求

为Apache Spark的Scala 2.12和2.13变体构建shared模块,spark2至spark35模块均依赖shared。目标是让spark35等模块编译出适配两个Scala版本的产物,同时shared模块需对应Spark变体构建以避免运行时错误。

实施方式

在spark33、spark34、spark35及shared模块中,通过Java Library插件引入scala213和testScala213源集,复用main和test的源码。默认依赖配置适配Scala 2.12,为Scala 2.13源集额外声明依赖,包括对shared模块的依赖。

遇到的问题

当从shared项目引入scala213RuntimeElements(或scala213ApiElements)配置时,依赖模块的编译类路径中缺少shared模块的编译类;但使用默认配置时,这些类存在于类路径中。

疑问

  1. 为何指定scala213RuntimeElements配置会导致shared模块的编译类在其他模块的编译类路径中缺失?
  2. 如何解决该问题,确保shared模块的类在Scala 2.12和2.13构建的类路径中均被包含?

相关build.gradle文件

shared/build.gradle

sourceSets {
    scala213 {
        java.srcDir("src/main/java")
        resources.srcDir("src/main/resources")
    }

    testScala213 {
        compileClasspath += sourceSets.scala213.output
        runtimeClasspath += sourceSets.scala213.output

        java.srcDir("src/test/java")
        resources.srcDir("src/test/resources")
    }
}

configurations {
    scala213Api
    
    scala213ApiElements {
        extendsFrom(scala213Api)
        canBeResolved = false
        canBeConsumed = true
    }
    
    scala213Implementation.extendsFrom(scala213Api)
    
    scala213RuntimeElements {
        extendsFrom(scala213Implementation, scala213RuntimeOnly)
        canBeResolved = false
        canBeConsumed = true
    }
    
    scala213CompileClasspath {
        extendsFrom(scala213CompileOnly, scala213Implementation)
        canBeResolved = true
    }

    scala213RuntimeClasspath {
        extendsFrom(scala213Implementation, scala213RuntimeOnly)
        canBeResolved = true
    }
    
    testScala213Implementation.extendsFrom(scala213Implementation)
    testScala213RuntimeOnly.extendsFrom(scala213RuntimeOnly)

    testScala213CompileClasspath {
        extendsFrom(testScala213CompileOnly, testScala213Implementation)
        canBeResolved = true
    }

    testScala213RuntimeClasspath {
        extendsFrom(testScala213Implementation, testScala213RuntimeOnly)
        canBeResolved = true
    }
}

spark33/build.gradle

sourceSets {
    scala213 {
        java.srcDir("src/main/java")
        resources.srcDir("src/main/resources")
    }

    testScala213 {
        compileClasspath += sourceSets.scala213.output
        runtimeClasspath += sourceSets.scala213.output

        java.srcDir("src/test/java")
        resources.srcDir("src/test/resources")
    }
}

dependencies {
    implementation(project(path: ":shared"))
    // others removed for brevity

    scala213Implementation(project(path: ":shared", configuration: "scala213RuntimeElements"))
    // others removed for brevity
}

问题原因

  1. 配置角色不匹配:scala213RuntimeElements是Gradle专为运行时依赖传递设计的消费端配置,仅包含运行时所需的类和依赖,默认不包含编译期可见的API类。你在scala213Implementation(编译期依赖配置)中引用它,自然会缺失编译类。
  2. 未关联源集输出:shared模块的scala213ApiElements和scala213RuntimeElements仅设置了依赖继承,但未绑定scala213源集的编译输出。Gradle的消费端配置必须明确声明要输出的产物,否则依赖模块无法解析到对应的类文件。

解决方法

步骤1:在shared模块中绑定源集输出到消费端配置

修改shared/build.gradle的configurations块,为消费端配置添加源集输出:

configurations {
    scala213Api
    
    scala213ApiElements {
        extendsFrom(scala213Api)
        canBeResolved = false
        canBeConsumed = true
        // 关联scala213源集的编译类文件
        outgoing.artifact(sourceSets.scala213.output)
    }
    
    scala213Implementation.extendsFrom(scala213Api)
    
    scala213RuntimeElements {
        extendsFrom(scala213Implementation, scala213RuntimeOnly)
        canBeResolved = false
        canBeConsumed = true
        // 关联scala213源集的编译类和资源文件
        outgoing.artifact(sourceSets.scala213.output)
        outgoing.artifact(sourceSets.scala213.resources)
    }
    
    // 其余配置保持不变
}

步骤2:在依赖模块中使用匹配的配置

修改spark33/build.gradle的依赖声明,区分编译期和运行时依赖:

dependencies {
    implementation(project(path: ":shared"))
    // others removed for brevity

    // 编译期依赖shared的Scala 2.13 API类
    scala213Implementation(project(path: ":shared", configuration: "scala213ApiElements"))
    // 运行时依赖shared的Scala 2.13完整产物
    scala213RuntimeOnly(project(path: ":shared", configuration: "scala213RuntimeElements"))
    // others removed for brevity
}

步骤3:规范测试源集类路径配置

在shared和spark模块的testScala213源集中,用Gradle标准方式关联类路径:

testScala213 {
    compileClasspath = sourceSets.scala213.compileClasspath + sourceSets.scala213.output
    runtimeClasspath = sourceSets.scala213.runtimeClasspath + sourceSets.scala213.output
    // 其余配置不变
}

关键说明

  • 消费端配置(canBeConsumed = true)必须明确绑定源集输出,否则依赖模块无法获取编译后的类。
  • 编译期依赖用ApiElements类型配置,运行时依赖用RuntimeElements,保证依赖传递的正确性。
  • 复用源集源码的方式可行,但需确保每个变体的配置都正确关联对应输出。

内容的提问来源于stack exchange,提问作者Marley

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 03:48:28