Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
622 views
in Technique[技术] by (71.8m points)

scala - Setting up sbt environment to hack on multiple libraries at once

Is there an equivalent to Leiningen's "checkouts" feature in sbt?

Here is what I want to accomplish:

I have two projects, an application Foo and library "Bar". I want to publish each of these projects independently. Foo depends on Bar, and the sbt project will direct sbt to download the jar for "Bar" from a repository whenever a third-party tries to build "Foo" (which is typical behavior).

Now, say I want to hack on both Foo and Bar at the same time. For example, while working on Foo, I want to directly edit and debug some of the source for Bar so the edits affect Foo (and then later rebuild Bar when it is convenient).

How can I instruct sbt to satisfy its dependency on Bar from its source code on my machine (rather than my local repository) during this hack session?

(P.S. I asked a similar question for Clojure/Leiningen. Leiningen has the "checkouts" feature which accomplishes this. I am wondering if there is something similar in sbt...)

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You can declare a source dependency from Foo to Bar via a project reference:

import sbt._

object FooBuild extends Build {
  lazy val root = Project(
    id = "foo",
    base = file(".")
  ) dependsOn(theBarBuild)

  lazy val theBarBuild = ProjectRef(
    base = file("/path/to/bar"),
    id = "bar")
}

This should also recompile Bar (if it has changed) whenever you compile Foo. Please note that the id of the project reference must match the actual id of the Bar project, which might be something like e.g. default-edd2f8 if you use a simple build definition (.sbt files only).

This technique is especially useful for plug-ins (see my blog post about this topic).

Edit:

You can kind of re-code the checkout behaviour like this:

import sbt._

object FooBuild extends Build {
  lazy val root = addCheckouts(Project(id = "foo", base = file(".")))

  def addCheckouts(proj: Project): Project = {
    val checkouts = proj.base.getCanonicalFile / "checkouts"
    if (! checkouts.exists) proj
    else proj.dependsOn(IO.listFiles(DirectoryFilter)(checkouts).map { dir =>
      ProjectRef(base = dir, id = dir.name): ClasspathDep[ProjectReference]
    }:_*)
  }
}

This checks your project directory for a checkouts directory, and if it exists, adds the directories therein (which should be symlinks to other projects) as project references to the project. It expects the symlink to be named like the actual ID of the linked project (e.g. default-edd2f8 or bar). If the directory doesn't exist, the build just works as before.

When you add or remove a symlink in the checkouts directory (or the directory itself), you must reload the Foo project to pick up the changes.

Hope this helps.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...