8

ゲーム用のマルチプロジェクト設定があります。'resources' と呼ばれる非常に特殊なサブ プロジェクトがあり、jar にパックされる画像、サウンド、texfile などのファイルのみが含まれています。

画像を処理してパックするカスタム タスクがあります。「src/main」内では、画像を保存する「前処理」フォルダーと、その他すべてを保存する「管理されていない」フォルダーを使用しています。私のタスクを実行すると、「前処理」のすべての画像がパックされて「リソース」に出力され、「管理されていない」のすべてがそのままコピーされます。

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 FAQのキャッシングをサポートしていることは知っていますが、それを自分のタスクに適応させる方法がわかりません。

フォルダー リストのサブフォルダー内のファイルが変更されていない場合、カスタム タスクで作業をやり直さないようにするにはどうすればよいですか?

4

1 に答える 1

5

Solution

Here is a solution that might suite you. However, I don't fully understand how FileFunction.cached works (more information after the code), so this is probably not the best possible solution:

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

Notes

  • My understanding of cached is, that it checks the inFiles for modifications, and that it invokes the actual operation if one of the files in the set changed. The exact meaning of changed is determined by the inStyle argument to cached.

  • It would be nice to directly pass a directory to cached, such that the actual operation is performed if anything in that directory changes. However, I doubt that it is currently possible.

  • I don't quite get what the behaviour with respect to the set of files returned by the actual operation is (here:Set(outJar)). I assume that the outStyle argument to cached is related to this, and I expected createJarFromFolder to be called whenever the JAR does not exist (regardless of changes to the input files), but that doesn't seem to be the case. That is, if you delete a JAR file but not change one of the files in the corresponding directory, the JAR will not be recreated.

  • The code is somewhat dodgy, because it only considers the files that are at the top of a particular folder when it comes to deciding if changes occurred in that folder. You probably want to make that recursive.

Epilogue

I'd love to see a better way of using SBT's caching feature. Should you get more information, e.g., from the mailing list, please post them here.

于 2012-07-25T09:33:59.100 に答える