Reputation: 189
I'm working with an application that loads plugins as jars from a directory. The plugins are to have the following layout:
plugin.myplugin.jar/
com.xxx.xxx/<compiled classes for plugin>
META-INF/MANIFEST.MF (defines the main entry point for the plugin)
lib/
com.fasterxml.jackson.core.jackson-core-2.3.1.jar
...
...
com.netflix.rxjava.rxjava-core-0.16.1.jar
...
<all dependencies used by the plugin in their original jar format>
I can do this manually by using the sbt-native-packager plugin to produce the top-level plugin.myplugin.jar and then copying the jars from target/universal/stage/lib into the lib directory within plugin.myplugin.jar using something like 7-zip. Doing this manually works... but I'm trying to automate this task though and currently I am at a bit of a loss as to how to override an sbt-native-packager task or write my own to do this. Any ideas?
Upvotes: 0
Views: 384
Reputation: 13749
This is how you could achieve what you want with pure SBT 0.13.x (the example should work for older versions in general, but maybe you'd have to use different operators).
import Path.flat
libraryDependencies ++= Seq(
"com.fasterxml.jackson.core" % "jackson-core" % "2.3.1",
"com.netflix.rxjava" % "rxjava-core" % "0.16.1"
// maybe more dpendencies
)
packageOptions in (Compile, packageBin) +=
Package.ManifestAttributes("PluginMainClass" -> "com.xxx.xxx.Class")
// this will copy all managed jars to the ./lib in your jar
mappings in (Compile, packageBin) ++= {
val cp = (managedClasspath in Compile).value.files
cp pair flatRebase("/lib")
}
// this will move all your compiled classes to folder ./com.xxx.xxx in your jar
mappings in (Compile, packageBin) ++= {
val compiledClasses = (products in Compile).value ** "*.class"
val classDirectoryBase = (classDirectory in Compile).value
compiledClasses pair rebase(classDirectoryBase, "com.xxx.xxx")
}
You can then use package
to build the jar. In the example as above the jar will look like this:
Length Date Time Name
--------- ---------- ----- ----
301 2014-05-09 20:13 META-INF/MANIFEST.MF
0 2014-05-09 20:13 /lib/
7126003 2013-09-27 11:44 /lib/scala-library.jar
197986 2013-12-28 02:01 /lib/jackson-core-2.3.1.jar
663553 2014-01-15 08:17 /lib/rxjava-core-0.16.1.jar
--------- -------
7987843 5 files
And the manifest will look like this
Manifest-Version: 1.0
Implementation-Vendor: default
Implementation-Title: q-23553321
Implementation-Version: 0.1-SNAPSHOT
Implementation-Vendor-Id: default
PluginMainClass: com.xxx.xxx.Class // MAIN CLASS OF THE PLUGIN
Specification-Vendor: default
Specification-Title: q-23553321
Specification-Version: 0.1-SNAPSHOT
Is it possible to get the compiled classes in a multi module build though? IE if I have /modules/X,Y,Z and I want the compiled classes from those projects bundled under "com.xxx.xxx" in the artifact?
The general answer is yes, if the classes are unique. To do that you can use Scopes. In the build.sbt
, replace the mappings for classes with the following:
// define the configuration axis
val anyProjectsCompile = ScopeFilter(inAnyProject, inConfigurations(Compile))
// we want to have class directory and products together to use rebase in the next step
val classDirectoryProducts = Def.task {
(classDirectory.value, products.value)
}
mappings in (Compile, packageBin) ++= {
classDirectoryProducts.all(anyProjectsCompile).value.flatMap { case (classDir, prods) =>
val compiledClasses = (prods ** "*.class").get
compiledClasses pair rebase(classDir, "com.xxx.xxx")
}
}
Upvotes: 1