I would like to have a Shake rule which deletes its own dependency — and passes lint mode.
There are multiple ways of doing this, but I am wondering whether there is a way to work with the build system, rather than hiding information from it.
For example, I have a directory of PNG files which I would like to batch-convert to JPEG XL. After successful conversion of an image, I would like to throw away the original PNG (accepting a small loss of quality in return for a 90% smaller file).
import Development.Shake
import Development.Shake.FilePath
main :: IO ()
main = shakeArgs shakeOptions $ do
phony "convert" $ do
pngs <- getDirectoryFiles "" ["*.png"]
need [png -<.> "jxl" | png <- pngs]
"*.jxl" %> \out -> do
let src = out -<.> "png"
need [src]
putInfo $ "Converting " ++ src ++ " -> " ++ out
cmd_ "cjxl --quiet" [src, out] -- This command is part of libjxl
putInfo $ "Removing " ++ src
liftIO $ removeFiles "." [src]
Running shake convert --lint
produces the following messages:
Converting scan_00028.png -> scan_00028.jxl
# cjxl (for scan_00028.jxl)
Removing scan_00028.png
Converting scan_00027.png -> scan_00027.jxl
# cjxl (for scan_00027.jxl)
Removing scan_00027.png
Lint checking error - 3 values have changed since being depended upon:
Key: getDirectoryFiles [scan_*.png]
Old: (scan_00027.png scan_00028.png,"")
New:
Key: scan_00027.png
Old: (Just File {mod=0xCB980C50,size=0x3286CCB,digest=NEQ},"")
New: <missing>
Key: scan_00028.png
Old: (Just File {mod=0xCB980C50,size=0x2EEF34A,digest=NEQ},"")
New: <missing>
Is there a way I can declare to Shake that those dependencies have been removed intentionally?
I am aware of multiple ways to work around the lint errors, such as:
removeFilesAfter
- this works, but is liable to fail when free disk space is low. I would prefer the PNG file to be removed promptly after conversion, rather than batched at the end of the build.getDirectoryFilesIO "" ["*.png"]
- just disabling dependency tracking is not what I want, I don't think.shakeArgsPrune
- effectively the same asremoveFilesAfter
.withTempDir
- theAction
which produces the intermediate PNG file could also do conversion to JXL. But it seems messier to combine different things into one step. Image acquisition and conversion can't run in parallel. Also I wouldn't want to throw away the source image if format conversion fails.Development.Shake.Forward
- This would probably work well if the only thing I wanted was conversion. But I have other build rules, for which dependency information would be helpful.
Thanks