【问题标题】:SBT - Multi project merge strategy and build sbt structure when using assemblySBT - 使用汇编时的多项目合并策略和构建 sbt 结构
【发布时间】:2017-01-15 05:11:19
【问题描述】:

我有一个项目,它由多个较小的项目组成,其中一些项目相互依赖,例如,有一个实用程序项目依赖于 commons 项目。 其他项目可能会也可能不会依赖于公用事业或公地,或者两者都不依赖。

在 build.sbt 中,我在文件末尾有程序集合并策略,以及程序集中的测试是 {}。

我的问题是:这是否正确,每个项目是否应该有自己的合并策略,如果是,依赖它的其他项目是否会从他们那里继承此策略?将合并策略包含在所有项目定义中似乎很笨拙,并且意味着大量重复代码。

这个问题也适用于测试,每个项目是否应该有测试是否应该执行的行,或者是否也将被继承?

提前致谢。如果有人知道一个合理(相对复杂)示例的链接,那就太好了。

【问题讨论】:

    标签: scala sbt sbt-assembly


    【解决方案1】:

    在我的日常工作中,我目前从事一个大型的多项目。不幸的是,它的源代码是封闭的,所以我不能分享细节,但我可以分享一些指导。

    1. 创建一个仅供根/容器项目使用的rootSettings,因为它通常不是程序集或发布步骤的一部分。它将包含以下内容:

      lazy val rootSettings := Seq(
        publishArtifact := false,
        publishArtifact in Test := false
      )
      
    2. 创建一个由所有子项目共享的commonSettings。将基础/共享程序集设置放在此处:

      lazy val commonSettings := Seq(
      
        // We use a common directory for all of the artifacts
        assemblyOutputPath in assembly := baseDirectory.value /
          "assembly" / (name.value + "-" + version.value + ".jar"),
      
        // This is really a one-time, global setting if all projects
        // use the same folder, but should be here if modified for
        // per-project paths.
        cleanFiles <+= baseDirectory { base => base / "assembly" },
      
        test in assembly := {},
      
        assemblyMergeStrategy in assembly := {
          case "BUILD" => MergeStrategy.discard
          case "logback.xml" => MergeStrategy.first
          case other: Any => MergeStrategy.defaultMergeStrategy(other)
        },
      
        assemblyExcludedJars in assembly := {
          val cp = (fullClasspath in assembly).value
          cp filter { _.data.getName.matches(".*finatra-scalap-compiler-deps.*") }
        }
      )
      
    3. 每个子项目都使用commonSettings,并应用特定于项目的覆盖:

      lazy val fubar = project.in(file("fubar-folder-name"))
        .settings(commonSettings: _*)
        .settings(
          // Project-specific settings here.
          assemblyMergeStrategy in assembly := {
            // The fubar-specific strategy
            case "fubar.txt" => MergeStrategy.discard
            case other: Any =>
              // Apply inherited "common" strategy
              val oldStrategy = (assemblyMergeStrategy in assembly).value
              oldStrategy(other)
          }
        )
        .dependsOn(
          yourCoreProject,
          // ...
        )
      
    4. 顺便说一句,如果使用 IntelliJ。不要将根项目变量命名为 root,因为这是最近项目菜单中的项目名称。

      lazy val myProjectRoot = project.in(file("."))
        .settings(rootSettings: _*)
        .settings(
          // ...
        )
        .dependsOn(
          // ...
        )
        .aggregate(
          fubar,
          // ...
        )
      

    您可能还需要添加自定义策略来组合 reference.conf 文件(用于 Typesafe Config 库):

    val reverseConcat: sbtassembly.MergeStrategy = new sbtassembly.MergeStrategy {
      val name = "reverseConcat"
      def apply(tempDir: File, path: String, files: Seq[File]): Either[String, Seq[(File, String)]] =
        MergeStrategy.concat(tempDir, path, files.reverse)
    }
    
    assemblyMergeStrategy in assembly := {
      case "reference.conf" => reverseConcat
      case other => MergeStrategy.defaultMergeStrategy(other)
    }
    

    【讨论】:

    • 赞成。顺便说一句,对于第一个和最后一个,我怎么知道哪个先到,顺序有保证吗?
    • @AbhijitSarkar 我在build.sbt 文件中使用的顺序是定义根和公共设置块,然后是子项目,最后是根以将它们捆绑在一起。构建顺序依赖于 dependsOnaggregate 列表。
    • @devstuff 依赖项怎么样?我最近遇到的一个问题是在一个 Akka 项目中,多个第三方库包含一个文件 reference.conf,我也是。所需的顺序是首先从 jar 中连接文件,最后是我的文件。但我的最终出现在合并文件的开头
    • @AbhijitSarkar 用自定义策略更新了我的答案。
    • 谢谢。我认为您假设我的文件将是Seq 中的第一个文件,对吗?
    猜你喜欢
    • 2023-04-10
    • 2015-03-11
    • 2013-09-28
    • 2021-05-28
    • 2014-12-04
    • 2018-11-18
    • 2013-06-21
    • 1970-01-01
    • 2013-07-25
    相关资源
    最近更新 更多