I have the following project structure:
/
├── project
| └── ...
├── src
| └── ...
├── lib
│ ├── prod-lib.jar
| └── test-lib.jar
└── build.sbt
And I need to compile with test-lib.jar for deploying into a testing environment and with prod-lib.jar for deploying into a production environment.
Both of them have the same API for accessing the things I need, so my source code does not have any problem with neither of them, but both have subtle differences on how they implement their execution in the background.
Is there a way to create a "sbt task" (Or maybe anything else) that can ignore one jar or the other, but in both perform the assembly task anyway?
Put your jars in different folders and set unmanagedBase key in Compile and Test scopes correspondingly:
> set unmanagedBase in Compile := baseDirectory.value / "lib-compile"
> show compile:assembly::unmanagedBase
[info] /project/foo/lib-compile
> set unmanagedBase in Test := baseDirectory.value / "lib-test"
> show test:assembly::unmanagedBase
[info] /project/foo/lib-test
But don't forget to call assembly task in the corresponding scope then (compile:assembly or test:assembly), because in Global it's still the default:
> show assembly::unmanagedBase
[info] /project/foo/lib
Related
I'm trying to define a multi-project build with a consequent number of subprojects:
.
├── build.sbt
├── project/
│ ├── dependencies.scala
│ ├── tasks.scala
│ └── settings.scala
├── lib_1/
│ └── src/
├── ...
└── lib_n/
└── src/
Those subprojects are currently defined in build.sbt:
val outputJarFolder = "/some/path/"
lazy val comonSettings = /* ... */
lazy val lib_1 = (project in file ("lib1")).settings(
name:="LibOne",
commonSettings,
libraryDependencies ++= Seq(scalaTest, jsonLib, scalaXML, commonsIo),
Compile/packageBin/artifactPath := file(outputJarFolder + "lib1.jar")
)
// ... more libs ...
lazy val lib_n = (project in file ("libn")).settings(
name:="LibLast",
commonSettings,
Compile/packageBin/artifactPath := file(outputJarFolder + "libn.jar")
)
.depensOn(lib_2, lib_12)
How can I define those subprojects in another file than build.sbt in order to "unclog" that file? I want to still be able to define them in their lexicographic order (so lazy is a must). I'm working with sbt version 1.2.8 and scala 2.10.
I've tried:
Putting the declaration of those lib_k variables in a scala file and importing it --> sbt says: "classes cannot be lazy".
Putting those declaration in an object (or in a class and instantiate it in build.sbt) --> sbt projects doesn't list any subproject.
sbt documentation mentions it, but doesn't emphasize too much (perhaps to avoid encouragement for too much variation in how builds are defined in the absence of a common convention):
The build definition is described in build.sbt (actually any files named *.sbt) in the project’s base directory.
So you can split your build.sbt file into several separate .sbt files in the root of the project with different names.
I also recommend reading documentation on Organizing the build.
How can I accumulate all the discoveredMainClasses of a project, along with its dependent sub projects in SBT?
For example, I have a project that looks like
├── bar
│ └── src
│ └── main
│ └── scala
│ └── BarMain.scala
├── build.sbt
├── foo
│ └── src
│ └── main
│ └── scala
│ └── FooMain.scala
├── project
│ └── build.properties
└── root
With one root project that aggregate(foo, bar), I get the following for discoveredMainClasses:
[info] foo/compile:discoveredMainClasses
[info] List(MainFoo)
[info] bar/compile:discoveredMainClasses
[info] List(MainBar)
[info] root/compile:discoveredMainClasses
[info] List()
With one root that only dependsOn(foo, bar) I get
> show discoveredMainClasses
[info] *
How can I have show root/discoveredMainClasses contain both MainFoo and MainBar?
For context, I have other tasks that depend on the output from discoveredMainClasses namely the makeBashScripts in native-packager
The core idea is to create a module that depends on all all the sub modules you want to include and configure all settings on this module.
This results in a build.sbt like this
lazy val root = project.in(file("."))
// package the root module, but not the sub modules
.enablePlugins(JavaAppPackaging)
.settings(
name := "application",
// add the discoveredMainClasses to this project
discoveredMainClasses in Compile ++= (discoveredMainClasses in (client, Compile)).value,
discoveredMainClasses in Compile ++= (discoveredMainClasses in (server, Compile)).value
)
// include these modules in the resulting package
.dependsOn(client, server)
lazy val client = project.in(file("client"))
.settings(
name := "client"
)
lazy val server = project.in(file("server"))
.settings(
name := "server"
)
The (discoveredMainClasses in (client, Compile)).value accesses the discoveredMainClasses from the client project in the Compile scope.
You can build and run your applications with
$ sbt universal:stage
$ ./target/universal/stage/bin/client-app
$ ./target/universal/stage/bin/server-app
A running example can be found here.
cheers,
Muki
An alternative way to #Muki's answer would be to define a ScopeFilter that includes everything but root and accumulate main classes that way. This has the advantage of not having to repeat client, server everywhere.
The resulting build.sbt:
lazy val allCompileButRootFilter =
ScopeFilter(inAggregates(ThisProject, includeRoot = false), inConfigurations(Compile))
lazy val root = project.in(file("."))
.aggregate(client, server)
.enablePlugins(JavaAppPackaging)
.settings(
discoveredMainClasses in Compile ++=
discoveredMainClasses.all(allCompileButRootFilter).value.flatten,
...
)
I've been using Gradle for most of my Scala projects, but I want to evaluate the suitability of SBT as a replacement. One of the things I've done in Gradle is to exclude a certain resource directory from the final build (for example, using CoffeeScript to write JavaScript files that will be included as final resources).
In Gradle, I'd do this by:
sourceSets {
main {
resources {
exclude 'com/example/export/dev' // exclude development resources
}
}
}
And this would exclude the resource package com.example.export.dev package from the final build.
How would I do the same in SBT? I've tried
unmanagedResourceDirectories in Compile -= (resourceDirectory in Compile).value / "com/example/export/dev"
but that doesn't do a thing (I understand why, but that doesn't really help). And the documentation on the SBT web site only talks about excluding file patterns (at Classpaths, sources, and resources).
As a more descriptive image, say we have the following resource directory structure:
com
\---example
\---export
\---dev
\---something
In the final output, I want:
com
\---example
\---export
\---something
The way to think in SBT is a bit different and I know it can be hard at first.
In your example, you need to modify the task that generate the resource files (or the task that selects the folders to look for resource files).
Here is an example of how I can select only the resource files that start with character 'a'.
(unmanagedResources in Compile) := (unmanagedResources in Compile).value.filter(_.getName.startsWith("a"))
Similarly if you want to modify the entire directory of the resource files you can do that like this:
(unmanagedResourceDirectories in Compile) := (unmanagedResourceDirectories in Compile).value.filter(_.getName.startsWith("a"))
Obviously my filters here are just and example, you can have any complex pattern that Scala supports.
The nice thing about SBT is that it's interactive. So you can check the result of your task by simply typing these at the REPL of your project:
> show compile:unmanagedResources
> show compile: unmanagedResourceDirectories
To check all the dependencies to the task do this from the REPL:
> inspect tree compile:unmanagedResources
Assumption:
SBT knows where to find all resources using the standard maven build directory layout. The above solution assumes that all resources are under the /resources directory. You can then access them from your Scala code using getClass.getResource("/folderInsideResources/file.txt").
Here is a sample directory layout for a mixed Java/Scala project with resources:
.
├── main
│ ├── java
│ │ └── com
│ │ └── a
│ │ └── b
│ │ └── Hello.java
│ ├── resources
│ │ ├── a.tx
│ │ └── b.tx
│ └── scala
│ └── com
│ └── a
│ └── b
│ └── ScalaHello.scala
└── test
├── resources
└── scala
└── com
└── a
└── b
└── ScalaHello.scala
To access the resource file just use:
getClass.getResource("/a.txt")
getClass.getResource("/b.txt")
From https://github.com/sbt/sbt-jshint/issues/14:
excludeFilter in unmanagedResources := {
val public = ((resourceDirectory in Compile).value / "com" / "example" / "export" / "dev").getCanonicalPath
new SimpleFileFilter(_.getCanonicalPath startsWith public)
}
I have created a project foo_proj with Intellij (using SBT template) and added a module test_mod to it. The abbreviated directory looks like this
foo_proj
├── src
│ └── main
│ └── scala-2.11
│ └── proj_obj.scala
└── test_mod
└── src
└── tmod.scala
The contents of proj_obj.scala are:
package com.base.proj
object proj_obj {
}
If would like to be able to import this object (proj_obj) into the module file tmod.scala, but when I try import com.base.proj, it can't find it.
I am new to Scala, so if I want to use stuff from the project src directory in other project modules, how else should I be structuring things? Or is this an Intellij IDEA configuration that I need to set?
Edit
The contents of the generated build.sbt are
name := "test_proj"
version := "1.0"
scalaVersion := "2.11.6"
to enable "submodules" (aka multiproject), all you need to do is add the following to your build.sbt file (or use a scala file under project dir):
lazy val root = project in file(".")
lazy val testModule = project in file("test_mod") dependsOn(root)
also, you should change test_mod dir structure.
either drop the src dir and put all your sources under the test_mod dir,
or use the sbt convention: src/main/scala or src/test/scala.
I have followed the instructions on SBT's documentation for setting up test configurations. I have three test configurations Test, IntegrationTest, and AcceptanceTest. So my src directory looks like this:
src/
acceptance/
scala/
it/
scala/
test/
scala/
My question is, how can I configure SBT to allow sharing of classes between these configurations? Example: I have a class in the "it" configuration for simplifying database setup and tear down. One of my acceptance tests in the "acceptance" configuration could make use of this class. How do I make that "it" class available to the test in "acceptance"
Many thanks in advance.
A configuration can extend another configuration to use that configuration's dependencies and classes. For example, the custom test configuration section shows this definition for the custom configuration:
lazy val FunTest = config("fun") extend(Test)
The extend part means that the compiled normal test sources will be on the classpath for fun sources. In your case, declare the acceptance configuration to extend the it configuration:
lazy val AcceptanceTest = config("acceptance") extend(IntegrationTest)
In case you want to stick with predefined configurations instead of defining new ones, and since both Test and IntegrationTest extend Runtime (one would expect IntegrationTest to extend Test…), you could use the following:
dependencyClasspath in IntegrationTest := (dependencyClasspath in IntegrationTest).value ++ (exportedProducts in Test).value
This should put all the classes you define in Test on the IntegrationTest classpth.
##EDIT:
I was just became aware to amuch better solution thanks to #mjhoy:
lazy val DeepIntegrationTest = IntegrationTest.extend(Test)
An approach is documented here: http://www.scala-sbt.org/release/docs/Detailed-Topics/Testing#additional-test-configurations-with-shared-sources
SBT uses the Maven default directory layout.
It will recognize folders unders src/test/scala to compile along with src/main/scala.
So, if you move the other folders under src/test/scala SBT will compile them and you can share code between them. e.g.:
└── scala
├── acceptance
│ └── scala
│ └── Acceptance.scala
├── it
│ └── scala
│ └── IT.scala
└── Test.scala
Running sbt test will compile all three files in the directory. So, with this Acceptance refer to and can create a new IT class for example.