Shake Haskell ビルド ライブラリを使用して、固定点に到達する必要があるプログラムを使用してルールを作成するにはどうすればよいですか? foo
ファイルinput
を取得して出力ファイルを生成するプログラムがありfoo
、出力ファイルが変更されなくなるまで繰り返し適用する必要があると想像してください。Shakeでそれをどのように書くことができますか?
このパターンの典型的な例が LaTeX です。
Shake Haskell ビルド ライブラリを使用して、固定点に到達する必要があるプログラムを使用してルールを作成するにはどうすればよいですか? foo
ファイルinput
を取得して出力ファイルを生成するプログラムがありfoo
、出力ファイルが変更されなくなるまで繰り返し適用する必要があると想像してください。Shakeでそれをどのように書くことができますか?
このパターンの典型的な例が LaTeX です。
まず、Latex を繰り返し呼び出しても常に固定点が生成されるとは限らないことに注意してください。そのため、反復に制限があることを確認してください。また、一部のディストリビューション (MikTex) では、必要な回数だけ自動的に実行される Latex バージョンが提供されているため、代わりにそれらを使用すると問題は解決します。
foo_transitive
独自のコマンドを書く
問題を解決する最も簡単な方法は、 の各実行に同じ依存関係があると仮定してfoo
、ビルド システムの外部で問題を解決することです。foo_transitive
コマンドをシェル スクリプトまたは Haskell 関数として記述するだけで、入力ファイルが提供されると、繰り返し実行して固定点に到達したかどうかを確認することで、出力ファイルが生成されます。ビルド システムが使用できるようfoo_transitive
になり、依存関係に関する問題はありません。
ビルドシステムでエンコードする
1 つのステップを作成するルールと、どのステップを使用するのが正しいかを判断するルールの 2 つのルールを作成する必要があります。
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
最初のルールはtempfile.2
fromtempfile.1
などを生成need ["tempfile.100"]
するため、100 回目の繰り返しを取得できます。各ステップで依存関係が変化する場合は、前の結果を見て新しい依存関係を計算できます。
2 番目のルールは、シーケンス内の値の各ペアをチェックし、それらが等しいときに停止するようにループします。これをプロダクション ビルド システムで実装する場合はreadFile'
、各要素を 2 回 ( asi-1
とas で 1 回ずつi
) 呼び出すことを避けたい場合があります。
@Neil Mitchellの回答を拡張すると、以下はfoo_transitive
. そうは言っても、この特定のケースでは、latexmk
which Does The Right Thing™ を使用します。
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