{-# LANGUAGE CPP #-}
module Hakyll.Core.UnixFilter
( unixFilter
, unixFilterLBS
) where
import Control.Concurrent (forkIO)
import Control.Concurrent.MVar (newEmptyMVar, putMVar, takeMVar)
import Control.DeepSeq (deepseq)
import Control.Monad (forM_)
import Data.ByteString.Lazy (ByteString)
import qualified Data.ByteString.Lazy as LB
import Data.IORef (newIORef, readIORef, writeIORef)
import System.Exit (ExitCode (..))
import System.IO (Handle, hClose, hFlush, hGetContents,
hPutStr, hSetEncoding, localeEncoding)
import System.Process
import Hakyll.Core.Compiler
unixFilter :: String
-> [String]
-> String
-> Compiler String
unixFilter :: String -> [String] -> String -> Compiler String
unixFilter = (Handle -> String -> IO ())
-> (Handle -> IO String)
-> String
-> [String]
-> String
-> Compiler String
forall o i.
Monoid o =>
(Handle -> i -> IO ())
-> (Handle -> IO o) -> String -> [String] -> i -> Compiler o
unixFilterWith Handle -> String -> IO ()
writer Handle -> IO String
reader
where
writer :: Handle -> String -> IO ()
writer Handle
handle String
input = do
Handle -> TextEncoding -> IO ()
hSetEncoding Handle
handle TextEncoding
localeEncoding
Handle -> String -> IO ()
hPutStr Handle
handle String
input
reader :: Handle -> IO String
reader Handle
handle = do
Handle -> TextEncoding -> IO ()
hSetEncoding Handle
handle TextEncoding
localeEncoding
out <- Handle -> IO String
hGetContents Handle
handle
deepseq out (return out)
unixFilterLBS :: String
-> [String]
-> ByteString
-> Compiler ByteString
unixFilterLBS :: String -> [String] -> ByteString -> Compiler ByteString
unixFilterLBS = (Handle -> ByteString -> IO ())
-> (Handle -> IO ByteString)
-> String
-> [String]
-> ByteString
-> Compiler ByteString
forall o i.
Monoid o =>
(Handle -> i -> IO ())
-> (Handle -> IO o) -> String -> [String] -> i -> Compiler o
unixFilterWith Handle -> ByteString -> IO ()
LB.hPutStr ((Handle -> IO ByteString)
-> String -> [String] -> ByteString -> Compiler ByteString)
-> (Handle -> IO ByteString)
-> String
-> [String]
-> ByteString
-> Compiler ByteString
forall a b. (a -> b) -> a -> b
$ \Handle
handle -> do
out <- Handle -> IO ByteString
LB.hGetContents Handle
handle
LB.length out `seq` return out
unixFilterWith :: Monoid o
=> (Handle -> i -> IO ())
-> (Handle -> IO o)
-> String
-> [String]
-> i
-> Compiler o
unixFilterWith :: forall o i.
Monoid o =>
(Handle -> i -> IO ())
-> (Handle -> IO o) -> String -> [String] -> i -> Compiler o
unixFilterWith Handle -> i -> IO ()
writer Handle -> IO o
reader String
programName [String]
args i
input = do
String -> Compiler ()
debugCompiler (String
"Executing external program " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
programName)
(output, err, exitCode) <- IO (o, String, ExitCode) -> Compiler (o, String, ExitCode)
forall a. IO a -> Compiler a
unsafeCompiler (IO (o, String, ExitCode) -> Compiler (o, String, ExitCode))
-> IO (o, String, ExitCode) -> Compiler (o, String, ExitCode)
forall a b. (a -> b) -> a -> b
$
(Handle -> i -> IO ())
-> (Handle -> IO o)
-> String
-> [String]
-> i
-> IO (o, String, ExitCode)
forall o i.
Monoid o =>
(Handle -> i -> IO ())
-> (Handle -> IO o)
-> String
-> [String]
-> i
-> IO (o, String, ExitCode)
unixFilterIO Handle -> i -> IO ()
writer Handle -> IO o
reader String
programName [String]
args i
input
forM_ (lines err) debugCompiler
case exitCode of
ExitCode
ExitSuccess -> o -> Compiler o
forall a. a -> Compiler a
forall (m :: * -> *) a. Monad m => a -> m a
return o
output
ExitFailure Int
e -> String -> Compiler o
forall a. String -> Compiler a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail (String -> Compiler o) -> String -> Compiler o
forall a b. (a -> b) -> a -> b
$
String
"Hakyll.Core.UnixFilter.unixFilterWith: " String -> String -> String
forall a. [a] -> [a] -> [a]
++
[String] -> String
unwords (String
programName String -> [String] -> [String]
forall a. a -> [a] -> [a]
: [String]
args) String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" gave exit code " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show Int
e String -> String -> String
forall a. [a] -> [a] -> [a]
++
String
". (Error: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
err String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
")"
unixFilterIO :: Monoid o
=> (Handle -> i -> IO ())
-> (Handle -> IO o)
-> String
-> [String]
-> i
-> IO (o, String, ExitCode)
unixFilterIO :: forall o i.
Monoid o =>
(Handle -> i -> IO ())
-> (Handle -> IO o)
-> String
-> [String]
-> i
-> IO (o, String, ExitCode)
unixFilterIO Handle -> i -> IO ()
writer Handle -> IO o
reader String
programName [String]
args i
input = do
#ifdef mingw32_HOST_OS
let pr = shell $ unwords (programName : args)
#else
let pr :: CreateProcess
pr = String -> [String] -> CreateProcess
proc String
programName [String]
args
#endif
(Just inh, Just outh, Just errh, pid) <-
CreateProcess
-> IO (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle)
createProcess CreateProcess
pr
{ std_in = CreatePipe
, std_out = CreatePipe
, std_err = CreatePipe
}
lock <- newEmptyMVar
outRef <- newIORef mempty
errRef <- newIORef ""
_ <- forkIO $ writer inh input >> hFlush inh >> hClose inh
_ <- forkIO $ do
out <- reader outh
hClose outh
writeIORef outRef out
putMVar lock ()
_ <- forkIO $ do
hSetEncoding errh localeEncoding
err <- hGetContents errh
_ <- deepseq err (return err)
hClose errh
writeIORef errRef err
putMVar lock ()
takeMVar lock
takeMVar lock
exitCode <- waitForProcess pid
out <- readIORef outRef
err <- readIORef errRef
return (out, err, exitCode)