module Distribution.Simple.Configure (configure,
writePersistBuildConfig,
getPersistBuildConfig,
checkPersistBuildConfig,
maybeGetPersistBuildConfig,
localBuildInfoFile,
getInstalledPackages,
configCompiler, configCompilerAux,
ccLdOptionsBuildInfo,
tryGetConfigStateFile,
checkForeignDeps,
)
where
import Distribution.Simple.Compiler
( CompilerFlavor(..), Compiler(compilerId), compilerFlavor, compilerVersion
, showCompilerId, unsupportedExtensions, PackageDB(..), PackageDBStack )
import Distribution.Package
( PackageName(PackageName), PackageIdentifier(PackageIdentifier), PackageId
, packageName, packageVersion, Package(..)
, Dependency(Dependency), simplifyDependency
, InstalledPackageId(..) )
import Distribution.InstalledPackageInfo as Installed
( InstalledPackageInfo, InstalledPackageInfo_(..)
, emptyInstalledPackageInfo )
import qualified Distribution.Simple.PackageIndex as PackageIndex
import Distribution.Simple.PackageIndex (PackageIndex)
import Distribution.PackageDescription as PD
( PackageDescription(..), GenericPackageDescription(..)
, Library(..), hasLibs, Executable(..), BuildInfo(..)
, HookedBuildInfo, updatePackageDescription, allBuildInfo
, FlagName(..) )
import Distribution.PackageDescription.Configuration
( finalizePackageDescription )
import Distribution.PackageDescription.Check
( PackageCheck(..), checkPackage, checkPackageFiles )
import Distribution.Simple.Program
( Program(..), ProgramLocation(..), ConfiguredProgram(..)
, ProgramConfiguration, defaultProgramConfiguration
, configureAllKnownPrograms, knownPrograms, lookupKnownProgram
, userSpecifyArgss, userSpecifyPaths
, lookupProgram, requireProgram, requireProgramVersion
, pkgConfigProgram, gccProgram, rawSystemProgramStdoutConf )
import Distribution.Simple.Setup
( ConfigFlags(..), CopyDest(..), fromFlag, fromFlagOrDefault, flagToMaybe )
import Distribution.Simple.InstallDirs
( InstallDirs(..), defaultInstallDirs, combineInstallDirs )
import Distribution.Simple.LocalBuildInfo
( LocalBuildInfo(..), ComponentLocalBuildInfo(..)
, absoluteInstallDirs, prefixRelativeInstallDirs )
import Distribution.Simple.Utils
( die, warn, info, setupMessage, createDirectoryIfMissingVerbose
, intercalate, cabalVersion, cabalBootstrapping
, withFileContents, writeFileAtomic
, withTempFile )
import Distribution.System
( OS(..), buildOS, buildPlatform )
import Distribution.Version
( Version(..), anyVersion, orLaterVersion, withinRange
, isSpecificVersion, isAnyVersion
, LowerBound(..), asVersionIntervals )
import Distribution.Verbosity
( Verbosity, lessVerbose )
import qualified Distribution.Simple.GHC as GHC
import qualified Distribution.Simple.JHC as JHC
import qualified Distribution.Simple.LHC as LHC
import qualified Distribution.Simple.NHC as NHC
import qualified Distribution.Simple.Hugs as Hugs
import Control.Monad
( when, unless, foldM, filterM )
import Data.List
( nub, partition, isPrefixOf, inits )
import Data.Maybe
( fromMaybe, isNothing )
import Data.Monoid
( Monoid(..) )
import System.Directory
( doesFileExist, getModificationTime, createDirectoryIfMissing, getTemporaryDirectory )
import System.Exit
( ExitCode(..), exitWith )
import System.FilePath
( (</>), isAbsolute )
import qualified System.Info
( compilerName, compilerVersion )
import System.IO
( hPutStrLn, stderr, hClose )
import Distribution.Text
( Text(disp), display, simpleParse )
import Text.PrettyPrint.HughesPJ
( comma, punctuate, render, nest, sep )
import Distribution.Compat.Exception ( catchExit, catchIO )
import Prelude hiding (catch)
tryGetConfigStateFile :: (Read a) => FilePath -> IO (Either String a)
tryGetConfigStateFile filename = do
exists <- doesFileExist filename
if not exists
then return (Left missing)
else withFileContents filename $ \str ->
case lines str of
[headder, rest] -> case checkHeader headder of
Just msg -> return (Left msg)
Nothing -> case reads rest of
[(bi,_)] -> return (Right bi)
_ -> return (Left cantParse)
_ -> return (Left cantParse)
where
checkHeader :: String -> Maybe String
checkHeader header = case parseHeader header of
Just (cabalId, compId)
| cabalId
== currentCabalId -> Nothing
| otherwise -> Just (badVersion cabalId compId)
Nothing -> Just cantParse
missing = "Run the 'configure' command first."
cantParse = "Saved package config file seems to be corrupt. "
++ "Try re-running the 'configure' command."
badVersion cabalId compId
= "You need to re-run the 'configure' command. "
++ "The version of Cabal being used has changed (was "
++ display cabalId ++ ", now "
++ display currentCabalId ++ ")."
++ badcompiler compId
badcompiler compId | compId == currentCompilerId = ""
| otherwise
= " Additionally the compiler is different (was "
++ display compId ++ ", now "
++ display currentCompilerId
++ ") which is probably the cause of the problem."
tryGetPersistBuildConfig :: FilePath -> IO (Either String LocalBuildInfo)
tryGetPersistBuildConfig distPref
= tryGetConfigStateFile (localBuildInfoFile distPref)
getPersistBuildConfig :: FilePath -> IO LocalBuildInfo
getPersistBuildConfig distPref = do
lbi <- tryGetPersistBuildConfig distPref
either die return lbi
maybeGetPersistBuildConfig :: FilePath -> IO (Maybe LocalBuildInfo)
maybeGetPersistBuildConfig distPref = do
lbi <- tryGetPersistBuildConfig distPref
return $ either (const Nothing) Just lbi
writePersistBuildConfig :: FilePath -> LocalBuildInfo -> IO ()
writePersistBuildConfig distPref lbi = do
createDirectoryIfMissing False distPref
writeFileAtomic (localBuildInfoFile distPref)
(showHeader pkgid ++ '\n' : show lbi)
where
pkgid = packageId (localPkgDescr lbi)
showHeader :: PackageIdentifier -> String
showHeader pkgid =
"Saved package config for " ++ display pkgid
++ " written by " ++ display currentCabalId
++ " using " ++ display currentCompilerId
where
currentCabalId :: PackageIdentifier
currentCabalId = PackageIdentifier (PackageName "Cabal") currentVersion
where currentVersion | cabalBootstrapping = Version [0] []
| otherwise = cabalVersion
currentCompilerId :: PackageIdentifier
currentCompilerId = PackageIdentifier (PackageName System.Info.compilerName)
System.Info.compilerVersion
parseHeader :: String -> Maybe (PackageIdentifier, PackageIdentifier)
parseHeader header = case words header of
["Saved", "package", "config", "for", pkgid,
"written", "by", cabalid, "using", compilerid]
-> case (simpleParse pkgid :: Maybe PackageIdentifier,
simpleParse cabalid,
simpleParse compilerid) of
(Just _,
Just cabalid',
Just compilerid') -> Just (cabalid', compilerid')
_ -> Nothing
_ -> Nothing
checkPersistBuildConfig :: FilePath -> FilePath -> IO ()
checkPersistBuildConfig distPref pkg_descr_file = do
t0 <- getModificationTime pkg_descr_file
t1 <- getModificationTime $ localBuildInfoFile distPref
when (t0 > t1) $
die (pkg_descr_file ++ " has been changed, please re-configure.")
localBuildInfoFile :: FilePath -> FilePath
localBuildInfoFile distPref = distPref </> "setup-config"
configure :: (GenericPackageDescription, HookedBuildInfo)
-> ConfigFlags -> IO LocalBuildInfo
configure (pkg_descr0, pbi) cfg
= do let distPref = fromFlag (configDistPref cfg)
verbosity = fromFlag (configVerbosity cfg)
setupMessage verbosity "Configuring" (packageId pkg_descr0)
createDirectoryIfMissingVerbose (lessVerbose verbosity) True distPref
let programsConfig = userSpecifyArgss (configProgramArgs cfg)
. userSpecifyPaths (configProgramPaths cfg)
$ configPrograms cfg
userInstall = fromFlag (configUserInstall cfg)
packageDbs = implicitPackageDbStack userInstall
(flagToMaybe $ configPackageDB cfg)
(comp, programsConfig') <- configCompiler
(flagToMaybe $ configHcFlavor cfg)
(flagToMaybe $ configHcPath cfg) (flagToMaybe $ configHcPkg cfg)
programsConfig (lessVerbose verbosity)
let version = compilerVersion comp
flavor = compilerFlavor comp
let pid = packageId pkg_descr0
internalPackage = emptyInstalledPackageInfo {
Installed.installedPackageId = InstalledPackageId $ display $ pid,
Installed.sourcePackageId = pid
}
internalPackageSet = PackageIndex.fromList [internalPackage]
maybeInstalledPackageSet <- getInstalledPackages (lessVerbose verbosity) comp
packageDbs programsConfig'
let
dependencySatisfiable = case maybeInstalledPackageSet of
Nothing -> const True
Just pkgs -> not . null . PackageIndex.lookupDependency pkgs'
where
pkgs' = PackageIndex.insert internalPackage pkgs
(pkg_descr0', flags) <-
case finalizePackageDescription
(configConfigurationsFlags cfg)
dependencySatisfiable
Distribution.System.buildPlatform
(compilerId comp)
(configConstraints cfg)
pkg_descr0
of Right r -> return r
Left missing ->
die $ "At least the following dependencies are missing:\n"
++ (render . nest 4 . sep . punctuate comma
. map (disp . simplifyDependency)
$ missing)
let pkg_descr = addExtraIncludeLibDirs pkg_descr0'
when (not (null flags)) $
info verbosity $ "Flags chosen: "
++ intercalate ", " [ name ++ "=" ++ display value
| (FlagName name, value) <- flags ]
checkPackageProblems verbosity pkg_descr0
(updatePackageDescription pbi pkg_descr)
let installedPackageSet = fromMaybe bogusPackageSet maybeInstalledPackageSet
bogusDependencies = map inventBogusPackageInfo
(buildDepends pkg_descr)
bogusPackageSet = PackageIndex.fromList bogusDependencies
selectDependencies =
(\xs -> ([ x | Left x <- xs ], [ x | Right x <- xs ]))
. map (selectDependency internalPackageSet installedPackageSet)
(failedDeps, allPkgDeps) = case flavor of
GHC -> selectDependencies (buildDepends pkg_descr)
JHC -> selectDependencies (buildDepends pkg_descr)
LHC -> selectDependencies (buildDepends pkg_descr)
_ -> ([], bogusSelection)
where
bogusSelection :: [ResolvedDependency]
bogusSelection = zipWith ExternalDependency
(buildDepends pkg_descr)
bogusDependencies
internalPkgDeps = [ pkgid | InternalDependency _ pkgid <- allPkgDeps ]
externalPkgDeps = [ pkg | ExternalDependency _ pkg <- allPkgDeps ]
when (not (null internalPkgDeps) && not (newPackageDepsBehaviour pkg_descr)) $
die $ "The field 'build-depends: "
++ intercalate ", " (map (display . packageName) internalPkgDeps)
++ "' refers to a library which is defined within the same "
++ "package. To use this feature the package must specify at "
++ "least 'cabal-version: >= 1.8'."
reportFailedDependencies failedDeps
reportSelectedDependencies verbosity allPkgDeps
packageDependsIndex <-
case PackageIndex.dependencyClosure installedPackageSet
(map Installed.installedPackageId externalPkgDeps) of
Left packageDependsIndex -> return packageDependsIndex
Right broken ->
die $ "The following installed packages are broken because other"
++ " packages they depend on are missing. These broken "
++ "packages must be rebuilt before they can be used.\n"
++ unlines [ "package "
++ display (packageId pkg)
++ " is broken due to missing package "
++ intercalate ", " (map display deps)
| (pkg, deps) <- broken ]
let pseudoTopPkg = emptyInstalledPackageInfo {
Installed.installedPackageId = InstalledPackageId (display (packageId pkg_descr)),
Installed.sourcePackageId = packageId pkg_descr,
Installed.depends = map Installed.installedPackageId externalPkgDeps
}
case PackageIndex.dependencyInconsistencies
. PackageIndex.insert pseudoTopPkg
$ packageDependsIndex of
[] -> return ()
inconsistencies ->
warn verbosity $
"This package indirectly depends on multiple versions of the same "
++ "package. This is highly likely to cause a compile failure.\n"
++ unlines [ "package " ++ display pkg ++ " requires "
++ display (PackageIdentifier name ver)
| (name, uses) <- inconsistencies
, (pkg, ver) <- uses ]
defaultDirs <- defaultInstallDirs flavor userInstall (hasLibs pkg_descr)
let installDirs = combineInstallDirs fromFlagOrDefault
defaultDirs (configInstallDirs cfg)
let extlist = nub $ concatMap extensions (allBuildInfo pkg_descr)
let exts = unsupportedExtensions comp extlist
unless (null exts) $ warn verbosity $
display flavor ++ " does not support the following extensions: " ++
intercalate ", " (map display exts)
let requiredBuildTools = concatMap buildTools (allBuildInfo pkg_descr)
programsConfig'' <-
configureAllKnownPrograms (lessVerbose verbosity) programsConfig'
>>= configureRequiredPrograms verbosity requiredBuildTools
(pkg_descr', programsConfig''') <- configurePkgconfigPackages verbosity
pkg_descr programsConfig''
split_objs <-
if not (fromFlag $ configSplitObjs cfg)
then return False
else case flavor of
GHC | version >= Version [6,5] [] -> return True
_ -> do warn verbosity
("this compiler does not support " ++
"--enable-split-objs; ignoring")
return False
let configLib lib = configComponent (libBuildInfo lib)
configExe exe = (exeName exe, configComponent(buildInfo exe))
configComponent bi = ComponentLocalBuildInfo {
componentPackageDeps =
if newPackageDepsBehaviour pkg_descr'
then [ (installedPackageId pkg, packageId pkg)
| pkg <- selectSubset bi externalPkgDeps ]
++ [ (InstalledPackageId (display pkgid), pkgid)
| pkgid <- selectSubset bi internalPkgDeps ]
else [ (installedPackageId pkg, packageId pkg)
| pkg <- externalPkgDeps ]
}
selectSubset :: Package pkg => BuildInfo -> [pkg] -> [pkg]
selectSubset bi pkgs =
[ pkg | pkg <- pkgs, packageName pkg `elem` names ]
where
names = [ name | Dependency name _ <- targetBuildDepends bi ]
let lbi = LocalBuildInfo{
installDirTemplates = installDirs,
compiler = comp,
buildDir = distPref </> "build",
scratchDir = fromFlagOrDefault
(distPref </> "scratch")
(configScratchDir cfg),
libraryConfig = configLib `fmap` library pkg_descr',
executableConfigs = configExe `fmap` executables pkg_descr',
installedPkgs = packageDependsIndex,
pkgDescrFile = Nothing,
localPkgDescr = pkg_descr',
withPrograms = programsConfig''',
withVanillaLib = fromFlag $ configVanillaLib cfg,
withProfLib = fromFlag $ configProfLib cfg,
withSharedLib = fromFlag $ configSharedLib cfg,
withProfExe = fromFlag $ configProfExe cfg,
withOptimization = fromFlag $ configOptimization cfg,
withGHCiLib = fromFlag $ configGHCiLib cfg,
splitObjs = split_objs,
stripExes = fromFlag $ configStripExes cfg,
withPackageDB = packageDbs,
progPrefix = fromFlag $ configProgPrefix cfg,
progSuffix = fromFlag $ configProgSuffix cfg
}
let dirs = absoluteInstallDirs pkg_descr lbi NoCopyDest
relative = prefixRelativeInstallDirs (packageId pkg_descr) lbi
unless (isAbsolute (prefix dirs)) $ die $
"expected an absolute directory name for --prefix: " ++ prefix dirs
info verbosity $ "Using " ++ display currentCabalId
++ " compiled by " ++ display currentCompilerId
info verbosity $ "Using compiler: " ++ showCompilerId comp
info verbosity $ "Using install prefix: " ++ prefix dirs
let dirinfo name dir isPrefixRelative =
info verbosity $ name ++ " installed in: " ++ dir ++ relNote
where relNote = case buildOS of
Windows | not (hasLibs pkg_descr)
&& isNothing isPrefixRelative
-> " (fixed location)"
_ -> ""
dirinfo "Binaries" (bindir dirs) (bindir relative)
dirinfo "Libraries" (libdir dirs) (libdir relative)
dirinfo "Private binaries" (libexecdir dirs) (libexecdir relative)
dirinfo "Data files" (datadir dirs) (datadir relative)
dirinfo "Documentation" (docdir dirs) (docdir relative)
sequence_ [ reportProgram verbosity prog configuredProg
| (prog, configuredProg) <- knownPrograms programsConfig''' ]
return lbi
where
addExtraIncludeLibDirs pkg_descr =
let extraBi = mempty { extraLibDirs = configExtraLibDirs cfg
, PD.includeDirs = configExtraIncludeDirs cfg}
modifyLib l = l{ libBuildInfo = libBuildInfo l `mappend` extraBi }
modifyExecutable e = e{ buildInfo = buildInfo e `mappend` extraBi}
in pkg_descr{ library = modifyLib `fmap` library pkg_descr
, executables = modifyExecutable `map` executables pkg_descr}
inventBogusPackageInfo :: Dependency -> InstalledPackageInfo
inventBogusPackageInfo (Dependency s vr) =
emptyInstalledPackageInfo {
Installed.sourcePackageId = case isSpecificVersion vr of
Just v -> PackageIdentifier s v
Nothing -> PackageIdentifier s (Version [] [])
}
reportProgram :: Verbosity -> Program -> Maybe ConfiguredProgram -> IO ()
reportProgram verbosity prog Nothing
= info verbosity $ "No " ++ programName prog ++ " found"
reportProgram verbosity prog (Just configuredProg)
= info verbosity $ "Using " ++ programName prog ++ version ++ location
where location = case programLocation configuredProg of
FoundOnSystem p -> " found on system at: " ++ p
UserSpecified p -> " given by user at: " ++ p
version = case programVersion configuredProg of
Nothing -> ""
Just v -> " version " ++ display v
hackageUrl :: String
hackageUrl = "http://hackage.haskell.org/package/"
data ResolvedDependency = ExternalDependency Dependency InstalledPackageInfo
| InternalDependency Dependency PackageId
data FailedDependency = DependencyNotExists PackageName
| DependencyNoVersion Dependency
selectDependency :: PackageIndex
-> PackageIndex
-> Dependency
-> Either FailedDependency ResolvedDependency
selectDependency internalIndex installedIndex
dep@(Dependency pkgname vr) =
case PackageIndex.lookupPackageName internalIndex pkgname of
[(_,[pkg])] | packageVersion pkg `withinRange` vr
-> Right $ InternalDependency dep (packageId pkg)
_ -> case PackageIndex.lookupDependency installedIndex dep of
[] -> Left $ DependencyNotExists pkgname
pkgs -> Right $ ExternalDependency dep $
case last pkgs of
(_ver, instances) -> head instances
reportSelectedDependencies :: Verbosity
-> [ResolvedDependency] -> IO ()
reportSelectedDependencies verbosity deps =
info verbosity $ unlines
[ "Dependency " ++ display (simplifyDependency dep)
++ ": using " ++ display pkgid
| resolved <- deps
, let (dep, pkgid) = case resolved of
ExternalDependency dep' pkg' -> (dep', packageId pkg')
InternalDependency dep' pkgid' -> (dep', pkgid') ]
reportFailedDependencies :: [FailedDependency] -> IO ()
reportFailedDependencies [] = return ()
reportFailedDependencies failed =
die (intercalate "\n\n" (map reportFailedDependency failed))
where
reportFailedDependency (DependencyNotExists pkgname) =
"there is no version of " ++ display pkgname ++ " installed.\n"
++ "Perhaps you need to download and install it from\n"
++ hackageUrl ++ display pkgname ++ "?"
reportFailedDependency (DependencyNoVersion dep) =
"cannot satisfy dependency " ++ display (simplifyDependency dep) ++ "\n"
getInstalledPackages :: Verbosity -> Compiler
-> PackageDBStack -> ProgramConfiguration
-> IO (Maybe PackageIndex)
getInstalledPackages verbosity comp packageDBs progconf = do
info verbosity "Reading installed packages..."
case compilerFlavor comp of
GHC -> Just `fmap` GHC.getInstalledPackages verbosity packageDBs progconf
JHC -> Just `fmap` JHC.getInstalledPackages verbosity packageDBs progconf
LHC -> Just `fmap` LHC.getInstalledPackages verbosity packageDBs progconf
_ -> return Nothing
implicitPackageDbStack :: Bool -> Maybe PackageDB -> PackageDBStack
implicitPackageDbStack userInstall maybePackageDB
| userInstall = GlobalPackageDB : UserPackageDB : extra
| otherwise = GlobalPackageDB : extra
where
extra = case maybePackageDB of
Just (SpecificPackageDB db) -> [SpecificPackageDB db]
_ -> []
newPackageDepsBehaviourMinVersion :: Version
newPackageDepsBehaviourMinVersion = Version { versionBranch = [1,7,1], versionTags = [] }
newPackageDepsBehaviour :: PackageDescription -> Bool
newPackageDepsBehaviour pkg_descr =
minVersionRequired >= newPackageDepsBehaviourMinVersion
where
minVersionRequired =
case asVersionIntervals (descCabalVersion pkg_descr) of
[] -> Version [0] []
((LowerBound v _, _):_) -> v
configureRequiredPrograms :: Verbosity -> [Dependency] -> ProgramConfiguration -> IO ProgramConfiguration
configureRequiredPrograms verbosity deps conf =
foldM (configureRequiredProgram verbosity) conf deps
configureRequiredProgram :: Verbosity -> ProgramConfiguration -> Dependency -> IO ProgramConfiguration
configureRequiredProgram verbosity conf (Dependency (PackageName progName) verRange) =
case lookupKnownProgram progName conf of
Nothing -> die ("Unknown build tool " ++ progName)
Just prog
| verRange == anyVersion -> do
(_, conf') <- requireProgram verbosity prog conf
return conf'
| otherwise -> do
(_, _, conf') <- requireProgramVersion verbosity prog verRange conf
return conf'
configurePkgconfigPackages :: Verbosity -> PackageDescription
-> ProgramConfiguration
-> IO (PackageDescription, ProgramConfiguration)
configurePkgconfigPackages verbosity pkg_descr conf
| null allpkgs = return (pkg_descr, conf)
| otherwise = do
(_, _, conf') <- requireProgramVersion
(lessVerbose verbosity) pkgConfigProgram
(orLaterVersion $ Version [0,9,0] []) conf
mapM_ requirePkg allpkgs
lib' <- updateLibrary (library pkg_descr)
exes' <- mapM updateExecutable (executables pkg_descr)
let pkg_descr' = pkg_descr { library = lib', executables = exes' }
return (pkg_descr', conf')
where
allpkgs = concatMap pkgconfigDepends (allBuildInfo pkg_descr)
pkgconfig = rawSystemProgramStdoutConf (lessVerbose verbosity)
pkgConfigProgram conf
requirePkg dep@(Dependency (PackageName pkg) range) = do
version <- pkgconfig ["--modversion", pkg]
`catchIO` (\_ -> die notFound)
`catchExit` (\_ -> die notFound)
case simpleParse version of
Nothing -> die "parsing output of pkg-config --modversion failed"
Just v | not (withinRange v range) -> die (badVersion v)
| otherwise -> info verbosity (depSatisfied v)
where
notFound = "The pkg-config package " ++ pkg ++ versionRequirement
++ " is required but it could not be found."
badVersion v = "The pkg-config package " ++ pkg ++ versionRequirement
++ " is required but the version installed on the"
++ " system is version " ++ display v
depSatisfied v = "Dependency " ++ display dep
++ ": using version " ++ display v
versionRequirement
| isAnyVersion range = ""
| otherwise = " version " ++ display range
updateLibrary Nothing = return Nothing
updateLibrary (Just lib) = do
bi <- pkgconfigBuildInfo (pkgconfigDepends (libBuildInfo lib))
return $ Just lib { libBuildInfo = libBuildInfo lib `mappend` bi }
updateExecutable exe = do
bi <- pkgconfigBuildInfo (pkgconfigDepends (buildInfo exe))
return exe { buildInfo = buildInfo exe `mappend` bi }
pkgconfigBuildInfo :: [Dependency] -> IO BuildInfo
pkgconfigBuildInfo [] = return mempty
pkgconfigBuildInfo pkgdeps = do
let pkgs = nub [ display pkg | Dependency pkg _ <- pkgdeps ]
ccflags <- pkgconfig ("--cflags" : pkgs)
ldflags <- pkgconfig ("--libs" : pkgs)
return (ccLdOptionsBuildInfo (words ccflags) (words ldflags))
ccLdOptionsBuildInfo :: [String] -> [String] -> BuildInfo
ccLdOptionsBuildInfo cflags ldflags =
let (includeDirs', cflags') = partition ("-I" `isPrefixOf`) cflags
(extraLibs', ldflags') = partition ("-l" `isPrefixOf`) ldflags
(extraLibDirs', ldflags'') = partition ("-L" `isPrefixOf`) ldflags'
in mempty {
PD.includeDirs = map (drop 2) includeDirs',
PD.extraLibs = map (drop 2) extraLibs',
PD.extraLibDirs = map (drop 2) extraLibDirs',
PD.ccOptions = cflags',
PD.ldOptions = ldflags''
}
configCompilerAux :: ConfigFlags -> IO (Compiler, ProgramConfiguration)
configCompilerAux cfg = configCompiler (flagToMaybe $ configHcFlavor cfg)
(flagToMaybe $ configHcPath cfg)
(flagToMaybe $ configHcPkg cfg)
programsConfig
(fromFlag (configVerbosity cfg))
where
programsConfig = userSpecifyArgss (configProgramArgs cfg)
. userSpecifyPaths (configProgramPaths cfg)
$ defaultProgramConfiguration
configCompiler :: Maybe CompilerFlavor -> Maybe FilePath -> Maybe FilePath
-> ProgramConfiguration -> Verbosity
-> IO (Compiler, ProgramConfiguration)
configCompiler Nothing _ _ _ _ = die "Unknown compiler"
configCompiler (Just hcFlavor) hcPath hcPkg conf verbosity = do
case hcFlavor of
GHC -> GHC.configure verbosity hcPath hcPkg conf
JHC -> JHC.configure verbosity hcPath hcPkg conf
LHC -> do (_,ghcConf) <- GHC.configure verbosity Nothing hcPkg conf
LHC.configure verbosity hcPath Nothing ghcConf
Hugs -> Hugs.configure verbosity hcPath hcPkg conf
NHC -> NHC.configure verbosity hcPath hcPkg conf
_ -> die "Unknown compiler"
checkForeignDeps :: PackageDescription -> LocalBuildInfo -> Verbosity -> IO ()
checkForeignDeps pkg lbi verbosity = do
ifBuildsWith allHeaders (commonCcArgs ++ makeLdArgs allLibs)
(return ())
(do missingLibs <- findMissingLibs
missingHdr <- findOffendingHdr
explainErrors missingHdr missingLibs)
where
allHeaders = collectField PD.includes
allLibs = collectField PD.extraLibs
ifBuildsWith headers args success failure = do
ok <- builds (makeProgram headers) args
if ok then success else failure
findOffendingHdr =
ifBuildsWith allHeaders cppArgs
(return Nothing)
(go . tail . inits $ allHeaders)
where
go [] = return Nothing
go (hdrs:hdrsInits) = do
ifBuildsWith hdrs cppArgs
(go hdrsInits)
(return . Just . last $ hdrs)
cppArgs = "-c":commonCcArgs
findMissingLibs = ifBuildsWith [] (makeLdArgs allLibs)
(return [])
(filterM (fmap not . libExists) allLibs)
libExists lib = builds (makeProgram []) (makeLdArgs [lib])
commonCcArgs = programArgs gccProg
++ hcDefines (compiler lbi)
++ [ "-I" ++ dir | dir <- collectField PD.includeDirs ]
++ ["-I."]
++ collectField PD.cppOptions
++ collectField PD.ccOptions
++ [ "-I" ++ dir
| dep <- deps
, dir <- Installed.includeDirs dep ]
++ [ opt
| dep <- deps
, opt <- Installed.ccOptions dep ]
commonLdArgs = [ "-L" ++ dir | dir <- collectField PD.extraLibDirs ]
++ collectField PD.ldOptions
++ [ "-L" ++ dir
| dep <- deps
, dir <- Installed.libraryDirs dep ]
makeLdArgs libs = [ "-l"++lib | lib <- libs ] ++ commonLdArgs
makeProgram hdrs = unlines $
[ "#include \"" ++ hdr ++ "\"" | hdr <- hdrs ] ++
["int main(int argc, char** argv) { return 0; }"]
collectField f = concatMap f allBi
allBi = allBuildInfo pkg
Just gccProg = lookupProgram gccProgram (withPrograms lbi)
deps = PackageIndex.topologicalOrder (installedPkgs lbi)
builds program args = do
tempDir <- getTemporaryDirectory
withTempFile tempDir ".c" $ \cName cHnd ->
withTempFile tempDir "" $ \oNname oHnd -> do
hPutStrLn cHnd program
hClose cHnd
hClose oHnd
_ <- rawSystemProgramStdoutConf verbosity
gccProgram (withPrograms lbi) (cName:"-o":oNname:args)
return True
`catchIO` (\_ -> return False)
`catchExit` (\_ -> return False)
explainErrors Nothing [] = return ()
explainErrors hdr libs = die $ unlines $
(if plural then "Missing dependencies on foreign libraries:"
else "Missing dependency on a foreign library:")
: case hdr of
Nothing -> []
Just h -> ["* Missing header file: " ++ h ]
++ case libs of
[] -> []
[lib] -> ["* Missing C library: " ++ lib]
_ -> ["* Missing C libraries: " ++ intercalate ", " libs]
++ [if plural then messagePlural else messageSingular]
where
plural = length libs >= 2
messageSingular =
"This problem can usually be solved by installing the system "
++ "package that provides this library (you may need the "
++ "\"-dev\" version). If the library is already installed "
++ "but in a non-standard location then you can use the flags "
++ "--extra-include-dirs= and --extra-lib-dirs= to specify "
++ "where it is."
messagePlural =
"This problem can usually be solved by installing the system "
++ "packages that provide these libraries (you may need the "
++ "\"-dev\" versions). If the libraries are already installed "
++ "but in a non-standard location then you can use the flags "
++ "--extra-include-dirs= and --extra-lib-dirs= to specify "
++ "where they are."
hcDefines :: Compiler -> [String]
hcDefines comp =
case compilerFlavor comp of
GHC -> ["-D__GLASGOW_HASKELL__=" ++ versionInt version]
JHC -> ["-D__JHC__=" ++ versionInt version]
NHC -> ["-D__NHC__=" ++ versionInt version]
Hugs -> ["-D__HUGS__"]
_ -> []
where
version = compilerVersion comp
versionInt :: Version -> String
versionInt (Version { versionBranch = [] }) = "1"
versionInt (Version { versionBranch = [n] }) = show n
versionInt (Version { versionBranch = n1:n2:_ })
=
let s1 = show n1
s2 = show n2
middle = case s2 of
_ : _ : _ -> ""
_ -> "0"
in s1 ++ middle ++ s2
checkPackageProblems :: Verbosity
-> GenericPackageDescription
-> PackageDescription
-> IO ()
checkPackageProblems verbosity gpkg pkg = do
ioChecks <- checkPackageFiles pkg "."
let pureChecks = checkPackage gpkg (Just pkg)
errors = [ e | PackageBuildImpossible e <- pureChecks ++ ioChecks ]
warnings = [ w | PackageBuildWarning w <- pureChecks ++ ioChecks ]
if null errors
then mapM_ (warn verbosity) warnings
else do mapM_ (hPutStrLn stderr . ("Error: " ++)) errors
exitWith (ExitFailure 1)