-
Notifications
You must be signed in to change notification settings - Fork 644
/
Setup.hs
327 lines (274 loc) · 12.5 KB
/
Setup.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
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
{-# LANGUAGE CPP #-}
#if !defined(MIN_VERSION_Cabal)
# define MIN_VERSION_Cabal(x,y,z) 0
#endif
import Control.Monad
import Data.IORef
import Control.Exception (SomeException, catch)
import Distribution.Simple
import Distribution.Simple.BuildPaths
import Distribution.Simple.InstallDirs as I
import Distribution.Simple.LocalBuildInfo as L
import qualified Distribution.Simple.Setup as S
import qualified Distribution.Simple.Program as P
import Distribution.Simple.Utils
import Distribution.Compiler
import Distribution.PackageDescription
import Distribution.Text
import Distribution.Verbosity
import System.Environment
import System.Exit
import System.FilePath ((</>), splitDirectories,isAbsolute)
import System.Directory
import qualified System.FilePath.Posix as Px
import System.Process
#if (MIN_VERSION_Cabal(2,0,0))
import Distribution.Types.UnqualComponentName
#endif
-- After Idris is built, we need to check and install the prelude and other libs
-- -----------------------------------------------------------------------------
-- Idris Command Path
-- make on mingw32 exepects unix style separators
#ifdef mingw32_HOST_OS
(<//>) = (Px.</>)
idrisCmd local = Px.joinPath $ splitDirectories $ ".." <//> ".." <//> buildDir local <//> "idris" <//> "idris"
#else
idrisCmd local = ".." </> ".." </> buildDir local </> "idris" </> "idris"
#endif
-- -----------------------------------------------------------------------------
-- Make Commands
-- use GNU make on FreeBSD
#if defined(freebsd_HOST_OS) || defined(dragonfly_HOST_OS)\
|| defined(openbsd_HOST_OS) || defined(netbsd_HOST_OS)
mymake = "gmake"
#else
mymake = "make"
#endif
make verbosity =
P.runProgramInvocation verbosity . P.simpleProgramInvocation mymake
#ifdef mingw32_HOST_OS
windres verbosity = P.runProgramInvocation verbosity . P.simpleProgramInvocation "windres"
#endif
-- -----------------------------------------------------------------------------
-- Flags
usesGMP :: S.ConfigFlags -> Bool
usesGMP flags =
case lookupFlagAssignment (mkFlagName "gmp") (S.configConfigurationsFlags flags) of
Just True -> True
Just False -> False
Nothing -> False
execOnly :: S.ConfigFlags -> Bool
execOnly flags =
case lookupFlagAssignment (mkFlagName "execonly") (S.configConfigurationsFlags flags) of
Just True -> True
Just False -> False
Nothing -> False
isRelease :: S.ConfigFlags -> Bool
isRelease flags =
case lookupFlagAssignment (mkFlagName "release") (S.configConfigurationsFlags flags) of
Just True -> True
Just False -> False
Nothing -> False
isFreestanding :: S.ConfigFlags -> Bool
isFreestanding flags =
case lookupFlagAssignment (mkFlagName "freestanding") (S.configConfigurationsFlags flags) of
Just True -> True
Just False -> False
Nothing -> False
#if !(MIN_VERSION_Cabal(2,0,0))
mkFlagName :: String -> FlagName
mkFlagName = FlagName
#endif
#if !(MIN_VERSION_Cabal(2,2,0))
lookupFlagAssignment :: FlagName -> FlagAssignment -> Maybe Bool
lookupFlagAssignment = lookup
#endif
-- -----------------------------------------------------------------------------
-- Clean
idrisClean _ flags _ _ = cleanStdLib
where
verbosity = S.fromFlag $ S.cleanVerbosity flags
cleanStdLib = makeClean "libs"
makeClean dir = make verbosity [ "-C", dir, "clean", "IDRIS=idris" ]
-- -----------------------------------------------------------------------------
-- Configure
gitHash :: IO String
gitHash = do h <- Control.Exception.catch (readProcess "git" ["rev-parse", "--short", "HEAD"] "")
(\e -> let e' = (e :: SomeException) in return "PRE")
return $ takeWhile (/= '\n') h
-- Generate a module that contains extra library directories passed
-- via command-line to cabal
generateBuildFlagsModule :: Verbosity -> FilePath -> [String] -> IO ()
generateBuildFlagsModule verbosity dir libdirs = do
let buildFlagsModulePath = dir </> "BuildFlags_idris" Px.<.> "hs"
putStrLn $ "Generating " ++ buildFlagsModulePath
createDirectoryIfMissingVerbose verbosity True dir
rewriteFileEx verbosity buildFlagsModulePath contents
where contents = "module BuildFlags_idris where \n\n" ++
"extraLibDirs :: [String]\n" ++
"extraLibDirs = " ++ show libdirs
-- Put the Git hash into a module for use in the program
-- For release builds, just put the empty string in the module
generateVersionModule verbosity dir release = do
hash <- gitHash
let versionModulePath = dir </> "Version_idris" Px.<.> "hs"
putStrLn $ "Generating " ++ versionModulePath ++
if release then " for release" else " for prerelease " ++ hash
createDirectoryIfMissingVerbose verbosity True dir
rewriteFileEx verbosity versionModulePath (versionModuleContents hash)
where versionModuleContents h = "module Version_idris where\n\n" ++
"gitHash :: String\n" ++
if release
then "gitHash = \"\"\n"
else "gitHash = \"git:" ++ h ++ "\"\n"
-- Generate a module that contains the lib path for a freestanding Idris
generateTargetModule verbosity dir targetDir = do
let absPath = isAbsolute targetDir
let targetModulePath = dir </> "Target_idris" Px.<.> "hs"
putStrLn $ "Generating " ++ targetModulePath
createDirectoryIfMissingVerbose verbosity True dir
rewriteFileEx verbosity targetModulePath (versionModuleContents absPath targetDir)
where versionModuleContents absolute td = "module Target_idris where\n\n" ++
"import System.FilePath\n" ++
"import System.Environment\n" ++
"getDataDir :: IO String\n" ++
if absolute
then "getDataDir = return \"" ++ td ++ "\"\n"
else "getDataDir = do \n" ++
" expath <- getExecutablePath\n" ++
" execDir <- return $ dropFileName expath\n" ++
" return $ execDir ++ \"" ++ td ++ "\"\n"
++ "getDataFileName :: FilePath -> IO FilePath\n"
++ "getDataFileName name = do\n"
++ " dir <- getDataDir\n"
++ " return (dir ++ \"/\" ++ name)"
-- a module that has info about existence and location of a bundled toolchain
generateToolchainModule verbosity srcDir toolDir = do
let commonContent = "module Tools_idris where\n\n"
let toolContent = case toolDir of
Just dir -> "hasBundledToolchain = True\n" ++
"getToolchainDir = \"" ++ dir ++ "\"\n"
Nothing -> "hasBundledToolchain = False\n" ++
"getToolchainDir = \"\""
let toolPath = srcDir </> "Tools_idris" Px.<.> "hs"
createDirectoryIfMissingVerbose verbosity True srcDir
rewriteFileEx verbosity toolPath (commonContent ++ toolContent)
idrisConfigure _ flags pkgdesc local = do
configureRTS
withLibLBI pkgdesc local $ \lib libcfg -> do
let libAutogenDir = autogenComponentModulesDir local libcfg
let libDirs = extraLibDirs $ libBuildInfo lib
generateBuildFlagsModule verbosity libAutogenDir libDirs
generateVersionModule verbosity libAutogenDir (isRelease (configFlags local))
if isFreestanding $ configFlags local
then do
toolDir <- lookupEnv "IDRIS_TOOLCHAIN_DIR"
generateToolchainModule verbosity libAutogenDir toolDir
targetDir <- lookupEnv "IDRIS_LIB_DIR"
case targetDir of
Just d -> generateTargetModule verbosity libAutogenDir d
Nothing -> error $ "Trying to build freestanding without a target directory."
++ " Set it by defining IDRIS_LIB_DIR."
else
generateToolchainModule verbosity libAutogenDir Nothing
where
verbosity = S.fromFlag $ S.configVerbosity flags
version = pkgVersion . package $ localPkgDescr local
-- This is a hack. I don't know how to tell cabal that a data file needs
-- installing but shouldn't be in the distribution. And it won't make the
-- distribution if it's not there, so instead I just delete
-- the file after configure.
configureRTS = make verbosity ["-C", "rts", "clean"]
#if !(MIN_VERSION_Cabal(2,0,0))
autogenComponentModulesDir lbi _ = autogenModulesDir lbi
#endif
#if !(MIN_VERSION_Cabal(2,0,0))
rewriteFileEx :: Verbosity -> FilePath -> String -> IO ()
rewriteFileEx _ = rewriteFile
#endif
-- -----------------------------------------------------------------------------
-- Build
getVersion :: Args -> S.BuildFlags -> IO HookedBuildInfo
getVersion args flags = do
hash <- gitHash
let buildinfo = (emptyBuildInfo { cppOptions = ["-DVERSION="++hash] }) :: BuildInfo
return (Just buildinfo, [])
#if !(MIN_VERSION_Cabal(2,0,0))
mkUnqualComponentName = id
#endif
idrisPreBuild args flags = do
#ifdef mingw32_HOST_OS
createDirectoryIfMissingVerbose verbosity True dir
windres verbosity ["icons/idris_icon.rc","-o", dir++"/idris_icon.o"]
return (Nothing, [(mkUnqualComponentName "idris", emptyBuildInfo { ldOptions = [dir ++ "/idris_icon.o"] })])
where
verbosity = S.fromFlag $ S.buildVerbosity flags
dir = S.fromFlagOrDefault "dist" $ S.buildDistPref flags
#else
return (Nothing, [])
#endif
idrisBuild _ flags _ local
= if (execOnly (configFlags local)) then buildRTS
else do buildStdLib
buildRTS
where
verbosity = S.fromFlag $ S.buildVerbosity flags
buildStdLib = do
putStrLn "Building libraries..."
makeBuild "libs"
where
makeBuild dir = make verbosity [ "-C", dir, "build" , "IDRIS=" ++ idrisCmd local]
buildRTS = make verbosity (["-C", "rts", "build"] ++
gmpflag (usesGMP (configFlags local)))
gmpflag False = []
gmpflag True = ["GMP=-DIDRIS_GMP"]
-- -----------------------------------------------------------------------------
-- Copy/Install
idrisInstall verbosity copy pkg local
= if (execOnly (configFlags local)) then installRTS
else do installStdLib
installRTS
installManPage
where
target = datadir $ L.absoluteInstallDirs pkg local copy
installStdLib = do
let target' = target -- </> "libs"
putStrLn $ "Installing libraries in " ++ target'
makeInstall "libs" target'
installRTS = do
let target' = target </> "rts"
putStrLn $ "Installing run time system in " ++ target'
makeInstall "rts" target'
installManPage = do
let mandest = mandir (L.absoluteInstallDirs pkg local copy) ++ "/man1"
notice verbosity $ unwords ["Copying man page to", mandest]
installOrdinaryFiles verbosity mandest [("man", "idris.1")]
makeInstall src target =
make verbosity [ "-C", src, "install" , "TARGET=" ++ target, "IDRIS=" ++ idrisCmd local]
-- -----------------------------------------------------------------------------
-- Test
-- There are two "dataDir" in cabal, and they don't relate to each other.
-- When fetching modules, idris uses the second path (in the pkg record),
-- which by default is the root folder of the project.
-- We want it to be the install directory where we put the idris libraries.
fixPkg pkg target = pkg { dataDir = target }
idrisTestHook args pkg local hooks flags = do
let target = datadir $ L.absoluteInstallDirs pkg local NoCopyDest
testHook simpleUserHooks args (fixPkg pkg target) local hooks flags
-- -----------------------------------------------------------------------------
-- Main
-- Install libraries during both copy and install
-- See https://github.com/haskell/cabal/issues/709
main = defaultMainWithHooks $ simpleUserHooks
{ postClean = idrisClean
, postConf = idrisConfigure
, preBuild = idrisPreBuild
, postBuild = idrisBuild
, postCopy = \_ flags pkg local ->
idrisInstall (S.fromFlag $ S.copyVerbosity flags)
(S.fromFlag $ S.copyDest flags) pkg local
, postInst = \_ flags pkg local ->
idrisInstall (S.fromFlag $ S.installVerbosity flags)
NoCopyDest pkg local
, testHook = idrisTestHook
}