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.
Related
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!
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.
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 get a play project to have another local scala project as a dependency. I have the local scala project deploying to my local M2 repository with this line in my configuration file.
publishTo := Some(Resolver.file("file", new File(Path.userHome.absolutePath+"/.m2/repository")))
And I am trying to load the dependency in my play project with this line
val appDependencies = Seq(
"com.experimentalcork" %% "timeywimeyentities" % "0.0.2"
)
val main = PlayProject(appName, appVersion, appDependencies, mainLang = SCALA).settings(
resolvers += "Local Maven Repository" at "file://" + Path.userHome.absolutePath + "/.m2/repository",testOptions in Test := Nil
)
In the logs as I do a 'play compile' it states that it can not find the dependency. It is looking in the place where I specified the dependency would be.
[warn] ==== Local Maven Repository: tried
[warn] file://C:/Users/caelrin/.m2/repository/com/experimentalcork/timeywimeyentities_2.9.1/0.0.2/timeywimeyentities_2.9.1-0.0.2.pom
And when I go to check that directory, I can confirm that the pom and jar files are there. I am completely baffled as to how it could look in the directory that contains the pom and not find it. Has anyone had any experiences with this?
You need a .dependsOn call too, I think.
val timeywimeyentities: Project = Project([Put all of your settings here for the project just like you would a play project])
val main = PlayProject(appName, appVersion, appDependencies, mainLang = SCALA).settings(
resolvers += "Local Maven Repository" at "file://" + Path.userHome.absolutePath + "/.m2/repository",testOptions in Test := Nil
).dependsOn(timeywimeyentities % "compile->compile")
Adding "compile->compile" makes the main code of your play project rely on the main code of your dependency. If you wanted to make the test code of your play project also depend on it, you could use "compile->test". If you want only the test code of both to see each other, you could use "test->test". You can also chain them together, for example: "compile->compile;test->test". If all you want is "compile->compile", you need not explicitly state it.
See https://github.com/harrah/xsbt/wiki/Getting-Started-Multi-Project for more information.
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?.