SBT monorepo中如何遍历声明子项目以避免配置代码重复
报错原因
你尝试的批量声明子项目的方案是可行的,触发报错的原因是sbt对build.sbt的静态语法约束:sbt会在编译构建配置时,强制校验所有通过project语法构造的项目必须直接赋值给顶层的lazy val,你通过map动态生成的项目没有对应顶层val声明,因此触发了校验报错。你之前改用Project.apply仍然报错,也是因为这个约束没有被满足。
可行解决方案
方案1:抽离公共构造方法(无需改动构建目录结构,适合子项目数量较少的场景)
将公共配置逻辑封装为工厂方法,逐个声明子项目val即可,完全没有重复配置代码:
// 公共项目构造工厂 def mkProject(projectName: String): Project = Project(projectName, file(projectName)) .settings( name := projectName, commonSettings, libraryDependencies ++= ModulesDependencies.get(projectName) ) // 逐个声明子项目val,每行仅需一行代码,无重复逻辑 lazy val projectA = mkProject("projectA") lazy val projectB = mkProject("projectB") lazy val projectC = mkProject("projectC") lazy val projects = Seq(projectA, projectB, projectC) lazy val root = (project in file(".")) .aggregate(projects: _*) .settings( crossScalaVersions := Nil, publish / skip := true )
方案2:使用project目录下的Scala构建代码(无需逐个声明val,适合子项目数量多、变动频繁的场景)
在根目录下创建project/Build.scala文件编写构建逻辑,该文件不会触发build.sbt的静态val校验,可以直接动态生成所有子项目:
import sbt._ import Keys._ object MonorepoBuild extends Build { // 公共配置 val commonSettings = Seq( scalaVersion := "2.13.12" // 其余公共配置写在这里 ) object ModulesDependencies { def get(projectName: String): Seq[ModuleID] = projectName match { // 你的项目依赖映射逻辑写在这里 case "projectA" => Seq("org.typelevel" %% "cats-core" % "2.10.0") case "projectB" => Seq("com.lihaoyi" %% "upickle" % "3.1.4") case _ => Seq.empty } } // 动态生成所有子项目 lazy val projects = Seq("projectA", "projectB", "projectC").map { projectName => Project(projectName, file(projectName)) .settings( name := projectName, commonSettings, libraryDependencies ++= ModulesDependencies.get(projectName) ) } // 根项目配置 lazy val root = Project("root", file(".")) .aggregate(projects.map(_.project): _*) .settings( crossScalaVersions := Nil, publish / skip := true ) }
使用该方案时,原有build.sbt里的项目配置可以全部移除,仅保留全局可选配置即可。
内容的提问来源于stack exchange,提问作者bo32
相关产品推荐
相关产品推荐

