-
Notifications
You must be signed in to change notification settings - Fork 843
/
Installed.hs
196 lines (181 loc) · 6.83 KB
/
Installed.hs
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ViewPatterns #-}
module Stack.Setup.Installed
( getCompilerVersion
, markInstalled
, unmarkInstalled
, listInstalled
, Tool (..)
, toolString
, toolNameString
, parseToolText
, filterTools
, toolExtraDirs
, installDir
, tempInstallDir
) where
import qualified Data.ByteString.Char8 as S8
import qualified Data.ByteString.Lazy as BL
import Data.Char ( isDigit )
import qualified Data.List as L
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import Distribution.System ( Platform (..) )
import qualified Distribution.System as Cabal
import Path ( (</>), filename, parseRelDir, parseRelFile )
import Path.IO ( doesDirExist, ignoringAbsence, listDir, removeFile )
import RIO.Process ( HasProcessContext, proc, readProcess_ )
import Stack.Constants
( relDirBin, relDirInclude, relDirLib, relDirLocal
, relDirMingw, relDirUsr
)
import Stack.Prelude
import Stack.Types.Compiler
( ActualCompiler (..), WhichCompiler (..) )
import Stack.Types.Config ( Config (..), HasConfig (..) )
import Stack.Types.Config.Exception ( ConfigPrettyException (..) )
import Stack.Types.ExtraDirs ( ExtraDirs (..) )
import Stack.Types.MsysEnvironment ( relDirMsysEnv )
data Tool
= Tool PackageIdentifier -- ^ e.g. ghc-7.8.4, msys2-20150512
| ToolGhcGit !Text !Text -- ^ e.g. ghc-git-COMMIT_ID-FLAVOUR
deriving Eq
-- | 'Tool' values are ordered by name (being @ghc-git@, for @ToolGhcGit _ _@)
-- alphabetically and then by version (later versions are ordered before
-- earlier versions, where applicable).
instance Ord Tool where
compare (Tool pkgId1) (Tool pkgId2) = if pkgName1 == pkgName2
then compare pkgVersion2 pkgVersion1 -- Later versions ordered first
else compare pkgName1 pkgName2
where
PackageIdentifier pkgName1 pkgVersion1 = pkgId1
PackageIdentifier pkgName2 pkgVersion2 = pkgId2
compare (Tool pkgId) (ToolGhcGit _ _) = compare (pkgName pkgId) "ghc-git"
compare (ToolGhcGit _ _) (Tool pkgId) = compare "ghc-git" (pkgName pkgId)
compare (ToolGhcGit c1 f1) (ToolGhcGit c2 f2) = if f1 == f2
then compare c1 c2
else compare f1 f2
toolString :: Tool -> String
toolString (Tool ident) = packageIdentifierString ident
toolString (ToolGhcGit commit flavour) = "ghc-git-" ++ T.unpack commit ++ "-" ++ T.unpack flavour
toolNameString :: Tool -> String
toolNameString (Tool ident) = packageNameString $ pkgName ident
toolNameString ToolGhcGit{} = "ghc-git"
parseToolText :: Text -> Maybe Tool
parseToolText (parseWantedCompiler -> Right WCGhcjs{}) = Nothing
parseToolText (parseWantedCompiler -> Right (WCGhcGit c f)) = Just (ToolGhcGit c f)
parseToolText (parsePackageIdentifier . T.unpack -> Just pkgId) = Just (Tool pkgId)
parseToolText _ = Nothing
markInstalled :: (MonadIO m, MonadThrow m)
=> Path Abs Dir
-> Tool
-> m ()
markInstalled programsPath tool = do
fpRel <- parseRelFile $ toolString tool ++ ".installed"
writeBinaryFileAtomic (programsPath </> fpRel) "installed"
unmarkInstalled :: MonadIO m
=> Path Abs Dir
-> Tool
-> m ()
unmarkInstalled programsPath tool = liftIO $ do
fpRel <- parseRelFile $ toolString tool ++ ".installed"
ignoringAbsence (removeFile $ programsPath </> fpRel)
listInstalled :: (MonadIO m, MonadThrow m)
=> Path Abs Dir
-> m [Tool]
listInstalled programsPath =
doesDirExist programsPath >>= \case
False -> pure []
True -> do (_, files) <- listDir programsPath
pure $ mapMaybe toTool files
where
toTool fp = do
x <- T.stripSuffix ".installed" $ T.pack $ toFilePath $ filename fp
parseToolText x
filterTools :: PackageName -- ^ package to find
-> (Version -> Bool) -- ^ which versions are acceptable
-> [Tool] -- ^ tools to filter
-> [PackageIdentifier]
filterTools name goodVersion installed =
[ pkgId | Tool pkgId <- installed
, pkgName pkgId == name
, goodVersion (pkgVersion pkgId) ]
getCompilerVersion ::
(HasProcessContext env, HasLogFunc env)
=> WhichCompiler
-> Path Abs File -- ^ executable
-> RIO env ActualCompiler
getCompilerVersion wc exe =
case wc of
Ghc -> do
logDebug "Asking GHC for its version"
bs <- fst <$> proc (toFilePath exe) ["--numeric-version"] readProcess_
let (_, ghcVersion) = versionFromEnd $ BL.toStrict bs
x <- ACGhc <$> parseVersionThrowing (T.unpack $ T.decodeUtf8 ghcVersion)
logDebug $ "GHC version is: " <> display x
pure x
where
versionFromEnd = S8.spanEnd isValid . fst . S8.breakEnd isValid
isValid c = c == '.' || isDigit c
-- | Binary directories for the given installed package
toolExtraDirs :: HasConfig env => Tool -> RIO env ExtraDirs
toolExtraDirs tool = do
config <- view configL
dir <- installDir config.localPrograms tool
case (config.platform, toolNameString tool) of
(Platform _ Cabal.Windows, isGHC -> True) -> pure mempty
{ bins =
[ dir </> relDirBin
, dir </> relDirMingw </> relDirBin
]
}
(Platform _ Cabal.Windows, "msys2") -> do
relDirMsysEnvPrefix <- case config.msysEnvironment of
Just msysEnv -> pure $ relDirMsysEnv msysEnv
Nothing -> throwM NoMsysEnvironmentBug
pure mempty
{ bins =
[ dir </> relDirMsysEnvPrefix </> relDirBin
, dir </> relDirUsr </> relDirBin
, dir </> relDirUsr </> relDirLocal </> relDirBin
]
, includes =
[ dir </> relDirMsysEnvPrefix </> relDirInclude
]
, libs =
[ dir </> relDirMsysEnvPrefix </> relDirLib
, dir </> relDirMsysEnvPrefix </> relDirBin
]
}
(_, isGHC -> True) -> pure mempty
{ bins =
[ dir </> relDirBin
]
}
(Platform _ x, toolName) -> do
prettyWarnL
[ flow "binDirs: unexpected OS/tool combo:"
, flow (show (x, toolName) <> ".")
]
pure mempty
where
isGHC n = "ghc" == n || "ghc-" `L.isPrefixOf` n
installDir :: (MonadReader env m, MonadThrow m)
=> Path Abs Dir
-> Tool
-> m (Path Abs Dir)
installDir programsDir tool = do
relativeDir <- parseRelDir $ toolString tool
pure $ programsDir </> relativeDir
tempInstallDir ::
(MonadReader env m, MonadThrow m)
=> Path Abs Dir
-> Tool
-> m (Path Abs Dir)
tempInstallDir programsDir tool = do
relativeDir <- parseRelDir $ toolString tool ++ ".temp"
pure $ programsDir </> relativeDir