diff options
author | Jasper Van der Jeugt <m@jaspervdj.be> | 2012-11-13 17:31:03 +0100 |
---|---|---|
committer | Jasper Van der Jeugt <m@jaspervdj.be> | 2012-11-13 17:31:03 +0100 |
commit | f0af2a3b79ea7eea3f521f79fd903f9023ec85df (patch) | |
tree | bbc460b65ab52879c616dffce1bb32fe8d8df2ac /src/Hakyll/Core | |
parent | d2e913f42434841c584b97ae9d5417ff2737c0ce (diff) | |
download | hakyll-f0af2a3b79ea7eea3f521f79fd903f9023ec85df.tar.gz |
WIP
Diffstat (limited to 'src/Hakyll/Core')
-rw-r--r-- | src/Hakyll/Core/Compiler.hs | 408 | ||||
-rw-r--r-- | src/Hakyll/Core/Compiler/Internal.hs | 33 | ||||
-rw-r--r-- | src/Hakyll/Core/Compiler/Require.hs | 63 | ||||
-rw-r--r-- | src/Hakyll/Core/Runtime.hs | 20 | ||||
-rw-r--r-- | src/Hakyll/Core/UnixFilter.hs | 69 | ||||
-rw-r--r-- | src/Hakyll/Core/Writable/CopyFile.hs | 21 | ||||
-rw-r--r-- | src/Hakyll/Core/Writable/WritableTuple.hs | 37 |
7 files changed, 222 insertions, 429 deletions
diff --git a/src/Hakyll/Core/Compiler.hs b/src/Hakyll/Core/Compiler.hs index ef9b03c..e59506f 100644 --- a/src/Hakyll/Core/Compiler.hs +++ b/src/Hakyll/Core/Compiler.hs @@ -1,97 +1,8 @@ --- | A Compiler manages targets and dependencies between targets --- --- The most distinguishing property of a 'Compiler' is that it is an Arrow. A --- compiler of the type @Compiler a b@ is simply a compilation phase which takes --- an @a@ as input, and produces a @b@ as output. --- --- Compilers are chained using the '>>>' arrow operation. If we have a compiler --- --- > getResourceString :: Compiler Resource String --- --- which reads the resource, and a compiler --- --- > readPage :: Compiler String (Page String) --- --- we can chain these two compilers to get a --- --- > (getResourceString >>> readPage) :: Compiler Resource (Page String) --- --- Most compilers can be created by combining smaller compilers using '>>>'. --- --- More advanced constructions are also possible using arrow, and sometimes --- these are needed. For a good introduction to arrow, you can refer to --- --- <http://en.wikibooks.org/wiki/Haskell/Understanding_arrows> --- --- A construction worth writing a few paragraphs about here are the 'require' --- functions. Different variants of this function are exported here, but they --- all serve more or less the same goal. --- --- When you use only '>>>' to chain your compilers, you get a linear pipeline -- --- it is not possible to add extra items from other compilers along the way. --- This is where the 'require' functions come in. --- --- This function allows you to reference other items, which are then added to --- the pipeline. Let's look at this crappy ASCII illustration which represents --- a pretty common scenario: --- --- > read resource >>> pandoc render >>> layout >>> relativize URL's --- > --- > @templates/fancy.html@ --- --- We want to construct a pipeline of compilers to go from our resource to a --- proper webpage. However, the @layout@ compiler takes more than just the --- rendered page as input: it needs the @templates/fancy.html@ template as well. --- --- This is an example of where we need the @require@ function. We can solve --- this using a construction that looks like: --- --- > ... >>> pandoc render >>> require >>> layout >>> ... --- > | --- > @templates/fancy.html@ ------/ --- --- This illustration can help us understand the type signature of 'require'. --- --- > require :: (Binary a, Typeable a, Writable a) --- > => Identifier a --- > -> (b -> a -> c) --- > -> Compiler b c --- --- Let's look at it in detail: --- --- > (Binary a, Typeable a, Writable a) --- --- These are constraints for the @a@ type. @a@ (the template) needs to have --- certain properties for it to be required. --- --- > Identifier a --- --- This is simply @templates/fancy.html@: the 'Identifier' of the item we want --- to 'require', in other words, the name of the item we want to add to the --- pipeline somehow. --- --- > (b -> a -> c) --- --- This is a function given by the user, specifying /how/ the two items shall be --- merged. @b@ is the output of the previous compiler, and @a@ is the item we --- just required -- the template. This means @c@ will be the final output of the --- 'require' combinator. --- --- > Compiler b c --- --- Indeed, we have now constructed a compiler which takes a @b@ and produces a --- @c@. This means that we have a linear pipeline again, thanks to the 'require' --- function. So, the 'require' function actually helps to reduce to complexity --- of Hakyll applications! --- --- Note that require will fetch a previously compiled item: in our example of --- the type @a@. It is /very/ important that the compiler which produced this --- value, produced the right type as well! --- -{-# LANGUAGE GeneralizedNewtypeDeriving, ScopedTypeVariables #-} +-------------------------------------------------------------------------------- +{-# LANGUAGE GeneralizedNewtypeDeriving #-} +{-# LANGUAGE ScopedTypeVariables #-} module Hakyll.Core.Compiler ( Compiler - , runCompiler , getIdentifier , getRoute , getRouteFor @@ -99,95 +10,52 @@ module Hakyll.Core.Compiler , getResourceString , getResourceLBS , getResourceWith - , fromDependency - , require_ , require - , requireA - , requireAll_ , requireAll - , requireAllA , cached , unsafeCompiler - , traceShowCompiler + , logCompiler , timedCompiler - , byPattern - , byExtension ) where -import Prelude hiding ((.), id) -import Control.Arrow ((>>>), (&&&), arr, first) -import Control.Applicative ((<$>), (*>)) -import Control.Exception (SomeException, handle) -import Control.Monad.Reader (ask) -import Control.Monad.Trans (liftIO) -import Control.Monad.Error (throwError) -import Control.Category (Category, (.), id) -import Data.List (find) -import System.Environment (getProgName) -import System.FilePath (takeExtension) -import Data.Binary (Binary) -import Data.Typeable (Typeable) -import Data.ByteString.Lazy (ByteString) - -import Hakyll.Core.Identifier -import Hakyll.Core.Identifier.Pattern -import Hakyll.Core.CompiledItem -import Hakyll.Core.Writable -import Hakyll.Core.ResourceProvider -import Hakyll.Core.Compiler.Internal -import Hakyll.Core.Store (Store) -import Hakyll.Core.Routes -import Hakyll.Core.Logger -import qualified Hakyll.Core.Store as Store - --- | Run a compiler, yielding the resulting target and it's dependencies. This --- version of 'runCompilerJob' also stores the result --- -runCompiler :: Compiler CompiledItem -- ^ Compiler to run - -> Identifier () -- ^ Target identifier - -> ResourceProvider -- ^ Resource provider - -> [Identifier ()] -- ^ Universe - -> Routes -- ^ Route - -> Store -- ^ Store - -> Bool -- ^ Was the resource modified? - -> Logger -- ^ Logger - -> IO (Throwing CompiledItem) -- ^ Resulting item -runCompiler compiler id' provider universe routes store modified logger = do - -- Run the compiler job - result <- handle (\(e :: SomeException) -> return $ Left $ show e) $ - runCompilerJob compiler id' provider universe routes store modified - logger +-------------------------------------------------------------------------------- +import Control.Applicative ((<$>)) +import Data.Binary (Binary) +import Data.ByteString.Lazy (ByteString) +import Data.Typeable (Typeable) +import Prelude hiding (id, (.)) +import System.Environment (getProgName) - -- Inspect the result - case result of - -- In case we compiled an item, we will store a copy in the cache first, - -- before we return control. This makes sure the compiled item can later - -- be accessed by e.g. require. - Right (CompiledItem x) -> - Store.set store ["Hakyll.Core.Compiler.runCompiler", show id'] x - -- Otherwise, we do nothing here - _ -> return () +-------------------------------------------------------------------------------- +import Hakyll.Core.Compiler.Internal +import Hakyll.Core.Compiler.Require +import Hakyll.Core.Identifier +import Hakyll.Core.Logger +import Hakyll.Core.ResourceProvider +import Hakyll.Core.Routes +import qualified Hakyll.Core.Store as Store +import Hakyll.Core.Writable - return result +-------------------------------------------------------------------------------- -- | Get the identifier of the item that is currently being compiled --- -getIdentifier :: Compiler (Identifier b) -getIdentifier = fromJob $ const $ CompilerM $ - castIdentifier . compilerIdentifier <$> ask +getIdentifier :: Compiler Identifier +getIdentifier = compilerIdentifier <$> compilerAsk + +-------------------------------------------------------------------------------- -- | Get the route we are using for this item --- getRoute :: Compiler (Maybe FilePath) -getRoute = getIdentifier >>> getRouteFor +getRoute = getIdentifier >>= getRouteFor + +-------------------------------------------------------------------------------- -- | Get the route for a specified item --- -getRouteFor :: Compiler (Identifier a -> Maybe FilePath) -getRouteFor = fromJob $ \identifier -> CompilerM $ do - routes <- compilerRoutes <$> ask +getRouteFor :: Identifier -> Compiler (Maybe FilePath) +getRouteFor identifier = do + routes <- compilerRoutes <$> compilerAsk return $ runRoutes routes identifier @@ -197,7 +65,6 @@ getResourceBody :: Compiler String getResourceBody = getResourceWith resourceBody - -------------------------------------------------------------------------------- -- | Get the resource we are compiling as a string getResourceString :: Compiler String @@ -213,188 +80,65 @@ getResourceLBS = getResourceWith $ const resourceLBS -------------------------------------------------------------------------------- -- | Overloadable function for 'getResourceString' and 'getResourceLBS' -getResourceWith :: (ResourceProvider -> Identifier a -> IO b) -> Compiler b -getResourceWith reader = fromJob $ \_ -> CompilerM $ do - provider <- compilerResourceProvider <$> ask - r <- compilerIdentifier <$> ask - let filePath = toFilePath r - if resourceExists provider r - then liftIO $ reader provider $ castIdentifier r - else throwError $ error' filePath - where - error' id' = "Hakyll.Core.Compiler.getResourceWith: resource " - ++ show id' ++ " not found" - --- | Auxiliary: get a dependency --- -getDependency :: (Binary a, Writable a, Typeable a) - => Identifier a -> CompilerM a -getDependency id' = CompilerM $ do - store <- compilerStore <$> ask - result <- liftIO $ - Store.get store ["Hakyll.Core.Compiler.runCompiler", show id'] - case result of - Store.NotFound -> throwError notFound - Store.WrongType e r -> throwError $ wrongType e r - Store.Found x -> return x - where - notFound = - "Hakyll.Core.Compiler.getDependency: " ++ show id' ++ " was " ++ - "not found in the cache, the cache might be corrupted or " ++ - "the item you are referring to might not exist" - wrongType e r = - "Hakyll.Core.Compiler.getDependency: " ++ show id' ++ " was found " ++ - "in the cache, but does not have the right type: expected " ++ show e ++ - " but got " ++ show r - --- | Variant of 'require' which drops the current value --- -require_ :: (Binary a, Typeable a, Writable a) - => Identifier a - -> Compiler a -require_ identifier = - fromDependency identifier >>> fromJob (const $ getDependency identifier) - --- | Require another target. Using this function ensures automatic handling of --- dependencies --- -{- -require :: (Binary a, Typeable a, Writable a) - => Identifier a - -> (b -> a -> c) - -> Compiler b c -require identifier = requireA identifier . arr . uncurry - --- | Arrow-based variant of 'require' --- -requireA :: (Binary a, Typeable a, Writable a) - => Identifier a - -> Compiler (b, a) c - -> Compiler b c -requireA identifier = (id &&& require_ identifier >>>) --} - --- | Variant of 'requireAll' which drops the current value --- -requireAll_ :: (Binary a, Typeable a, Writable a) - => Pattern a - -> Compiler [a] -requireAll_ pattern = fromDependencies (const getDeps) *> fromJob requireAll_' +getResourceWith :: (ResourceProvider -> Identifier -> IO a) -> Compiler a +getResourceWith reader = do + provider <- compilerProvider <$> compilerAsk + id' <- compilerIdentifier <$> compilerAsk + let filePath = toFilePath id' + if resourceExists provider id' + then compilerUnsafeIO $ reader provider id' + else compilerThrow $ error' filePath where - getDeps = map castIdentifier . filterMatches pattern . map castIdentifier - requireAll_' = const $ CompilerM $ do - deps <- getDeps . compilerUniverse <$> ask - mapM (unCompilerM . getDependency) deps + error' fp = "Hakyll.Core.Compiler.getResourceWith: resource " ++ + show fp ++ " not found" --- | Require a number of targets. Using this function ensures automatic handling --- of dependencies --- -{- -requireAll :: (Binary a, Typeable a, Writable a) - => Pattern a - -> (b -> [a] -> c) - -> Compiler b c -requireAll pattern = requireAllA pattern . arr . uncurry - --- | Arrow-based variant of 'requireAll' --- -requireAllA :: (Binary a, Typeable a, Writable a) - => Pattern a - -> Compiler (b, [a]) c - -> Compiler b c -requireAllA pattern = (id &&& requireAll_ pattern >>>) --} +-------------------------------------------------------------------------------- cached :: (Binary a, Typeable a, Writable a) => String -> Compiler a -> Compiler a -cached name (Compiler d j) = Compiler d $ CompilerM $ do - logger <- compilerLogger <$> ask - identifier <- castIdentifier . compilerIdentifier <$> ask - store <- compilerStore <$> ask - modified <- compilerResourceModified <$> ask - progName <- liftIO getProgName - report logger $ "Checking cache: " ++ if modified then "modified" else "OK" +cached name compiler = do + logger <- compilerLogger <$> compilerAsk + id' <- compilerIdentifier <$> compilerAsk + store <- compilerStore <$> compilerAsk + provider <- compilerProvider <$> compilerAsk + modified <- compilerUnsafeIO $ resourceModified provider id' + compilerUnsafeIO $ report logger $ + "Checking cache: " ++ if modified then "modified" else "OK" if modified - then do v <- unCompilerM $ j () - liftIO $ Store.set store [name, show identifier] v - return v - else do v <- liftIO $ Store.get store [name, show identifier] - case v of Store.Found v' -> return v' - _ -> throwError (error' progName) + then do + x <- compiler + compilerUnsafeIO $ Store.set store [name, show id'] x + return x + else do + x <- compilerUnsafeIO $ Store.get store [name, show id'] + progName <- compilerUnsafeIO getProgName + case x of Store.Found x' -> return x' + _ -> compilerThrow (error' progName) where error' progName = "Hakyll.Core.Compiler.cached: Cache corrupt! " ++ "Try running: " ++ progName ++ " clean" --- | Create an unsafe compiler from a function in IO --- -unsafeCompiler :: (a -> IO b) -- ^ Function to lift - -> Compiler (a -> b) -- ^ Resulting compiler -unsafeCompiler f = fromJob $ CompilerM . liftIO . f --- | Compiler for debugging purposes --- -traceShowCompiler :: Show a => Compiler a a -traceShowCompiler = fromJob $ \x -> CompilerM $ do - logger <- compilerLogger <$> ask - report logger $ show x - return x +-------------------------------------------------------------------------------- +unsafeCompiler :: IO a -> Compiler a +unsafeCompiler = compilerUnsafeIO --- | Log and time a compiler --- -timedCompiler :: String -- ^ Message - -> Compiler a b -- ^ Compiler to time - -> Compiler a b -- ^ Resulting compiler -timedCompiler msg (Compiler d j) = Compiler d $ \x -> CompilerM $ do - logger <- compilerLogger <$> ask - timed logger msg $ unCompilerM $ j x --- | Choose a compiler by identifier --- --- For example, assume that most content files need to be compiled --- normally, but a select few need an extra step in the pipeline: --- --- > compile $ pageCompiler >>> byPattern id --- > [ ("projects.md", addProjectListCompiler) --- > , ("sitemap.md", addSiteMapCompiler) --- > ] --- -byPattern :: Compiler a b -- ^ Default compiler - -> [(Pattern (), Compiler a b)] -- ^ Choices - -> Compiler a b -- ^ Resulting compiler -byPattern defaultCompiler choices = Compiler deps job - where - -- Lookup the compiler, give an error when it is not found - lookup' identifier = maybe defaultCompiler snd $ - find (\(p, _) -> matches p identifier) choices - -- Collect the dependencies of the choice - deps = do - identifier <- castIdentifier . dependencyIdentifier <$> ask - compilerDependencies $ lookup' identifier - -- Collect the job of the choice - job x = CompilerM $ do - identifier <- castIdentifier . compilerIdentifier <$> ask - unCompilerM $ compilerJob (lookup' identifier) x +-------------------------------------------------------------------------------- +-- | Compiler for debugging purposes +logCompiler :: String -> Compiler () +logCompiler msg = do + logger <- compilerLogger <$> compilerAsk + compilerUnsafeIO $ report logger msg --- | Choose a compiler by extension --- --- Example: --- --- > match "css/*" $ do --- > route $ setExtension "css" --- > compile $ byExtension (error "Not a (S)CSS file") --- > [ (".css", compressCssCompiler) --- > , (".scss", sass) --- > ] --- --- This piece of code will select the @compressCssCompiler@ for @.css@ files, --- and the @sass@ compiler (defined elsewhere) for @.scss@ files. --- -byExtension :: Compiler a b -- ^ Default compiler - -> [(String, Compiler a b)] -- ^ Choices - -> Compiler a b -- ^ Resulting compiler -byExtension defaultCompiler = byPattern defaultCompiler . map (first extPattern) - where - extPattern c = predicate $ (== c) . takeExtension . toFilePath + +-------------------------------------------------------------------------------- +-- | Log and time a compiler +timedCompiler :: String -- ^ Message + -> Compiler a -- ^ Compiler to time + -> Compiler a -- ^ Resulting compiler +timedCompiler msg compiler = Compiler $ \r -> + timed (compilerLogger r) msg $ unCompiler compiler r diff --git a/src/Hakyll/Core/Compiler/Internal.hs b/src/Hakyll/Core/Compiler/Internal.hs index f211367..5b7fb51 100644 --- a/src/Hakyll/Core/Compiler/Internal.hs +++ b/src/Hakyll/Core/Compiler/Internal.hs @@ -5,19 +5,21 @@ module Hakyll.Core.Compiler.Internal ( CompilerRead (..) , CompilerResult (..) - , Compiler + , Compiler (..) , runCompiler , compilerTell , compilerAsk , compilerThrow , compilerCatch , compilerResult + , compilerUnsafeIO ) where -------------------------------------------------------------------------------- import Control.Applicative (Alternative (..), Applicative (..)) +import Control.Exception (SomeException, handle) import Data.Monoid (mappend, mempty) @@ -34,19 +36,17 @@ import Hakyll.Core.Store -- | Environment in which a compiler runs data CompilerRead = CompilerRead { -- | Target identifier - compilerIdentifier :: Identifier + compilerIdentifier :: Identifier , -- | Resource provider - compilerResourceProvider :: ResourceProvider + compilerProvider :: ResourceProvider , -- | List of all known identifiers - compilerUniverse :: [Identifier] + compilerUniverse :: [Identifier] , -- | Site routes - compilerRoutes :: Routes + compilerRoutes :: Routes , -- | Compiler store - compilerStore :: Store - , -- | Flag indicating if the underlying resource was modified - compilerResourceModified :: Bool + compilerStore :: Store , -- | Logger - compilerLogger :: Logger + compilerLogger :: Logger } @@ -111,7 +111,10 @@ instance Applicative Compiler where -------------------------------------------------------------------------------- runCompiler :: Compiler a -> CompilerRead -> IO (CompilerResult a) -runCompiler = unCompiler +runCompiler compiler read' = handle handler $ unCompiler compiler read' + where + handler :: SomeException -> IO (CompilerResult a) + handler e = return $ CompilerError $ show e -------------------------------------------------------------------------------- @@ -128,7 +131,7 @@ compilerAsk = Compiler $ \r -> return $ CompilerDone r mempty -------------------------------------------------------------------------------- -compilerTell :: [Dependency] -> Compiler () +compilerTell :: CompilerWrite -> Compiler () compilerTell deps = Compiler $ \_ -> return $ CompilerDone () deps {-# INLINE compilerTell #-} @@ -154,3 +157,11 @@ compilerCatch (Compiler x) f = Compiler $ \r -> do compilerResult :: CompilerResult a -> Compiler a compilerResult x = Compiler $ \_ -> return x {-# INLINE compilerResult #-} + + +-------------------------------------------------------------------------------- +compilerUnsafeIO :: IO a -> Compiler a +compilerUnsafeIO io = Compiler $ \_ -> do + x <- io + return $ CompilerDone x mempty +{-# INLINE compilerUnsafeIO #-} diff --git a/src/Hakyll/Core/Compiler/Require.hs b/src/Hakyll/Core/Compiler/Require.hs new file mode 100644 index 0000000..1dc96e7 --- /dev/null +++ b/src/Hakyll/Core/Compiler/Require.hs @@ -0,0 +1,63 @@ +-------------------------------------------------------------------------------- +module Hakyll.Core.Compiler.Require + ( save + , require + , requireAll + ) where + + +-------------------------------------------------------------------------------- +import Control.Applicative ((<$>)) +import Data.Binary (Binary) +import Data.Typeable + + +-------------------------------------------------------------------------------- +import Hakyll.Core.Compiler.Internal +import Hakyll.Core.Dependencies +import Hakyll.Core.Identifier +import Hakyll.Core.Identifier.Pattern +import Hakyll.Core.Store (Store) +import qualified Hakyll.Core.Store as Store + + +-------------------------------------------------------------------------------- +save :: (Binary a, Typeable a) => Store -> Identifier -> a -> IO () +save store identifier x = Store.set store (key identifier) x + + +-------------------------------------------------------------------------------- +require :: (Binary a, Typeable a) => Identifier -> Compiler a +require id' = do + store <- compilerStore <$> compilerAsk + + compilerTell [Identifier id'] + compilerResult $ CompilerRequire id' $ do + result <- compilerUnsafeIO $ Store.get store (key id') + case result of + Store.NotFound -> compilerThrow notFound + Store.WrongType e r -> compilerThrow $ wrongType e r + Store.Found x -> return x + where + notFound = + "Hakyll.Core.Compiler.Require.require: " ++ show id' ++ " was " ++ + "not found in the cache, the cache might be corrupted or " ++ + "the item you are referring to might not exist" + wrongType e r = + "Hakyll.Core.Compiler.Require.require: " ++ show id' ++ " was found " ++ + "in the cache, but does not have the right type: expected " ++ show e ++ + " but got " ++ show r + + +-------------------------------------------------------------------------------- +requireAll :: (Binary a, Typeable a) => Pattern -> Compiler [a] +requireAll pattern = do + universe <- compilerUniverse <$> compilerAsk + let matching = filterMatches pattern universe + compilerTell [Pattern pattern matching] + mapM require matching + + +-------------------------------------------------------------------------------- +key :: Identifier -> [String] +key identifier = ["Hakyll.Core.Compiler.Require", show identifier] diff --git a/src/Hakyll/Core/Runtime.hs b/src/Hakyll/Core/Runtime.hs index e9fb6cd..7354119 100644 --- a/src/Hakyll/Core/Runtime.hs +++ b/src/Hakyll/Core/Runtime.hs @@ -23,6 +23,7 @@ import System.FilePath ((</>)) -------------------------------------------------------------------------------- import Hakyll.Core.CompiledItem import Hakyll.Core.Compiler.Internal +import Hakyll.Core.Compiler.Require import Hakyll.Core.Configuration import Hakyll.Core.Dependencies import Hakyll.Core.Identifier @@ -159,16 +160,14 @@ chase trail id' config <- runtimeConfiguration <$> ask section logger $ "Processing " ++ show id' - isModified <- liftIO $ resourceModified provider id' let compiler = todo M.! id' read' = CompilerRead - { compilerIdentifier = id' - , compilerResourceProvider = provider - , compilerUniverse = map fst universe - , compilerRoutes = routes - , compilerStore = store - , compilerResourceModified = isModified - , compilerLogger = logger + { compilerIdentifier = id' + , compilerProvider = provider + , compilerUniverse = map fst universe + , compilerRoutes = routes + , compilerStore = store + , compilerLogger = logger } result <- timed logger "Compiling" $ liftIO $ runCompiler compiler read' @@ -177,7 +176,7 @@ chase trail id' CompilerError e -> throwError e -- Huge success - CompilerDone compiled facts -> do + CompilerDone (CompiledItem compiled) facts -> do -- Write if necessary case runRoutes routes id' of Nothing -> return () @@ -186,6 +185,9 @@ chase trail id' liftIO $ makeDirectories path liftIO $ write path compiled + -- Save! (For require) + liftIO $ save store id' compiled + -- Update state modify $ \s -> s { runtimeDone = S.insert id' (runtimeDone s) diff --git a/src/Hakyll/Core/UnixFilter.hs b/src/Hakyll/Core/UnixFilter.hs index dcbbaaf..e86c58a 100644 --- a/src/Hakyll/Core/UnixFilter.hs +++ b/src/Hakyll/Core/UnixFilter.hs @@ -1,29 +1,32 @@ +-------------------------------------------------------------------------------- -- | A Compiler that supports unix filters. --- module Hakyll.Core.UnixFilter ( unixFilter , unixFilterLBS ) where -import Control.Concurrent (forkIO) -import System.Posix.Process (executeFile, forkProcess) -import System.Posix.IO ( dupTo, createPipe, stdInput - , stdOutput, closeFd, fdToHandle - ) -import System.IO ( Handle, hPutStr, hClose, hGetContents - , hSetEncoding, localeEncoding - ) -import Data.ByteString.Lazy (ByteString) +-------------------------------------------------------------------------------- +import Control.Concurrent (forkIO) +import Data.ByteString.Lazy (ByteString) import qualified Data.ByteString.Lazy as LB +import System.IO (Handle, hClose, hGetContents, hPutStr, + hSetEncoding, localeEncoding) +import System.Posix.IO (closeFd, createPipe, dupTo, fdToHandle, + stdInput, stdOutput) +import System.Posix.Process (executeFile, forkProcess) + + +-------------------------------------------------------------------------------- +import Hakyll.Core.Compiler -import Hakyll.Core.Compiler +-------------------------------------------------------------------------------- -- | Use a unix filter as compiler. For example, we could use the 'rev' program -- as a compiler. -- --- > rev :: Compiler Resource String --- > rev = getResourceString >>> unixFilter "rev" [] +-- > rev :: Compiler String +-- > rev = getResourceString >>= unixFilter "rev" [] -- -- A more realistic example: one can use this to call, for example, the sass -- compiler on CSS files. More information about sass can be found here: @@ -34,12 +37,13 @@ import Hakyll.Core.Compiler -- -- > match "style.scss" $ do -- > route $ setExtension "css" --- > compile $ getResourceString >>> unixFilter "sass" ["-s", "--scss"] --- > >>> arr compressCss --- -unixFilter :: String -- ^ Program name - -> [String] -- ^ Program args - -> Compiler String String -- ^ Resulting compiler +-- > compile $ getResourceString >>= +-- > unixFilter "sass" ["-s", "--scss"] >>= +-- > compressCssCompiler +unixFilter :: String -- ^ Program name + -> [String] -- ^ Program args + -> String -- ^ Program input + -> Compiler String -- ^ Program output unixFilter = unixFilterWith writer reader where writer handle input = do @@ -49,30 +53,35 @@ unixFilter = unixFilterWith writer reader hSetEncoding handle localeEncoding hGetContents handle + +-------------------------------------------------------------------------------- -- | Variant of 'unixFilter' that should be used for binary files -- -- > match "music.wav" $ do -- > route $ setExtension "ogg" --- > compile $ getResourceLBS >>> unixFilter "oggenc" ["-"] --- -unixFilterLBS :: String -- ^ Program name - -> [String] -- ^ Program args - -> Compiler ByteString ByteString -- ^ Resulting compiler +-- > compile $ getResourceLBS >>= unixFilter "oggenc" ["-"] +unixFilterLBS :: String -- ^ Program name + -> [String] -- ^ Program args + -> ByteString -- ^ Program input + -> Compiler ByteString -- ^ Program output unixFilterLBS = unixFilterWith LB.hPutStr LB.hGetContents + +-------------------------------------------------------------------------------- -- | Overloaded compiler --- unixFilterWith :: (Handle -> i -> IO ()) -- ^ Writer -> (Handle -> IO o) -- ^ Reader -> String -- ^ Program name -> [String] -- ^ Program args - -> Compiler i o -- ^ Resulting compiler -unixFilterWith writer reader programName args = + -> i -- ^ Program input + -> Compiler o -- ^ Program output +unixFilterWith writer reader programName args input = timedCompiler ("Executing external program " ++ programName) $ - unsafeCompiler $ unixFilterIO writer reader programName args + unsafeCompiler $ unixFilterIO writer reader programName args input + +-------------------------------------------------------------------------------- -- | Internally used function --- unixFilterIO :: (Handle -> i -> IO ()) -> (Handle -> IO o) -> String @@ -83,7 +92,7 @@ unixFilterIO writer reader programName args input = do -- Create pipes (stdinRead, stdinWrite) <- createPipe (stdoutRead, stdoutWrite) <- createPipe - + -- Fork the child _ <- forkProcess $ do -- Copy our pipes over the regular stdin/stdout diff --git a/src/Hakyll/Core/Writable/CopyFile.hs b/src/Hakyll/Core/Writable/CopyFile.hs index 6cc08f2..2d92891 100644 --- a/src/Hakyll/Core/Writable/CopyFile.hs +++ b/src/Hakyll/Core/Writable/CopyFile.hs @@ -1,6 +1,7 @@ -------------------------------------------------------------------------------- -- | Exports simple compilers to just copy files -{-# LANGUAGE GeneralizedNewtypeDeriving, DeriveDataTypeable #-} +{-# LANGUAGE DeriveDataTypeable #-} +{-# LANGUAGE GeneralizedNewtypeDeriving #-} module Hakyll.Core.Writable.CopyFile ( CopyFile (..) , copyFileCompiler @@ -8,16 +9,16 @@ module Hakyll.Core.Writable.CopyFile -------------------------------------------------------------------------------- -import Control.Arrow ((>>^)) -import System.Directory (copyFile) -import Data.Typeable (Typeable) -import Data.Binary (Binary) +import Control.Applicative ((<$>)) +import Data.Binary (Binary) +import Data.Typeable (Typeable) +import System.Directory (copyFile) -------------------------------------------------------------------------------- -import Hakyll.Core.Writable -import Hakyll.Core.Compiler -import Hakyll.Core.Identifier +import Hakyll.Core.Compiler +import Hakyll.Core.Identifier +import Hakyll.Core.Writable -------------------------------------------------------------------------------- @@ -32,5 +33,5 @@ instance Writable CopyFile where -------------------------------------------------------------------------------- -copyFileCompiler :: Compiler a CopyFile -copyFileCompiler = getIdentifier >>^ CopyFile . toFilePath +copyFileCompiler :: Compiler CopyFile +copyFileCompiler = CopyFile . toFilePath <$> getIdentifier diff --git a/src/Hakyll/Core/Writable/WritableTuple.hs b/src/Hakyll/Core/Writable/WritableTuple.hs deleted file mode 100644 index 741d2c7..0000000 --- a/src/Hakyll/Core/Writable/WritableTuple.hs +++ /dev/null @@ -1,37 +0,0 @@ --- | This module exposes a writable type 'WritableTuple' which is a simple --- newtype wrapper around a tuple. --- --- The idea is that, given a tuple @(a, b)@, @a@ is the value you actually want --- to save to the disk, and @b@ is some additional info that you /don't/ want to --- save, but that you need later, for example in a 'require' clause. --- -{-# LANGUAGE GeneralizedNewtypeDeriving, DeriveDataTypeable #-} -module Hakyll.Core.Writable.WritableTuple - ( WritableTuple (..) - , writableTupleFst - , writableTupleSnd - , writableTupleCompiler - ) where - -import Control.Arrow (arr) - -import Data.Typeable (Typeable) -import Data.Binary (Binary) - -import Hakyll.Core.Writable -import Hakyll.Core.Compiler - -newtype WritableTuple a b = WritableTuple {unWritableTuple :: (a, b)} - deriving (Show, Eq, Ord, Binary, Typeable) - -instance Writable a => Writable (WritableTuple a b) where - write dst (WritableTuple (x, _)) = write dst x - -writableTupleFst :: WritableTuple a b -> a -writableTupleFst = fst . unWritableTuple - -writableTupleSnd :: WritableTuple a b -> b -writableTupleSnd = snd . unWritableTuple - -writableTupleCompiler :: Compiler (a, b) (WritableTuple a b) -writableTupleCompiler = arr WritableTuple |