module Distribution.Simple.GHC (
configure, getInstalledPackages, build, makefile, installLib, installExe,
ghcOptions,
ghcVerbosityOptions
) where
import Distribution.Simple.GHC.Makefile
import qualified Distribution.Simple.GHC.IPI641 as IPI641
import qualified Distribution.Simple.GHC.IPI642 as IPI642
import Distribution.Simple.Setup ( CopyFlags(..), MakefileFlags(..),
fromFlag, fromFlagOrDefault)
import Distribution.PackageDescription as PD
( PackageDescription(..), BuildInfo(..),
withLib,
Executable(..), withExe, Library(..),
libModules, hcOptions )
import Distribution.InstalledPackageInfo
( InstalledPackageInfo
, parseInstalledPackageInfo )
import qualified Distribution.InstalledPackageInfo as InstalledPackageInfo
( InstalledPackageInfo_(..) )
import Distribution.Simple.PackageIndex
import qualified Distribution.Simple.PackageIndex as PackageIndex
import Distribution.ParseUtils ( ParseResult(..) )
import Distribution.Simple.LocalBuildInfo
( LocalBuildInfo(..), InstallDirs(..) )
import Distribution.Simple.InstallDirs
import Distribution.Simple.BuildPaths
import Distribution.Simple.Utils
import Distribution.Package
( PackageIdentifier, Package(..), PackageName(..) )
import qualified Distribution.ModuleName as ModuleName
import Distribution.Simple.Program
( Program(..), ConfiguredProgram(..), ProgramConfiguration, ProgArg
, ProgramLocation(..), rawSystemProgram, rawSystemProgramConf
, rawSystemProgramStdout, rawSystemProgramStdoutConf, requireProgram
, userMaybeSpecifyPath, programPath, lookupProgram, addKnownProgram
, ghcProgram, ghcPkgProgram, arProgram, ranlibProgram, ldProgram
, gccProgram, stripProgram )
import Distribution.Simple.Compiler
( CompilerFlavor(..), CompilerId(..), Compiler(..), compilerVersion
, OptimisationLevel(..), PackageDB(..), Flag, extensionsToFlags )
import Distribution.Version
( Version(..), VersionRange(..), orLaterVersion )
import Distribution.System
( OS(..), buildOS )
import Distribution.Verbosity
import Distribution.Text
( display, simpleParse )
import Language.Haskell.Extension (Extension(..))
import Control.Monad ( unless, when )
import Data.Char
import Data.List
import Data.Maybe ( catMaybes )
import System.Directory ( removeFile, renameFile,
getDirectoryContents, doesFileExist,
getTemporaryDirectory )
import System.FilePath ( (</>), (<.>), takeExtension,
takeDirectory, replaceExtension, splitExtension )
import System.IO (openFile, IOMode(WriteMode), hClose, hPutStrLn)
import Distribution.Compat.Exception (catchExit, catchIO)
import Distribution.Compat.Permissions (copyPermissions)
import Distribution.Compat.CopyFile
( copyExecutableFile )
configure :: Verbosity -> Maybe FilePath -> Maybe FilePath
-> ProgramConfiguration -> IO (Compiler, ProgramConfiguration)
configure verbosity hcPath hcPkgPath conf = do
(ghcProg, conf') <- requireProgram verbosity ghcProgram
(orLaterVersion (Version [6,4] []))
(userMaybeSpecifyPath "ghc" hcPath conf)
let Just ghcVersion = programVersion ghcProg
(ghcPkgProg, conf'') <- requireProgram verbosity ghcPkgProgram {
programFindLocation = guessGhcPkgFromGhcPath ghcProg
}
(orLaterVersion (Version [0] []))
(userMaybeSpecifyPath "ghc-pkg" hcPkgPath conf')
let Just ghcPkgVersion = programVersion ghcPkgProg
when (ghcVersion /= ghcPkgVersion) $ die $
"Version mismatch between ghc and ghc-pkg: "
++ programPath ghcProg ++ " is version " ++ display ghcVersion ++ " "
++ programPath ghcPkgProg ++ " is version " ++ display ghcPkgVersion
languageExtensions <- getLanguageExtensions verbosity ghcProg
let comp = Compiler {
compilerId = CompilerId GHC ghcVersion,
compilerExtensions = languageExtensions
}
conf''' = configureToolchain ghcProg conf''
return (comp, conf''')
guessGhcPkgFromGhcPath :: ConfiguredProgram -> Verbosity -> IO (Maybe FilePath)
guessGhcPkgFromGhcPath ghcProg verbosity
= do let path = programPath ghcProg
dir = takeDirectory path
versionSuffix = takeVersionSuffix (dropExeExtension path)
guessNormal = dir </> "ghc-pkg" <.> exeExtension
guessVersioned = dir </> ("ghc-pkg" ++ versionSuffix) <.> exeExtension
guesses | null versionSuffix = [guessNormal]
| otherwise = [guessVersioned, guessNormal]
info verbosity $ "looking for package tool: ghc-pkg near compiler in " ++ dir
exists <- mapM doesFileExist guesses
case [ file | (file, True) <- zip guesses exists ] of
[] -> return Nothing
(pkgtool:_) -> do info verbosity $ "found package tool in " ++ pkgtool
return (Just pkgtool)
where takeVersionSuffix :: FilePath -> String
takeVersionSuffix = reverse . takeWhile (`elem ` "0123456789.-") . reverse
dropExeExtension :: FilePath -> FilePath
dropExeExtension filepath =
case splitExtension filepath of
(filepath', extension) | extension == exeExtension -> filepath'
| otherwise -> filepath
configureToolchain :: ConfiguredProgram -> ProgramConfiguration
-> ProgramConfiguration
configureToolchain ghcProg =
addKnownProgram gccProgram {
programFindLocation = findProg gccProgram (baseDir </> "gcc.exe"),
programPostConf = configureGcc
}
. addKnownProgram ldProgram {
programFindLocation = findProg ldProgram (libDir </> "ld.exe"),
programPostConf = configureLd
}
where
compilerDir = takeDirectory (programPath ghcProg)
baseDir = takeDirectory compilerDir
libDir = baseDir </> "gcc-lib"
includeDir = baseDir </> "include" </> "mingw"
isWindows = case buildOS of Windows -> True; _ -> False
findProg :: Program -> FilePath -> Verbosity -> IO (Maybe FilePath)
findProg prog location | isWindows = \_ -> do
exists <- doesFileExist location
if exists then return (Just location) else return Nothing
| otherwise = programFindLocation prog
configureGcc :: Verbosity -> ConfiguredProgram -> IO [ProgArg]
configureGcc
| isWindows = \_ gccProg -> case programLocation gccProg of
FoundOnSystem {} -> return ["-B" ++ libDir, "-I" ++ includeDir]
UserSpecified {} -> return []
| otherwise = \_ _ -> return []
configureLd :: Verbosity -> ConfiguredProgram -> IO [ProgArg]
configureLd verbosity ldProg = do
tempDir <- getTemporaryDirectory
ldx <- withTempFile tempDir ".c" $ \testcfile testchnd ->
withTempFile tempDir ".o" $ \testofile testohnd -> do
hPutStrLn testchnd "int foo() {}"
hClose testchnd; hClose testohnd
rawSystemProgram verbosity ghcProg ["-c", testcfile,
"-o", testofile]
withTempFile tempDir ".o" $ \testofile' testohnd' ->
do
hClose testohnd'
rawSystemProgramStdout verbosity ldProg
["-x", "-r", testofile, "-o", testofile']
return True
`catchIO` (\_ -> return False)
`catchExit` (\_ -> return False)
if ldx
then return ["-x"]
else return []
getLanguageExtensions :: Verbosity -> ConfiguredProgram -> IO [(Extension, Flag)]
getLanguageExtensions verbosity ghcProg
| ghcVersion >= Version [6,7] [] = do
exts <- rawSystemStdout verbosity (programPath ghcProg)
["--supported-languages"]
let readExtension str = do
ext <- simpleParse ("No" ++ str)
case ext of
UnknownExtension _ -> simpleParse str
_ -> return ext
return $ extensionHacks
++ [ (ext, "-X" ++ display ext)
| Just ext <- map readExtension (lines exts) ]
| otherwise = return oldLanguageExtensions
where
Just ghcVersion = programVersion ghcProg
extensionHacks = [ (NamedFieldPuns, "-XRecordPuns")
| ghcVersion >= Version [6,8] []
&& ghcVersion < Version [6,10] [] ]
oldLanguageExtensions :: [(Extension, Flag)]
oldLanguageExtensions =
[(OverlappingInstances , "-fallow-overlapping-instances")
,(TypeSynonymInstances , "-fglasgow-exts")
,(TemplateHaskell , "-fth")
,(ForeignFunctionInterface , "-fffi")
,(NoMonomorphismRestriction , "-fno-monomorphism-restriction")
,(NoMonoPatBinds , "-fno-mono-pat-binds")
,(UndecidableInstances , "-fallow-undecidable-instances")
,(IncoherentInstances , "-fallow-incoherent-instances")
,(Arrows , "-farrows")
,(Generics , "-fgenerics")
,(NoImplicitPrelude , "-fno-implicit-prelude")
,(ImplicitParams , "-fimplicit-params")
,(CPP , "-cpp")
,(BangPatterns , "-fbang-patterns")
,(KindSignatures , fglasgowExts)
,(RecursiveDo , fglasgowExts)
,(ParallelListComp , fglasgowExts)
,(MultiParamTypeClasses , fglasgowExts)
,(FunctionalDependencies , fglasgowExts)
,(Rank2Types , fglasgowExts)
,(RankNTypes , fglasgowExts)
,(PolymorphicComponents , fglasgowExts)
,(ExistentialQuantification , fglasgowExts)
,(ScopedTypeVariables , "-fscoped-type-variables")
,(FlexibleContexts , fglasgowExts)
,(FlexibleInstances , fglasgowExts)
,(EmptyDataDecls , fglasgowExts)
,(PatternGuards , fglasgowExts)
,(GeneralizedNewtypeDeriving , fglasgowExts)
,(MagicHash , fglasgowExts)
,(UnicodeSyntax , fglasgowExts)
,(PatternSignatures , fglasgowExts)
,(UnliftedFFITypes , fglasgowExts)
,(LiberalTypeSynonyms , fglasgowExts)
,(TypeOperators , fglasgowExts)
,(GADTs , fglasgowExts)
,(RelaxedPolyRec , fglasgowExts)
,(ExtendedDefaultRules , "-fextended-default-rules")
,(UnboxedTuples , fglasgowExts)
,(DeriveDataTypeable , fglasgowExts)
,(ConstrainedClassMethods , fglasgowExts)
]
where
fglasgowExts = "-fglasgow-exts"
getInstalledPackages :: Verbosity -> PackageDB -> ProgramConfiguration
-> IO (PackageIndex InstalledPackageInfo)
getInstalledPackages verbosity packagedb conf = do
let packagedbs = case packagedb of
GlobalPackageDB -> [GlobalPackageDB]
_ -> [GlobalPackageDB, packagedb]
pkgss <- getInstalledPackages' verbosity packagedbs conf
let pkgs = concatMap snd pkgss
Just ghcProg = lookupProgram ghcProgram conf
compilerDir = takeDirectory (programPath ghcProg)
topDir = takeDirectory compilerDir
pkgs' = map (substTopDir topDir) pkgs
pi1 = PackageIndex.fromList pkgs'
rtsPackages = lookupPackageName pi1 (PackageName "rts")
rtsPackages' = map removeMingwIncludeDir rtsPackages
pi2 = pi1 `merge` fromList rtsPackages'
return pi2
removeMingwIncludeDir :: InstalledPackageInfo -> InstalledPackageInfo
removeMingwIncludeDir pkg =
let ids = InstalledPackageInfo.includeDirs pkg
ids' = filter (not . ("mingw" `isSuffixOf`)) ids
in pkg { InstalledPackageInfo.includeDirs = ids' }
getInstalledPackages' :: Verbosity -> [PackageDB] -> ProgramConfiguration
-> IO [(PackageDB, [InstalledPackageInfo])]
getInstalledPackages' verbosity packagedbs conf
| ghcVersion >= Version [6,9] [] =
sequence
[ do str <- rawSystemProgramStdoutConf verbosity ghcPkgProgram conf
["dump", packageDbGhcPkgFlag packagedb]
`catchExit` \_ -> die $ "ghc-pkg dump failed"
case parsePackages str of
Left ok -> return (packagedb, ok)
_ -> die "failed to parse output of 'ghc-pkg dump'"
| packagedb <- packagedbs ]
where
parsePackages str =
let parsed = map parseInstalledPackageInfo (splitPkgs str)
in case [ msg | ParseFailed msg <- parsed ] of
[] -> Left [ pkg | ParseOk _ pkg <- parsed ]
msgs -> Right msgs
Just ghcProg = lookupProgram ghcProgram conf
Just ghcVersion = programVersion ghcProg
splitPkgs :: String -> [String]
splitPkgs = map unlines . splitWith ("---" ==) . lines
where
splitWith :: (a -> Bool) -> [a] -> [[a]]
splitWith p xs = ys : case zs of
[] -> []
_:ws -> splitWith p ws
where (ys,zs) = break p xs
packageDbGhcPkgFlag GlobalPackageDB = "--global"
packageDbGhcPkgFlag UserPackageDB = "--user"
packageDbGhcPkgFlag (SpecificPackageDB path) = "--package-conf=" ++ path
getInstalledPackages' verbosity packagedbs conf = do
str <- rawSystemProgramStdoutConf verbosity ghcPkgProgram conf ["list"]
let pkgFiles = [ init line | line <- lines str, last line == ':' ]
dbFile packagedb = case (packagedb, pkgFiles) of
(GlobalPackageDB, global:_) -> return $ Just global
(UserPackageDB, _global:user:_) -> return $ Just user
(UserPackageDB, _global:_) -> return $ Nothing
(SpecificPackageDB specific, _) -> return $ Just specific
_ -> die "cannot read ghc-pkg package listing"
pkgFiles' <- mapM dbFile packagedbs
sequence [ withFileContents file $ \content -> do
pkgs <- readPackages file content
return (db, pkgs)
| (db , Just file) <- zip packagedbs pkgFiles' ]
where
readPackages
| ghcVersion >= Version [6,4,2] []
= \file content -> case reads content of
[(pkgs, _)] -> return (map IPI642.toCurrent pkgs)
_ -> failToRead file
| otherwise
= \file content -> case reads content of
[(pkgs, _)] -> return (map IPI641.toCurrent pkgs)
_ -> failToRead file
Just ghcProg = lookupProgram ghcProgram conf
Just ghcVersion = programVersion ghcProg
failToRead file = die $ "cannot read ghc package database " ++ file
substTopDir :: FilePath -> InstalledPackageInfo -> InstalledPackageInfo
substTopDir topDir ipo
= ipo {
InstalledPackageInfo.importDirs
= map f (InstalledPackageInfo.importDirs ipo),
InstalledPackageInfo.libraryDirs
= map f (InstalledPackageInfo.libraryDirs ipo),
InstalledPackageInfo.includeDirs
= map f (InstalledPackageInfo.includeDirs ipo),
InstalledPackageInfo.frameworkDirs
= map f (InstalledPackageInfo.frameworkDirs ipo),
InstalledPackageInfo.haddockInterfaces
= map f (InstalledPackageInfo.haddockInterfaces ipo),
InstalledPackageInfo.haddockHTMLs
= map f (InstalledPackageInfo.haddockHTMLs ipo)
}
where f ('$':'t':'o':'p':'d':'i':'r':rest) = topDir ++ rest
f x = x
build :: PackageDescription -> LocalBuildInfo -> Verbosity -> IO ()
build pkg_descr lbi verbosity = do
let pref = buildDir lbi
pkgid = packageId pkg_descr
runGhcProg = rawSystemProgramConf verbosity ghcProgram (withPrograms lbi)
ifVanillaLib forceVanilla = when (forceVanilla || withVanillaLib lbi)
ifProfLib = when (withProfLib lbi)
ifSharedLib = when (withSharedLib lbi)
ifGHCiLib = when (withGHCiLib lbi && withVanillaLib lbi)
withLib pkg_descr () $ \lib -> do
info verbosity "Building library..."
libBi <- hackThreadedFlag verbosity
(compiler lbi) (withProfLib lbi) (libBuildInfo lib)
let libTargetDir = pref
forceVanillaLib = TemplateHaskell `elem` extensions libBi
createDirectoryIfMissingVerbose verbosity True libTargetDir
let ghcArgs =
["-package-name", display pkgid ]
++ constructGHCCmdLine lbi libBi libTargetDir verbosity
++ map display (libModules pkg_descr)
ghcArgsProf = ghcArgs
++ ["-prof",
"-hisuf", "p_hi",
"-osuf", "p_o"
]
++ ghcProfOptions libBi
ghcArgsShared = ghcArgs
++ ["-dynamic",
"-hisuf", "dyn_hi",
"-osuf", "dyn_o", "-fPIC"
]
++ ghcSharedOptions libBi
unless (null (libModules pkg_descr)) $
do ifVanillaLib forceVanillaLib (runGhcProg ghcArgs)
ifProfLib (runGhcProg ghcArgsProf)
ifSharedLib (runGhcProg ghcArgsShared)
unless (null (cSources libBi)) $ do
info verbosity "Building C Sources..."
sequence_ [do let (odir,args) = constructCcCmdLine lbi libBi pref
filename verbosity
createDirectoryIfMissingVerbose verbosity True odir
runGhcProg args
ifSharedLib (runGhcProg (args ++ ["-fPIC", "-osuf dyn_o"]))
| filename <- cSources libBi]
info verbosity "Linking..."
let cObjs = map (`replaceExtension` objExtension) (cSources libBi)
cSharedObjs = map (`replaceExtension` ("dyn_" ++ objExtension)) (cSources libBi)
vanillaLibFilePath = libTargetDir </> mkLibName pkgid
profileLibFilePath = libTargetDir </> mkProfLibName pkgid
sharedLibFilePath = libTargetDir </> mkSharedLibName pkgid
(compilerId (compiler lbi))
ghciLibFilePath = libTargetDir </> mkGHCiLibName pkgid
stubObjs <- fmap catMaybes $ sequence
[ findFileWithExtension [objExtension] [libTargetDir]
(ModuleName.toFilePath x ++"_stub")
| x <- libModules pkg_descr ]
stubProfObjs <- fmap catMaybes $ sequence
[ findFileWithExtension ["p_" ++ objExtension] [libTargetDir]
(ModuleName.toFilePath x ++"_stub")
| x <- libModules pkg_descr ]
stubSharedObjs <- fmap catMaybes $ sequence
[ findFileWithExtension ["dyn_" ++ objExtension] [libTargetDir]
(ModuleName.toFilePath x ++"_stub")
| x <- libModules pkg_descr ]
hObjs <- getHaskellObjects pkg_descr libBi lbi
pref objExtension True
hProfObjs <-
if (withProfLib lbi)
then getHaskellObjects pkg_descr libBi lbi
pref ("p_" ++ objExtension) True
else return []
hSharedObjs <-
if (withSharedLib lbi)
then getHaskellObjects pkg_descr libBi lbi
pref ("dyn_" ++ objExtension) False
else return []
unless (null hObjs && null cObjs && null stubObjs) $ do
sequence_
[ removeFile libFilePath `catchIO` \_ -> return ()
| libFilePath <- [vanillaLibFilePath, profileLibFilePath
,sharedLibFilePath, ghciLibFilePath] ]
let arVerbosity | verbosity >= deafening = "v"
| verbosity >= normal = ""
| otherwise = "c"
arArgs = ["q"++ arVerbosity]
++ [vanillaLibFilePath]
arObjArgs =
hObjs
++ map (pref </>) cObjs
++ stubObjs
arProfArgs = ["q"++ arVerbosity]
++ [profileLibFilePath]
arProfObjArgs =
hProfObjs
++ map (pref </>) cObjs
++ stubProfObjs
ldArgs = ["-r"]
++ ["-o", ghciLibFilePath <.> "tmp"]
ldObjArgs =
hObjs
++ map (pref </>) cObjs
++ stubObjs
ghcSharedObjArgs =
hSharedObjs
++ map (pref </>) cSharedObjs
++ stubSharedObjs
ghcSharedLinkArgs =
[ "-no-auto-link-packages",
"-shared",
"-dynamic",
"-o", sharedLibFilePath ]
++ ghcSharedObjArgs
++ ["-package-name", display pkgid ]
++ (concat [ ["-package", display pkg] | pkg <- packageDeps lbi ])
++ ["-l"++extraLib | extraLib <- extraLibs libBi]
++ ["-L"++extraLibDir | extraLibDir <- extraLibDirs libBi]
runLd ldLibName args = do
exists <- doesFileExist ldLibName
rawSystemProgramConf verbosity ldProgram (withPrograms lbi)
(args ++ if exists then [ldLibName] else [])
renameFile (ldLibName <.> "tmp") ldLibName
runAr = rawSystemProgramConf verbosity arProgram (withPrograms lbi)
maxCommandLineSize = 30 * 1024
ifVanillaLib False $ xargs maxCommandLineSize
runAr arArgs arObjArgs
ifProfLib $ xargs maxCommandLineSize
runAr arProfArgs arProfObjArgs
ifGHCiLib $ xargs maxCommandLineSize
(runLd ghciLibFilePath) ldArgs ldObjArgs
ifSharedLib $ runGhcProg ghcSharedLinkArgs
withExe pkg_descr $ \exe@Executable { exeName = exeName', modulePath = modPath } -> do
info verbosity $ "Building executable: " ++ exeName' ++ "..."
exeBi <- hackThreadedFlag verbosity
(compiler lbi) (withProfExe lbi) (buildInfo exe)
let exeNameReal = exeName' <.>
(if null $ takeExtension exeName' then exeExtension else "")
let targetDir = pref </> exeName'
let exeDir = targetDir </> (exeName' ++ "-tmp")
createDirectoryIfMissingVerbose verbosity True targetDir
createDirectoryIfMissingVerbose verbosity True exeDir
unless (null (cSources exeBi)) $ do
info verbosity "Building C Sources."
sequence_ [do let (odir,args) = constructCcCmdLine lbi exeBi
exeDir filename verbosity
createDirectoryIfMissingVerbose verbosity True odir
runGhcProg args
| filename <- cSources exeBi]
srcMainFile <- findFile (exeDir : hsSourceDirs exeBi) modPath
let cObjs = map (`replaceExtension` objExtension) (cSources exeBi)
let binArgs linkExe profExe =
(if linkExe
then ["-o", targetDir </> exeNameReal]
else ["-c"])
++ constructGHCCmdLine lbi exeBi exeDir verbosity
++ [exeDir </> x | x <- cObjs]
++ [srcMainFile]
++ ["-optl" ++ opt | opt <- PD.ldOptions exeBi]
++ ["-l"++lib | lib <- extraLibs exeBi]
++ ["-L"++libDir | libDir <- extraLibDirs exeBi]
++ concat [["-framework", f] | f <- PD.frameworks exeBi]
++ if profExe
then ["-prof",
"-hisuf", "p_hi",
"-osuf", "p_o"
] ++ ghcProfOptions exeBi
else []
when (withProfExe lbi && TemplateHaskell `elem` extensions exeBi)
(runGhcProg (binArgs False False))
runGhcProg (binArgs True (withProfExe lbi))
hackThreadedFlag :: Verbosity -> Compiler -> Bool -> BuildInfo -> IO BuildInfo
hackThreadedFlag verbosity comp prof bi
| not mustFilterThreaded = return bi
| otherwise = do
warn verbosity $ "The ghc flag '-threaded' is not compatible with "
++ "profiling in ghc-6.8 and older. It will be disabled."
return bi { options = filterHcOptions (/= "-threaded") (options bi) }
where
mustFilterThreaded = prof && compilerVersion comp < Version [6, 10] []
&& "-threaded" `elem` hcOptions GHC bi
filterHcOptions p hcoptss =
[ (hc, if hc == GHC then filter p opts else opts)
| (hc, opts) <- hcoptss ]
getHaskellObjects :: PackageDescription -> BuildInfo -> LocalBuildInfo
-> FilePath -> String -> Bool -> IO [FilePath]
getHaskellObjects pkg_descr _ lbi pref wanted_obj_ext allow_split_objs
| splitObjs lbi && allow_split_objs = do
let dirs = [ pref </> (ModuleName.toFilePath x ++ "_split")
| x <- libModules pkg_descr ]
objss <- mapM getDirectoryContents dirs
let objs = [ dir </> obj
| (objs',dir) <- zip objss dirs, obj <- objs',
let obj_ext = takeExtension obj,
'.':wanted_obj_ext == obj_ext ]
return objs
| otherwise =
return [ pref </> ModuleName.toFilePath x <.> wanted_obj_ext
| x <- libModules pkg_descr ]
constructGHCCmdLine
:: LocalBuildInfo
-> BuildInfo
-> FilePath
-> Verbosity
-> [String]
constructGHCCmdLine lbi bi odir verbosity =
["--make"]
++ ghcVerbosityOptions verbosity
++ ghcOptions lbi bi odir
ghcVerbosityOptions :: Verbosity -> [String]
ghcVerbosityOptions verbosity
| verbosity >= deafening = ["-v"]
| verbosity >= normal = []
| otherwise = ["-w", "-v0"]
ghcOptions :: LocalBuildInfo -> BuildInfo -> FilePath -> [String]
ghcOptions lbi bi odir
= ["-hide-all-packages"]
++ (case withPackageDB lbi of
GlobalPackageDB -> ["-no-user-package-conf"]
UserPackageDB -> []
SpecificPackageDB db -> ["-no-user-package-conf"
,"-package-conf", db])
++ (if splitObjs lbi then ["-split-objs"] else [])
++ ["-i"]
++ ["-i" ++ odir]
++ ["-i" ++ l | l <- nub (hsSourceDirs bi)]
++ ["-i" ++ autogenModulesDir lbi]
++ ["-I" ++ autogenModulesDir lbi]
++ ["-I" ++ odir]
++ ["-I" ++ dir | dir <- PD.includeDirs bi]
++ ["-optP" ++ opt | opt <- cppOptions bi]
++ [ "-optP-include", "-optP"++ (autogenModulesDir lbi </> cppHeaderName) ]
++ [ "-#include \"" ++ inc ++ "\"" | inc <- PD.includes bi ]
++ [ "-odir", odir, "-hidir", odir ]
++ (if compilerVersion c >= Version [6,8] []
then ["-stubdir", odir] else [])
++ (concat [ ["-package", display pkg] | pkg <- packageDeps lbi ])
++ (case withOptimization lbi of
NoOptimisation -> []
NormalOptimisation -> ["-O"]
MaximumOptimisation -> ["-O2"])
++ hcOptions GHC bi
++ extensionsToFlags c (extensions bi)
where c = compiler lbi
constructCcCmdLine :: LocalBuildInfo -> BuildInfo -> FilePath
-> FilePath -> Verbosity -> (FilePath,[String])
constructCcCmdLine lbi bi pref filename verbosity
= let odir | compilerVersion (compiler lbi) >= Version [6,4,1] [] = pref
| otherwise = pref </> takeDirectory filename
in
(odir,
ghcCcOptions lbi bi odir
++ (if verbosity >= deafening then ["-v"] else [])
++ ["-c",filename])
ghcCcOptions :: LocalBuildInfo -> BuildInfo -> FilePath -> [String]
ghcCcOptions lbi bi odir
= ["-I" ++ dir | dir <- PD.includeDirs bi]
++ (case withPackageDB lbi of
SpecificPackageDB db -> ["-package-conf", db]
_ -> [])
++ concat [ ["-package", display pkg] | pkg <- packageDeps lbi ]
++ ["-optc" ++ opt | opt <- PD.ccOptions bi]
++ (case withOptimization lbi of
NoOptimisation -> []
_ -> ["-optc-O2"])
++ ["-odir", odir]
mkGHCiLibName :: PackageIdentifier -> String
mkGHCiLibName lib = "HS" ++ display lib <.> "o"
makefile :: PackageDescription -> LocalBuildInfo -> MakefileFlags -> IO ()
makefile pkg_descr lbi flags = do
let file = fromFlagOrDefault "Makefile"(makefileFile flags)
verbosity = fromFlag (makefileVerbosity flags)
targetExists <- doesFileExist file
when targetExists $
die ("Not overwriting existing copy of " ++ file)
h <- openFile file WriteMode
let Just lib = library pkg_descr
bi = libBuildInfo lib
packageIdStr = display (packageId pkg_descr)
(arProg, _) <- requireProgram verbosity arProgram AnyVersion
(withPrograms lbi)
(ldProg, _) <- requireProgram verbosity ldProgram AnyVersion
(withPrograms lbi)
let builddir = buildDir lbi
Just ghcProg = lookupProgram ghcProgram (withPrograms lbi)
Just ghcVersion = programVersion ghcProg
let decls = [
("modules", unwords (map display (PD.exposedModules lib ++ otherModules bi))),
("GHC", programPath ghcProg),
("GHC_VERSION", (display (compilerVersion (compiler lbi)))),
("VANILLA_WAY", if withVanillaLib lbi then "YES" else "NO"),
("WAYS", (if withProfLib lbi then "p " else "") ++ (if withSharedLib lbi then "dyn" else "")),
("odir", builddir),
("package", packageIdStr),
("GHC_OPTS", unwords $ programArgs ghcProg
++ ["-package-name", packageIdStr ]
++ ghcOptions lbi bi (buildDir lbi)),
("MAKEFILE", file),
("C_SRCS", unwords (cSources bi)),
("GHC_CC_OPTS", unwords (ghcCcOptions lbi bi (buildDir lbi))),
("GHCI_LIB", if withGHCiLib lbi
then builddir </> mkGHCiLibName (packageId pkg_descr)
else ""),
("soext", dllExtension),
("LIB_LD_OPTS", unwords (["-package-name", packageIdStr]
++ concat [ ["-package", display pkg] | pkg <- packageDeps lbi ]
++ ["-l"++libName | libName <- extraLibs bi]
++ ["-L"++libDir | libDir <- extraLibDirs bi])),
("AR", programPath arProg),
("LD", programPath ldProg ++ concat [" " ++ arg | arg <- programArgs ldProg ]),
("GENERATE_DOT_DEPEND", if ghcVersion >= Version [6,9] []
then "-dep-makefile $(odir)/.depend"
else "-optdep-f -optdep$(odir)/.depend")
]
mkRules srcdir = [
"$(odir_)%.$(osuf) : " ++ srcdir ++ "/%.hs",
"\t$(GHC) $(GHC_OPTS) -c $< -o $@ -ohi $(basename $@).$(hisuf)",
"",
"$(odir_)%.$(osuf) : " ++ srcdir ++ "/%.lhs",
"\t$(GHC) $(GHC_OPTS) -c $< -o $@ -ohi $(basename $@).$(hisuf)",
"",
"$(odir_)%.$(osuf) : " ++ srcdir ++ "/%.$(way_)s",
"\t@$(RM) $@",
"\t$(GHC) $(GHC_CC_OPTS) -c $< -o $@",
"",
"$(odir_)%.$(osuf) : " ++ srcdir ++ "/%.S",
"\t@$(RM) $@",
"\t$(GHC) $(GHC_CC_OPTS) -c $< -o $@",
"",
"$(odir_)%.$(osuf)-boot : " ++ srcdir ++ "/%.hs-boot",
"\t$(GHC) $(GHC_OPTS) -c $< -o $@ -ohi $(basename $@).$(way_)hi-boot",
"",
"$(odir_)%.$(osuf)-boot : " ++ srcdir ++ "/%.lhs-boot",
"\t$(GHC) $(GHC_OPTS) -c $< -o $@ -ohi $(basename $@).$(way_)hi-boot",
""]
vars = ["WAY_p_OPTS = -prof",
"WAY_dyn_OPTS = -fPIC -dynamic",
"WAY_dyn_CC_OPTS = -fPIC",
"",
"ifneq \"$(way)\" \"\"",
"way_ := $(way)_",
"_way := _$(way)",
"GHC_OPTS += $(WAY_$(way)_OPTS)",
"GHC_OPTS += -hisuf $(way_)hi -hcsuf $(way_)hc -osuf $(osuf)",
"GHC_CC_OPTS += $(WAY_$(way)_CC_OPTS)",
"endif",
"",
"osuf = $(way_)o",
"hisuf = $(way_)hi",
"",
"ifneq \"$(odir)\" \"\"",
"odir_ = $(odir)/",
"else",
"odir_ =",
"endif",
""]
rules = concatMap mkRules (hsSourceDirs bi)
hPutStrLn h "# DO NOT EDIT! Automatically generated by Cabal\n"
hPutStrLn h $ unlines (map (\(a,b)-> a ++ " = " ++ munge b) decls)
hPutStrLn h $ unlines vars
hPutStrLn h makefileTemplate
hPutStrLn h $ unlines rules
hClose h
where
munge "" = ""
munge ('#':s) = '\\':'#':munge s
munge ('\\':s) = '/':munge s
munge (c:s) = c : munge s
installExe :: CopyFlags
-> LocalBuildInfo
-> InstallDirs FilePath
-> InstallDirs FilePath
-> FilePath
-> (FilePath, FilePath)
-> PackageDescription
-> IO ()
installExe flags lbi installDirs pretendInstallDirs buildPref (progprefix, progsuffix) pkg_descr
= do let verbosity = fromFlag (copyVerbosity flags)
useWrapper = fromFlag (copyUseWrapper flags)
binDir = bindir installDirs
createDirectoryIfMissingVerbose verbosity True binDir
withExe pkg_descr $ \Executable { exeName = e } -> do
let exeFileName = e <.> exeExtension
exeDynFileName = e <.> "dyn" <.> exeExtension
fixedExeBaseName = progprefix ++ e ++ progsuffix
installBinary dest = do
copyExe verbosity
(buildPref </> e </> exeFileName) (dest <.> exeExtension)
exists <- doesFileExist (buildPref </> e </> exeDynFileName)
if exists then
copyFileVerbose verbosity
(buildPref </> e </> exeDynFileName) (dest <.> "dyn" <.> exeExtension)
else
return ()
stripExe verbosity lbi exeFileName (dest <.> exeExtension)
if useWrapper
then do
let libExecDir = libexecdir installDirs
pretendLibExecDir = libexecdir pretendInstallDirs
absExeFileName =
libExecDir </> fixedExeBaseName <.> exeExtension
pretendAbsExeFileName =
pretendLibExecDir </> fixedExeBaseName <.> exeExtension
wrapperFileName = binDir </> fixedExeBaseName
myPkgId = packageId (PD.package (localPkgDescr lbi))
myCompilerId = compilerId (compiler lbi)
env = (ExecutableNameVar,
toPathTemplate pretendAbsExeFileName)
: fullPathTemplateEnv myPkgId myCompilerId
pretendInstallDirs
createDirectoryIfMissingVerbose verbosity True libExecDir
installBinary (libExecDir </> fixedExeBaseName)
wrapperTemplate <- readFile (e <.> "wrapper")
let wrapper = fromPathTemplate
$ substPathTemplate env
$ toPathTemplate wrapperTemplate
writeFileAtomic wrapperFileName wrapper
copyPermissions absExeFileName wrapperFileName
else do
installBinary (binDir </> fixedExeBaseName)
copyExe :: Verbosity -> FilePath -> FilePath -> IO ()
copyExe verbosity src dest = do
info verbosity ("copy " ++ src ++ " to " ++ dest)
copyExecutableFile src dest
stripExe :: Verbosity -> LocalBuildInfo -> FilePath -> FilePath -> IO ()
stripExe verbosity lbi name path = when (stripExes lbi) $
case lookupProgram stripProgram (withPrograms lbi) of
Just strip -> rawSystemProgram verbosity strip args
Nothing -> unless (buildOS == Windows) $
warn verbosity $ "Unable to strip executable '" ++ name
++ "' (missing the 'strip' program)"
where
args = path : case buildOS of
OSX -> ["-x"]
_ -> []
installLib :: CopyFlags
-> LocalBuildInfo
-> FilePath
-> FilePath
-> FilePath
-> PackageDescription -> IO ()
installLib flags lbi targetDir dynlibTargetDir builtDir
pkg@PackageDescription{library=Just lib} =
unless (fromFlag $ copyInPlace flags) $ do
let verbosity = fromFlag (copyVerbosity flags)
copy src dst n = do
createDirectoryIfMissingVerbose verbosity True dst
copyFileVerbose verbosity (src </> n) (dst </> n)
copyModuleFiles ext =
smartCopySources verbosity [builtDir] targetDir
(libModules pkg) [ext]
ifVanilla $ copyModuleFiles "hi"
ifProf $ copyModuleFiles "p_hi"
ifVanilla $ copy builtDir targetDir vanillaLibName
ifProf $ copy builtDir targetDir profileLibName
ifGHCi $ copy builtDir targetDir ghciLibName
ifShared $ copy builtDir dynlibTargetDir sharedLibName
ifVanilla $ updateLibArchive verbosity lbi
(targetDir </> vanillaLibName)
ifProf $ updateLibArchive verbosity lbi
(targetDir </> profileLibName)
where
vanillaLibName = mkLibName pkgid
profileLibName = mkProfLibName pkgid
ghciLibName = mkGHCiLibName pkgid
sharedLibName = mkSharedLibName pkgid (compilerId (compiler lbi))
pkgid = packageId pkg
hasLib = not $ null (libModules pkg)
&& null (cSources (libBuildInfo lib))
ifVanilla = when (hasLib && withVanillaLib lbi)
ifProf = when (hasLib && withProfLib lbi)
ifGHCi = when (hasLib && withGHCiLib lbi)
ifShared = when (hasLib && withSharedLib lbi)
installLib _ _ _ _ _ PackageDescription{library=Nothing}
= die $ "Internal Error. installLibGHC called with no library."
updateLibArchive :: Verbosity -> LocalBuildInfo -> FilePath -> IO ()
updateLibArchive verbosity lbi path =
case lookupProgram ranlibProgram (withPrograms lbi) of
Just ranlib -> rawSystemProgram verbosity ranlib [path]
Nothing -> case lookupProgram arProgram (withPrograms lbi) of
Just ar -> rawSystemProgram verbosity ar ["-s", path]
Nothing -> warn verbosity $
"Unable to generate a symbol index for the static "
++ "library '" ++ path
++ "' (missing the 'ranlib' and 'ar' programs)"