4

I'm switching (or trying to) from the brilliant tup to haskell shake as my build system..

Only I can't figure out how to get shake to rebuild files on changes.

I could of course use inotify or a wrapper like filewatcher or even watchman.

Since I'm using shake though, I was wondering how to integrate with twitch which shares the do syntax, but otherwise doesn't provide much in way of documentation..

The ultimate goal is to use pandoc for multi format documents.

The only reason tup was inadequate was because it doesn't support targets.

HaoZeke
  • 674
  • 9
  • 19

2 Answers2

5

First of all, you should to write your own shake build rules. Then, when some source file will be changed, you should to run your build rules to produce your targets.

Like this:

main = defaultMain $ do
  "src/*.md" |> const build

build = shakeArgs shake{shakeFiles="out"} $ do
    want ["out/foo.html", "out/foo.pdf"]

    "out/*.html" %> \out -> do
        let src = "src" </> dropDirectory1 out -<.> "md"
        cmd_ "pandoc -o" [out] src

    "out/*.pdf" %> \out -> do
        let src = "src" </> dropDirectory1 out -<.> "md"
        cmd_ "pandoc -o" [out] src

When a markdown file in src directory will be changed, then out/foo.html and out/foo.pdf will be updated.

If you want to optimize work of shake then you can do like this:

main = defaultMain $ do
  "src/*.md" |> build . dependentTargets

build targets = shakeArgs shake{shakeFiles="out"} $ do
    want targets
    ...

dependentTargets src
    | "*.md" ?== src = ["out/foo.html", "out/foo.pdf"]
    | otherwise = []

The package twitch recommends to use extension OverloadedStrings for compile code like this:

"src/*.md" |> ...

But this leads to ambiguous code in other parts of the program. For fix that, you can explicitly converting String to Dep like this:

import Data.String

fromString "src/*.md" |> ...

You can improve this code by redefining the (|>) operator:

import Data.String
import Twitch hiding ((|>))

pattern |> callback = addModify callback $ fromString pattern

"src/*.md" |> ...
freestyle
  • 3,692
  • 11
  • 21
  • Somehow I'm getting Couldn't match type ‘[Char]’ with ‘DepM ()’ for ‘do { "src/*.md" |> const build }’ – HaoZeke Nov 09 '17 at 17:48
  • Use extension `OverloadedStrings` (`{-# LANGUAGE OverloadedStrings #-}`) for implicitly converting `String -> Dep` or `Data.String.fromString` for explicitly converting. – freestyle Nov 09 '17 at 18:22
  • This leads to `Ambiguous type variable ‘t1’ arising from the literal ‘"pandoc -o"’ prevents the constraint ‘(Data.String.IsString t1)’ from being solved. In the expression: do { let src = "src" > dropDirectory1 out -<.> "md"; cmd "pandoc -o" [out] src }` Sorry if I'm getting too verbose.. – HaoZeke Nov 09 '17 at 19:05
  • I updated answer with recommendations for fix that problems. – freestyle Nov 09 '17 at 20:00
1

I use shake for building a web site and have wrapped it into twitch to rerun the shake build when some files change. The main call for the watching functions (it uses forkIO to watches in two directories, and each can run shake) is bracketed; it also starts the web server.

mainWatch :: SiteLayout -> Port -> Path Abs Dir ->  IO ()
mainWatch layout  bakedPort bakedPath = bracketIO
        (do  -- first
            shake layout
            watchDough <-  forkIO (mainWatchDough layout)   -- calls shake
            watchTemplates <-  forkIO (mainWatchThemes layout) -- calls shake
            scotty bakedPort (site bakedPath)
            return (watchDough,watchTemplates) )
        (\(watchDough,watchTemplates) -> do -- last
                    putIOwords ["main2 end"]
                    killThread watchDough
                    killThread watchTemplates
                    return ()
            )
        (\watch -> do   -- during
                    return ()
            )

Hope this can be adapted to your case!

user855443
  • 2,596
  • 3
  • 25
  • 37