如何在Shake中编写定点构建规则,例如Latex

Nei*_*ell 16 haskell shake-build-system

使用Shake Haskell构建库,如何使用需要达到固定点的程序编写规则?想象一下,我有一个程序foo,它接受一个文件input并生成一个输出文件,应该foo重复应用,直到输出文件没有改变.我怎么能在Shake中写出来?

这种模式的典型例子是LaTeX.

Nei*_*ell 13

首先,请注意,重复调用Latex并不总是产生一个固定点,因此请确保您对迭代有一定的约束.此外,一些发行版(MikTex)提供的Latex版本会根据需要自动运行多次,因此如果您使用它们,则问题就会消失.

编写自己的foo_transitive命令

假设每次运行foo具有相同的依赖关系,解决问题的最简单方法是解决构建系统之外的问题.只需编写一个foo_transitive命令,无论是作为shell脚本还是作为Haskell函数,当提供输入文件时,通过重复运行并检查它是否已达到固定点来生成输出文件.构建系统现在可以使用foo_transitive,并且没有关于依赖项的问题.

在构建系统中对其进行编码

您需要编写两个规则,一个用于执行一个步骤,另一个用于确定哪个步骤是正确的步骤:

let step i = "tempfile" <.> show i

"tempfile.*" *> \out -> do
    let i = read $ takeExtension out :: Int
    if i == 0 then
        copyFile "input" out
    else
        let prev = step (i-1)
        need [prev]
        -- perhaps require addition dependencies, depending on prev
        system' "foo" [prev,out]

"output" *> \out -> do
    let f i = do
            old <- readFile' $ step (i-1)
            new <- readFile' $ step i
            if old == new || i > 100 then copyFile (step i) out else f (i+1)
    f 1
Run Code Online (Sandbox Code Playgroud)

第一个规则tempfile.2tempfile.1等生成,所以我们可以need ["tempfile.100"]得到第100次迭代.如果依赖关系在每个步骤中发生变化,我们可以查看先前的结果来计算新的依赖关系.

第二个规则循环检查序列中的每对值,并在它们相等时停止.如果要在生产构建系统中实现它,您可能希望避免readFile'两次调用每个元素(一次为i-1一次i).