8

我有一个游戏的多项目设置。有一个名为“resources”的非常具体的子项目,它只包含要打包到 jar 中的图像、声音和 texfiles 等文件。

我有一个自定义任务来处理图像并打包它们。在“src/main”中,我使用了一个文件夹“预处理”,图像应该放在其中,而“非托管”文件夹则放在其他所有文件夹中。通过运行我的任务,“preprocess”中的所有图像都被打包并输出到“resources”,“unmanaged”中的所有内容都按原样复制。

val texturePacker = TaskKey[Unit]("texture-packer", "Runs libgdx's Texture Packer")

val texturePackerTask = texturePacker := {
  println("Packaging textures...")
  val inputDir = file("resources/src/main/preprocess")
  val outputDir = file("resources/src/main/resources")

  val folders = inputDir.asFile.listFiles filter (_.isDirectory)

  println("Sub-Folders:" + folders.mkString(", "))

  // Run Texture Packer
  for (subfolder <- folders) {
    println("Building assets for:" + subfolder)
    val args = Array(subfolder.toString, outputDir.toString, subfolder.getName)
    com.badlogic.gdx.tools.imagepacker.TexturePacker2.main(args)
  }

  // Copy unmanaged resources
  IO.copyDirectory(file("resources/src/main/unmanaged"), file("resources/src/main/resources"))
}

然后在“资源”项目的设置中:

...
packageBin in Compile <<= packageBin in Compile dependsOn(texturePacker)
...

其他子项目依赖于与其运行相关的 packageBin。这样,每当我运行项目时,我都会获得最新的资源状态。我不希望它按需提供。问题是每次运行都需要很长时间来处理。我知道 SBT 支持缓存SBT 常见问题解答,但我不明白如何使其适应我的任务。

如果文件夹列表中子文件夹中的文件未修改,如何使我的自定义任务避免重做工作?

4

1 回答 1

5

解决方案

这是一个可能适合您的解决方案。但是,我并不完全了解其FileFunction.cached工作原理(代码后的更多信息),因此这可能不是最好的解决方案:

val testCache = TaskKey[Unit]("test-cache", "Test SBT's cache")

val testCacheTask = testCache := {
  println("Testing cache ...")

  val inputDir = file("test/src")   /* Take direct subdirectories from here */
  val outputDir = file("test/dest") /* Create archives here */
  val cacheDir = file("test/cache") /* Store cache information here */

  /* Get all direct subdirectories of inputDir */
  val folders = inputDir.asFile.listFiles.filter{_.isDirectory}

  folders.foreach{folder =>
    /* Get all files in the folder (not recursively) */
    val files = folder.listFiles.toSet

    /* Wrap actual function in a function that provides basic caching
     * functionalities.
     */
    val cachedFun =
      FileFunction.cached(cacheDir / folder.name,
                          FilesInfo.lastModified, /* inStyle */
                          FilesInfo.exists)       /* outStyle */
                         {(inFiles: Set[File]) =>

        createJarFromFolder(folder,
                            inFiles,
                            outputDir / (folder.name + ".jar"))
      }

    /* Call wrapped function with files in the current folder */
    cachedFun(files)
  }
}

/* Creates a JAR archive with all files (this time recursively) in
 * the given folder.
 */
val createJarFromFolder = (folder: File, inFiles: Set[File], outJar: File) => {
  println("At least one of the %d files in %s have changed. Creating %s."
          .format(inFiles.size, folder, outJar))

  /* Replace this by your actual operation */
  val cmdSeq = Seq("jar", "cf", outJar.toString, "-C" , folder + "/", ".")
  println("cmdSeq = " + cmdSeq)
  println("jar: " + cmdSeq.!!)

  Set(outJar)
}

笔记

  • 我的理解cached是,它检查inFiles修改,并且如果集合中的一个文件发生更改,它会调用实际操作。changed的​​确切含义由 to 的inStyle参数确定cached

  • 最好直接将目录传递给cached,这样如果该目录中的任何内容发生更改,就会执行实际操作。但是,我怀疑目前是否可行。

  • 我不太明白实际操作返回的文件集的行为是什么(这里:)Set(outJar)。我假设 to 的outStyle参数cached与此相关,并且我希望createJarFromFolder在 JAR 不存在时被调用(不管输入文件的更改如何),但情况似乎并非如此。也就是说,如果您删除一个 JAR 文件但不更改相应目录中的文件之一,则不会重新创建该 JAR。

  • 该代码有些狡猾,因为在确定该文件夹中是否发生更改时,它只考虑位于特定文件夹顶部的文件。你可能想让那个递归。

结语

我很想看到使用 SBT 缓存功能的更好方法。如果您从邮件列表中获得更多信息,请在此处发布。

于 2012-07-25T09:33:59.100 回答