hsenv-0.3: src/Process.hs
module Process ( outsideProcess
, outsideProcess'
, insideProcess
, ghcPkgDbPathLocation
) where
import MyMonad
import Paths
import Types
import Util.IO (Environment, readProcessWithExitCodeInEnv, which)
import Data.Maybe (fromMaybe)
import System.Environment (getEnvironment)
import System.Exit (ExitCode(..))
import System.FilePath ((</>))
import System.Process (readProcessWithExitCode)
runProcess :: Maybe Environment -> FilePath -> [String] -> Maybe String -> MyMonad String
runProcess env prog args input = do
case input of
Nothing -> return ()
Just inp -> do
trace "Using the following input:"
indentMessages $ mapM_ trace $ lines inp
let execProcess = case env of
Nothing -> readProcessWithExitCode prog args (fromMaybe "" input)
Just env' -> readProcessWithExitCodeInEnv env' prog args input
(exitCode, output, errors) <- liftIO execProcess
debug $ case exitCode of
ExitSuccess -> "Process exited successfully"
ExitFailure errCode -> "Process failed with exit code " ++ show errCode
case output of
"" -> trace "Empty process output"
_ -> do
trace "Process output:"
indentMessages $ mapM_ trace $ lines output
case errors of
"" -> trace "Empty process error output"
_ -> do
trace "Process error output:"
indentMessages $ mapM_ trace $ lines errors
case exitCode of
ExitSuccess -> return output
ExitFailure errCode -> throwError $ MyException $ prog ++ " process failed with status " ++ show errCode
-- run regular process, takes:
-- * program name, looks for it in $PATH,
-- * list of arguments
-- * maybe standard input
-- returns standard output
outsideProcess :: String -> [String] -> Maybe String -> MyMonad String
outsideProcess progName args input = do
debug $ unwords $ ["Running outside process:", progName] ++ args
indentMessages $ do
trace $ unwords ["Looking for", progName, "in $PATH"]
program <- liftIO $ which Nothing progName
case program of
Nothing -> throwError $ MyException $ unwords ["No", progName, "in $PATH"]
Just programPath -> do
trace $ unwords [progName, "->", programPath]
runProcess Nothing programPath args input
outsideProcess' :: String -> [String] -> MyMonad String
outsideProcess' progName args = outsideProcess progName args Nothing
-- returns path to GHC (installed from tarball) builtin package database
externalGhcPkgDb :: MyMonad FilePath
externalGhcPkgDb = do
trace "Checking where GHC (installed from tarball) keeps its package database"
indentMessages $ do
dirStructure <- hseDirStructure
let ghcPkg = ghcDir dirStructure </> "bin" </> "ghc-pkg"
trace $ unwords ["Running process:", ghcPkg, "list"]
ghcPkgOutput <- indentMessages $ runProcess Nothing ghcPkg ["list"] Nothing
debug "Trying to parse ghc-pkg's output"
case lines ghcPkgOutput of
[] -> throwError $ MyException "ghc-pkg returned empty output"
lineWithPath:_ ->
case lineWithPath of
"" -> throwError $ MyException "ghc-pkg's first line of output is empty"
_ -> do
-- ghc-pkg ends pkg db path with trailing colon
-- but only when not run from the terminal
let path = init lineWithPath
debug $ "Found: " ++ path
return path
-- returns value of GHC_PACKAGE_PATH that should be used inside virtual environment
-- defined in this module, because insideProcess needs it
ghcPkgDbPathLocation :: MyMonad String
ghcPkgDbPathLocation = do
trace "Determining value of GHC_PACKAGE_PATH to be used inside virtual environment"
dirStructure <- hseDirStructure
ghc <- asks ghcSource
case ghc of
System -> return $ ghcPackagePath dirStructure
Tarball _ -> do
externalGhcPkgDbPath <- indentMessages externalGhcPkgDb
return $ ghcPackagePath dirStructure ++ ":" ++ externalGhcPkgDbPath
virtualEnvironment :: MyMonad Environment
virtualEnvironment = do
debug "Calculating unix env dictionary used inside virtual environment"
indentMessages $ do
env <- liftIO getEnvironment
ghcPkgDb <- ghcPkgDbPathLocation
debug $ "$GHC_PACKAGE_PATH=" ++ ghcPkgDb
pathVar <- insidePathVar
debug $ "$PATH=" ++ pathVar
let varToBeOverridden var = var `elem` ["GHC_PACKAGE_PATH", "PATH"]
strippedEnv = filter (not . varToBeOverridden . fst) env
return $ [("GHC_PACKAGE_PATH", ghcPkgDb), ("PATH", pathVar)] ++ strippedEnv
-- run process from inside the virtual environment, takes:
-- * program name, looks for it in (in order):
-- - cabal bin dir (e.g. .hsenv*/cabal/bin)
-- - ghc bin dir (e.g. .hsenv*/ghc/bin), only when using ghc from tarball
-- - $PATH
-- * list of arguments
-- * maybe standard input
-- returns standard output
-- process is run in altered environment (new $GHC_PACKAGE_PATH env var,
-- adjusted $PATH var)
insideProcess :: String -> [String] -> Maybe String -> MyMonad String
insideProcess progName args input = do
debug $ unwords $ ["Running inside process:", progName] ++ args
indentMessages $ do
pathVar <- insidePathVar
trace $ unwords ["Looking for", progName, "in", pathVar]
program <- liftIO $ which (Just pathVar) progName
case program of
Nothing -> throwError $ MyException $ unwords ["No", progName, "in", pathVar]
Just programPath -> do
trace $ unwords [progName, "->", programPath]
env <- virtualEnvironment
runProcess (Just env) programPath args input