Cactus
Cactus

Reputation: 27626

Why does Shake think this file has changed?

Full Shakefile.hs:

import Development.Shake
import Development.Shake.FilePath

outDir = "_build"

expensiveProcess :: IO ()
expensiveProcess = do
    putStrLn "expensiveProcess"
    writeFile (outDir </> "b") "b"

main = shakeArgs shakeOptions{ shakeFiles = outDir } $ do
    outDir </> "a" %> \out -> do
        alwaysRerun
        writeFileChanged out "a"

    outDir </> "b" %> \out -> do
        need [outDir </> "a"]
        liftIO expensiveProcess

Let's say I start with an empty _build directory, and shake _build/b. In that case, _build/a has changed (since it's a new file), so we run some expensive process to generate _build/b:

$ rm -rf _build; stack exec -- shake --trace --trace _build/b 
% Starting run
% Number of actions = 1
% Number of builtin rules = 9 [FilesQ,DoesDirectoryExistQ,GetDirectoryContentsQ,AlwaysRerunQ,GetDirectoryDirsQ,DoesFileExistQ,GetDirectoryFilesQ,GetEnvQ,FileQ]
% Number of user rule types = 1
% Number of user rules = 2
% Before usingLockFile on _build/.shake.lock
% After usingLockFile
% Missing -> Running, _build/b
# _build/b
% Missing -> Running, _build/a
# _build/a
% Missing -> Running, alwaysRerun
% Running -> Ready, alwaysRerun
    = ((),"") (changed)
% Running -> Ready, _build/a
    = ((Just File {mod=0x5F12F628,size=0x1,digest=NEQ},"")) (changed)
expensiveProcess
% Running -> Ready, _build/b
    = ((Just File {mod=0x4D7E9358,size=0x1,digest=NEQ},"")) (changed)
Build completed in 0.00s

If I now shake _build/b again, then expensiveProcess doesn't run (good!), since its input _build/a hasn't changed. However, its output is marked as changed which can cause problems further downstream, if anything else depends on _build/b:

$ stack exec -- shake --trace --trace _build/b 
% Starting run
% Number of actions = 1
% Number of builtin rules = 9 [FilesQ,DoesDirectoryExistQ,GetDirectoryContentsQ,AlwaysRerunQ,GetDirectoryDirsQ,DoesFileExistQ,GetDirectoryFilesQ,GetEnvQ,FileQ]
% Number of user rule types = 1
% Number of user rules = 2
% Before usingLockFile on _build/.shake.lock
% After usingLockFile
% Chunk 0 [len 34] 01000100000000000000040000000100000001000000010000000000000000000000 Id 1 = (StepKey (),Loaded (Result {result = "\SOH\NUL\NUL\NUL", built = Step 1, changed = Step 1, depends = [], execution = 0.0, traces = []}))
% Chunk 1 [len 30] 0a000400000000000000000000000100000001000000728ad03600000000 Id 4 = (alwaysRerun,Loaded (Result {result = "", built = Step 1, changed = Step 1, depends = [], execution = 6.215e-6, traces = []}))
% Chunk 2 [len 66] 080003000000080000005f6275696c642f611400000000000000000000002af6125f03000000010000000100000001000000fd957b39080000000400000004000000 Id 3 = (_build/a,Loaded (Result {result = "\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL*\246\DC2_\ETX\NUL\NUL\NUL\SOH\NUL\NUL\NUL", built = Step 1, changed = Step 1, depends = [[Id 4]], execution = 2.39931e-4, traces = []}))
% Chunk 3 [len 66] 080002000000080000005f6275696c642f621400000000000000000000005a937e4d03000000010000000100000001000000a1942b39080000000400000003000000 Id 2 = (_build/b,Loaded (Result {result = "\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NULZ\147~M\ETX\NUL\NUL\NUL\SOH\NUL\NUL\NUL", built = Step 1, changed = Step 1, depends = [[Id 3]], execution = 1.63632e-4, traces = []}))
% Chunk 4 [len 50] 000005000000000000000000000001000000010000000000000008000000040000000200000008000000ed872a3aed872a3a Id 5 = (Root,Loaded (Result {result = "", built = Step 1, changed = Step 1, depends = [[Id 2]], execution = 0.0, traces = [Trace {traceMessage = "", traceStart = 6.50524e-4, traceEnd = 6.50524e-4}]}))
% Read 5 chunks, plus 0 slop
% Found at most 6 distinct entries out of 5
% Loaded -> Running, _build/b
% Loaded -> Running, _build/a
% Loaded -> Running, alwaysRerun
% Running -> Ready, alwaysRerun
    = ((),"") (changed)
# _build/a
% Running -> Ready, _build/a
    = ((Just File {mod=0x5F12F628,size=0x1,digest=NEQ},"")) (unchanged)
% Running -> Ready, _build/b
    = ((Just File {mod=0x4D7E9358,size=0x1,digest=NEQ},"")) (changed)
Build completed in 0.00s

Why is _build/b marked as changed in this second run?

Upvotes: 0

Views: 55

Answers (1)

Neil Mitchell
Neil Mitchell

Reputation: 9250

Shake v0.19.6 had a bug (fixed in 90a52d9) where if a rule didn't rebuild at all it reported (changed) if the last time the rule ran it changed. In your case, the very first build caused _build/b to change, so provided _build/b didn't run again, it would continue to say (changed). That's not very useful, so Shake now reports (didn't run) for rules that didn't run, and that's what it now reports for _build/b the second time around.

% Running -> Ready, _build/a
    = ((Just File {mod=0xE205992F,size=0x1,digest=NEQ},"")) (unchanged)
% Running -> Ready, _build/b
    = ((Just File {mod=0xE208A5E4,size=0x1,digest=NEQ},"")) (didn't run)

This bug only impacted the output with trace turned on, internally Shake knows not to continue building, even before this fix.

Upvotes: 1

Related Questions