我想做什么:
首先,让我介绍一个我想要实现的(非常)简化的版本。考虑以下多项目:
root
|___backend
|
|___frontend
|
|___deployer
后端与oneJar打包在一起,并作为一个独立的进程执行后台工作。前端是一个网络服务播放项目(打包为 zip 文件dist
)。部署器是另一个与oneJar打包的自可执行 jar ,但打包已修改为包含其他项目工件。部署者的工作是初始化系统。它将其他工件部署到指定的机器上,并初始化分布式系统。
我的问题是什么:
基本上,我正在尝试(不成功)将播放的 dist zip 工件和后端 oneJar 自我可执行工件打包在部署器 jar 中(与其他资源文件一起)
部署器 jar 应如下所示:
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....
到目前为止我有什么:
构建.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)
...
后端/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)
...
前端/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)
...
部署者/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)
...
请注意,在根 build.sbt 中,我有每个配置的类路径依赖项 "optional->compile"
。我在查看文件后把它放在那里ivy.xml
:
...
<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>
...
并且看到该dist.zip
工件仅在optional
范围内找到,我想我可以通过这种方式将这个工件作为依赖项(虽然似乎没有工作......)。另外,当我尝试注释掉的行时,我收到一个错误,说它是错误的类型。
====================================================
当我写这个问题时,我发现我做错了什么......
更新:
我错过了一些东西(很多片段被复制和粘贴......)。首先,部署者根本不应该dependOn(backend,frontend)
。只是aggregate
。此外,这些工件(后端和前端)根本不应该在部署者的类路径上可见。所以这一行:
classpathTypes := classpathTypes.value + "zip"
是不需要的。此外,转换文件mappings in oneJar
中的代码deployer/build.sbt
可能要简单得多。只需要照顾资源。最后,注释掉的行实际上应该是:
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"}
差不多就是这样。