What is the best way to handle multiple run targets in SBT? - scala

I'm attempting to create a Build.scala that has multiple projects/targets for the run action. I had started trying to define it as a sub-project like:
val this_one = Project(
id = "Main",
base = file(".")
settings = ...
)
My first guess is that I have the wrong settings stuff, or the config doesn't belong in a Project definition like this.
To elaborate a little, I have more than one main() I want to run in a single source tree. There are a couple of tools that go with the main project itself, and I would like to be able to execute them selectively from the sbt console.

Turns out SBT is just smart enough to figure it out by itself! If you have multiple classes with a main(), it gives you a choice when you type "run" at the SBT prompt!

You can specify the main class using run-main. Something like:
sbt "run-main com.company.project.Main1"
sbt "run-main com.company.project.Main2"

Related

How to import unmanaged dependencies into code

I'm trying to use sbt to pull down an existing github project and then (obviously) use it in my code. My abbreviated build.sbt is shown below:
lazy val myProjInGit = RootProject(
uri("git://github.com/me/sheet-ref.git#master"))
lazy val root = (project in file("."))
.dependsOn(myProjInGit)
.settings(
name := "MainProj",
)
libraryDependencies ++= Seq( /*other stuff... */)
This is probably a basic one, but how should I then be importing this into code? Could I use, say, import myProjInGit.SampleObject.sampleFunction in my Main.scala? I can see a disconcerting lack of info on SO about this topic so I'm not sure that I'm not barking up completely the wrong tree. Thanks in advance for any help.
EDIT: some further details here. The project I am trying to import is just a library, so it only contains a package object. To try to debug things a little, I'm trying to import it as a ProjectRef, rather than a RootProject. I've discovered the projects sbt command, and if I run it on the project I'm trying to import (not the main one), it gives me:
sbt:SheetRef> projects
[info] In file:/data/va-projects/sheet-ref/
[info] * SheetRef
So... it seems that external project is called SheetRef. If I try to import SheetRef using
lazy val sheetRef = ProjectRef(
uri("git://github.com/me/sheet-ref.git#master"), "SheetRef")
then I get an sbt error:
[error] No project 'SheetRef' in 'git://github.com/me/sheet-r
ef.git#master'.
[error] Valid project IDs: root
Which feels unintuitive. I can import it as root, but then I seem to be unable to access it from my code - if it requires an import statement, it's not obvious to me what it should be.
The dependency has to be an SBT project, so SBT knows how to build it. If it is, then yes, you access it just like any other dependency in your code, because it's added to the classpath (after being built) just like any other dependency.
Which feels unintuitive. I can import it as root, but then I seem to be unable to access it from my code - if it requires an import statement, it's not obvious to me what it should be.
The thing is, it doesn't matter how you add the dependency; it could be a normal library dependency, an unmanaged dependency, a source dependency, etc. Those are SBT concepts, which have no meaning to the Scala compiler.
So the import statement can't depend on project names, jar names or anything like that. In fact, it's the same as if you just put those files into your own project.
Is there someone that made such a thing work? I am trying the same, and somehow manage to get the git project imported but I have no idea how to access the imported scala components.

What is the issue with this sbt file?

When I import a SBT project into intelliJ, the build.sbt file is showing lot of errors as shown in the following screenshot. Wondering what might be the issue
IDEA Version 13.1.4
I also see the following
The following source roots are outside of the corresponding base directories:
C:\Users\p0c\Downloads\recfun\src\main\resources
C:\Users\p0c\Downloads\recfun\src\test\java
C:\Users\p0c\Downloads\recfun\src\test\scala
C:\Users\p0c\Downloads\recfun\src\test\resources
These source roots cannot be included in the IDEA project model. Please consider using shared SBT projects instead of shared source roots.
I think the question perhaps does not provide all the required information to answer conclusively, but I'll give it a spin anyways -
Since sbt runs correctly when invoked from the shell, we know the sbt file is fine. I use Idea for my Scala and sbt projects and I am sure the Idea sbt support works very well, but! Idea is far more restrictive than sbt when it comes to project structure. It is really easy to create a valid sbt project structure that Idea can't support very well.
Given that the source roots error indicates that the recfun/src folder is not in the project folder, it seems clear that this error is not emitted during the processing of recfun/build.sbt. The screenshot shows you have at least three different sbt files, progfun-recfun, submission and scala-recfun. Since Idea will also create projects like submission-build, and you have an assignment-build project there, something is probably broken in the project structure, not from the sbt viewpoint - there you're fine, you can build - but from the Idea viewpoint, which is more restrictive.
My suggestion to resolve this would be to change your project structure as follows. First, have a top level project with a build.sbt. Then create a sub-project for each src folder you want. Do not put a src folder in your top level project. Each sub-project needs a build.sbt as well.
Second, make sure the sub-projects build correctly with sbt when run from the shell. Arrange the sub-project build.sbt files with the proper dependencies, using this syntax:
lazy val a = ProjectRef(file("../a"), "a")
lazy val b = ProjectRef(file("../b"), "b")
lazy val root = Project(id = "c", base = file(".")) dependsOn (a, b)
(This example has three sister projects a, b and c, where c depends on a and b. The three projects are placed in directories with the same name, within the root directory. The code snippet is from the build file for c.)
Third, arrange your top level build.sbt to aggregate the sub-projects, using this syntax in the top level build.sbt:
lazy val a = ProjectRef(file("a"), "a")
lazy val b = ProjectRef(file("b"), "b")
lazy val c = ProjectRef(file("c"), "c")
lazy val root = (project in file(".")).
aggregate(a, b, c)
Building this top level project will build each of the sub-projects a, b and c, and the dependencies established in the sub-project build files will ensure they are built in the right order.
Fourth, import the top level project into Idea, and all should be well.
You can get fancy with the file structure if you want, because the project references use relative paths, but it's usually nice to at least start simple.
I had a lot of frustration with sbt and Idea at the start, I hope this helps :)

How to define custom configuration to filter ScalaTest tests in build.sbt?

I already know (and have working) the ability to
http://www.scala-sbt.org/0.13/docs/Testing.html
http://code.hootsuite.com/tagged-tests-with-sbt/
http://alvinalexander.com/scala/scalatest-mark-tests-tags-to-include-exclude-sbt
for project/Build.scala style sbt projects.
However, I do not know how (or if it is possible) to do this in a simple build.sbt project: the problem is when configs is called on the Project.
If it is possible to apply Tag filters to scalatest, how does one do it in a build.sbt?
Note: I don't want the filter in Test, I want it in a custom Configuration. The workaround is to use test-only * -- -l SlowTest (which is kinda clunky).
It looks like one can now define a root in a build.sbt, so the above sytax is allowed
e.g.
lazy val root = project in file(".") settings (
...
) configs ( MY CONFIG GOES HERE)

IntelliJ Idea sbt managed source file

I am using sbt-buildinfo plugin that generates Scala source from my build definitions, allowing me to reference project name, version, etc. from my Scala code.
It does this by generating a file BuiltInfo.scala with the following contents:
package hello
case object BuildInfo {
val name = "helloworld"
val version = "0.1-SNAPSHOT"
val scalaVersion = "2.10.3"
val sbtVersion = "0.13.2"
}
in
target/scala-2.10/src_managed/main/sbt-buildinfo/BuildInfo.scala.
Everything compiles and I can reference those vals.
However, IntelliJ Idea doesn't recognize BuildInfo.scala as a managed source file, so that it would stop showing me errors. Any idea how to do that?
Thanks!
Grega, are you working in a Play Framework project? Or do you have any SBT sub-projects? I don't have a complete answer, but may have a lead.
This same problem shows up in my IDEA projects when using sbt-buildinfo and sbt-scalaxb. Frustratingly, it has worked intermittently—usually after lots of tinkering around, but inexplicably stops.
I wound up digging a bit deeper (and eventually issued bug report SCL-7182 to JetBrains), and noticed the root cause was having a sub-project. When present, IDEA doesn't correctly identify src_managed for the root project, but does for the sub-project.
A work-around, for now, is to manually add the correct src_managed directory to your project's sources using the Project Structure dialog.
For reference, I'm running version 0.38.437 of the Scala plugin on IntelliJ IDEA 13.1.3.

How do I set a system property for my project in sbt?

I'm sure I'm missing something really simple... I want to set the system property java.awt.headless to true for my sbt project. Reading the page on properties I think that I need to use system or systemOptional. In my project file I've tried things like:
lazy val javaAwtHeadless = system[Boolean]("java.awt.headless")
Setting it as a user property (e.g. lazy val javaAwtHeadless = property[Boolean]) and setting the accompanying value in build.properties made the property visible in the sbt console but not within sbt's Scala console (via System.getProperty("java.awt.headless")).
set java.awt.headless true from the sbt console works, including being set in the Scala console, but it doesn't persist to the next time I launch sbt.
A straightforward method would be to edit the batch file or shell script that you use to run sbt and add -Dprop=val
If I needed this option for all sbt tasks, I'd set it as follows in build.sbt
javaOptions += "-Djava.awt.headless=true"
If it was just for one task, eg: run, you can scope that:
javaOptions in Runtime += "-Djava.awt.headless=true"
If you're trying to set SBT properties, like plugin settings, then the following worked for me with 0.13+. The following however did work, when trying to pass in Liquibase settings, like password, from our CI frameworks.
In your build.sbt
Ugly, but supplies defaults, and optionally grabs from System.properties. This way you've got your default and override cases covered.
def sysPropOrDefault(propName:String,default:String):String = Option(System.getProperty(propName)).getOrElse(default)
liquibaseUsername := sysPropOrDefault("liquibase.username","change_me")
liquibasePassword := sysPropOrDefault("liquibase.password","chuck(\)orris")
From the commandline
Now just override via -Dprop=value like you would with Maven or other JVM programs. Note props appear before SBT task.
sbt -Dliquibase.password="shh" -Dliquibase.username="bob" liquibase:liquibase-update