shh-0.7.2.1: app/shh-app.hs
{-# LANGUAGE LambdaCase #-}
module Main where
import Control.DeepSeq (force)
import Control.Exception
import Control.Monad
import Data.ByteString.Lazy.Char8 (ByteString,pack)
import Shh
import System.IO
import System.IO.Error
import System.Environment
import System.Exit
import System.IO.Temp
import System.Directory
import System.Posix.Process
defaultShell = "\
\{-# LANGUAGE TemplateHaskell #-}\n\
\module Shell where\n\
\import Shh\n\
\$(loadEnv SearchPath)\n\
\"
defaultInitGhci = "\
\:seti -XNoOverloadedLists\n\
\import Shh\n\
\"
extraInitGhci = "\
\import Shh.Prompt\n\
\:set prompt-function formatPrompt \"\\n\\ESC[1;32m[%u@%h:%w]λ \\ESC[0m\"\n\
\:set prompt-cont \"| \"\n\
\"
defaultWrapper = "\
\#! /usr/bin/env sh\n\
\exec \"$@\"\n\
\"
debug = putStrLn
doIfMissing :: FilePath -> IO () -> IO ()
doIfMissing fp a = do
doesFileExist fp >>= \case
True -> pure ()
False -> a
writeIfMissing :: FilePath -> String -> IO ()
writeIfMissing fp s = doIfMissing fp (writeFile fp s)
main :: IO ()
main = do
a <- getArgs
shhDir <- lookupEnv "SHH_DIR" >>= \case
Nothing -> lookupEnv "HOME" >>= \case
Nothing -> error "Please specify HOME or SHH_DIR environment variables"
Just h -> pure $ h <> "/.shh"
Just s -> pure s
let
wrapper :: String
wrapper = shhDir <> "/wrapper"
debug $ "Shh home is: " <> shhDir
createDirectoryIfMissing False shhDir
withCurrentDirectory shhDir $ do
case a of
["--rebuild"] -> do
removeFile "Shell.hi"
removeFile "Shell.o"
[] -> pure ()
["--help"] -> do
putStrLn "usage: shh [--rebuild]"
exitSuccess
_ -> error $ "Unknown arguments: " ++ show a
writeIfMissing "wrapper" defaultWrapper
setPermissions "wrapper" $
setOwnerExecutable True $
setOwnerReadable True $
setOwnerWritable True $
emptyPermissions
doIfMissing "init.ghci" $ do
putStrLn "Generating init.ghci..."
putStrLn " ... checking for shh..."
tryFailure (exe (pack wrapper) "ghc" "-e" "import Shh") >>= \case
Left _ -> do
putStrLn "Please make the shh and shh-extras packages available in the shh"
putStrLn "environment (install it globally or modify the wrapper, see docs)."
putStrLn "Aborting"
exitFailure
Right _ -> writeFile "init.ghci" defaultInitGhci
putStrLn " ... checking for shh-extras..."
tryFailure (exe (pack wrapper) "ghc" "-e" "import Shh.Prompt") >>= \case
Left _ -> do
putStrLn "## WARNING ##########################################################"
putStrLn "# You do not have the shh-extras library installed, and so we are"
putStrLn "# generating a reduced functionality init.ghci file. To restore full"
putStrLn "# functionality, install shh-extras and re-generate your init.ghci"
putStrLn $ "# file by deleting " <> shhDir <> "/init.ghci and re-running shh"
putStrLn "#####################################################################"
Right _ -> appendFile "init.ghci" extraInitGhci
writeIfMissing "Shell.hs" defaultShell
writeIfMissing "paths" ""
pp <- readFile "paths"
cp <- show <$> pathBins
pathDiff <- evaluate $ force pp /= cp
outdated <- catch (do
shellMod <- getModificationTime "Shell.hs"
hiMod <- getModificationTime "Shell.hi"
pure (shellMod > hiMod)
) (\e -> if isDoesNotExistError e then pure True else throwIO e)
when (outdated || pathDiff) $ do
putStrLn "Rebuilding Shell.hs..."
writeFile "paths" cp
-- Use absolute path of Shell.hs so that GHCi doesn't recompile.
exe (pack wrapper) "ghc" "-c" "-dynamic" (shhDir <> "/Shell.hs")
executeFile wrapper False ["ghci", "-ghci-script", shhDir <> "/init.ghci", shhDir <> "/Shell.hs"] Nothing