module Distribution.Simple.Configure (configure,
writePersistBuildConfig,
getPersistBuildConfig,
checkPersistBuildConfigOutdated,
maybeGetPersistBuildConfig,
localBuildInfoFile,
getInstalledPackages,
configCompiler, configCompilerAux,
ccLdOptionsBuildInfo,
tryGetConfigStateFile,
checkForeignDeps,
)
where
import Distribution.Simple.Compiler
( CompilerFlavor(..), Compiler(compilerId), compilerFlavor, compilerVersion
, showCompilerId, unsupportedLanguages, unsupportedExtensions
, PackageDB(..), PackageDBStack )
import Distribution.Package
( PackageName(PackageName), 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(..), specVersion, GenericPackageDescription(..)
, Library(..), hasLibs, Executable(..), BuildInfo(..), allExtensions
, HookedBuildInfo, updatePackageDescription, allBuildInfo
, FlagName(..), TestSuite(..) )
import Distribution.PackageDescription.Configuration
( finalizePackageDescription, mapTreeData )
import Distribution.PackageDescription.Check
( PackageCheck(..), checkPackage, checkPackageFiles )
import Distribution.Simple.Hpc ( enableCoverage )
import Distribution.Simple.Program
( Program(..), ProgramLocation(..), ConfiguredProgram(..)
, ProgramConfiguration, defaultProgramConfiguration
, configureAllKnownPrograms, knownPrograms, lookupKnownProgram, addKnownProgram
, userSpecifyArgss, userSpecifyPaths
, 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, inplacePackageId
, allComponentsBy, Component(..), foldComponent, ComponentName(..) )
import Distribution.Simple.BuildPaths
( autogenModulesDir )
import Distribution.Simple.Utils
( die, warn, info, setupMessage, createDirectoryIfMissingVerbose
, intercalate, cabalVersion
, withFileContents, writeFileAtomic
, withTempFile )
import Distribution.System
( OS(..), buildOS, buildPlatform )
import Distribution.Version
( Version(..), anyVersion, orLaterVersion, withinRange, isAnyVersion )
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 qualified Distribution.Simple.UHC as UHC
import Control.Monad
( when, unless, foldM, filterM, forM )
import Data.List
( nub, partition, isPrefixOf, inits, find )
import Data.Maybe
( isNothing, catMaybes, mapMaybe )
import Data.Monoid
( Monoid(..) )
import Data.Graph
( SCC(..), graphFromEdges, transposeG, vertices, stronglyConnCompR )
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") 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
checkPersistBuildConfigOutdated :: FilePath -> FilePath -> IO Bool
checkPersistBuildConfigOutdated distPref pkg_descr_file = do
t0 <- getModificationTime pkg_descr_file
t1 <- getModificationTime $ localBuildInfoFile distPref
return (t0 > t1)
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)
buildDir' = distPref </> "build"
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]
installedPackageSet <- getInstalledPackages (lessVerbose verbosity) comp
packageDbs programsConfig'
let
dependencySatisfiable =
not . null . PackageIndex.lookupDependency pkgs'
where
pkgs' = PackageIndex.insert internalPackage installedPackageSet
enableTest t = t { testEnabled = fromFlag (configTests cfg) }
flaggedTests = map (\(n, t) -> (n, mapTreeData enableTest t))
(condTestSuites pkg_descr0)
pkg_descr0'' = pkg_descr0 { condTestSuites = flaggedTests }
(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 =
enableCoverage (fromFlag (configLibCoverage cfg)) distPref
$ 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 selectDependencies =
(\xs -> ([ x | Left x <- xs ], [ x | Right x <- xs ]))
. map (selectDependency internalPackageSet installedPackageSet)
(failedDeps, allPkgDeps) = selectDependencies (buildDepends pkg_descr)
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 langlist = nub $ catMaybes $ map defaultLanguage (allBuildInfo pkg_descr)
let langs = unsupportedLanguages comp langlist
when (not (null langs)) $
die $ "The package " ++ display (packageId pkg_descr0)
++ " requires the following languages which are not "
++ "supported by " ++ display (compilerId comp) ++ ": "
++ intercalate ", " (map display langs)
let extlist = nub $ concatMap allExtensions (allBuildInfo pkg_descr)
let exts = unsupportedExtensions comp extlist
when (not (null exts)) $
die $ "The package " ++ display (packageId pkg_descr0)
++ " requires the following language extensions which are not "
++ "supported by " ++ display (compilerId comp) ++ ": "
++ intercalate ", " (map display exts)
let requiredBuildTools = concatMap buildTools (allBuildInfo pkg_descr)
let programsConfig'' = foldr (addInternalExe buildDir') programsConfig'
(executables 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))
configTest test = (testName test,
configComponent(testBuildInfo test))
configComponent bi = ComponentLocalBuildInfo {
componentPackageDeps =
if newPackageDepsBehaviour pkg_descr'
then [ (installedPackageId pkg, packageId pkg)
| pkg <- selectSubset bi externalPkgDeps ]
++ [ (inplacePackageId 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 ipDeps component =
mapMaybe exeDepToComp (buildTools bi)
++ mapMaybe libDepToComp (targetBuildDepends bi)
where
bi = foldComponent libBuildInfo buildInfo testBuildInfo component
exeDepToComp (Dependency (PackageName name) _) =
CExe `fmap` find ((==) name . exeName)
(executables pkg_descr')
libDepToComp (Dependency pn _)
| pn `elem` map packageName internalPkgDeps =
CLib `fmap` library pkg_descr'
libDepToComp _ = Nothing
let sccs = (stronglyConnCompR . map lkup . vertices . transposeG) g
where (g, lkup, _) = graphFromEdges
$ allComponentsBy pkg_descr'
$ \c -> (c, key c, map key (ipDeps c))
key = foldComponent (const "library") exeName testName
buildOrder <- forM sccs $ \scc -> case scc of
AcyclicSCC (c,_,_) -> return (foldComponent (const CLibName)
(CExeName . exeName)
(CTestName . testName)
c)
CyclicSCC vs ->
die $ "Found cycle in intrapackage dependency graph:\n "
++ intercalate " depends on "
(map (\(_,k,_) -> "'" ++ k ++ "'") (vs ++ [head vs]))
let lbi = LocalBuildInfo {
configFlags = cfg,
extraConfigArgs = [],
installDirTemplates = installDirs,
compiler = comp,
buildDir = buildDir',
scratchDir = fromFlagOrDefault
(distPref </> "scratch")
(configScratchDir cfg),
libraryConfig = configLib `fmap` library pkg_descr',
executableConfigs = configExe `fmap` executables pkg_descr',
testSuiteConfigs = configTest `fmap` testSuites pkg_descr',
compBuildOrder = buildOrder,
installedPkgs = packageDependsIndex,
pkgDescrFile = Nothing,
localPkgDescr = pkg_descr',
withPrograms = programsConfig'''',
withVanillaLib = fromFlag $ configVanillaLib cfg,
withProfLib = fromFlag $ configProfLib cfg,
withSharedLib = fromFlag $ configSharedLib cfg,
withDynExe = fromFlag $ configDynExe 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
addInternalExe bd exe =
let nm = exeName exe in
addKnownProgram Program {
programName = nm,
programFindLocation = \_ -> return $ Just $ bd </> nm </> nm,
programFindVersion = \_ _ -> return Nothing,
programPostConf = \_ _ -> return []
}
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}
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 PackageIndex
getInstalledPackages verbosity comp packageDBs progconf = do
info verbosity "Reading installed packages..."
case compilerFlavor comp of
GHC -> GHC.getInstalledPackages verbosity packageDBs progconf
Hugs->Hugs.getInstalledPackages verbosity packageDBs progconf
JHC -> JHC.getInstalledPackages verbosity packageDBs progconf
LHC -> LHC.getInstalledPackages verbosity packageDBs progconf
NHC -> NHC.getInstalledPackages verbosity packageDBs progconf
UHC -> UHC.getInstalledPackages verbosity comp packageDBs progconf
flv -> die $ "don't know how to find the installed packages for "
++ display flv
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 =
specVersion pkg >= newPackageDepsBehaviourMinVersion
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
UHC -> UHC.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 ccArgs
(return Nothing)
(go . tail . inits $ allHeaders)
where
go [] = return Nothing
go (hdrs:hdrsInits) =
ifBuildsWith hdrs cppArgs
(ifBuildsWith hdrs ccArgs
(go hdrsInits)
(return . Just . Right . last $ hdrs))
(return . Just . Left . last $ hdrs)
cppArgs = "-E":commonCppArgs
ccArgs = "-c":commonCcArgs
findMissingLibs = ifBuildsWith [] (makeLdArgs allLibs)
(return [])
(filterM (fmap not . libExists) allLibs)
libExists lib = builds (makeProgram []) (makeLdArgs [lib])
commonCppArgs = hcDefines (compiler lbi)
++ [ "-I" ++ autogenModulesDir 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 ]
commonCcArgs = commonCppArgs
++ collectField PD.ccOptions
++ [ 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
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:"
| missing ]
++ case hdr of
Just (Left h) -> ["* Missing (or bad) header file: " ++ h ]
_ -> []
++ case libs of
[] -> []
[lib] -> ["* Missing C library: " ++ lib]
_ -> ["* Missing C libraries: " ++ intercalate ", " libs]
++ [if plural then messagePlural else messageSingular | missing]
++ case hdr of
Just (Left _) -> [ headerCppMessage ]
Just (Right h) -> [ (if missing then "* " else "")
++ "Bad header file: " ++ h
, headerCcMessage ]
_ -> []
where
plural = length libs >= 2
missing = not (null libs)
|| case hdr of Just (Left _) -> True; _ -> False
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."
headerCppMessage =
"If the header file does exist, it may contain errors that "
++ "are caught by the C compiler at the preprocessing stage. "
++ "In this case you can re-run configure with the verbosity "
++ "flag -v3 to see the error messages."
headerCcMessage =
"The header file contains a compile error. "
++ "You can re-run configure with the verbosity flag "
++ "-v3 to see the error messages from the C compiler."
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)