@@ -0,0 +1,66 @@
1+-- | A Compiler that supports unix filters.
3+module Hakyll.Core.UnixFilter
7+import Control.Concurrent (forkIO)
8+import System.IO (hPutStr, hClose, hGetContents)
9+import System.Posix.Process (executeFile, forkProcess)
10+import System.Posix.IO ( dupTo, createPipe, stdInput
11+ , stdOutput, closeFd, fdToHandle
14+import Hakyll.Core.Compiler
16+-- | Use a unix filter as compiler. For example, we could use the 'rev' program
19+-- > rev :: Compiler Resource String
20+-- > rev = getResourceString >>> unixFilter "rev" []
22+-- A more realistic example: one can use this to call, for example, the sass
23+-- compiler on CSS files. More information about sass can be found here:
25+-- <http://sass-lang.com/>
27+-- The code is fairly straightforward, given that we use @.scss@ for sass:
29+-- > route "style.scss" $ setExtension "css"
30+-- > compile "style.scss" $
31+-- > getResourceString >>> unixFilter "sass" ["-s", "--scss"]
32+-- > >>> arr compressCss
34+unixFilter :: String -- ^ Program name
35+ -> [String] -- ^ Program args
36+ -> Compiler String String -- ^ Resulting compiler
37+unixFilter programName args = unsafeCompiler $ \input -> do
39+ (stdinRead, stdinWrite) <- createPipe
40+ (stdoutRead, stdoutWrite) <- createPipe
43+ _ <- forkProcess $ do
44+ -- Copy our pipes over the regular stdin/stdout
45+ _ <- dupTo stdinRead stdInput
46+ _ <- dupTo stdoutWrite stdOutput
48+ -- Close the now unneeded file descriptors in the child
49+ mapM_ closeFd [stdinWrite, stdoutRead, stdinRead, stdoutWrite]
51+ -- Execute the program
52+ _ <- executeFile programName True args Nothing
55+ -- On the parent side, close the client-side FDs.
56+ mapM_ closeFd [stdinRead, stdoutWrite]
58+ -- Write the input to the child pipe
60+ stdinWriteHandle <- fdToHandle stdinWrite
61+ hPutStr stdinWriteHandle input
62+ hClose stdinWriteHandle
64+ -- Receive the output from the child
65+ stdoutReadHandle <- fdToHandle stdoutRead
66+ hGetContents stdoutReadHandle