Search code examples
shake-build-system

How to avoid output synchronization problems in parallel builds?


When using parallel builds in shake, i get malformed output like this:

[»] Compiling src/Game/Game.cpp
[»] Compiling [»] Compiling [»] Compiling src/Graphics/Image/Png/PngLoader.cpp
src/Main.cpp
src/System/HeartBeat.cpp
[»] Compiling src/Window/Window.cpp
[»] Compiling src/Window/GlfwContext.cpp

I suppose that it is some kind of synchronization problem with my printing. I should note that i am using the following as an output command:

shakeOutput = const $ BS.putStr . BS.pack

And the status message printing section my rules looks like this:

liftIO $ setSGR [SetColor Foreground Vivid Green]
putNormal "[\175] Compiling "
liftIO $ setSGR [SetColor Foreground Vivid Yellow]
putNormal $ c ++ "\n"
liftIO $ setSGR [Reset]

Is there a way to avoid that kind of printing problems with my output built in in shake? If not what kind of haskell syncronization method would be appropriate to use, knowing that the printing code is inside a shake rule?


Solution

  • The reason shakeOutput defaults to const $ BS.putStr . BS.pack is because the BS.putStr takes the Haskell console lock, and thus ensures output isn't interleaved. I don't believe this is a contractual guarantee of BS.putStr, but if it ever stops being so, I will add a lock around shakeOutput by default. There are two approaches to fix the problem:

    Add a lock

    One approach is to add a lock, ideally a renterant Mutex (not something I've seen in Haskell already, but can be synthesised relatively easily on top of MVar and myThreadId). Then you make shakeOutput take the lock, and in your snippets which call shakeOutput multiple times you also take the lock around the entire block. The Mutex probably wants to be stored in a top-level unsafePerformIO CAF.

    Use a single shakeOutput call

    Since shakeOutput is already atomic you can just reuse that property by rewriting your code as:

    putNormal $
        setSGRCode [SetColor Foreground Vivid Green] ++
        "[\175] Compiling " ++
         setSGRCode [SetColor Foreground Vivid Yellow] ++
         c ++ "\n" ++
         setSGRCode [Reset]
    

    I'd encourage the second approach, since it's much simpler, and also makes sure that if the verbosity is turned to quiet the codes don't still get printed.