SBT: Access managed resources of a subproject? - scala

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

Related

Intertwined dependencies between sbt plugin and projects within multi-project build that uses the plugin itself

I'm developing a library that includes an sbt plugin. Naturally, I'm using sbt to build this (multi-project) library. My (simplified) project looks as follows:
myProject/ # Top level of library
-> models # One project in the multi-project sbt build.
-> src/main/scala/... # Defines common models for both sbt-plugin and framework
-> sbt-plugin # The sbt plugin build
-> src/main/scala/...
-> framework # The framework. Ideally, the sbt plugin is run as part of
-> src/main/scala/... # compiling this directory.
-> project/ # Multi-project build configuration
Is there a way to have the sbt-plugin defined in myProject/sbt-plugin be hooked into the build for myProject/framework all in a unified build?
Note: similar (but simpler) question: How to develop sbt plugin in multi-project build with projects that use it?
Is there a way to have the sbt-plugin defined in myProject/sbt-plugin be hooked into the build for myProject/framework all in a unified build?
I have a working example on Github eed3si9n/plugin-bootstrap. It's not super pretty, but it kind of works. We can take advantage of the fact that sbt is recursive.
The project directory is another build inside your build, which knows how to build your build. To distinguish the builds, we sometimes use the term proper build to refer to your build, and meta-build to refer to the build in project. The projects inside the metabuild can do anything any other project can do. Your build definition is an sbt project.
By extension, we can think of the sbt plugins to be library- or inter-project dependencies to the root project of your metabuild.
meta build definition (project/plugins.sbt)
In this example, think of the metabuild as a parallel universe or shadow world that has parallel multi-build structure as the proper build (root, model, sbt-plugin).
To reuse the source code from model and sbt-plugin subprojects in the proper build, we can re-create the multi-project build in the metabuild. This way we don't need to get into the circular dependency.
addSbtPlugin("com.eed3si9n" % "sbt-doge" % "0.1.5")
lazy val metaroot = (project in file(".")).
dependsOn(metaSbtSomething)
lazy val metaModel = (project in file("model")).
settings(
sbtPlugin := true,
scalaVersion := "2.10.6",
unmanagedSourceDirectories in Compile :=
mirrorScalaSource((baseDirectory in ThisBuild).value.getParentFile / "model")
)
lazy val metaSbtSomething = (project in file("sbt-plugin")).
dependsOn(metaModel).
settings(
sbtPlugin := true,
scalaVersion := "2.10.6",
unmanagedSourceDirectories in Compile :=
mirrorScalaSource((baseDirectory in ThisBuild).value.getParentFile / "sbt-plugin")
)
def mirrorScalaSource(baseDirectory: File): Seq[File] = {
val scalaSourceDir = baseDirectory / "src" / "main" / "scala"
if (scalaSourceDir.exists) scalaSourceDir :: Nil
else sys.error(s"Missing source directory: $scalaSourceDir")
}
When sbt loads up, it will build metaModel and metaSbtSomething first, and use metaSbtSomething as a plugin to your proper build.
If you have any other plugins you need you can just add it to project/plugins.sbt normally as I've added sbt-doge.
proper build (build.sbt)
The proper build looks like a normal multi-project build.
As you can see framework subproject uses SomethingPlugin. Important thing is that they share the source code, but the target directory is completely separated, so there are no interference once the proper build is loaded, and you are changing code around.
import Dependencies._
lazy val root = (project in file(".")).
aggregate(model, framework, sbtSomething).
settings(inThisBuild(List(
scalaVersion := scala210,
organization := "com.example"
)),
name := "Something Root"
)
// Defines common models for both sbt-plugin and framework
lazy val model = (project in file("model")).
settings(
name := "Something Model",
crossScalaVersions := Seq(scala211, scala210)
)
// The framework. Ideally, the sbt plugin is run as part of building this.
lazy val framework = (project in file("framework")).
enablePlugins(SomethingPlugin).
dependsOn(model).
settings(
name := "Something Framework",
crossScalaVersions := Seq(scala211, scala210),
// using sbt-something
somethingX := "a"
)
lazy val sbtSomething = (project in file("sbt-plugin")).
dependsOn(model).
settings(
sbtPlugin := true,
name := "sbt-something",
crossScalaVersions := Seq(scala210)
)
demo
In the SomethingPlugin example, I'm defining something task that uses foo.Model.x.
package foo
import sbt._
object SomethingPlugin extends AutoPlugin {
def requries = sbt.plugins.JvmPlugin
object autoImport {
lazy val something = taskKey[Unit]("")
lazy val somethingX = settingKey[String]("")
}
import autoImport._
override def projectSettings = Seq(
something := { println(s"something! ${Model.x}") }
)
}
Here's how we can invoke something task from the build:
Something Root> framework/something
something! 1
[success] Total time: 0 s, completed May 29, 2016 3:01:07 PM
1 comes from foo.Model.x, so this demonstrates that we are using the sbt-something plugin in framework subproject, and that the plugin is using metaModel.

Sharing code between Play and Console app in scala

I am looking to share a common module between a Play app in scala and a scala console app. My directory structure looks like this:
RootFolder
-- consoleApp
src/main/scala: MyApp.scala
-- playApp
app/controllers: MyController.scala
-- common
src/main/scala: MyLib.scala
-- project
Build.scala
plugins.sbt
The following is my Build.scala, and it works for glueing the common module and the play app.
object ApplicationBuild extends Build {
val appName = "helloworld"
val appVersion = "1.0"
val appDependencies = Seq(
// Add your project dependencies here,
)
val common = Project("common", file("common"))
val main = play.Project(appName, appVersion, appDependencies, path=file("playApp")).settings(
// Add your own project settings here
).dependsOn(common)
}
How would I combine the console app with common?
You can actually do the same thing with your console project. The syntax varies a bit, here's the documentation for 0.13.2.
lazy val console = Project("console", file("consoleApp")).dependsOn("common")

Simple SBT Module in Play Project Causes Play to Crash for Unknown reason - play 2.1

https://github.com/jasongoodwin/play21-multimodule-demo has the code.
I've recreated this problem on 3 different projects. I don't know what's up.
once you put the aggregate and dependsOn in the build.scala file the project throws an error when trying to start play.
[info] Loading global plugins from /Users/jgoodwin/.sbt/plugins [info]
Loading project definition from
/Users/jgoodwin/Development/src/ninjakeyboard/test/tmp/play21-multimodule-demo/project
[error] java.lang.ExceptionInInitializerError [error] Use 'last' for
the full log. Project loading failed: (r)etry, (q)uit, (l)ast, or
(i)gnore? q
Build file
import sbt._ import Keys._ import play.Project._
object ApplicationBuild extends Build {
val appName = "multimodule-demo" val appVersion =
"1.0-SNAPSHOT" //val scalaVersion = "2.10.0"
val appDependencies = Seq(
// Add your project dependencies here,
jdbc,
anorm )
val buildSettings = (
scalaVersion := "2.10.0"
)
val main = play.Project(appName, appVersion, appDependencies, path =
file("web")).settings(
// Add your own project settings here ).aggregate(testmodule).dependsOn(testmodule)
val testmodule = Project(
"testmodule",
file("testmodlue"),
settings = buildSettings )
}
I noticed in your github build.scala that testmodule is declared (and initialized) after the main module, thus the null exception during project loading.
I made the same mistake and putting the modules in the right order made it work.
I just started playing with Play2 and Scala, but I believe the use of lazy val could also help.
Try to create a fresh project:
Run command: play new MyApp
Run play clean eclipse
Make dir modules (inside MyApp)
Run command play new MyModule
Run command play clean eclipse
Rename controller to MyModuleCon, and change the routes file
Run command play clean publish-local
Add to your MyApp Build.scala the dependency: "mymodule" % "mymodule_2.10" % "1.0-SNAPSHOT"
Rename main route to app.routes (because modules has also routes, although it can be deleted!)
On the main project run: play dependencies eclipse
Voila, you have your submodule, see my github: https://github.com/adis-me/PlayModuleExample
Just run play run and visit main project at: http://localhost:9000 and then visit the submodule: http://localhost:9000/module.
This should do the trick, for you, I think!
EDIT: 2013-03-27
Changing the above configuration to a real sub project setup, follow these steps:
Run on MyApp: play clean
Change MyApp's Build.scala file:
object ApplicationBuild extends Build {
val appName = "MyAppp"
val appVersion = "1.0-SNAPSHOT"
val appDependencies = Seq(
// Add your project dependencies here,
javaCore,
javaJdbc,
javaEbean
)
val subproject = play.Project(
"sub-project", appVersion, appDependencies, path = file("modules/MyModule")
)
val main = play.Project(appName, appVersion, appDependencies).settings(
// Add your own project settings here
).dependsOn(subproject).aggregate(subproject)
}
Run commands: play clean eclipse
Run commands: play run
That's it, you can no visit the same urls as above mentioned.
It can be that your idea does not recognize the classes from your subproject, just reference the MyModule project as referenced library for the main project, and you are good to go.
Good luck!

Include Scala and Java Nature in Play2 Eclipse Project with javaCore

I have an sbt play2 project that I want to configure for eclipse, to include both a Scala nature and Java nature automatically(in the generated .project file). I can do this inside of eclipse by manually adding the nature, but I want the eclipse plugin to do this for me automatically.
The plugin's default behavior is to add both natures UNLESS you include javaCore in your appDependencies, which I need to do. When you include javaCore, mainLang is set automatically to JAVA, and the Scala Nature is excluded in the configuration.
see: https://github.com/playframework/Play20/wiki/Migration under Changes to the Build File
Is there a way to override mainLang and set it to Scala? Or is there another way to include the Scala Nature along side the Java Nature?
import sbt._
import Keys._
import play.Project._
object ApplicationBuild extends Build {
val appName = "SampleApp"
val appVersion = "1.0-SNAPSHOT"
val appDependencies = Seq(
javaCore, javaJdbc, javaEbean
)
val main = play.Project(appName, appVersion, appDependencies).settings(
// Want to set mainLang = SCALA here, but don't know how
)
}
I use sbt for everything, so play commands are off limits (to avoid additional system dependencies)
Edit: I'm using play-sbt 2.1.0, Scala 2.10.1-RC1, and sbt 0.12.2
By looking at the Play sbt-eclipse source code (here and here), I managed to make it work using the following Build.scala file:
import sbt._
import Keys._
import play.Project._
object ApplicationBuild extends Build {
val appName = "SampleApp"
val appVersion = "1.0-SNAPSHOT"
val appDependencies = Seq(
javaCore, javaJdbc, javaEbean
)
import com.typesafe.sbteclipse.core._
import com.typesafe.sbteclipse.core.EclipsePlugin._
import scala.xml.transform.RewriteRule
val main = play.Project(appName, appVersion, appDependencies).settings(
EclipseKeys.projectFlavor := EclipseProjectFlavor.Scala,
EclipseKeys.projectTransformerFactories := Seq[EclipseTransformerFactory[RewriteRule]]()
)
}
Do not forget the 3 imports.
There should be a better way using directly eclipseCommandSettings("SCALA") but I did not find it.

Play! 2.0 framework multi Module project

I would need to have two different projects, let's say internal and external, which use the same data layer, and I would like to avoid replicating the configuration file for dryness reasons.
I have looked to the sub projects documentation at http://www.playframework.org/documentation/2.0.2/SBTSubProjects but the doc is pretty short.
I am now aware of the possibility to modularize the configuration, thanks to #Georg Engel
import sbt._
import Keys._
import PlayProject._
object ApplicationBuild extends Build {
val appName = "MyApp"
val appVersion = "1.0-SNAPSHOT"
val appDependencies = Seq(
// Add your project dependencies here,
)
lazy val common = Project(appName + "-common", file("modules/common"))
lazy val website = PlayProject(
appName + "-website", appVersion, path = file("modules/website")
).dependsOn(common)
lazy val adminArea = PlayProject(
appName + "-admin", appVersion, path = file("modules/admin")
).dependsOn(common)
lazy val main = PlayProject(appName, appVersion, appDependencies, mainLang = SCALA).settings(
// Add your own project settings here
).dependsOn(
website, adminArea
)
}
and the compilation errors I had where only due to the reverse router (canceling routes but not controller actions result in this)
Here is what I do and have done. I make a multi-module Maven project where I basically keep all my core reusable code.
Then for all my other web projects (projects that make a WAR) I use SBT, Gradle, and even Ant with Maven plugins in some cases. Those projects hold there own config (like db host and creds).
framework
- pom.xml
- db-module
- pom.xml
- src/main/resources # possible classpath loading config here
- etc...
- mail-module
- pom.xml
- etc...
- service-module
- pom.xml
- etc...
Other projects then just depend on the framework and for SBT projects (play 2.0) you can set it up so that one of your resolvers is a local maven repo: https://github.com/harrah/xsbt/wiki/Getting-Started-Library-Dependencies
EDIT for clarification: Framework pom.xml is the parent project. You can make the mail-module depend on the db-module and then in your separate web app project you can just depend on the mail-module and you will get both the mail-module and the db -module.
Many people throw Maven under the bus but it still does multi-module projects better than anything else.
More explanation:
http://www.sonatype.com/books/mvnex-book/reference/multimodule-web-spring-sect-intro.html#fig-multimodule-web-spring-projects
Download the example maven pom files here: http://www.sonatype.com/book/mvn-examples-1.0.zip ... Chapter 7 is the multi-module.
We are using submodules like this (where "core" is the shared submodule):
Build.scala
val coreModule = PlayProject(appName + "-core", "1.0", appDependencies, path = file("modules") / "core")
val main = PlayProject(appName + "-app", appVersion, appDependencies, mainLang = SCALA).settings(
// Add your own project settings here
).dependsOn(coreModule).aggregate(coreModule)
Unfortunatelly submodules have to live under the project tree ("../core" as path isn't possible) - so we are using git submodules to get the shared module into the tree:
git submodule add git://git.example.com/modules.git modules
git submodule init
git submodule update
Propably SVN externals, mercurial submodules etc. will do this job too.
A source dependency on a module living somewhere in your source tree can help you to achieve the build you want :
import sbt._
import Keys._
import PlayProject._
object ApplicationBuild extends Build {
val appName = "test"
val appVersion = "1.0-SNAPSHOT"
val appDependencies = Seq(
// Add your project dependencies here,
)
lazy val main = PlayProject(appName, appVersion, appDependencies, mainLang = SCALA).settings(
// Add your own project settings here
).dependsOn(common)
lazy val common = RootProject(file("../common"))
}
You cannot mix a play project with another, so your configuration should be in "dependencies". The good thing with source dependencies is they are live in your project (thanks to the recursivity of SBT). If the source of your dependency change, your main project get the change at the next compile.
You can check the complete structure of my multimodule play app here: https://github.com/un-jon/play2MultiModule.