1

我想做什么:

首先,让我介绍一个我想要实现的(非常)简化的版本。考虑以下多项目:

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"}

差不多就是这样。

4

0 回答 0