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

Haskell 如何在Shake中编写定点构建规则,例如Latex,haskell,shake-build-system,Haskell,Shake Build System,使用Shake Haskell构建库,如何使用需要到达固定点的程序编写规则?假设我有一个程序foo,它接受一个文件输入,并生成一个输出文件,该文件应该反复应用foo,直到输出文件不变。我怎么能用Shake写呢 此模式的典型示例是LaTeX。首先,请注意重复调用LaTeX并不总是产生一个固定点,因此请确保在迭代中有一个边界。此外,一些发行版(MikTex)提供了Latex版本,可以自动运行任意次数,因此如果您使用这些版本,问题就会消失 编写自己的foo_transitivecommand 假设每次

使用Shake Haskell构建库,如何使用需要到达固定点的程序编写规则?假设我有一个程序
foo
,它接受一个文件
输入
,并生成一个输出文件,该文件应该反复应用
foo
,直到输出文件不变。我怎么能用Shake写呢


此模式的典型示例是LaTeX。

首先,请注意重复调用LaTeX并不总是产生一个固定点,因此请确保在迭代中有一个边界。此外,一些发行版(MikTex)提供了Latex版本,可以自动运行任意次数,因此如果您使用这些版本,问题就会消失

编写自己的
foo_transitive
command

假设每次运行
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
让步骤i=“tempfile”显示i
“tempfile.*”>\out->do
让我=读取$takeExtension::Int
如果i==0,那么
复制文件“输入”输出
其他的
设上一步=步骤(i-1)
需要[上]
--可能需要添加依赖项,具体取决于prev
系统“foo”[prev,out]
“输出”*>\out->do
让f i=do
旧的100然后复制文件(步骤i)出其他f(i+1)
F1
第一条规则从
tempfile.1
生成
tempfile.2
,依此类推,因此我们可以
需要[“tempfile.100”]
来获得第100次迭代。如果依赖项在每个步骤中发生变化,我们可以查看前面的结果来计算新的依赖项

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

展开的答案,下面是
foo\u transitive
的示例代码。话虽如此,对于这种特殊情况,我只会使用
latexmk
,它做正确的事情™.

import Control.Monad.Fix (fix, mfix)
import Control.Monad.IO.Class (MonadIO(liftIO))
import Text.Printf (printf)

type SHA = Int

data TeXCompilationStage 
  = Init
  | BibTeX
  | Recompile SHA
  deriving (Show, Eq)

data TeXResult
  = Stable SHA
  | Unstable
  deriving (Show, Eq)

f retry x budgetLaTeXCalls
  | budgetLaTeXCalls <= 0
      = do
          liftIO $ putStrLn "Budget for LaTeX depleted; result didn't converge"
          return Unstable
  | otherwise
      = case x of
          Init        -> do
            liftIO $ do 
              putStrLn "Init"
              putStrLn "  # latex"
            retry BibTeX (budgetLaTeXCalls-1)
          BibTeX      -> do
            liftIO $ do
              putStrLn "BibTeX"
              putStrLn "  # bibtex"
            retry (Recompile 0) budgetLaTeXCalls
          Recompile previousSHA -> do
            let budgetLaTeXCalls' = budgetLaTeXCalls - 1
                calculcatedSHA    = 3
            liftIO $ do
              printf "Recompile (budget: %d)\n" budgetLaTeXCalls
              printf "  Prevous SHA:%d\n  Current SHA:%d\n" previousSHA calculcatedSHA
            if calculcatedSHA == previousSHA
              then do
                liftIO $ putStrLn "  Stabilized"
                return $ Stable calculcatedSHA
              else do
                liftIO $ putStrLn "  Unstable"
                retry (Recompile (previousSHA+1)) (budgetLaTeXCalls-1)

latex :: Int -> IO TeXResult
latex = fix f Init
import Control.Monad.Fix(Fix,mfix)
进口管制.Monad.IO.Class(MonadIO(liftIO))
导入Text.Printf(Printf)
类型SHA=Int
数据编译阶段
=Init
|BibTeX
|重新编译SHA
推导(显示,等式)
数据传输结果
=稳定SHA
|不稳定
推导(显示,等式)
f重试x次呼叫
|我打电话给你
liftIO$do
putStrLn“初始化”
putStrLn“#乳胶”
重试BibTeX(budgetLaTeXCalls-1)
BibTeX->do
liftIO$do
putStrLn“BibTeX”
putStrLn“#bibtex”
重试(重新编译0)budgetLaTeXCalls
重新编译PreviousHA->do
让budgetLaTeXCalls'=budgetLaTeXCalls-1
CalcultatedSha=3
liftIO$do
printf“重新编译(预算:%d)\n”预算调用
printf“Previous SHA:%d\n当前SHA:%d\n”previousSHA CalcultatedSHA
如果CalcultatedSha==previousSHA
那就做吧
liftIO$putStrLn“稳定”
返回$Stable calcultatedsa
否则会
liftIO$putStrLn“不稳定”
重试(重新编译(previousSHA+1))(budgetLaTeXCalls-1)
latex::Int->IO TeXResult
latex=fix f Init