具有Shake的多输入多输出编译器

时间:2013-06-25 00:00:08

标签: haskell shake-build-system

我正在尝试使用Shake来构建Java代码,由于javac编译器的不寻常特性,我有点卡住了。通常,对于大型项目的每个模块,使用该模块的源文件的所有作为输入调用编译器,并在一次传递中生成所有输出文件。随后我们通常会获取编译器生成的.class文件,并将它们组装成一个JAR(基本上只是一个ZIP)。

例如,典型的Java模块项目安排如下:

  • 一个src目录,其中包含多个.java文件,其中一些嵌套在树的深处。
  • 包含编译器输出的bin目录。通常,此输出遵循相同的目录结构和文件名,.class替换每个.java文件,但映射必须一对一:单个.java文件可以生成零到多个.class文件!

我想在Shake中定义的规则如下:

1)如果src下的任何文件比bin下的任何文件更新,则删除bin的所有内容重新创建:

javac -d bin <recursive list of .java files under src>

我知道这个规则似乎过多了,但是如果不调用编译器,我们就无法知道输出变化的程度,即使是单个输入文件中的一个小变化也是如此。

2)如果bin下的任何文件比module.jar更新,则使用以下内容重新创建module.jar

jar cf module.jar -C bin .

非常感谢!

PS 静脉响应“只需使用Ant / Maven / Gradle /”将不胜感激!我知道这些工具提供开箱即用的Java编译,但它们更难以编写和聚合。这就是为什么我想试验一个基于Haskell / Shake的工具。

1 个答案:

答案 0 :(得分:10)

编写产生多个输出的规则,其名称无法静态确定可能有点棘手。通常的方法是查找名称静态已知且始终为need的输出,如果不存在,则创建一个伪文件以用作静态输出(根据ghc-make, the .result file)。在你的情况下,你有module.jar作为最终输出,所以我会写:

"module.jar" *> \out -> do
    javas <- getDirectoryFiles "" ["src//*.java"]
    need javas
    liftIO $ removeFiles "" ["bin//*"]
    liftIO $ createDirectory "bin"
    () <- cmd "javac -d bin" javas
    classes <- getDirectoryFiles "" ["bin//*.class"]
    need classes
    cmd "jar cf" [out] "-C bin ."

将它分成两个规则没有任何好处,因为你永远不依赖于.class文件(并且实际上不能,因为它们在名称上是不可预测的),并且如果任何源文件发生变化,那么你无论如何都会重建module.jar。此规则包含您提及的所有依赖项,如果您添加/重命名/删除任何.java.class文件,则会自动重新编译,因为会跟踪getDirectoryFiles调用。