on v0.12.3 help sign in
Idris-dev/Setup.hs
323 lines 13 kB history raw
1{-# LANGUAGE CPP #-}
3import Control.Monad
4import Data.IORef
5import Control.Exception (SomeException, catch)
7import Distribution.Simple
8import Distribution.Simple.BuildPaths (autogenModulesDir)
9import Distribution.Simple.InstallDirs as I
10import Distribution.Simple.LocalBuildInfo as L
11import qualified Distribution.Simple.Setup as S
12import qualified Distribution.Simple.Program as P
13import Distribution.Simple.Utils (createDirectoryIfMissingVerbose, rewriteFile, notice, installOrdinaryFiles)
14import Distribution.Compiler
15import Distribution.PackageDescription
16import Distribution.Text
18import System.Environment
19import System.Exit
20import System.FilePath ((</>), splitDirectories,isAbsolute)
21import System.Directory
22import qualified System.FilePath.Posix as Px
23import System.Process
25-- After Idris is built, we need to check and install the prelude and other libs
27-- -----------------------------------------------------------------------------
28-- Idris Command Path
30-- make on mingw32 exepects unix style separators
31#ifdef mingw32_HOST_OS
32(<//>) = (Px.</>)
33idrisCmd local = Px.joinPath $ splitDirectories $ ".." <//> ".." <//> buildDir local <//> "idris" <//> "idris"
34#else
35idrisCmd local = ".." </> ".." </> buildDir local </> "idris" </> "idris"
36#endif
38-- -----------------------------------------------------------------------------
39-- Make Commands
41-- use GNU make on FreeBSD
42#if defined(freebsd_HOST_OS) || defined(dragonfly_HOST_OS)\
43 || defined(openbsd_HOST_OS) || defined(netbsd_HOST_OS)
44mymake = "gmake"
45#else
46mymake = "make"
47#endif
48make verbosity =
49 P.runProgramInvocation verbosity . P.simpleProgramInvocation mymake
51#ifdef mingw32_HOST_OS
52windres verbosity = P.runProgramInvocation verbosity . P.simpleProgramInvocation "windres"
53#endif
54-- -----------------------------------------------------------------------------
55-- Flags
57usesGMP :: S.ConfigFlags -> Bool
58usesGMP flags =
59 case lookup (FlagName "gmp") (S.configConfigurationsFlags flags) of
60 Just True -> True
61 Just False -> False
62 Nothing -> False
64execOnly :: S.ConfigFlags -> Bool
65execOnly flags =
66 case lookup (FlagName "execonly") (S.configConfigurationsFlags flags) of
67 Just True -> True
68 Just False -> False
69 Nothing -> False
71isRelease :: S.ConfigFlags -> Bool
72isRelease flags =
73 case lookup (FlagName "release") (S.configConfigurationsFlags flags) of
74 Just True -> True
75 Just False -> False
76 Nothing -> False
78isFreestanding :: S.ConfigFlags -> Bool
79isFreestanding flags =
80 case lookup (FlagName "freestanding") (S.configConfigurationsFlags flags) of
81 Just True -> True
82 Just False -> False
83 Nothing -> False
85-- -----------------------------------------------------------------------------
86-- Clean
88idrisClean _ flags _ _ = cleanStdLib
89 where
90 verbosity = S.fromFlag $ S.cleanVerbosity flags
92 cleanStdLib = makeClean "libs"
94 makeClean dir = make verbosity [ "-C", dir, "clean", "IDRIS=idris" ]
96-- -----------------------------------------------------------------------------
97-- Configure
99gitHash :: IO String
100gitHash = do h <- Control.Exception.catch (readProcess "git" ["rev-parse", "--short", "HEAD"] "")
101 (\e -> let e' = (e :: SomeException) in return "PRE")
102 return $ takeWhile (/= '\n') h
104-- Put the Git hash into a module for use in the program
105-- For release builds, just put the empty string in the module
106generateVersionModule verbosity dir release = do
107 hash <- gitHash
108 let versionModulePath = dir </> "Version_idris" Px.<.> "hs"
109 putStrLn $ "Generating " ++ versionModulePath ++
110 if release then " for release" else " for prerelease " ++ hash
111 createDirectoryIfMissingVerbose verbosity True dir
112 rewriteFile versionModulePath (versionModuleContents hash)
114 where versionModuleContents h = "module Version_idris where\n\n" ++
115 "gitHash :: String\n" ++
116 if release
117 then "gitHash = \"\"\n"
118 else "gitHash = \"git:" ++ h ++ "\"\n"
120-- Generate a module that contains the lib path for a freestanding Idris
121generateTargetModule verbosity dir targetDir = do
122 let absPath = isAbsolute targetDir
123 let targetModulePath = dir </> "Target_idris" Px.<.> "hs"
124 putStrLn $ "Generating " ++ targetModulePath
125 createDirectoryIfMissingVerbose verbosity True dir
126 rewriteFile targetModulePath (versionModuleContents absPath targetDir)
127 where versionModuleContents absolute td = "module Target_idris where\n\n" ++
128 "import System.FilePath\n" ++
129 "import System.Environment\n" ++
130 "getDataDir :: IO String\n" ++
131 if absolute
132 then "getDataDir = return \"" ++ td ++ "\"\n"
133 else "getDataDir = do \n" ++
134 " expath <- getExecutablePath\n" ++
135 " execDir <- return $ dropFileName expath\n" ++
136 " return $ execDir ++ \"" ++ td ++ "\"\n"
137 ++ "getDataFileName :: FilePath -> IO FilePath\n"
138 ++ "getDataFileName name = do\n"
139 ++ " dir <- getDataDir\n"
140 ++ " return (dir ++ \"/\" ++ name)"
142-- a module that has info about existence and location of a bundled toolchain
143generateToolchainModule verbosity srcDir toolDir = do
144 let commonContent = "module Tools_idris where\n\n"
145 let toolContent = case toolDir of
146 Just dir -> "hasBundledToolchain = True\n" ++
147 "getToolchainDir = \"" ++ dir ++ "\"\n"
148 Nothing -> "hasBundledToolchain = False\n" ++
149 "getToolchainDir = \"\""
150 let toolPath = srcDir </> "Tools_idris" Px.<.> "hs"
151 createDirectoryIfMissingVerbose verbosity True srcDir
152 rewriteFile toolPath (commonContent ++ toolContent)
154idrisConfigure _ flags _ local = do
155 configureRTS
156 generateVersionModule verbosity (autogenModulesDir local) (isRelease (configFlags local))
157 if isFreestanding $ configFlags local
158 then do
159 toolDir <- lookupEnv "IDRIS_TOOLCHAIN_DIR"
160 generateToolchainModule verbosity (autogenModulesDir local) toolDir
161 targetDir <- lookupEnv "IDRIS_LIB_DIR"
162 case targetDir of
163 Just d -> generateTargetModule verbosity (autogenModulesDir local) d
164 Nothing -> error $ "Trying to build freestanding without a target directory."
165 ++ " Set it by defining IDRIS_LIB_DIR."
166 else
167 generateToolchainModule verbosity (autogenModulesDir local) Nothing
168 where
169 verbosity = S.fromFlag $ S.configVerbosity flags
170 version = pkgVersion . package $ localPkgDescr local
172 -- This is a hack. I don't know how to tell cabal that a data file needs
173 -- installing but shouldn't be in the distribution. And it won't make the
174 -- distribution if it's not there, so instead I just delete
175 -- the file after configure.
176 configureRTS = make verbosity ["-C", "rts", "clean"]
178idrisPreSDist args flags = do
179 let dir = S.fromFlag (S.sDistDirectory flags)
180 let verb = S.fromFlag (S.sDistVerbosity flags)
181 generateVersionModule verb "src" True
182 generateTargetModule verb "src" "./libs"
183 generateToolchainModule verb "src" Nothing
184 preSDist simpleUserHooks args flags
186idrisSDist sdist pkgDesc bi hooks flags = do
187 pkgDesc' <- addGitFiles pkgDesc
188 sdist pkgDesc' bi hooks flags
189 where
190 addGitFiles :: PackageDescription -> IO PackageDescription
191 addGitFiles pkgDesc = do
192 files <- gitFiles
193 return $ pkgDesc { extraSrcFiles = extraSrcFiles pkgDesc ++ files}
194 gitFiles :: IO [FilePath]
195 gitFiles = liftM lines (readProcess "git" ["ls-files"] "")
197idrisPostSDist args flags desc lbi = do
198 Control.Exception.catch (do let file = "src" </> "Version_idris" Px.<.> "hs"
199 let targetFile = "src" </> "Target_idris" Px.<.> "hs"
200 putStrLn $ "Removing generated modules:\n "
201 ++ file ++ "\n" ++ targetFile
202 removeFile file
203 removeFile targetFile)
204 (\e -> let e' = (e :: SomeException) in return ())
205 postSDist simpleUserHooks args flags desc lbi
207-- -----------------------------------------------------------------------------
208-- Build
210getVersion :: Args -> S.BuildFlags -> IO HookedBuildInfo
211getVersion args flags = do
212 hash <- gitHash
213 let buildinfo = (emptyBuildInfo { cppOptions = ["-DVERSION="++hash] }) :: BuildInfo
214 return (Just buildinfo, [])
216idrisPreBuild args flags = do
217#ifdef mingw32_HOST_OS
218 createDirectoryIfMissingVerbose verbosity True dir
219 windres verbosity ["icons/idris_icon.rc","-o", dir++"/idris_icon.o"]
220 return (Nothing, [("idris", emptyBuildInfo { ldOptions = [dir ++ "/idris_icon.o"] })])
221 where
222 verbosity = S.fromFlag $ S.buildVerbosity flags
223 dir = S.fromFlagOrDefault "dist" $ S.buildDistPref flags
224#else
225 return (Nothing, [])
226#endif
228idrisBuild _ flags _ local = unless (execOnly (configFlags local)) $ do
229 buildStdLib
230 buildRTS
231 where
232 verbosity = S.fromFlag $ S.buildVerbosity flags
234 buildStdLib = do
235 putStrLn "Building libraries..."
236 makeBuild "libs"
237 where
238 makeBuild dir = make verbosity [ "-C", dir, "build" , "IDRIS=" ++ idrisCmd local]
240 buildRTS = make verbosity (["-C", "rts", "build"] ++
241 gmpflag (usesGMP (configFlags local)))
243 gmpflag False = []
244 gmpflag True = ["GMP=-DIDRIS_GMP"]
246-- -----------------------------------------------------------------------------
247-- Copy/Install
249idrisInstall verbosity copy pkg local = unless (execOnly (configFlags local)) $ do
250 installStdLib
251 installRTS
252 installManPage
253 where
254 target = datadir $ L.absoluteInstallDirs pkg local copy
256 installStdLib = do
257 let target' = target </> "libs"
258 putStrLn $ "Installing libraries in " ++ target'
259 makeInstall "libs" target'
261 installRTS = do
262 let target' = target </> "rts"
263 putStrLn $ "Installing run time system in " ++ target'
264 makeInstall "rts" target'
266 installManPage = do
267 let mandest = mandir (L.absoluteInstallDirs pkg local copy) ++ "/man1"
268 notice verbosity $ unwords ["Copying man page to", mandest]
269 installOrdinaryFiles verbosity mandest [("man", "idris.1")]
272 makeInstall src target =
273 make verbosity [ "-C", src, "install" , "TARGET=" ++ target, "IDRIS=" ++ idrisCmd local]
275-- -----------------------------------------------------------------------------
276-- Test
278-- FIXME: We use the __GLASGOW_HASKELL__ macro because MIN_VERSION_cabal seems
279-- to be broken !
281-- There are two "dataDir" in cabal, and they don't relate to each other.
282-- When fetching modules, idris uses the second path (in the pkg record),
283-- which by default is the root folder of the project.
284-- We want it to be the install directory where we put the idris libraries.
285fixPkg pkg target = pkg { dataDir = target }
287-- The "Args" argument of the testHooks has been added in cabal 1.22.0,
288-- and should therefore be ignored for prior versions.
289#if __GLASGOW_HASKELL__ < 710
290originalTestHook _ = testHook simpleUserHooks
291#else
292originalTestHook = testHook simpleUserHooks
293#endif
295idrisTestHook args pkg local hooks flags = do
296 let target = datadir $ L.absoluteInstallDirs pkg local NoCopyDest
297 originalTestHook args (fixPkg pkg target) local hooks flags
299-- -----------------------------------------------------------------------------
300-- Main
302-- Install libraries during both copy and install
303-- See https://github.com/haskell/cabal/issues/709
304main = defaultMainWithHooks $ simpleUserHooks
305 { postClean = idrisClean
306 , postConf = idrisConfigure
307 , preBuild = idrisPreBuild
308 , postBuild = idrisBuild
309 , postCopy = \_ flags pkg local ->
310 idrisInstall (S.fromFlag $ S.copyVerbosity flags)
311 (S.fromFlag $ S.copyDest flags) pkg local
312 , postInst = \_ flags pkg local ->
313 idrisInstall (S.fromFlag $ S.installVerbosity flags)
314 NoCopyDest pkg local
315 , preSDist = idrisPreSDist
316 , sDistHook = idrisSDist (sDistHook simpleUserHooks)
317 , postSDist = idrisPostSDist
318#if __GLASGOW_HASKELL__ < 710
319 , testHook = idrisTestHook ()
320#else
321 , testHook = idrisTestHook
322#endif
323 }