Merge remote-tracking branch 'origin/master' into HEAD
This commit is contained in:
commit
4d7355cf33
104
yesod/Build.hs
104
yesod/Build.hs
@ -1,46 +1,50 @@
|
|||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
{-# LANGUAGE CPP #-}
|
||||||
module Build
|
module Build
|
||||||
( getDeps
|
( getDeps
|
||||||
, touchDeps
|
, touchDeps
|
||||||
, touch
|
, touch
|
||||||
, recompDeps
|
, recompDeps
|
||||||
, findHaskellFiles
|
|
||||||
) where
|
) where
|
||||||
|
|
||||||
-- FIXME there's a bug when getFileStatus applies to a file
|
-- FIXME there's a bug when getFileStatus applies to a file
|
||||||
-- temporary deleted (e.g., Vim saving a file)
|
-- temporary deleted (e.g., Vim saving a file)
|
||||||
|
|
||||||
import Control.Applicative ((<|>), many)
|
import Control.Applicative ((<|>), many)
|
||||||
import Control.Exception (SomeException, try)
|
|
||||||
import Control.Monad (when, filterM, forM, forM_)
|
|
||||||
|
|
||||||
import qualified Data.Attoparsec.Text.Lazy as A
|
import qualified Data.Attoparsec.Text.Lazy as A
|
||||||
import Data.Char (isSpace)
|
import Data.Char (isSpace, isUpper)
|
||||||
|
import qualified Data.Text.Lazy.IO as TIO
|
||||||
|
|
||||||
|
import Control.Exception (SomeException, try)
|
||||||
|
import Control.Monad (when, filterM, forM, forM_, (>=>))
|
||||||
|
|
||||||
import Data.Monoid (mappend)
|
import Data.Monoid (mappend)
|
||||||
import Data.List (isSuffixOf)
|
|
||||||
import qualified Data.Map as Map
|
import qualified Data.Map as Map
|
||||||
import qualified Data.Set as Set
|
import qualified Data.Set as Set
|
||||||
import qualified Data.Text.Lazy.IO as TIO
|
|
||||||
|
|
||||||
import qualified System.Posix.Types
|
import qualified System.Posix.Types
|
||||||
import System.Directory
|
import System.Directory
|
||||||
import System.FilePath (replaceExtension, (</>))
|
import System.FilePath (takeExtension, replaceExtension, (</>))
|
||||||
import System.PosixCompat.Files (getFileStatus, setFileTimes,
|
import System.PosixCompat.Files (getFileStatus, setFileTimes,
|
||||||
accessTime, modificationTime)
|
accessTime, modificationTime)
|
||||||
|
|
||||||
touch :: IO ()
|
|
||||||
touch = touchDeps id updateFileTime =<< getDeps
|
|
||||||
|
|
||||||
recompDeps :: IO ()
|
touch :: IO ()
|
||||||
recompDeps = touchDeps hiFile removeHi =<< getDeps
|
touch = touchDeps id updateFileTime =<< fmap snd (getDeps [])
|
||||||
|
|
||||||
|
recompDeps :: [FilePath] -> IO ()
|
||||||
|
recompDeps = getDeps >=> touchDeps hiFile removeHi . snd
|
||||||
|
|
||||||
type Deps = Map.Map FilePath (Set.Set FilePath)
|
type Deps = Map.Map FilePath (Set.Set FilePath)
|
||||||
|
|
||||||
getDeps :: IO Deps
|
getDeps :: [FilePath] -> IO ([FilePath], Deps)
|
||||||
getDeps = do
|
getDeps hsSourceDirs = do
|
||||||
hss <- findHaskellFiles "."
|
let defSrcDirs = case hsSourceDirs of
|
||||||
deps' <- mapM determineHamletDeps hss
|
[] -> ["."]
|
||||||
return $ fixDeps $ zip hss deps'
|
ds -> ds
|
||||||
|
hss <- fmap concat $ mapM findHaskellFiles defSrcDirs
|
||||||
|
deps' <- mapM determineDeps hss
|
||||||
|
return $ (hss, fixDeps $ zip hss deps')
|
||||||
|
|
||||||
touchDeps :: (FilePath -> FilePath) ->
|
touchDeps :: (FilePath -> FilePath) ->
|
||||||
(FilePath -> FilePath -> IO ()) ->
|
(FilePath -> FilePath -> IO ()) ->
|
||||||
@ -103,42 +107,52 @@ findHaskellFiles path = do
|
|||||||
fmap concat $ mapM go contents
|
fmap concat $ mapM go contents
|
||||||
where
|
where
|
||||||
go ('.':_) = return []
|
go ('.':_) = return []
|
||||||
go ('c':"abal-dev") = return []
|
go filename = do
|
||||||
go ('d':"ist") = return []
|
d <- doesDirectoryExist full
|
||||||
go x = do
|
if not d
|
||||||
let y = path </> x
|
then if isHaskellFile
|
||||||
d <- doesDirectoryExist y
|
then return [full]
|
||||||
if d
|
else return []
|
||||||
then findHaskellFiles y
|
else if isHaskellDir
|
||||||
else if ".hs" `isSuffixOf` x || ".lhs" `isSuffixOf` x
|
then findHaskellFiles full
|
||||||
then return [y]
|
else return []
|
||||||
else return []
|
where
|
||||||
|
-- this could fail on unicode
|
||||||
|
isHaskellDir = isUpper (head filename)
|
||||||
|
isHaskellFile = takeExtension filename `elem` watch_files
|
||||||
|
full = path </> filename
|
||||||
|
watch_files = [".hs", ".lhs"]
|
||||||
|
|
||||||
data TempType = Hamlet | Verbatim | Messages FilePath | StaticFiles FilePath
|
data TempType = StaticFiles FilePath
|
||||||
|
| Verbatim | Messages FilePath | Hamlet
|
||||||
deriving Show
|
deriving Show
|
||||||
|
|
||||||
determineHamletDeps :: FilePath -> IO [FilePath]
|
determineDeps :: FilePath -> IO [FilePath]
|
||||||
determineHamletDeps x = do
|
determineDeps x = do
|
||||||
y <- TIO.readFile x -- FIXME catch IO exceptions
|
y <- TIO.readFile x -- FIXME catch IO exceptions
|
||||||
let z = A.parse (many $ (parser <|> (A.anyChar >> return Nothing))) y
|
let z = A.parse (many $ (parser <|> (A.anyChar >> return Nothing))) y
|
||||||
case z of
|
case z of
|
||||||
A.Fail{} -> return []
|
A.Fail{} -> return []
|
||||||
A.Done _ r -> mapM go r >>= filterM doesFileExist . concat
|
A.Done _ r -> mapM go r >>= filterM doesFileExist . concat
|
||||||
where
|
where
|
||||||
|
go (Just (StaticFiles fp, _)) = getFolderContents fp
|
||||||
go (Just (Hamlet, f)) = return [f, "templates/" ++ f ++ ".hamlet"]
|
go (Just (Hamlet, f)) = return [f, "templates/" ++ f ++ ".hamlet"]
|
||||||
go (Just (Verbatim, f)) = return [f]
|
go (Just (Verbatim, f)) = return [f]
|
||||||
go (Just (Messages f, _)) = return [f]
|
go (Just (Messages f, _)) = return [f]
|
||||||
go (Just (StaticFiles fp, _)) = getFolderContents fp
|
|
||||||
go Nothing = return []
|
go Nothing = return []
|
||||||
|
|
||||||
parser = do
|
parser = do
|
||||||
ty <- (A.string "$(hamletFile " >> return Hamlet)
|
ty <- (do _ <- A.string "\nstaticFiles \""
|
||||||
|
x' <- A.many1 $ A.satisfy (/= '"')
|
||||||
|
return $ StaticFiles x')
|
||||||
|
<|> (A.string "$(parseRoutesFile " >> return Verbatim)
|
||||||
|
<|> (A.string "$(hamletFile " >> return Hamlet)
|
||||||
<|> (A.string "$(ihamletFile " >> return Hamlet)
|
<|> (A.string "$(ihamletFile " >> return Hamlet)
|
||||||
<|> (A.string "$(whamletFile " >> return Hamlet)
|
<|> (A.string "$(whamletFile " >> return Hamlet)
|
||||||
<|> (A.string "$(html " >> return Hamlet)
|
<|> (A.string "$(html " >> return Hamlet)
|
||||||
<|> (A.string "$(widgetFile " >> return Hamlet)
|
<|> (A.string "$(widgetFile " >> return Hamlet)
|
||||||
<|> (A.string "$(Settings.hamletFile " >> return Hamlet)
|
<|> (A.string "$(Settings.hamletFile " >> return Hamlet)
|
||||||
<|> (A.string "$(Settings.widgetFile " >> return Hamlet)
|
<|> (A.string "$(Settings.widgetFile " >> return Hamlet)
|
||||||
<|> (A.string "$(parseRoutesFile " >> return Verbatim)
|
|
||||||
<|> (A.string "$(persistFile " >> return Verbatim)
|
<|> (A.string "$(persistFile " >> return Verbatim)
|
||||||
<|> (
|
<|> (
|
||||||
A.string "$(persistFileWith " >>
|
A.string "$(persistFileWith " >>
|
||||||
@ -153,10 +167,6 @@ determineHamletDeps x = do
|
|||||||
y <- A.many1 $ A.satisfy (/= '"')
|
y <- A.many1 $ A.satisfy (/= '"')
|
||||||
_ <- A.string "\""
|
_ <- A.string "\""
|
||||||
return $ Messages $ concat [x', "/", y, ".msg"])
|
return $ Messages $ concat [x', "/", y, ".msg"])
|
||||||
<|> (do
|
|
||||||
_ <- A.string "\nstaticFiles \""
|
|
||||||
x' <- A.many1 $ A.satisfy (/= '"')
|
|
||||||
return $ StaticFiles x')
|
|
||||||
case ty of
|
case ty of
|
||||||
Messages{} -> return $ Just (ty, "")
|
Messages{} -> return $ Just (ty, "")
|
||||||
StaticFiles{} -> return $ Just (ty, "")
|
StaticFiles{} -> return $ Just (ty, "")
|
||||||
@ -169,13 +179,13 @@ determineHamletDeps x = do
|
|||||||
_ <- A.char ')'
|
_ <- A.char ')'
|
||||||
return $ Just (ty, y)
|
return $ Just (ty, y)
|
||||||
|
|
||||||
getFolderContents :: FilePath -> IO [FilePath]
|
getFolderContents :: FilePath -> IO [FilePath]
|
||||||
getFolderContents fp = do
|
getFolderContents fp = do
|
||||||
cs <- getDirectoryContents fp
|
cs <- getDirectoryContents fp
|
||||||
let notHidden ('.':_) = False
|
let notHidden ('.':_) = False
|
||||||
notHidden ('t':"mp") = False
|
notHidden ('t':"mp") = False
|
||||||
notHidden _ = True
|
notHidden _ = True
|
||||||
fmap concat $ forM (filter notHidden cs) $ \c -> do
|
fmap concat $ forM (filter notHidden cs) $ \c -> do
|
||||||
let f = fp ++ '/' : c
|
let f = fp ++ '/' : c
|
||||||
isFile <- doesFileExist f
|
isFile <- doesFileExist f
|
||||||
if isFile then return [f] else getFolderContents f
|
if isFile then return [f] else getFolderContents f
|
||||||
|
|||||||
@ -1,6 +1,4 @@
|
|||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{-# LANGUAGE QuasiQuotes #-}
|
|
||||||
{-# LANGUAGE TemplateHaskell #-}
|
|
||||||
{-# LANGUAGE ScopedTypeVariables #-}
|
{-# LANGUAGE ScopedTypeVariables #-}
|
||||||
{-# LANGUAGE CPP #-}
|
{-# LANGUAGE CPP #-}
|
||||||
module Devel
|
module Devel
|
||||||
@ -16,7 +14,7 @@ import qualified Distribution.ModuleName as D
|
|||||||
|
|
||||||
import Control.Concurrent (forkIO, threadDelay)
|
import Control.Concurrent (forkIO, threadDelay)
|
||||||
import qualified Control.Exception as Ex
|
import qualified Control.Exception as Ex
|
||||||
import Control.Monad (forever, when)
|
import Control.Monad (forever, when, unless)
|
||||||
|
|
||||||
import Data.Char (isUpper, isNumber)
|
import Data.Char (isUpper, isNumber)
|
||||||
import qualified Data.List as L
|
import qualified Data.List as L
|
||||||
@ -31,7 +29,7 @@ import System.PosixCompat.Files (modificationTime, getFileStatus)
|
|||||||
import System.Process (createProcess, proc, terminateProcess, readProcess,
|
import System.Process (createProcess, proc, terminateProcess, readProcess,
|
||||||
waitForProcess, rawSystem)
|
waitForProcess, rawSystem)
|
||||||
|
|
||||||
import Build (recompDeps, getDeps,findHaskellFiles)
|
import Build (recompDeps, getDeps)
|
||||||
|
|
||||||
lockFile :: FilePath
|
lockFile :: FilePath
|
||||||
lockFile = "dist/devel-terminate"
|
lockFile = "dist/devel-terminate"
|
||||||
@ -43,11 +41,10 @@ writeLock = do
|
|||||||
|
|
||||||
removeLock :: IO ()
|
removeLock :: IO ()
|
||||||
removeLock = try_ (removeFile lockFile)
|
removeLock = try_ (removeFile lockFile)
|
||||||
|
|
||||||
devel :: Bool -> [String] -> IO ()
|
devel :: Bool -> [String] -> IO ()
|
||||||
devel isCabalDev passThroughArgs = do
|
devel isCabalDev passThroughArgs = do
|
||||||
|
|
||||||
checkDevelFile
|
checkDevelFile
|
||||||
|
|
||||||
writeLock
|
writeLock
|
||||||
|
|
||||||
putStrLn "Yesod devel server. Press ENTER to quit"
|
putStrLn "Yesod devel server. Press ENTER to quit"
|
||||||
@ -55,20 +52,20 @@ devel isCabalDev passThroughArgs = do
|
|||||||
cabal <- D.findPackageDesc "."
|
cabal <- D.findPackageDesc "."
|
||||||
gpd <- D.readPackageDescription D.normal cabal
|
gpd <- D.readPackageDescription D.normal cabal
|
||||||
|
|
||||||
checkCabalFile gpd
|
hsSourceDirs <- checkCabalFile gpd
|
||||||
|
|
||||||
_<- rawSystem cmd args
|
_<- rawSystem cmd args
|
||||||
|
|
||||||
mainLoop
|
mainLoop hsSourceDirs
|
||||||
|
|
||||||
_ <- getLine
|
_ <- getLine
|
||||||
writeLock
|
writeLock
|
||||||
exitSuccess
|
exitSuccess
|
||||||
where
|
where
|
||||||
cmd | isCabalDev == True = "cabal-dev"
|
cmd | isCabalDev = "cabal-dev"
|
||||||
| otherwise = "cabal"
|
| otherwise = "cabal"
|
||||||
|
|
||||||
diffArgs | isCabalDev == True = [
|
diffArgs | isCabalDev = [
|
||||||
"--cabal-install-arg=-fdevel" -- legacy
|
"--cabal-install-arg=-fdevel" -- legacy
|
||||||
, "--cabal-install-arg=-flibrary-only"
|
, "--cabal-install-arg=-flibrary-only"
|
||||||
]
|
]
|
||||||
@ -78,8 +75,8 @@ devel isCabalDev passThroughArgs = do
|
|||||||
]
|
]
|
||||||
args = "configure":diffArgs ++ ["--disable-library-profiling" ]
|
args = "configure":diffArgs ++ ["--disable-library-profiling" ]
|
||||||
|
|
||||||
mainLoop :: IO ()
|
mainLoop :: [FilePath] -> IO ()
|
||||||
mainLoop = do
|
mainLoop hsSourceDirs = do
|
||||||
ghcVer <- ghcVersion
|
ghcVer <- ghcVersion
|
||||||
when isCabalDev (rawSystem cmd ["build"] >> return ()) -- cabal-dev fails with strange errors sometimes if we cabal-dev buildinfo before cabal-dev build
|
when isCabalDev (rawSystem cmd ["build"] >> return ()) -- cabal-dev fails with strange errors sometimes if we cabal-dev buildinfo before cabal-dev build
|
||||||
pkgArgs <- ghcPackageArgs isCabalDev ghcVer
|
pkgArgs <- ghcPackageArgs isCabalDev ghcVer
|
||||||
@ -87,19 +84,19 @@ devel isCabalDev passThroughArgs = do
|
|||||||
forever $ do
|
forever $ do
|
||||||
putStrLn "Rebuilding application..."
|
putStrLn "Rebuilding application..."
|
||||||
|
|
||||||
recompDeps
|
recompDeps hsSourceDirs
|
||||||
|
|
||||||
list <- getFileList
|
list <- getFileList hsSourceDirs
|
||||||
exit <- rawSystem cmd ["build"]
|
exit <- rawSystem cmd ["build"]
|
||||||
|
|
||||||
case exit of
|
case exit of
|
||||||
ExitFailure _ -> putStrLn "Build failure, pausing..."
|
ExitFailure _ -> putStrLn "Build failure, pausing..."
|
||||||
_ -> do
|
_ -> do
|
||||||
removeLock
|
removeLock
|
||||||
putStrLn $ "Starting development server: runghc " ++ L.intercalate " " devArgs
|
putStrLn $ "Starting development server: runghc " ++ L.unwords devArgs
|
||||||
(_,_,_,ph) <- createProcess $ proc "runghc" devArgs
|
(_,_,_,ph) <- createProcess $ proc "runghc" devArgs
|
||||||
watchTid <- forkIO . try_ $ do
|
watchTid <- forkIO . try_ $ do
|
||||||
watchForChanges list
|
watchForChanges hsSourceDirs list
|
||||||
putStrLn "Stopping development server..."
|
putStrLn "Stopping development server..."
|
||||||
writeLock
|
writeLock
|
||||||
threadDelay 1000000
|
threadDelay 1000000
|
||||||
@ -108,17 +105,16 @@ devel isCabalDev passThroughArgs = do
|
|||||||
ec <- waitForProcess ph
|
ec <- waitForProcess ph
|
||||||
putStrLn $ "Exit code: " ++ show ec
|
putStrLn $ "Exit code: " ++ show ec
|
||||||
Ex.throwTo watchTid (userError "process finished")
|
Ex.throwTo watchTid (userError "process finished")
|
||||||
watchForChanges list
|
watchForChanges hsSourceDirs list
|
||||||
|
|
||||||
try_ :: forall a. IO a -> IO ()
|
try_ :: forall a. IO a -> IO ()
|
||||||
try_ x = (Ex.try x :: IO (Either Ex.SomeException a)) >> return ()
|
try_ x = (Ex.try x :: IO (Either Ex.SomeException a)) >> return ()
|
||||||
|
|
||||||
type FileList = Map.Map FilePath EpochTime
|
type FileList = Map.Map FilePath EpochTime
|
||||||
|
|
||||||
getFileList :: IO FileList
|
getFileList :: [FilePath] -> IO FileList
|
||||||
getFileList = do
|
getFileList hsSourceDirs = do
|
||||||
files <- findHaskellFiles "."
|
(files, deps) <- getDeps hsSourceDirs
|
||||||
deps <- getDeps
|
|
||||||
let files' = files ++ map fst (Map.toList deps)
|
let files' = files ++ map fst (Map.toList deps)
|
||||||
fmap Map.fromList $ flip mapM files' $ \f -> do
|
fmap Map.fromList $ flip mapM files' $ \f -> do
|
||||||
efs <- Ex.try $ getFileStatus f
|
efs <- Ex.try $ getFileStatus f
|
||||||
@ -126,19 +122,19 @@ getFileList = do
|
|||||||
Left (_ :: Ex.SomeException) -> (f, 0)
|
Left (_ :: Ex.SomeException) -> (f, 0)
|
||||||
Right fs -> (f, modificationTime fs)
|
Right fs -> (f, modificationTime fs)
|
||||||
|
|
||||||
watchForChanges :: FileList -> IO ()
|
watchForChanges :: [FilePath] -> FileList -> IO ()
|
||||||
watchForChanges list = do
|
watchForChanges hsSourceDirs list = do
|
||||||
newList <- getFileList
|
newList <- getFileList hsSourceDirs
|
||||||
if list /= newList
|
if list /= newList
|
||||||
then return ()
|
then return ()
|
||||||
else threadDelay 1000000 >> watchForChanges list
|
else threadDelay 1000000 >> watchForChanges hsSourceDirs list
|
||||||
|
|
||||||
checkDevelFile :: IO ()
|
checkDevelFile :: IO ()
|
||||||
checkDevelFile = do
|
checkDevelFile = do
|
||||||
e <- doesFileExist "devel.hs"
|
e <- doesFileExist "devel.hs"
|
||||||
when (not e) $ failWith "file devel.hs not found"
|
unless e $ failWith "file devel.hs not found"
|
||||||
|
|
||||||
checkCabalFile :: D.GenericPackageDescription -> IO ()
|
checkCabalFile :: D.GenericPackageDescription -> IO [FilePath]
|
||||||
checkCabalFile gpd = case D.condLibrary gpd of
|
checkCabalFile gpd = case D.condLibrary gpd of
|
||||||
Nothing -> failWith "incorrect cabal file, no library"
|
Nothing -> failWith "incorrect cabal file, no library"
|
||||||
Just ct ->
|
Just ct ->
|
||||||
@ -146,19 +142,15 @@ checkCabalFile gpd = case D.condLibrary gpd of
|
|||||||
Nothing ->
|
Nothing ->
|
||||||
failWith "no development flag found in your configuration file. Expected a 'library-only' flag or the older 'devel' flag"
|
failWith "no development flag found in your configuration file. Expected a 'library-only' flag or the older 'devel' flag"
|
||||||
Just dLib -> do
|
Just dLib -> do
|
||||||
case (D.hsSourceDirs . D.libBuildInfo) dLib of
|
let hsSourceDirs = D.hsSourceDirs . D.libBuildInfo $ dLib
|
||||||
[] -> return ()
|
fl <- getFileList hsSourceDirs
|
||||||
["."] -> return ()
|
let unlisted = checkFileList fl dLib
|
||||||
_ ->
|
unless (null unlisted) $ do
|
||||||
putStrLn $ "WARNING: yesod devel may not work correctly with " ++
|
putStrLn "WARNING: the following source files are not listed in exposed-modules or other-modules:"
|
||||||
"custom hs-source-dirs"
|
mapM_ putStrLn unlisted
|
||||||
fl <- getFileList
|
when (D.fromString "Application" `notElem` D.exposedModules dLib) $
|
||||||
let unlisted = checkFileList fl dLib
|
putStrLn "WARNING: no exposed module Application"
|
||||||
when (not . null $ unlisted) $ do
|
return hsSourceDirs
|
||||||
putStrLn "WARNING: the following source files are not listed in exposed-modules or other-modules:"
|
|
||||||
mapM_ putStrLn unlisted
|
|
||||||
when (D.fromString "Application" `notElem` D.exposedModules dLib) $ do
|
|
||||||
putStrLn "WARNING: no exposed module Application"
|
|
||||||
|
|
||||||
failWith :: String -> IO a
|
failWith :: String -> IO a
|
||||||
failWith msg = do
|
failWith msg = do
|
||||||
@ -209,7 +201,7 @@ lookupDevelLib ct | found = Just (D.condTreeData ct)
|
|||||||
where
|
where
|
||||||
found = not . null . map (\(_,x,_) -> D.condTreeData x) .
|
found = not . null . map (\(_,x,_) -> D.condTreeData x) .
|
||||||
filter isDevelLib . D.condTreeComponents $ ct
|
filter isDevelLib . D.condTreeComponents $ ct
|
||||||
isDevelLib ((D.Var (D.Flag (D.FlagName f))), _, _) = f `elem` ["library-only", "devel"]
|
isDevelLib (D.Var (D.Flag (D.FlagName f)), _, _) = f `elem` ["library-only", "devel"]
|
||||||
isDevelLib _ = False
|
isDevelLib _ = False
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -34,7 +34,7 @@ mkYesodDispatch "~sitearg~" resources~sitearg~
|
|||||||
-- migrations handled by Yesod.
|
-- migrations handled by Yesod.
|
||||||
makeApplication :: AppConfig DefaultEnv Extra -> Logger -> IO Application
|
makeApplication :: AppConfig DefaultEnv Extra -> Logger -> IO Application
|
||||||
makeApplication conf logger = do
|
makeApplication conf logger = do
|
||||||
foundation <- makeFoundation conf logger
|
foundation <- makeFoundation conf setLogger
|
||||||
app <- toWaiAppPlain foundation
|
app <- toWaiAppPlain foundation
|
||||||
return $ logWare app
|
return $ logWare app
|
||||||
where
|
where
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user