我们有一个大型的多项目,其中有许多我们使用 sbt 构建的模块。事实证明,我们有很多关于打包不需要的 jar 的问题。作为处理该问题的第一步,我们创建了一个已用工件的“全局 map ”,其定义如下:
project/Build.scala
:
type PartialFunction2[-T1,-T2,+R] = PartialFunction[Tuple2[T1,T2],R]
lazy val dependenciesManager = settingKey[PartialFunction2[String, String, ModuleID]]("a setting containing versions for dependencies. if we only use it to declare dependencies, we can avoid a lot of version collisions.")
build.sbt
根目录:
dependenciesManager in Global := {
case ("ch.qos.logback","logback-classic") => "ch.qos.logback" % "logback-classic" % "1.1.1"
case ("com.typesafe","config") => "com.typesafe" % "config" % "1.2.0"
case ("com.typesafe","scalalogging-slf4j") => "com.typesafe" %% "scalalogging-slf4j" % "1.1.0"
case ("com.typesafe.akka",art) => "com.typesafe.akka" %% art % "2.2.4"
case ("com.typesafe.play", art) => "com.typesafe.play" %% art % "2.2.3"
...
}
这允许我们在任何模块自己的 build.sbt
文件中使用以下语法:
libraryDependencies <++= (dependenciesManager)(dm => Seq(
dm("com.typesafe.akka","akka-cluster"),
dm("com.typesafe.akka","akka-contrib"),
dm("com.typesafe.play","play"),
dm("ch.qos.logback","logback-classic")
))
其中没有提到依赖版本。 但是,由于传递依赖仍然会导致问题,它只解决了部分问题。
所以我在想,如果我能以某种方式定义一个像上面那样的依赖关系映射,sbt 的解析机制将“通过它”并且它可能会改变所要求的依赖关系。
例如,假设我正在使用 logback
和 slf4j-api
,我不想要 log4j
的传递依赖或commons-logging
被添加到类路径中,而是像 jcl-over-slf4j
这样的桥。像上面那样的 map 可以解决这个问题。
此外,不会加载不同的版本。以及任何可能已更改其名称的工件(例如 google-collections
现在称为 guava
或 org.jboss.netty
已更改的 groupID到 io.netty
) 将不是问题。
当然,只要给定的依赖项不是所要求的,就应该在屏幕上打印一条警告,并且无论何时添加新的依赖项,用户都应该手动将它添加到 map 中,并包含所有不存在的传递依赖项yet(否则会抛出匹配错误)
所以我的问题是,是否有可能实现类似的目标? 如果是,怎么办?
目前关于如何解决这个问题的想法:
- 修改
ivyReport
任务或update
任务:据我了解,sbt 创建了一个 IvyReport xml 文件,根据这个文件, ivy 获取请求的工件。我想以某种方式修改 update任务或生成的报告,因此传递依赖项将是我想要的,而不是最初获取的依赖项。 - 使用类似于我们已有的解决方案:仅将映射中的所有
ModuleID
标记为intransitive()
,但是(以某种方式)获取它们的依赖项,并获取它们(在映射到想要的工件之后)
最佳答案
我没用过,但你可以试试sbt-one-log并可能将其用作您要解决的其他问题的起点。
关于scala - 覆盖 sbt 依赖解析机制(并防止 jar hell),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23739836/