Real SBT Classpath at Runtime - scala

I have some test cases that need to look at the classpath to extract the paths of some files/directories in there. This works fine in the IDE.
The problem is that, when running SBT test, Properties.javaClassPath gives me /usr/share/sbt-launcher-packaging/bin/sbt-launch.jar.
The classpath is fine when I run show test:dependency-classpath. Is there a way to obtain that information from inside the running Scala/Java program? Or is there a way to toss it into a system property or environment variable?

By default the tests are run inside of the SBT process, so the classpath will look like it did when you started sbt (I guess sbt does some trixery to dynamicly load the classes for the tests, not sure). One way to do what you want is to run your tests in a forked jvm, that way sbt will start a new jvm to run the test suite and that should have the expected class path:
fork in Test := true

I have been working on understanding how the EmbeddedCassandra works in the spark-cassandra-connector project which uses the classpath to start up and control a Cassandra instance. Here is a line from their configuration that gets the correct classpath.
(compile in IntegrationTest) <<= (compile in Test, compile in IntegrationTest) map { (_, c) => c }
The entire source can be found here: https://github.com/datastax/spark-cassandra-connector/blob/master/project/Settings.scala
Information on the <<= operator can be found here: http://www.scala-sbt.org/0.12.2/docs/Getting-Started/More-About-Settings.html#computing-a-value-based-on-other-keys-values. I'm aware that this is not the current version of sbt, but the definition still holds.

Related

Unable to get a resource when running an main class or test in sbt

I'm trying to run a test that uses a resource in sbt. The test depends on a util that loads a resource via ClassLoader.getSystemResourceAsStream(...), which results unexpectedly in null. If I run the same test in Intellij or via bazel, the test succeeds. I additionally performed a test by creating a main class to list all resources according to the ResourceList example given in an answer to this question, and this confirmed that that file and many others were inaccessible at runtime.
These resources are not contained in the resources directories that sbt usually uses; they are contained in a jar file that is included in the lib directory. The sources in my project built by sbt depend heavily on this jar, and compilation is successful, so it appears the issue may be specific to resources. One thing I noticed is that the resource can be loaded if I use a ClassLoader object and call getResourceAsStream instead of using the static method ClassLoader.getSystemResourceAsStream.
Does anyone know how to resolve this issue (short of copying out all resources from the jar file in lib)?
Try running tests in forked JVM:
Test / fork := true
Unmanaged dependencies in lib/ should end up by default on all the classpaths, including test, without having to do anything special:
Dependencies in lib go on all the classpaths (for compile, test, run,
and console).
Note, when running tests in IntelliJ you might be using IntelliJ's internal build system as opposed to SBT shell, which could be the reason why it worked in IntelliJ. To run tests in IntelliJ via sbt shell, select Use sbt checkbox in Edit Configurations...
I would suggest using getClass.getResourceAsStream, as this anyways uses system ClassLoader as fallback:
public InputStream getResourceAsStream(String name) {
name = resolveName(name);
ClassLoader cl = getClassLoader0();
if (cl==null) {
// A system class.
return ClassLoader.getSystemResourceAsStream(name);
}
return cl.getResourceAsStream(name);
}

In SBT, how do you override compile to run arbitrary code afterwards?

I'm trying to use SBT to build a project that depends on bytecode enhancement. Basically, I need to run some code after compile using the classpath in the current scope (so the command can find the classes to modify), and then make sure that compile doesn't run again afterwards to undo the enhancement.
I'm using SBT 0.13.12, if that matters.
I believe you would want to make a new sbt task and have it depend on compile. Then use that rather than compile.
lazy val bytecodeEnhancedCompile = taskKey[Unit]("bytecode Enhance")
bytecodeEnhancedCompile <<= bytecodeEnhancedCompile dependsOn (compile in Compile)
bytecodeEnhancedCompile := {
....
}

SBT: modify the order of dependencies in the classpath

I'm currently experiencing a problem with Specs2 + SBT where my tests always fail via command-line because of dependency order in the classpath. Specs2 requires that the Mockito jars come after the Specs2 jars so that Mockito classes can be overridden to fix issues with by-name scala method parameters (see this issue for more information: https://github.com/etorreborre/specs2/issues/428).
In IntelliJ, I can order my dependencies via the Project Structure/Modules/Dependencies window, which fixes my tests when run inside IntelliJ, however, I have not found a solution to fix this issue when running my tests on the command-line via sbt test.
Does anyone know if it is possible to change the classpath order of dependencies for SBT using settings in build.sbt (or similar)?
To my knowledge you need to make sure that specs2-mock comes before mockito in your libraryDependencies setting.

Using sbt 0.13.1, tests won't compile using the generated externalIvyFile

For our Scala development we currently use ivy + ant, but we are also trying to use sbt for our development workflow. This would be for the continuous incremental compilation when not using an IDE.
sbt uses ivy, so in theory this should work. But when using an ivy external file the tests won't compile.
To reproduce this you can even use the generated ivy.xml file from any sbt project.
Here are the steps to reproduce the error on a sbt project with tests,
from the sbt console run deliverLocal (deliver-local in previous versions of sbt)
copy the generated ivy file into your project home and rename it to 'ivy.xml'. From my understanding using this file should be equivalent to declaring the dependencies in build.sbt.
edit the build.sbt, add externalIvyFile() on one line and then comment all dependencies declarations
in the console, run reload, then test
compile will run just fine, but test will fail at compile time. None of the dependencies will be honoured, not even the production code of the current project.
What am I missing?
In my case it worked with the following build.sbt:
externalIvyFile()
classpathConfiguration in Compile := Compile
classpathConfiguration in Test := Test
classpathConfiguration in Runtime := Runtime
You just need the extra three lines in the end. Here is a link for more info: http://www.scala-sbt.org/release/docs/Detailed-Topics/Library-Management.html#ivy-file-dependency-configuration
Look for the Full Ivy Example. I hope it helps!
EDIT: Just to be complete - here is what pointed me to the above link: https://github.com/sbt/sbt/issues/849.

How to compile tests with SBT without running them

Is there a way to build tests with SBT without running them?
My own use case is to run static analysis on the test code by using a scalac plugin. Another possible use case is to run some or all of the test code using a separate runner than the one built into SBT.
Ideally there would be a solution to this problem that applies to any SBT project. For example, Maven has a test-compile command that can be used just to compile the tests without running them. It would be great if SBT had the same thing.
Less ideal, but still very helpful, would be solutions that involve modifying the project's build files.
Just use the Test / compile command.
Test/compile works for compiling your unit tests.
To compile integration tests you can use IntegrationTest/compile.
Another hint to continuously compile on every file change: ~Test/compile
We have a build.sbt file that is used for multiple projects. Doing sbt test:compile compiled the tests for every single project and took over 30 minutes.
I found out I can compile only the tests for a specific project named xyz by doing:
sbt xyz/test:compile
Using sbt version 1.5.0 and higher test:compile returns deprecation warning.
Use Test / compile.
(docs)