Cabal-2.2.0.0: tests/custom-setup/IdrisSetup.hs
-- This is Setup.hs script from idris-1.1.1
{-
Copyright (c) 2011 Edwin Brady
School of Computer Science, University of St Andrews
All rights reserved.
This code is derived from software written by Edwin Brady
(eb@cs.st-andrews.ac.uk).
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. None of the names of the copyright holders may be used to endorse
or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*** End of disclaimer. ***
-}
{-# LANGUAGE CPP #-}
{-# OPTIONS_GHC -fno-warn-deprecations #-}
module IdrisSetup (main) where
#if !defined(MIN_VERSION_Cabal)
# define MIN_VERSION_Cabal(x,y,z) 0
#endif
#if !defined(MIN_VERSION_base)
# define MIN_VERSION_base(x,y,z) 0
#endif
import Control.Monad
import Data.IORef
import Control.Exception (SomeException, catch)
import Data.String (fromString)
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 (createDirectoryIfMissingVerbose, rewriteFile, notice, installOrdinaryFiles)
import Distribution.Compiler
import Distribution.PackageDescription
import Distribution.Text
import System.Environment
import System.Exit
import System.FilePath ((</>), splitDirectories,isAbsolute)
import System.Directory
import qualified System.FilePath.Posix as Px
import System.Process
-- This is difference from vanilla idris-1.1.1
configConfigurationsFlags :: S.ConfigFlags -> [(FlagName, Bool)]
#if MIN_VERSION_Cabal(2,1,0)
configConfigurationsFlags = unFlagAssignment . S.configConfigurationsFlags
#else
configConfigurationsFlags = S.configConfigurationsFlags
#endif
#if !MIN_VERSION_base(4,6,0)
lookupEnv :: String -> IO (Maybe String)
lookupEnv v = lookup v `fmap` getEnvironment
#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 lookup (mkFlagName "gmp") (configConfigurationsFlags flags) of
Just True -> True
Just False -> False
Nothing -> False
execOnly :: S.ConfigFlags -> Bool
execOnly flags =
case lookup (mkFlagName "execonly") (configConfigurationsFlags flags) of
Just True -> True
Just False -> False
Nothing -> False
isRelease :: S.ConfigFlags -> Bool
isRelease flags =
case lookup (mkFlagName "release") (configConfigurationsFlags flags) of
Just True -> True
Just False -> False
Nothing -> False
isFreestanding :: S.ConfigFlags -> Bool
isFreestanding flags =
case lookup (mkFlagName "freestanding") (configConfigurationsFlags flags) of
Just True -> True
Just False -> False
Nothing -> False
#if !(MIN_VERSION_Cabal(2,0,0))
mkFlagName :: String -> FlagName
mkFlagName = FlagName
#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
-- 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
rewriteFile 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
rewriteFile 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
rewriteFile toolPath (commonContent ++ toolContent)
idrisConfigure _ flags pkgdesc local = do
configureRTS
withLibLBI pkgdesc local $ \_ libcfg -> do
let libAutogenDir = autogenComponentModulesDir local libcfg
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
idrisPreSDist args flags = do
let dir = S.fromFlag (S.sDistDirectory flags)
let verb = S.fromFlag (S.sDistVerbosity flags)
generateVersionModule verb "src" True
generateTargetModule verb "src" "./libs"
generateToolchainModule verb "src" Nothing
preSDist simpleUserHooks args flags
idrisSDist sdist pkgDesc bi hooks flags = do
pkgDesc' <- addGitFiles pkgDesc
sdist pkgDesc' bi hooks flags
where
addGitFiles :: PackageDescription -> IO PackageDescription
addGitFiles pkgDesc = do
files <- gitFiles
return $ pkgDesc { extraSrcFiles = extraSrcFiles pkgDesc ++ files}
gitFiles :: IO [FilePath]
gitFiles = liftM lines (readProcess "git" ["ls-files"] "")
idrisPostSDist args flags desc lbi = do
Control.Exception.catch (do let file = "src" </> "Version_idris" Px.<.> "hs"
let targetFile = "src" </> "Target_idris" Px.<.> "hs"
putStrLn $ "Removing generated modules:\n "
++ file ++ "\n" ++ targetFile
removeFile file
removeFile targetFile)
(\e -> let e' = (e :: SomeException) in return ())
postSDist simpleUserHooks args flags desc lbi
-- -----------------------------------------------------------------------------
-- Build
getVersion :: Args -> S.BuildFlags -> IO HookedBuildInfo
getVersion args flags = do
hash <- gitHash
let buildinfo = (emptyBuildInfo { cppOptions = ["-DVERSION="++hash] }) :: BuildInfo
return (Just buildinfo, [])
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, [(fromString "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
, preSDist = idrisPreSDist
, sDistHook = idrisSDist (sDistHook simpleUserHooks)
, postSDist = idrisPostSDist
, testHook = idrisTestHook
}