更改某些生成资源的放置位置

Change where certain generated resources are placed

我目前有两个项目;一个用于服务器端 (Akka-HTTP) and one for the client-side (ScalaJS)。客户端生成我想使用服务器端提供的 .js 文件。为了让服务器端访问这些文件,我在服务器端项目中添加了这个 build.sbt 条目:

lazy val server = project.in(file("server"))
  ...
  .settings(
    (resourceGenerators in Compile) <+= Def.task {
      val f1 = (fullOptJS in client).value.data
      val f1sm = f1.getParentFile / (f1.getName + ".map")
      f1 :: f1sm :: (packageMinifiedJSDependencies in client).value :: Nil
    }
  )

但是,这会将我的 .js.map 文件放入 classes 文件夹的根目录中。因为我想像这样服务我的资产:

pathPrefix("assets") {
  ...
  pathPrefix("client") {
    getFromResourceDirectory("<directory where .js and .map files are placed>")
  }
}

着重于 #getFromResourceDirectory 方法,这不适用于我 classes 文件夹根目录中的资源。我的 application.conf 与生成的文件放在同一目录中,这将使全世界都可以看到。

我想要的是我的 .js.map 文件位于 classes 目录的子目录中。像 client/ 这样我可以像 getFromResourceDirectory("client") 一样使用 #getFromResourceDirectory 方法,不用担心我的配置文件对外界可用。

我的问题 - 我该如何实现?

我已经尝试添加:

lazy val client = project.in(file("client"))
  ...
  .settings(inConfig(Compile)((fullOptJS :: packageMinifiedJSDependencies :: Nil)
    .map(a => (crossTarget in a) ~= (_ / "client")))
  )

到我的客户端项目,它只将生成的 .js.map 文件放在客户端项目的客户端文件夹中 scala-2.11 文件夹:

但设置不会传播到服务器端项目的 classes 文件夹:

这是理想的情况:

深入研究 SBT 让我走上了以下道路;首先,我检查了 server/compile:resourceGenerators(使用 sbt inspect server/compile:resourceGenerators):

[info] ...
[info] Dependencies:
[info]  server/compile:discoveredSbtPlugins
[info]  server/compile:resourceManaged
[info]  client/compile:packageMinifiedJSDependencies
[info]  client/compile:fullOptJS
[info] Reverse dependencies:
[info]  server/compile:managedResources
[info] ...

然后我检查了server/compile:managedResources

[info] ...
[info] Dependencies:
[info]  server/compile:resourceGenerators
[info] Reverse dependencies:
[info]  server/compile:resources
[info] ...

然后我检查了 server/compile:resources:

[info] ...
[info] Dependencies:
[info]  server/compile:managedResources
[info]  server/compile:unmanagedResources
[info] Reverse dependencies:
[info]  server/compile:copyResources
[info] ...

然后我检查了 server/compile:copyResources:

[info] ...
[info] Defined at:
[info]  (sbt.Defaults) Defaults.scala:295
[info] Dependencies:
[info]  server/compile:classDirectory
[info]  server/compile:resources
[info]  server/compile:resourceDirectories
[info]  server/compile:copyResources::streams
[info] ...

然后我在第 295 行检查了来自 SBT 的 Defaults.scala 文件:

copyResources <<= copyResourcesTask

然后我检查了这个 copyResourcesTask 定义:

def copyResourcesTask =
  (classDirectory, resources, resourceDirectories, streams) map { (target, resrcs, dirs, s) =>
    val cacheFile = s.cacheDirectory / "copy-resources"
    val mappings = (resrcs --- dirs) pair (rebase(dirs, target) | flat(target))
    s.log.debug("Copy resource mappings: " + mappings.mkString("\n\t", "\n\t", ""))
    Sync(cacheFile)(mappings)
    mappings
  }

mappings val 在这种情况下非常重要。它在负责所有资源的各种任务创建的路径与这些资源将在 target 文件夹中接收的最终路径之间创建映射。如果资源路径中没有单个 resourceDirectories 条目,则 #rebase method is (mostly) responsible for these mappings but it falls back on the #flat 方法。通过简单地将我的客户端项目的 crossTarget 路径添加到 resourceDirectories 设置,rebase 将替换所有内容,直到我的 .js.mapclient/<filename> 部分文件而不是回退到 #flat 方法,只替换 <filename> 部分,结合此设置:

lazy val client = project.in(file("client"))
  ...
  .settings(inConfig(Compile)((fullOptJS :: packageMinifiedJSDependencies :: Nil)
    .map(a => (crossTarget in a) ~= (_ / "client")))
  )

正是我想要的行为。


TL;DR

添加到您的客户端项目:

.settings(inConfig(Compile)((fullOptJS :: packageMinifiedJSDependencies :: Nil)
  .map(a => (crossTarget in a) ~= (_ / "client")))
)

添加到您的服务器端项目:

.settings(inConfig(Compile)((resourceGenerators <+= Def.task {
  val f1 = (fullOptJS in client).value.data
  val f1sm = f1.getParentFile / (f1.getName + ".map")
  f1 :: f1sm :: (packageMinifiedJSDependencies in client).value :: Nil
}) :: (resourceDirectories += (crossTarget in client).value) :: Nil))

@Martijn,感谢伟大的解决方案。

这真是帮了我大忙。 这里很少更新。

用于 frontend/client 设置。

  .settings(inConfig(Compile)(
    (fullOptJS :: fastOptJS :: packageScalaJSLauncher
     :: packageJSDependencies :: packageMinifiedJSDependencies :: Nil)
      .map(f => (crossTarget in f) ~= (_ / "sjsout"))
  ))

用于 backend/server 设置。

  .settings(
    (resourceGenerators in Compile) += Def.task {
      val fastJsOut = (fastOptJS in Compile in frontend).value.data
      val fastJsSourceMap = fastJsOut.getParentFile / (fastJsOut.getName + ".map")
      val fullJsOut = (fullOptJS in Compile in frontend).value.data
      val fullJsSourceMap = fullJsOut.getParentFile / (fullJsOut.getName + ".map")
      Seq(
        fastJsOut,
        fastJsSourceMap,
        //fullJsOut,
        //fullJsSourceMap,
        (packageScalaJSLauncher in Compile in frontend).value.data,
        (packageJSDependencies in Compile in frontend).value
        //(packageMinifiedJSDependencies in Compile in frontend).value
      )
    }.taskValue,
    (resourceDirectories in Compile) += (crossTarget in frontend).value,
    watchSources ++= (watchSources in frontend).value
  )