Include .thrift File from External Repository - sbt

I have an Sbt project "A" with an A.thrift file. I need to include a B.thrift file in the definition of the A.thrift file. The problem is that the B.thrift file resides in an external repository / Sbt project "B". What would be the best way to do it?

There are two things that I needed to do to solve this.
First, I needed to add Compile / scroogePublishThrift := true and Compile / scroogeThriftSourceFolder := file('path/to/the/thrift/files') configuration to the "B" Sbt project to include raw thrift files as sources to the jar.
Second, I needed to add Compile / scroogeThriftDependencies := Seq("projectB_2.12") (where "projectB_2" is the GroupId of the project "B") in the "A" sbt project to set a dependency on the "B" project.

Related

What is the priority between .sbt files?

[SBT documentation] says
Put settings in a .sbt file in a project that isn't checked into version control, such as <project>/local.sbt. sbt combines the settings from multiple .sbt files, so you can still have the standard <project>/build.sbt and check that into version control.
However, if two .sbt files define the same setting, which one wins?
As of sbt 0.13.8, we might load *.sbt files in alphabetical order, but I don't think we have documented this behavior to be true. Thus, it's likely an implementation detail at this point.
We do promise:
setting order within the same file
plugin dependencies specified by auto plugins
settings loaded by auto plugin, then settings in *.sbt
So I would recommend using auto plugin if possible. Another way of de-prioritizing a setting is to scope in ThisBuild. You can either use an auto plugin or some project to define:
organization in ThisBuild := "com.example"
It's alphabetic ordering:
a.sbt
name := "a"
b.sbt
name := "b"
Test:
% sbt 'show name'
[info] Loading global plugins from /Users/dnw/.dotfiles/.sbt/0.13/plugins
[info] Loading project definition from /Users/dnw/Desktop/t-2015-04-18.1551/project
[info] Set current project to b (in build file:/Users/dnw/Desktop/t-2015-04-18.1551/)
[info] b

What are key differences between sbt-pack and sbt-assembly?

I've just stumbled upon the sbt-pack plugin. The development stream seems steady. It's surprising to me as I believed that the only plugin for (quoting sbt-pack's headline) "creating distributable Scala packages." is sbt-assembly (among the other features).
What are the key differences between the plugins? When should I use one over the other?
(Disclaimer: I maintain sbt-assembly)
sbt-assembly
sbt-assembly creates a fat JAR - a single JAR file containing all class files from your code and libraries. By evolution, it also contains ways of resolving conflicts when multiple JARs provide the same file path (like config or README file). It involves unzipping of all library JARs, so it's a bit slow, but these are heavily cached.
sbt-pack
sbt-pack keeps all the library JARs intact, moves them into target/pack directory (as opposed to ivy cache where they would normally live), and makes a shell script for you to run them.
sbt-native-packager
sbt-native-packager is similar to sbt-pack but it was started by a sbt committer Josh Suereth, and now maintained by highly capable Nepomuk Seiler (also known as muuki88). The plugin supports a number of formats like Windows msi file and Debian deb file. The recent addition is a support for Docker images.
All are viable means of creating deployment images. In certain cases like deploying your application to a web framework etc., it might make things easier if you're dealing with one file as opposed to a dozen.
Honorable mention: sbt-progard and sbt-onejar.
Although Eugene Yokota's explanation is complete, I would like to explain the mentioned plugins with package command in the aspect of usages and how different results are generated.
Directory settings and build.sbt
lazy val commonSettings = Seq(
organization := "stackOverFlow",
scalaVersion := "2.11.12",
version := "1.0",
)
lazy val app = (project in file ("app")).
enablePlugins(PackPlugin).
settings(commonSettings)
Above build.sbt file declares project called app and includes all the source files in the app directory. To enable Pack plugins, enablePlugins(PackPlugin) should be included in the sbt file.
Also, I've put the below line in project/plugins.sbt file to use pack plugins in our project
addSbtPlugin("org.xerial.sbt" % "sbt-pack" % "0.9.3")
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.5")
The package is already integrated into the sbt by default, so you don't have to explicitly specify the plugins using addSbtPlugins. However, the sbt-pack and sbt-assembly plugins are not included in the sbt by default, so you have to specify that you want to use them. addSbtPlugin is a way to say that "I want to use xxx, yyy plugins in my project" to your sbt.
Also, I implemented two contrived scala files in the ./app/src/main/scala:
AppBar.scala
class AppBar {
def printDescription() = println(AppBar.getDescription)
}
object AppBar {
private val getDescription: String = "Hello World, I am AppBar"
def main (args: Array[String]): Unit = {
val appBar = new AppBar
appBar.printDescription()
}
}
AppFoo.scala
class AppFoo {
def printDescription() = println(AppFoo.getDescription)
}
object AppFoo {
private val getDescription: String = "Hello World, I am AppFoo"
def main (args: Array[String]): Unit = {
val appFoo = new AppFoo
appFoo.printDescription()
}
}
sbt package
This is very basic sbt command included in the sbt to help you distribute your project through the jar file. The jar file generated by the package command is located in the projectDirectoy/target/scala-2.11/app_2.11-1.0.jar (Here, the specified scalaVersion and version setting keys included in the build.sbt file are used to generate the jar file name).
When you look inside the jar, you can see the class files generated by the sbt tool, which is the result of compiling the sources in the app/src/main/scala. Also, it includes a MANIFEST file.
$vi projectDirectoy/target/scala-2.11/app_2.11-1.0.jar
META-INF/MANIFEST.MF
AppBar$.class
AppBar.class
AppFoo.class
AppFoo$.class
Note that it only includes the class files generated from the scala files located in the app/src/main/scala directory. The jar file generated by the package command does not include any scala related libraries such as collection in the scala library (e.g., collection.mutable.Map.class). Therefore, to execute the program you may require scala library because the generate jar file only contains the minimal classes generated from the scala sources that I implemented. That is the reason why the jar file contains AppBar.class, AppBar$.class for companion object, etc.
sbt-assembly
As mentioned by the Eugene Yokota, sbt-assembly also help you distribute your project through generating the jar file; however the generated jar file includes not only the class files generated by your source code, but also all the libraries that you need to execute the program. For example, to execute the main function defined in the AppFoo object, you may need scala libraries. Also, when you add external libraries in your project, which can be included by adding the dependencies to the libraryDependencies key.
libraryDependencies ++= Seq("org.json4s" %% "json4s-jackson" % "3.5.3")
For example, you can include json4s libraries in your project, and jar files related to supporting json4s in your project also will be added to the final jar file generated by the sbt-assembly. In other words, when you invoke assembly in your sbt, it generates one jar file containing all the requirements to execute your program, so that you don't need another dependency to execute yout program.
When you prompt assembly command in your sbt shell, then it will generate one jar file in your target directory. In this case, you may find the app-assembly-1.0.jar in the app/target/scala-2.11 directory. When you look inside the jar file, you can find that it contains lots of classes.
$vi projectDirectoy/target/scala-2.11/app_2.11-1.0.jar
ETA-INF/MANIFEST.MF
scala/
scala/annotation/
scala/annotation/meta/
scala/annotation/unchecked/
scala/beans/
scala/collection/
scala/collection/concurrent/
scala/collection/convert/
scala/collection/generic/
scala/collection/immutable/
scala/collection/mutable/
scala/collection/parallel/
scala/collection/parallel/immutable/
scala/collection/parallel/mutable/
scala/collection/script/
scala/compat/
scala/concurrent/
scala/concurrent/duration/
scala/concurrent/forkjoin/
scala/concurrent/impl/
scala/concurrent/util/
scala/io/
scala/math/
scala/ref/
scala/reflect/
scala/reflect/macros/
scala/reflect/macros/internal/
scala/runtime/
scala/sys/
scala/sys/process/
scala/text/
scala/util/
scala/util/control/
scala/util/hashing/
scala/util/matching/
AppBar$.class
AppBar.class
AppFoo$.class
AppFoo.class
......
As mentioned before, because the jar file generated by the assembly contains all the dependencies such as scala and external libraries to execute your program in the jar, you may think that you can invoke the main functions defined in the AppFoo object and AppBar object.
jaehyuk#ubuntu:~/work/sbt/app/target/scala-2.11$ java -cp './*' AppFoo
Hello World, I am AppFoo
jaehyuk#ubuntu:~/work/sbt/app/target/scala-2.11$ java -cp './*' AppBar
Hello World, I am AppBar
Yeah~ you can execute the main function using the generated jar file.
sbt-pack
sbt-pack is almost same as the sbt-assembly; it saves all the library on which your project depends as jar files required to execute your program. However, sbt-pack doesn't integrate all the dependencies into one jar files, instead, it generates multiple jar files which correspond to one library dependencies and your classes (e.g., AppFoo.class).
Also, interestingly it automatically generates scripts for invoking all the main functions defined in your scala source files and Makefiles to install the program. Let's take a look at the pack directory created after you prompt pack command on your sbt shell.
jaehyuk#ubuntu:~/work/sbt/app/target/pack$ ls
bin lib Makefile VERSION
jaehyuk#ubuntu:~/work/sbt/app/target/pack$ ls bin/
app-bar app-bar.bat app-foo app-foo.bat
jaehyuk#ubuntu:~/work/sbt/app/target/pack$ ls lib/
app_2.11-1.0.jar sbt_2.12-0.1.0-SNAPSHOT.jar scala-library-2.11.12.jar
jaehyuk#ubuntu:~/work/sbt/app/target/pack$
As shown in the above, two directories and two files are created; bin contains all the script files to execute the functions defined in your sources (each file is a script that helps you execute the main method defined in your scala files); lib contains all the required jar files to execute your program; and lastly Makefile can be used to install your program and dependent libraries in your system.
For the details, please refer the github pages for each plugins.

Create standalone jar using SBT

I was a heavy Maven user and now I'm gradually using SBT for some of my projects.
I'd like to know how could I use SBT to create a standalone Java project? This project should be packaged as a JAR file and this JAR file would be used as a dependency in another SBT project.
In Maven, I could tell in my pom.xml what type of artifact it should produce when I build it. Is there something similar that I can do in SBT?
There is a difference between standalone and making a project useable as a dependency or another project. In the first case, you would use a plugin such as sbt-assembly. What it will do is create one jar file containing the project class files along with all of its dependencies. If you write an application, what you get is a double-clickable jar that you can execute from anywhere.
If you want to use your project A as a dependency for another project B, you have different options. You could just package the class files of A, using sbt package (answer of #Channing Walton). Then you could drop the resulting .jar file in the lib directory of project B. However, if A also requires libraries, you must make sure that they also end up in project B's libraries.
A better approach is to publish your project. You can do that purely on your local machine, using sbt publish-local. That will store the jar as produced by package in a special local directory which can be accessed from sbt in another project, along with a POM file that contains the dependencies of A. It will use a group-ID (organization) and artifact-ID (name) and a version of your project A. For example, in build.sbt:
name := "projecta"
version := "0.1.0-SNAPSHOT"
organization := "com.github.myname"
scalaVersion := "2.10.3"
publishMavenStyle := true
After publishing with sbt publish-local, you can add the following dependency to your project B:
libraryDependencies += "com.github.myname" %% "projecta" % "0.1.0-SNAPSHOT"
If you have a pure Java project, you can omit the Scala version suffix, i.e. in Project A:
crossPaths := false
autoScalaLibrary := false
And then in Project B:
libraryDependencies += "com.github.myname" % "projecta" % "0.1.0-SNAPSHOT"
(using only one % character between group and artifact ID).
More on publishing in the sbt documentation.
'sbt package' will produce a jar file.
If you want it to be executable you need to add the following to your .sbt config:
mainClass in Compile := Some("your.main.Class")
Sure, you can use 'sbt package' command, it creates a jar file but this jar will be without any dependencies. To run it necessary to specify 'classpath' arg to the libraries.
In your case you wish a standalone runnable file. And you need to add the dependencies.
To do this you can use 'assembly' plugin for SBT, see https://github.com/sbt/sbt-assembly/
Afterward you can just run 'sbt assembly' command, it provides a fat jar file with all dependencies that you can deploy and run anywhere and at any time.
For details see this article
publishLocal
builds the artifact and publish in the local Ivy repository making it available for your local project dependencies.
publishM2
same as above, but the artifact is published in local Maven repo instead of Ivy repo.
I think the easiest way to produce a stand-alone jar with your project in it,
is sadly not lying inside sbt.
I personally use my IDE: Intellij to make the jar (through the 'build artifact' feature).
Thanks to Intellij I can easily choose which library I want to include in the jar or not, (for instance the scala stl).
IMHO, this is by far the simplest method to get an executable jar for your project.
If you put the scala stl you can run your jar with the "java -jar" command, if you don't you have to run it somewhere with the correct version of scala installed with "scala".

Handling unmanaged classpath jars in a library using SBT so that a dependent project can access them

I'm writing a library which depends on code (let's call it foo.jar) which is only available as a binary jar. As is standard, I'm putting this in the lib/ directory so SBT will treat is as an unmanaged dependency. This is fine so far.
However, since this is a library, I'd like to be able to publish it so that other projects which depend on it to also have access to the unmanaged code in foo.jar without having to manually locate it. I originally thought I could use a fat jar plugin such as SBT Assembly to create a jar with the dependencies, but that doesn't affect what is actually published using sbt publish-local – it only creates a fat jar when you run sbt assembly. Is there some standard simple way to handle this? It seems like a bad idea for every library which uses unmanaged dependencies to break when used by other projects downstream so I wonder if I'm missing something obvious.
I don't know if that's a good use of sbt-assembly, since other libraries could depend on a different version of foo.jar etc.
One way to work around it is to publish foo.jar in a Maven repository yourself. Some people in Scala and/or sbt community have been talking about bintray. It's still in beta, but looks promising if you want some jars published.
You might be able to get the result you want by manipulating the mappings in (Compile, packageBin) to include the files you want your packaged jar to have (publish uses the output from packageBin). This technique will allow you to include absolutely any file you want within the jar. The official sbt doc is here: http://www.scala-sbt.org/0.12.3/docs/Howto/package.html#contents
As an example, consider the common case of including a .properties file within your jar. Lets say you need to include "messages.properties" under the path "com/bigco/messages.properties" in your packaged jar. And lets say that this file is under src/main/scala/ ... You can add the following to your build.sbt:
mappings in (Compile, packageBin) <+= baseDirectory map { base =>
(base / "src" / "main" / "scala" / "com" / "bigco" / "messages.properties") -> "com/bigco/messages.properties"
}
To attempt to answer your original question, you could unzip foo.jar and add each one of the class files within to the packaged jar, according to their correct package paths. So something similar to
mappings in (Compile, packageBin) <+= baseDirectory map { base =>
(base / path / to / unzipped / file.class) -> "path.to.unzipped.file.class"
...
}
Or you might be able to get away with simply including foo.jar at the root of the packaged jar like so:
mappings in (Compile, packageBin) <+= baseDirectory map { base =>
(base / "lib" / "foo.jar") -> "foo.jar"
}

sbt - copy managed libraries to lib

How can I copy managed libraries to a specific folder with Scala Build Tool?
For example:
All the jars from this managed library, would be put in 'project/lib'
libraryDependencies += "com.miglayout" % "miglayout-swt" % "4.2"
I found the solution. Pretty simple actually.
Add the following to build.sbt
This will copy the jars to project/lib_managed/jars//*.jar
retrieveManaged := true
I'm not sure about how to specify a specific folder, but this is good enough to get the jars under the project folder.

Resources