what am I trying to do:
first, let me present a (very) simplified version of what i'm trying to achieve. consider the following multi-project:
root
|___backend
|
|___frontend
|
|___deployer
the backend is packaged with oneJar, and performs as a standalone process that does the background work. the frontend is a web-service play project (packaged as a zip file with dist
). the deployer is yet another self executable jar that is packaged with oneJar, but the packaging is modified to include the other projects artifacts. the deployer job is to initialize the system. it deploys the other artifacts to specified machines, and initializes the distributed system.
what is my problem:
basically, i'm trying (unsuccessfully) to get the play's dist zip artifact & the backend oneJar self executable artifact packaged inside the deployer jar (with other resources files)
the deployer jar should look like:
deployer-executable.jar
|___0/
| |___backend-selfexec.jar
| |___frontenf-dist.zip
|
|___1/
| |___other resources (mostly configuration files)
| |___ ...
|
|___META-INF/ ...
|___com/simontuffs/onejar/ ...
|___doc/ ...
|___lib/ ...
|___main/deployer-VERSION.jar
|___other resources (such as logback.xml) and oneJar files....
what do I have so far:
build.sbt
...
lazy val backend = project in file("backend")
lazy val frontend = project in file("frontend")
lazy val deployer = project in file("deployer") dependsOn(backend % "optional->compile", frontend % "optional->compile") aggregate(backend, frontend)
...
backend/build.sbt
...
seq(com.github.retronym.SbtOneJar.oneJarSettings: _*)
exportJars := true
mainClass in oneJar := Some("org.product.backend.Main")
artifact in oneJar <<= moduleName(Artifact(_, "selfexec"))
addArtifact(artifact in (Compile, oneJar), oneJar)
...
frontend/build.sbt
import play.Project._
...
play.Project.playScalaSettings
lazy val dist = com.typesafe.sbt.SbtNativePackager.NativePackagerKeys.dist
lazy val publishDist = TaskKey[sbt.File]("publish-dist", "publish the dist artifact")
publish <<= (publish) dependsOn dist
publishLocal <<= (publishLocal) dependsOn dist
artifact in publishDist ~= {
(art: Artifact) => art.copy(`type` = "zip", extension = "zip", classifier = Some("dist"))
}
publishDist <<= (target in Universal, normalizedName, version) map { (targetDir, id, version) =>
val packageName = s"$id-$version"
targetDir / (packageName + ".zip")
}
addArtifact(artifact in publishDist, publishDist)
...
deployer/build.sbt
...
seq(com.github.retronym.SbtOneJar.oneJarSettings: _*)
exportJars := true
mainClass in oneJar := Some("org.product.deployer.Main")
unmanagedResources in Compile := Seq() //don't add resources from "src/main/resources" to inner jar, only to the fat one-jar.
classpathTypes := classpathTypes.value + "zip" //don't ommit the dist zip file from classpath
mappings in oneJar := {
def isNeedToBeInDir0(f: File) = f.getName == "frontend-VERSION-dist.zip" || f.getName == "backend-VERSION-selfexec.jar"
def nameForPackaging(name: String): String = if(name.contains("frontend")) "frontend.zip" else "backend.jar"
//following method could be replaced with: http://www.scala-sbt.org/release/docs/Detailed-Topics/Mapping-Files.html#relative-to-a-directory
def files2TupleRec(pathPrefix: String, dir: File): Seq[Tuple2[File,String]] = {
sbt.IO.listFiles(dir) flatMap {
f => {
if(f.isFile) Seq((f,s"${pathPrefix}${f.getName}"))
else files2TupleRec(s"${pathPrefix}${f.getName}/",f)
}
}
}
val oldSeq = (mappings in oneJar).value
oldSeq.filterNot(t => isNeedToBeInDir0(t._1)) ++
oldSeq.filter(t => isNeedToBeInDir0(t._1)).map{
case (f,_) => (f,s"/0/${nameForPackaging(f.getName)}") //NOT WORKING
} ++
files2TupleRec("",file("deployer/src/main/resources"))
}
//following lines is commented out because it's also not working, but it shows very clearly what i'm trying to do.
//(types are wrong. i need File but have sbt.Artifact):
//mappings in oneJar <+= (artifact in LocalProject("backend") in oneJar) map {_ -> "/0/backend.jar"}
//mappings in oneJar <+= (artifact in LocalProject("frontend") in oneJar) map {_ -> "/0/frontend.zip"}
artifact in oneJar <<= moduleName(Artifact(_, "executable"))
addArtifact(artifact in (Compile, oneJar), oneJar)
...
notice that in the root build.sbt, i have per-configuration classpath dependencies "optional->compile"
. Iv'e put it there after looking at the ivy.xml
file:
...
<configurations>
<conf name="compile" visibility="public" description=""/>
<conf name="runtime" visibility="public" description="" extends="compile"/>
<conf name="test" visibility="public" description="" extends="runtime"/>
<conf name="provided" visibility="public" description=""/>
<conf name="optional" visibility="public" description=""/>
<conf name="sources" visibility="public" description=""/>
<conf name="pom" visibility="public" description=""/>
</configurations>
<publications>
<artifact name="frontend_2.10" type="zip" ext="zip" conf="compile,runtime,test,provided,optional,sources,pom" e:classifier="dist"/>
<artifact name="frontend_2.10" type="pom" ext="pom" conf="pom"/>
<artifact name="frontend_2.10" type="jar" ext="jar" conf="compile"/>
<artifact name="frontend_2.10" type="src" ext="jar" conf="sources" e:classifier="sources"/>
</publications>
...
and seeing that the dist.zip
artifact is only found in optional
scope, I thought I could get this artifact as a dependency that way (not seem to be working though...). also, when i tried out the commented out lines, i got an error saying it's the wrong type.
==================================================
as i'm writing the question, i figured out what I've done wrong...
UPDATE:
there's a few things i missed (to much snippets were copy & pasted...). first, the deployer should not dependOn(backend,frontend)
at all. just aggregate
. moreover, these artifacts (backend & frontend) should not be visible on deployer's classpath at all. so this line:
classpathTypes := classpathTypes.value + "zip"
is unneeded. also, the code to transform the mappings in oneJar
in the deployer/build.sbt
file, could be much more simple. only need to take care of resources. and finally, the commented out lines, should actually be:
mappings in oneJar <+= (artifactPath in LocalProject("backend") in oneJar) map {_ -> "0/backend.jar"}
mappings in oneJar <+= (packageBin in LocalProject("frontend") in Universal) map {_ -> "0/frontend.zip"}
that's pretty much it.