如何在没有相同项目根的情况下进行sbt-(本地)多项目?

时间:2015-08-29 20:01:28

标签: scala intellij-idea dependencies sbt multi-project

我想在intellij中同时处理多个sbt项目。项目是“单向依赖”,意味着一个是(可重用)核心,另一个是构建在该核心上的实际应用程序。两者目前都在开发中。

我希望核心驻留在除应用程序之外的另一个基本/根目录中,因此:

- /core
-- build.sbt

- /application
-- build.sbt

我希望能够

  1. 修改同一IntelliJ窗口中的两个项目
  2. 将两个项目都保留在各自的文件夹中(周围没有包装文件夹!)。 Core也将用于其他应用程序, no 当前“应用程序”的兄弟应用程序,因此我不希望它们位于同一个根文件夹下!
  3. 到目前为止,我尝试过哪些问题以及遇到了哪些问题:

    设置如

    lazy val core = project.in(file("../core"))
    lazy val application = project.in(file(".")).dependsOn(core)
    

    不起作用,因为sbt断言多项目设置中的每个项目的目录都包含在同一个构建根目录中:

      

    SBT   java.lang.AssertionError:断言失败:构建根/应用程序

    中不包含目录/核心

    设置如

    lazy val core = RootProject(file("../core"))
    lazy val application = project.in(file(".")).dependsOn(core)
    

    不是解决方案,因为:

    1. 我可以将这两个项目放在一个IntelliJ窗口中,然后
    2. 奇怪的是,虽然在应用程序中找不到核心类 进口工作立即起作用
    3. 现在我是一个新手,我猜(并希望)必须有解决这个问题的方法。我不可能是唯一一个想要在没有包装层的情况下分离我的项目但仍然可以在我选择的IDE中修改它们的人。

      编辑

      @ OlegRudenko的解决方案对我来说是半工作的。由于core也有一些依赖关系,我无法在application中编译或使用它。

      core引入一些依赖关系,例如记录器,当我在application并尝试使用core的组件时,编译器会尖叫我,因为它无法在core中找到依赖项(例如记录器)

      另外,core拉入,例如lwjgl和我想在application中使用它的一些组件,没有机会,因为它无法找到core的依赖项的包。

      现在我所做的是一个黑客的非解决方案。我只是在同一个intellij项目中开发coreapplication并保持git-repo私有。

      这根本不是一个解决方案,因为我想开源core,而application现在是封闭源代码,我仍然希望同时处理这两个问题,优化{{ 1}}等等。

3 个答案:

答案 0 :(得分:7)

如果某个框架不允许我这样做,我宁愿不打架而是要找到妥协。

SBT不允许在项目根目录之外拥有子项目。 所以我的解决方案是在我的项目中创建一个符号链接到子项目,它位于外面:

common/
common/core/  - I want this as a sub-project inside of my project
application/
application/ui/  - This is my project

application/ui/core/ -> ../../common/core/  - This is my goal

必须

  • 自动工作
  • 在不同的操作系统上工作
  • 清晰易懂
  • 使用标准工具

解决方案是将以下行添加到SBT文件中:

val __createLinkToCore = {
  import java.nio.file.{FileSystemException, Files, Paths}
  import scala.util.Try

  val corePath = Paths.get("..", "..", "common", "core")
  if(!Files.exists(corePath)) throw new IllegalStateException("Target path does not exist")

  val linkPath = Paths.get("core")
  if(Files.exists(linkPath)) Files.delete(linkPath)

  Try {Files.createSymbolicLink(linkPath, corePath)}.recover {
    case cause: FileSystemException if System.getProperty("os.name").toLowerCase.contains("windows") =>
      val junctionCommand = Array("cmd", "/c", "mklink", "/J", linkPath.toAbsolutePath.normalize().toString, corePath.toAbsolutePath.normalize().toString)
      val junctionResult = new java.lang.ProcessBuilder(junctionCommand: _*).inheritIO().start().waitFor()
      if(junctionResult != 0) throw new Exception("mklink failed", cause)
      linkPath
  }.get
}

每次启动SBT时,但在对代码执行任何操作之前 - 在编译之前执行此操作等等。它不会在每次编译时重新创建链接 - 每次SBT启动时只会重建一次。

代码确实如下:

  • 获取所需文件夹的路径(如果不存在则失败)
  • 获取链接路径(如果存在则删除)
  • 尝试使用Java 7+ API创建新链接
  • 如果它在Windows 7+上运行且没有管理员权限,则会失败。在这种情况下,我使用标准但特定于Windows的工具mklink /J
  • 创建一个联结

此解决方案的限制:

  • 它不适用于FAT文件系统
  • 你见过其他限制吗?让我知道!

答案 1 :(得分:3)

我不认为这在SBT中是可行的(但我很乐意听到其他情况)。

以下是我在IntelliJ中为解决此问题所做的工作:

  • 打开"应用程序"在IntelliJ;刷新SBT定义
  • 关闭SBT自动更新(文件 - >设置 - >构建 - > SBT)
  • 执行文件 - >新的 - >来自现有来源的模块,选择"核心" checkout目录,从SBT导入
    • 勾选“下载来源”,但不是“自动更新”
  • 您现在应该在同一个IntelliJ窗口中同时拥有“应用程序”和“核心”(但是#34;应用程序"仍然取决于"核心" JAR不是您的实时来源)
  • 右键单击"应用程序"在IntelliJ中项目并选择“打开模块设置”
    • 打开“依赖项”标签
    • 找到"核心"库中的SBT依赖(-ies)并删除它们
    • 添加"模块依赖"在"核心"项目
  • 此时您应该手动重建项目,因为已知IntelliJ在添加模块时会对类路径感到困惑。
  • 您现在可以将这两个项目作为IntelliJ中的单个代码库进行开发
    • 请注意," application"中的“sbt test”等SBT命令行工具将继续使用"核心" JAR而不是本地修改的来源
    • ("应用程序&#34中的SBT Build.scala文件;仍引用"核心"版本号为JAR)
  • 当您准备好提交时,您需要:
    • 将您的更改提交至"核心",更新版本号
    • 运行" sbt publishLocal"在"核心"中,记下生成的版本号
    • 更新"应用程序"中的Build.scala文件引用新的"核心"版本
    • 运行" sbt test"或类似的检查一切都在SBT模式下工作
    • 提交"申请"改变,推动两者

答案 2 :(得分:1)

您可以使用 ProjectRef

lazy val my_application = (project in file ("."))
    .aggregate(my_core)
    .dependsOn(my_core)

lazy val my_core = ProjectRef(file("../my_core"), "my_core")

然后,关于目录结构,my_applicationmy_core是兄弟姐妹。