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

解决SBT项目中Avro与Protobuf代码生成插件冲突问题

问题:SBT 1.7.2中sbt-avrohugger与sbt-protoc共存时Avro代码生成不完整

场景描述

原本通过sbt-avrohugger(2.0.0-RC19)实现从*.avsc、*.avdl文件生成Scala代码的功能正常。添加sbt-protoc(1.0.7)和ScalaPB compilerplugin(0.11.15)实现*.proto文件代码生成后,Protobuf代码生成正常,但Avro仅生成部分文件,导致测试编译失败。

配置代码

plugins.sbt

addSbtPlugin("com.julianpeeters" % "sbt-avrohugger" % "2.0.0-RC19")
addSbtPlugin("com.thesamet" % "sbt-protoc" % "1.0.7")

libraryDependencies += "com.thesamet.scalapb" %% "compilerplugin" % "0.11.15"

build.sbt

lazy val `job` = (project in file("."))
  .settings(avroHuggerSettings)
  .settings(protobufSettings)

lazy val avroHuggerSettings = Seq.concat(
  inConfig(IntegrationTest)(SbtAvrohugger.baseSettings),
  Seq(
    Compile / sourceGenerators += (Compile / avroScalaGenerate).taskValue,
    Test / sourceGenerators += (Test / avroScalaGenerate).taskValue,
    IntegrationTest / sourceGenerators += (IntegrationTest / avroScalaGenerate).taskValue,
    Compile / avroSourceDirectories := Seq(new java.io.File(s"${baseDirectory.value}/src/main/resources/avro")),
    Test / avroSourceDirectories := Seq(new java.io.File(s"${baseDirectory.value}/src/test/resources/avro")),
    IntegrationTest / avroSourceDirectories := Seq(new java.io.File(s"${baseDirectory.value}/src/it/resources/avro")),
    Compile / avroScalaCustomTypes := avroCustomTypes,
    Test / avroScalaCustomTypes := avroCustomTypes,
    IntegrationTest / avroScalaCustomTypes := avroCustomTypes
  )
)

lazy val protobufSettings = Seq(
  Compile / PB.targets := Seq(
    scalapb.gen() -> (Compile / sourceManaged).value
  )
)

文件路径说明:

  • Avro文件:src/main/resources/avro、src/test/resources/avro、src/it/resources/avro
  • Proto文件:src/main/protobuf
  • 生成代码目录:target/scala-2.12/src_managed/main(分不同包)

构建日志片段

[info] Considering source directories ...resources/avro
[info] Compiling AVSC ... .avsc to .../target/scala-2.12/src_managed/main/compiled_avro
[info] Compiling AVSC ... .avsc to .../target/scala-2.12/src_managed/main/compiled_avro
[info] Compiling Avro IDL ... .avdl
[info] Compiling 4 protobuf files to .../target/scala-2.12/src_managed/main
[info] Compiling Avro IDL ... .avdl
[info] Compiling Avro IDL ... .avdl
[info] Compiling Avro IDL ... .avdl
[info] Compiling Avro IDL ... .avdl
[info] Compiling Avro IDL ... .avdl
[info] Compiling Avro IDL ... .avdl
[info] compiling 139 Scala sources to .../target/scala-2.12/classes
[info] done compiling
// same as above but for some test avro classes
// failing test output
[error] (Test / compileIncremental) Compilation failed
[error] Total time: 9 s, completed Jun 5, 2024, 4:11:10 PM

已尝试的方法

  • 尝试调整插件运行顺序,定义自定义TaskKey控制执行顺序,无效果
  • 试错法修改配置,未解决问题

解决方案

1. 显式设置任务依赖,确保Avro生成优先完成

SBT的sourceGenerators会并行执行任务,可能导致Avro生成未完成就开始编译或Protobuf生成。可以让Protobuf生成任务依赖于Avro生成任务,强制顺序执行:

// 修改protobufSettings,添加依赖
lazy val protobufSettings = Seq(
  Compile / PB.compileProtoc := (Compile / PB.compileProtoc).dependsOn(Compile / avroScalaGenerate).value,
  Test / PB.compileProtoc := (Test / PB.compileProtoc).dependsOn(Test / avroScalaGenerate).value,
  IntegrationTest / PB.compileProtoc := (IntegrationTest / PB.compileProtoc).dependsOn(IntegrationTest / avroScalaGenerate).value,
  Compile / PB.targets := Seq(
    scalapb.gen() -> (Compile / sourceManaged).value
  )
)

2. 分离Avro与Protobuf的代码生成目录

虽然两者生成在不同包,但共享同一sourceManaged根目录可能引发文件系统竞争。给Avro生成指定独立子目录:

// 修改avroHuggerSettings中的生成目录配置
lazy val avroHuggerSettings = Seq.concat(
  inConfig(IntegrationTest)(SbtAvrohugger.baseSettings),
  Seq(
    // 为每个配置设置独立的生成目录
    Compile / avroScalaGenerate := SbtAvrohugger.AvroScalaGenerateTask.generate(
      (Compile / avroSourceDirectories).value,
      (Compile / sourceManaged).value / "avro", // Avro生成到avro子目录
      (Compile / avroScalaCustomTypes).value,
      (Compile / avroScalaGenerator).value
    ),
    Test / avroScalaGenerate := SbtAvrohugger.AvroScalaGenerateTask.generate(
      (Test / avroSourceDirectories).value,
      (Test / sourceManaged).value / "avro",
      (Test / avroScalaCustomTypes).value,
      (Test / avroScalaGenerator).value
    ),
    IntegrationTest / avroScalaGenerate := SbtAvrohugger.AvroScalaGenerateTask.generate(
      (IntegrationTest / avroSourceDirectories).value,
      (IntegrationTest / sourceManaged).value / "avro",
      (IntegrationTest / avroScalaCustomTypes).value,
      (IntegrationTest / avroScalaGenerator).value
    ),
    // 其他原有配置不变
    Compile / sourceGenerators += (Compile / avroScalaGenerate).taskValue,
    Test / sourceGenerators += (Test / avroScalaGenerate).taskValue,
    IntegrationTest / sourceGenerators += (IntegrationTest / avroScalaGenerate).taskValue,
    Compile / avroSourceDirectories := Seq(new java.io.File(s"${baseDirectory.value}/src/main/resources/avro")),
    Test / avroSourceDirectories := Seq(new java.io.File(s"${baseDirectory.value}/src/test/resources/avro")),
    IntegrationTest / avroSourceDirectories := Seq(new java.io.File(s"${baseDirectory.value}/src/it/resources/avro")),
    Compile / avroScalaCustomTypes := avroCustomTypes,
    Test / avroScalaCustomTypes := avroCustomTypes,
    IntegrationTest / avroScalaCustomTypes := avroCustomTypes
  )
)

3. 清理缓存后重新构建

SBT的增量编译缓存可能导致任务未触发,执行以下命令彻底清理后重新构建:

sbt clean assembly

4. 确保Avro文件全量被扫描

检查Avro源目录是否包含所有子目录,添加includeFilter确保所有.avsc/.avdl文件被处理:

// 在avroHuggerSettings中添加
Compile / avroIncludeFilter := "*.avsc" || "*.avdl",
Test / avroIncludeFilter := "*.avsc" || "*.avdl",
IntegrationTest / avroIncludeFilter := "*.avsc" || "*.avdl"

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 23:12:34