forked from haskell/haskell-ide-engine
-
Notifications
You must be signed in to change notification settings - Fork 0
/
install.hs
executable file
·288 lines (251 loc) · 10.1 KB
/
install.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
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
#!/usr/bin/env stack
{- stack
--stack-yaml shake.yaml
--install-ghc
runghc
--package shake
--package tar
--package zlib
-}
import qualified Data.ByteString.Lazy as BS
import qualified Codec.Archive.Tar as Tar
import qualified Codec.Compression.GZip as GZip
import Development.Shake
import Development.Shake.Command
import Development.Shake.FilePath
import Control.Monad
import System.Environment ( getProgName
, unsetEnv
)
import System.Info ( os
, arch
)
import Data.List ( dropWhileEnd
, intersperse
)
import Data.Char ( isSpace )
type VersionNumber = String
type GhcPath = String
-- |Defines all different hie versions that are buildable.
-- If they are edited, make sure to maintain the order of the versions.
hieVersions :: [VersionNumber]
hieVersions =
["8.2.1", "8.2.2", "8.4.2", "8.4.3", "8.4.4", "8.6.1", "8.6.2", "8.6.3"]
-- |Most recent version of hie.
-- Important for `dist`, the `hie-wrapper` of the most recent hie
-- will be copied to the tar-archive.
mostRecentHieVersion :: VersionNumber
mostRecentHieVersion = last hieVersions
main :: IO ()
main = do
-- unset GHC_PACKAGE_PATH for cabal
unsetEnv "GHC_PACKAGE_PATH"
shakeArgs shakeOptions { shakeFiles = "_build" } $ do
want ["help"]
phony "ghc" $ do
ghc <- getGhcPath
command_ [] ghc ["--version"]
liftIO $ putStrLn "GHC"
phony "submodules" updateSubmodules
phony "cabal" (getGhcPath >>= installCabal)
phony "all" helpMessage
phony "help" helpMessage
phony "build" (need (reverse $ map ("hie-" ++) hieVersions))
phony "build-all" (need ["build"] >> need ["build-docs"])
phony "dist" buildDist
phony "build-docs" (need (reverse $ map ("build-doc-hie-" ++) hieVersions))
phony "test" (forM_ hieVersions test)
phony "build-copy-compiler-tool" $ forM_ hieVersions buildCopyCompilerTool
forM_ hieVersions
(\version -> phony ("build-doc-hie-" ++ version) $ buildDoc version)
forM_
hieVersions
(\version -> phony ("hie-" ++ version) $ do
need ["submodules"]
need ["cabal"]
buildHie version
installHie version
)
phony "icu-macos-fix"
(need ["icu-macos-fix-install"] >> need ["icu-macos-fix-build"])
phony "icu-macos-fix-install" (command_ [] "brew" ["install", "icu4c"])
phony "icu-macos-fix-build" $ mapM_ buildIcuMacosFix hieVersions
-- |Creates a compressed tar-archive consisting of all hie versions and `hie-wrapper`.
-- Creates a temporary folder, copies all hie versions to it and compresses it in the end.
buildDist :: Action ()
buildDist = do
need ["submodules"]
need ["cabal"]
-- Create the name of the resulting tar file.
Stdout gitRef' <- command [] "git" ["describe", "--tags"]
let gitRef = trim gitRef'
let hieDistName = concat ["hie-", gitRef, "-", arch, "-", os]
-- define name constants for later use
let hieWrapper = "hie-wrapper" <.> exe
let hie = "hie" <.> exe
let mkHie version = "hie-" ++ version <.> exe
withTempDir
(\temporaryDir -> do
forM_ hieVersions $ \hieVersion -> do
buildHie hieVersion
-- after building `hie` copy it to the temporary folder
localInstallRoot <- getLocalInstallRoot hieVersion
copyFile' (localInstallRoot </> "bin" </> hie)
(temporaryDir </> mkHie hieVersion)
-- if the most recent hie-* version is copied,
-- copy it again as the default hie version
-- Also, add its hie-wrapper to the tar archive
when (hieVersion == mostRecentHieVersion) $ do
copyFile' (localInstallRoot </> "bin" </> hieWrapper)
(temporaryDir </> hieWrapper)
copyFile' (localInstallRoot </> "bin" </> hie) (temporaryDir </> hie)
-- After every hie has been built, pack them into a tar.
-- Encrypt the resulting tar file with gzip
liftIO
$ BS.writeFile (hieDistName ++ ".tar.gz")
. GZip.compress
. Tar.write
=<< Tar.pack temporaryDir (hieWrapper : hie : map mkHie hieVersions)
)
return ()
buildIcuMacosFix :: VersionNumber -> Action ()
buildIcuMacosFix version = execStackWithYaml_
version
[ "build"
, "text-icu"
, "--extra-lib-dirs=/usr/local/opt/icu4c/lib"
, "--extra-include-dirs=/usr/local/opt/icu4c/include"
]
updateSubmodules :: Action ()
updateSubmodules = do
command_ [] "git" ["submodule", "sync", "--recursive"]
command_ [] "git" ["submodule", "update", "--init", "--recursive"]
installCabal :: GhcPath -> Action ()
installCabal ghc = do
execStack_ ["install", "cabal-install"]
execCabal_ ["update"]
execCabal_ ["install", "Cabal-2.4.1.0", "--with-compiler=" ++ ghc]
buildHie :: VersionNumber -> Action ()
buildHie versionNumber = do
when (versionNumber `elem` ["hie-8.2.2", "hie-8.2.1"])
$ execStackWithYaml_ versionNumber ["install", "happy"]
execStackWithYaml_ versionNumber ["build"]
`actionOnException` liftIO (putStrLn buildFailMsg)
buildFailMsg :: String
buildFailMsg =
let starsLine
= "\n******************************************************************\n"
in starsLine
++ "building failed, "
++ "try running `stack clean` and restart the build\n"
++ "if this does not work, open an issue at \n"
++ "https://github.com/haskell/haskell-ide-engine"
++ starsLine
installHie :: VersionNumber -> Action ()
installHie versionNumber = do
execStackWithYaml_ versionNumber ["install"]
localBinDir <- getLocalBin
localInstallRoot <- getLocalInstallRoot versionNumber
let hie = "hie" <.> exe
copyFile' (localInstallRoot </> "bin" </> hie)
(localBinDir </> "hie-" ++ versionNumber <.> exe)
copyFile' (localInstallRoot </> "bin" </> hie)
(localBinDir </> "hie-" ++ dropExtension versionNumber <.> exe)
buildCopyCompilerTool :: VersionNumber -> Action ()
buildCopyCompilerTool versionNumber =
execStackWithYaml_ versionNumber ["build", "--copy-compiler-tool"]
test :: VersionNumber -> Action ()
test versionNumber = execStackWithYaml_ versionNumber ["test"]
buildDoc :: VersionNumber -> Action ()
buildDoc versionNumber = do
execStackWithYaml_ versionNumber ["install", "hoogle"]
execStackWithYaml_ versionNumber ["exec", "hoogle", "generate"]
helpMessage :: Action ()
helpMessage = do
scriptName <- liftIO getProgName
out ""
out "Usage:"
out' ("stack " <> scriptName <> " <target>")
out ""
out "Targets:"
mapM_ (out' . showTarget) targets
out ""
where
out = liftIO . putStrLn
out' = out . (" " ++)
-- |Number of spaces the target name including whitespace should have.
-- At least twenty, maybe more if target names are long. At most length of the longest target plus five.
space :: Int
space = maximum (20 : map ((+ 5) . length . fst) targets)
-- |Show a target.
-- Concatenates the target with its help message and inserts whitespace between them.
showTarget :: (String, String) -> String
showTarget (target, msg) =
target ++ replicate (space - length target) ' ' ++ msg
-- |Target for a specific ghc version
hieTarget :: String -> (String, String)
hieTarget version =
("hie-" ++ version, "Builds hie for GHC version " ++ version ++ " only")
allVersionMessage :: String
allVersionMessage =
let msg = intersperse ", " hieVersions
lastVersion = last msg
in concat $ (init $ init msg) ++ [" and ", lastVersion]
-- All targets with their respective help message.
targets =
[ ( "build"
, "Builds hie for all supported GHC versions ("
++ allVersionMessage
++ ")"
)
, ( "build-all"
, "Builds hie and hoogle databases for all supported GHC versions"
)
, ("cabal", "NOTE 3: This is needed for stack only projects too")
, ( "build-docs"
, "Builds the Hoogle database for all supported GHC versions"
)
, ("test" , "Runs hie tests")
, ("icu-macos-fix", "Fixes icu related problems in MacOS")
, ("dist", "Creates a tarball containing all the hie binaries")
, ("help" , "Show help")
]
++ map hieTarget hieVersions
execStackWithYaml_ :: VersionNumber -> [String] -> Action ()
execStackWithYaml_ versionNumber args = do
let stackFile = "stack-" ++ versionNumber ++ ".yaml"
command_ [] "stack" (("--stack-yaml=" ++ stackFile) : args)
execStackWithYaml :: CmdResult r => VersionNumber -> [String] -> Action r
execStackWithYaml versionNumber args = do
let stackFile = "stack-" ++ versionNumber ++ ".yaml"
command [] "stack" (("--stack-yaml=" ++ stackFile) : args)
execStack :: CmdResult r => [String] -> Action r
execStack = command [] "stack"
execStack_ :: [String] -> Action ()
execStack_ = command_ [] "stack"
execCabal_ :: [String] -> Action ()
execCabal_ = command_ [] "cabal"
-- |Get the path to the GHC compiler executable linked to the local `stack.yaml`
-- Equal to the command `stack path --compiler-exe`
getGhcPath :: Action GhcPath
getGhcPath = do
Stdout ghc' <- execStack ["path", "--compiler-exe"]
return $ trim ghc'
-- |Read the local install root of the stack project specified by the VersionNumber
-- Returns the filepath of the local install root.
-- Equal to the command `stack path --local-install-root`
getLocalInstallRoot :: VersionNumber -> Action FilePath
getLocalInstallRoot hieVersion = do
Stdout localInstallRoot' <- execStackWithYaml
hieVersion
["path", "--local-install-root"]
return $ trim localInstallRoot'
-- |Get the local binary path of stack.
-- Equal to the command `stack path --local-bin`
getLocalBin :: Action FilePath
getLocalBin = do
Stdout stackLocalDir' <- execStack ["path", "--local-bin"]
return $ trim stackLocalDir'
-- |Trim the end of a string
trim :: String -> String
trim = dropWhileEnd isSpace