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")
Related
I have a multi project setup like this:
lazy val kalosrpc = project
.settings(
libraryDependencies ++= Seq(
"io.grpc" % "grpc-netty" % scalapb.compiler.Version.grpcJavaVersion
)
).dependsOn(kalosgen)
lazy val kalosgen = project
.settings(
// settings not important
)
The main class of kalosgen generates a model via slick-codegen and places is it in:
kalosgen/target/scala-2.13/src_managed/main
in the package com.kalos.gen. It also compiles protobufs into scala classes at compile time but that package is in the classpath as expected.
I can then import those files from kalosgen into kalosrpc, intelliJ does not complain and has full access to the type information defined in those files. So I run kalosgen/compile and the packages are generated as I expect however when I follow that up with kalosrpc/compile I get:
object gen is not a member of package com.kalos
I've tried changing the name of the packages but it doesn't fix anything. Based on the information presented here my project configuration seems correct.
Try executing show sourceManaged from sbt which should output the location of where generated files should end up, for example in my project it is at
.../myproject/target/scala-2.13/src_managed
It likely should be
kalosgen/target/scala-2.13/src_managed/main/com/kalos/gen
instead of
kalosgen/target/scala-2.13/main/com/kalos/gen
Also double check generated files have package statements at the top.
The problem here was that I was generating the sources in scala code via the slick-codegen utility:
import slick.codegen.SourceCodeGenerator
object Main extends App {
val url = "hidden"
val user = "hidden"
val password = "hidden"
val dbDriver = "com.mysql.jdbc.Driver"
val profile = "slick.jdbc.MySQLProfile"
SourceCodeGenerator.main(
Array(
profile,
dbDriver,
url,
"./kalosgen/target/scala-2.13/src_managed/main",
"com.kalos.gen",
user,
password
)
)
}
My guess is that you have to generate sources via SBT tasks to have them recognized by SBT as being valid sources (at least for the purposes of inter project dependencies), so I was able to translate the above code to a task that runs at compile time in build.sbt:
lazy val gen = project
.settings(
libraryDependencies ++= Seq(
"dependencies"
),
sourceGenerators in Compile += Def.task {
val outDir = (sourceManaged in Compile).value.getPath
(runner in Compile).value.run(
"slick.codegen.SourceCodeGenerator",
(dependencyClasspath in Compile).value.files,
Array(
"slick.jdbc.MySQLProfile",
"com.mysql.jdbc.Driver",
"url",
outDir,
"com.kalos.gen",
"username",
"password"
),
streams.value.log
)
Seq(file(outDir + "/com/kalos/gen/Tables.scala"))
}.taskValue
)
Now the generated Tables.scala appears as expected in the class path and my project compiles. If someone with more knowledge of sbt could provide a more comprehensive explanation of why this happened I will gladly accept it as the proper answer.
I have project with externl projectes, registered in my build file with RootProject class.
In general, I have 4 folders: app0, app1, app3 and app-all on same level.
There is project folder in the app-all with following Build.scala file:
import sbt._
object AppBuild extends Build {
lazy val app2 = RootProject(file("../app0"))
lazy val app3 = RootProject(file("../app3"))
lazy val app1 = RootProject(file("../app1"))
lazy val all = Seq(japp1, app2, app3)
lazy val root = project.in(file(".")).dependsOn(all.map(ClasspathDependency(_, None)) :_*)
// lazy val root = project.in(file(".")).aggregate(all.map(sbt.Project.projectToRef) :_*)
}
The problem is, that sbt is building those 3 sub-projects in random order.
Once it starts with app1 another time it starts with app3. Same for both aggregate and dependsOn invocation. What are rules of aggregation?
Can I set order somehow?
I expect to set smth like app1.dependsOn(app3) but actually I cannot do it, because it's "project reference" but not real project.
Sbt version 0.13.
--
Note: This is test sample with one file, you can create 4 folders by yourself, try to build everything from apps-all and see what is happeninng.
In latest sbt 0.13 it's done like this.
lazy val `root` = (project in file(".")).
aggregate(`sub-a`, `sub-b`)
lazy val `sub-a` = (project in file("a"))
lazy val `sub-b` = (project in file("b")).
dependsOn(`sub-a`)
According to docs, http://www.scala-sbt.org/0.13/docs/Multi-Project.html#Classpath+dependencies
This also creates an ordering between the projects when compiling them
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!
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 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.