-
Notifications
You must be signed in to change notification settings - Fork 21
Expand file tree
/
Copy pathStack.hs
More file actions
133 lines (112 loc) · 4.36 KB
/
Copy pathStack.hs
File metadata and controls
133 lines (112 loc) · 4.36 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
{-# LANGUAGE CPP #-}
module Stack
( -- * The bits of information needed from `stack`
StackConfig (..)
, findStackYaml
, getStackConfig
) where
import Data.Char (isSpace)
#if __GLASGOW_HASKELL__ < 709
import Control.Applicative((<$>), (<*>))
import System.IO
#endif
import System.Process
import System.FilePath
import System.Directory
import Control.Monad (filterM)
import Control.Exception
import Types
-- TODO: Move into Types?
data StackConfig = StackConfig { stackYaml :: FilePath
, stackDist :: FilePath
, stackDbs :: [FilePath]
, stackGhcBinDir :: FilePath
, stackGhcLibDir :: FilePath
}
deriving (Eq, Show)
-- | Search for a @stack.yaml@ upwards in given file path tree.
findStackYaml :: FilePath -> IO (Maybe FilePath)
findStackYaml = fmap (fmap trim) . execStackInPath "path --config-location"
-- | Run @stack path@ to compute @StackConfig@
getStackConfig :: CommandExtra -> IO (Maybe StackConfig)
getStackConfig CommandExtra { ceStackYamlPath = Nothing } = return Nothing
getStackConfig CommandExtra { ceStackYamlPath = Just p } = do
dbs <- getStackDbs root
dist <- getStackDist root
ghcBinDir <- getStackGhcBinDir root
ghcLibDir <- getStackGhcLibDir root
return $ StackConfig p <$> dist
<*> dbs
<*> ghcBinDir
<*> ghcLibDir
where
root = takeDirectory p
getStackGhcBinDir :: FilePath -> IO (Maybe FilePath)
getStackGhcBinDir = fmap (fmap trim) . execStackInPath "path --compiler-bin"
getStackGhcLibDir :: FilePath -> IO (Maybe FilePath)
getStackGhcLibDir p = do
ghc <- (trim <$>) <$> execStackInPath "path --compiler-exe" p
case ghc of
Just exe -> (trim <$>) <$> execInPath (exe ++ " --print-libdir") p
Nothing -> return Nothing
getStackDist :: FilePath -> IO (Maybe FilePath)
getStackDist p = (trim <$>) <$> execStackInPath "path --dist-dir" p
getStackDbs :: FilePath -> IO (Maybe [FilePath])
getStackDbs p =
execStackInPath "path --ghc-package-path" p >>=
maybe (return Nothing) (fmap return . extractDbs)
extractDbs :: String -> IO [FilePath]
extractDbs = filterM doesDirectoryExist . stringPaths
stringPaths :: String -> [String]
stringPaths = splitBy ':' . trim
--------------------------------------------------------------------------------
-- | Generic Helpers
--------------------------------------------------------------------------------
splitBy :: Char -> String -> [String]
splitBy c str
| null str' = [x]
| otherwise = x : splitBy c (tail str')
where
(x, str') = span (c /=) str
trim :: String -> String
trim = f . f
where
f = reverse . dropWhile isSpace
-- Execute stack command in path (if stack is available)
execStackInPath :: String -> FilePath -> IO (Maybe String)
execStackInPath a p =
findExecutable "stack" >>= maybe (return Nothing) (const $ execInPath ("stack " ++ a) p)
#if __GLASGOW_HASKELL__ < 709
execInPath :: String -> FilePath -> IO (Maybe String)
execInPath cmd p = do
eIOEstr <- try $ createProcess prc :: IO (Either IOError ProcH)
case eIOEstr of
Right (_, Just h, _, _) -> Just <$> getClose h
Right (_, Nothing, _, _) -> return Nothing
-- This error is most likely "/bin/sh: stack: command not found"
-- which is caused by the package containing a stack.yaml file but
-- no stack command is in the PATH.
Left _ -> return Nothing
where
prc = (shell cmd) { cwd = Just $ takeDirectory p }
getClose :: Handle -> IO String
getClose h = do
str <- hGetContents h
hClose h
return str
type ProcH = (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle)
-- Not deleting this because this is likely more robust than the above! (but
-- only works on process-1.2.3.0 onwards
#else
execInPath :: String -> FilePath -> IO (Maybe String)
execInPath cmd p = do
eIOEstr <- try $ readCreateProcess prc "" :: IO (Either IOError String)
return $ case eIOEstr of
Right s -> Just s
-- This error is most likely "/bin/sh: stack: command not found"
-- which is caused by the package containing a stack.yaml file but
-- no stack command is in the PATH.
Left _ -> Nothing
where
prc = (shell cmd) { cwd = Just p }
#endif