I'm trying to create an SBT plugin that provides common plugins and settings for my organization's projects (something like a Maven parent POM).
Since most of these settings should also be present in the commons project itself, I'm adding the sources as unmanagedSourceDirectories in plugins.sbt so the autoplugins I define for the proper project are also present on the meta-project (a neat trick I took from sbt-release plugin).
Things work just fine, except I still need to duplicate the addSbtPlugin entries when I want a plugin for booth my proper project and meta-project. If I don't do this, my proper-project won't compile since the plugins' classes are not loaded.
I tried moving all the shared plugin dependencies to a separate autoplugin assuming SBT would compile it and add the library dependencies to the meta-project so they would be there when my common plugin is compiled, but it doesn't work.
I would like to understand why this fails and if there is some way to thinker with the compilation order to somehow make it work. If not, I would gladly hear any alternatives you guys know to avoid having to maintain duplicate versions of all my shared plugins.
Here is a simplified version of my code:
project structure
/common
|-build.sbt
|-/project
| |-plugins.sbt
|-/src/main/scala/package
|-Dependencies.scala
|-MyCommonPlugin.scala
build.sbt
sbtPlugin := true
name := "common"
plugins.sbt
unmanagedSourceDirectories in Compile += baseDirectory.value.getParentFile / "src" / "main" / "scala"
Dependencies.scala
import sbt._
import sbt.Keys._
//Plugins I intend to share between build.sbt and plugins.sbt
object Dependencies extends AutoPlugin {
override def trigger = allRequirements
override lazy val projectSettings = super.projectSettings ++ Seq(
addSbtPlugin("com.typesafe.sbt" % "sbt-git" % "0.8.5")
)
}
MyCommonPlugin.scala
import sbt._
import sbt.Keys._
//this import fails! object sbt is not a member of package com.typesafe
import com.typesafe.sbt.GitBranchPrompt.{ projectSettings => gitBranchPromptSettings }
object MyCommonPlugin extends AutoPlugin {
override def trigger = allRequirements
override lazy val projectSettings =
super.projectSettings ++
gitBranchPromptSettings ++
Seq(
// My common settings
)
}
To make this code work I would need to repeat the addSbtPlugin("com.typesafe.sbt" % "sbt-git" % "0.8.5") on plugins.sbt.
Related
I'm using IntelliJ with SBT plugin and I've added the following lines to build.sbt in order to add a dependency to a private github repository:
lazy val g = RootProject(uri("ssh://git#github.com/XXXX/myrepo.git"))
lazy val root = project in file("myproject") dependsOn g
After running sbt, my referenced project is successfully cloned, but build fails due to output path clashes between my base project and a root project that is automatically added each time I refresh sbt after modifying build.sbt.
I was having this same issue awhile back.
I'm not sure what causes it but I know that if you use the multi-project setup for sbt (root/project/build.scala) instead of the simple one (root/build.sbt) Intellij respects your settings.
Try the multi-project setup like this and see if it solves your problem:
import sbt.Keys._
import sbt._
lazy val g = RootProject(uri("ssh://git#github.com/XXXX/myrepo.git"))
object MyProjectBuild extends Build {
lazy val project = Project("myproject", file(".")) // <-- Make sure to name your project what you want the module to be named
.settings(
name := "myproject", // <-- Same here
version := "1.0",
scalaVersion := "2.11.4",
libraryDependencies ++=Seq(
"org.scalatest" % "scalatest_2.11" % "2.2.0" % "test",
)
).dependsOn(g)
}
Coming from Ruby on Rails, I used to just do a gem 'some_random_gem' and then bundle in the terminal and there were my dependencies. Now that I'm bumping my head against scala and play, I'm discovering that it might not be that easy on this playground.
I'm trying to runa sample application and this is the warning/error:
module not found: com.wingnest.play2#play21-frames-titan-plugin_2.10;1.3-module-2.4.4
Assume I do not know anything. Assume I have no experience and no scala background. I do have some, but can you give me a description on how to solve this?
I'm believe you will need the projects/build.scala file, so here it is:
import sbt._
import Keys._
import play.Project._
object ApplicationBuild extends Build {
val appName = "play21-frames-titan-simple-app"
val appVersion = "1.2-module-2.3.2"
val titanVersion = "0.4.2"
val appDependencies = Seq(
"com.wingnest.play2" % "play21-frames-titan-plugin_2.10" % "1.3-module-2.4.4",
"com.thinkaurelius.titan" % "titan-cassandra" % {titanVersion},
javaCore
)
val main = play.Project(appName, appVersion, appDependencies).settings(
resolvers += "Oracle Releases" at "http://download.oracle.com/maven/"
)
}
First off, there are two types of plugins.
sbt plugins are declared in project/plugins.sbt using addSbtPlugin("organization" % "plugin-name" % "version") - the same way that Play is enabled (Play is nothing more than an sbt plugin).
You can find more info about it in the sbt plugins documentation.
Play plugins are normal dependencies but must be activated in the conf/play.plugins file (create it if non-existent) using the <priority>:<qualified-plugin-name> syntax, e.g. 500:se.radley.plugin.salat.SalatPlugin.
Unfortunately, this part of Play is not documented.
I'm trying out sbt's direct dependsOn feature with a git repository ("project A") hosted at Github. I am using a stable tag reference, and in my test project ("project B"), sbt does clone project A from source and starts compiling. However compilation fails with project A's own dependencies seemingly missing (i.e. it doesn't seem to pick up anything defined in project A's build.sbt).
Is this a different from maven/ivy managed dependencies? Do I need to include all the transitive dependencies in my child project B? Sounds a bit weird to me. That would kind of kill off the whole effort, as I'm having like a dozen libraries on which project A depends.
To illustrate:
Project A (online on Github as source):
// build.sbt:
version := "1.2.3"
libraryDependencies += "org.foo" %% "bar" % "1.0"
Project B (local):
// project/Build.scala
import sbt._
import Keys._
object Build extends sbt.Build {
lazy val projA = RootProject(uri("git://github.com/me/projA.git#v1.2.3"))
lazy val projB = Project(id = "project-B", base = file(".").dependsOn(projA)
}
This goes:
[info] Compiling 678 Scala sources to /Users/me/.sbt/staging/
5666eafa865fdf605be3/target/scala-2.10/classes...
[error] /Users/me/.sbt/staging/5666eafa865fdf605be3/src/main/scala/com/me/
BarKeeper.scala:3: not found: object bar
[error] import org.foo.bar
[error] ^
So do I have to re-declare the library dependency on "org.foo" %% "bar" % "1.0"? I hope not!
This was purely my own fault, not sbt's. I had overseen an unmanaged library (folder lib) in project A. After exchanging it for a Maven managed version (folder lib_managed), project A now correctly compiles from source in the staging for project B.
In an SBT Plugin, I'm trying to access to managed resources of subprojects.
Here is the build file:
import sbt._
import Keys._
import play.Project._
object ApplicationBuild extends Build {
val appName = "demo"
val appVersion = "1.0-SNAPSHOT"
val appDependencies = Seq(
"org.jruby" % "jruby-complete" % "1.7.1"
)
val widgets = play.Project("widgets", appVersion, appDependencies, path = file("widgets"))
val main = play.Project(appName, appVersion, appDependencies, path = file("demo"))
.dependsOn(widgets)
}
I'm working in an SBT plugin defined in plugins.sbt.
Now, I need to use resources files from the subproject (widgets) during compilation of the parent project (demo).
So far the closest I've got to is the buildDependencies settings key - but I'm only getting ProjectRef objects, and the only information is the build base and the project id. I couldn't find a way to get to that project's resources directory.
I'm not familiar with writing plugins, but at least in your build.sbt you can define the resource file.
Or, again in the build.sbt you can create a "common" project that others reference, like:
lazy val common = (project in file("common"))
.settings(
Seq(
includeFilter in unmanagedResources := new SimpleFileFilter(_.getCanonicalPath.startsWith((sourceDirectory.value / "main" / "resources").getCanonicalPath))
)
)
Then other code (e.g. a Task) could reference this like:
lazy val doSomething = taskKey[Seq[File]]("Does something useful")
lazy val doSomethingSetting = doIt := {
val resourceDir = (resourceDirectory in common in Compile).value
println(resourceDir)
}
So your other projects could run this or reference that directory
Hopefully there's a straight forward way to implement one of those solutions for a plugin vs a build?
Unfortunately I do not believe this is possible. I was trying something similar but found the following in the documentation:
Note: At runtime, all plugins for all builds are loaded in a separate, parent class loader of the class loaders for builds. This means that plugins will not see classes or resources from build definitions
See: SBT Plugins
I am trying to figure out how idea will recognize thrid party dependencies when using SBT. When I use the sbt plugin gen-idea it seems to download all the necessary dependencies which get put into my ~/.ivy/ directory as expected. How can intellij use these deps?
EDIT:
One thing I noticed is if I make a new idea project instead of just a module then this works? Any idea why this would be? I would like to be able to have multiple sbt modules in the same project.
The sbt-idea plugin works with multi-module sbt project. We have been using it since somewhere around sbt-0.10.0, and currently are at sbt-0.11.2. It seems like you have the dependency part of the build file set up ok, so here's an example of how we do the project setup from a full specification Build.scala file:
object Vcaf extends Build {
import Resolvers._
import Dependencies._
import BuildSettings._
lazy val vcafDb = Project(
id = "vcaf-db",
base = file("./vcaf-db"),
dependencies = Seq(),
settings = buildSettings ++ /* proguard */ SbtOneJar.oneJarSettings ++ Seq(libraryDependencies := dbDeps, resolvers := cseResolvers)
)
lazy val vcaf = Project(
"vcaf",
file("."),
dependencies = Seq(vcafDb),
aggregate = Seq(vcafDb),
settings = buildSettings ++ Seq(libraryDependencies := vcafDeps, resolvers := cseResolvers) ++ webSettings
)
}
In the example, the vcaf-db project is in the a folder within the vcaf project folder. The vcaf-db project does not have it's own build.sbt or Build.scala file. You'll notice that we are specifying libraryDependencies for each project, which may or may not be your missing link.
As ChrisJamesC mentioned, you need to do a "reload" from within SBT (or exit sbt and come back in) to pick up changes to your build definition. After the project is reloaded, you should be able to do a "gen-idea no-classifiers no-sbt-classifiers" and get an intellij project that has the main project, modules, and library access as defined in the build file.
Hope it helps!
If you want multiple SBT modules in one IDEA project, you can use sbt multi-project builds (aka subprojects). Just create a master project that refers to the modules as sub-projects, then run gen-idea on the master. To specify dependencies among the modules you have to use Build.scala (not build.sbt), as in jxstanford's answer or like this:
lazy val foo = Project(id = "foo", base = file("foo"))
lazy val bar = Project(id = "bar", base = file("bar")) dependsOn(foo)
One level of subprojects works fine (with the dependencies correctly reflected in the resulting IDEA project), but nested subprojects don't seem to work. Also, it seems to be an sbt restriction that the subprojects must live in subdirectories of the master project (i.e., file("../foo") is not allowed).
See also How to manage multiple interdependent modules with SBT and IntelliJ IDEA?.