{-# LANGUAGE CPP #-} ----------------------------------------------------------------------------- -- | -- Module : Distribution.Simple.GHC.Internal -- Copyright : Isaac Jones 2003-2007 -- -- Maintainer : cabal-devel@haskell.org -- Portability : portable -- -- This module contains functions shared by GHC (Distribution.Simple.GHC) -- and GHC-derived compilers. module Distribution.Simple.GHC.Internal ( configureToolchain, getLanguages, getExtensions, targetPlatform, getGhcInfo, componentCcGhcOptions, componentGhcOptions, mkGHCiLibName, filterGhciFlags, ghcLookupProperty, getHaskellObjects, mkGhcOptPackages, substTopDir, checkPackageDbEnvVar ) where import Distribution.Simple.GHC.ImplInfo ( GhcImplInfo (..) ) import Distribution.Package ( InstalledPackageId, PackageId ) import Distribution.InstalledPackageInfo ( InstalledPackageInfo ) import qualified Distribution.InstalledPackageInfo as InstalledPackageInfo ( InstalledPackageInfo_(..) ) import Distribution.PackageDescription as PD ( BuildInfo(..), Library(..), libModules , hcOptions, usedExtensions, ModuleRenaming, lookupRenaming ) import Distribution.Compat.Exception ( catchExit, catchIO ) import Distribution.Simple.Compiler ( CompilerFlavor(..), Compiler(..), DebugInfoLevel(..), OptimisationLevel(..) ) import Distribution.Simple.Program.GHC import Distribution.Simple.Setup ( toFlag ) import qualified Distribution.ModuleName as ModuleName import Distribution.Simple.Program ( Program(..), ConfiguredProgram(..), ProgramConfiguration , ProgramLocation(..), ProgramSearchPath, ProgramSearchPathEntry(..) , rawSystemProgram, rawSystemProgramStdout, programPath , addKnownProgram, arProgram, ldProgram, gccProgram, stripProgram , getProgramOutput ) import Distribution.Simple.Program.Types ( suppressOverrideArgs ) import Distribution.Simple.LocalBuildInfo ( LocalBuildInfo(..), ComponentLocalBuildInfo(..) , LibraryName(..) ) import Distribution.Simple.Utils import Distribution.Simple.BuildPaths import Distribution.System ( buildOS, OS(..), Platform, platformFromTriple ) import Distribution.Text ( display, simpleParse ) import Distribution.Utils.NubList ( toNubListR ) import Distribution.Verbosity import Language.Haskell.Extension ( Language(..), Extension(..), KnownExtension(..) ) import qualified Data.Map as M import Data.Char ( isSpace ) import Data.Maybe ( fromMaybe, maybeToList, isJust ) import Control.Monad ( unless, when ) #if __GLASGOW_HASKELL__ < 710 import Data.Monoid ( Monoid(..) ) #endif import System.Directory ( getDirectoryContents, getTemporaryDirectory ) import System.Environment ( getEnv ) import System.FilePath ( (), (<.>), takeExtension, takeDirectory ) import System.IO ( hClose, hPutStrLn ) targetPlatform :: [(String, String)] -> Maybe Platform targetPlatform ghcInfo = platformFromTriple =<< lookup "Target platform" ghcInfo -- | Adjust the way we find and configure gcc and ld -- configureToolchain :: GhcImplInfo -> ConfiguredProgram -> M.Map String String -> ProgramConfiguration -> ProgramConfiguration configureToolchain implInfo ghcProg ghcInfo = addKnownProgram gccProgram { programFindLocation = findProg gccProgram extraGccPath, programPostConf = configureGcc } . addKnownProgram ldProgram { programFindLocation = findProg ldProgram extraLdPath, programPostConf = configureLd } . addKnownProgram arProgram { programFindLocation = findProg arProgram extraArPath } . addKnownProgram stripProgram { programFindLocation = findProg stripProgram extraStripPath } where compilerDir = takeDirectory (programPath ghcProg) baseDir = takeDirectory compilerDir mingwBinDir = baseDir "mingw" "bin" libDir = baseDir "gcc-lib" includeDir = baseDir "include" "mingw" isWindows = case buildOS of Windows -> True; _ -> False binPrefix = "" mkExtraPath :: Maybe FilePath -> FilePath -> [FilePath] mkExtraPath mbPath mingwPath | isWindows = mbDir ++ [mingwPath] | otherwise = mbDir where mbDir = maybeToList . fmap takeDirectory $ mbPath extraGccPath = mkExtraPath mbGccLocation windowsExtraGccDir extraLdPath = mkExtraPath mbLdLocation windowsExtraLdDir extraArPath = mkExtraPath mbArLocation windowsExtraArDir extraStripPath = mkExtraPath mbStripLocation windowsExtraStripDir -- on Windows finding and configuring ghc's gcc & binutils is a bit special (windowsExtraGccDir, windowsExtraLdDir, windowsExtraArDir, windowsExtraStripDir) | separateGccMingw implInfo = (baseDir, libDir, libDir, libDir) | otherwise = -- GHC >= 6.12 let b = mingwBinDir binPrefix in (b, b, b, b) findProg :: Program -> [FilePath] -> Verbosity -> ProgramSearchPath -> IO (Maybe FilePath) findProg prog extraPath v searchpath = programFindLocation prog v searchpath' where searchpath' = (map ProgramSearchPathDir extraPath) ++ searchpath -- Read tool locations from the 'ghc --info' output. Useful when -- cross-compiling. mbGccLocation = M.lookup "C compiler command" ghcInfo mbLdLocation = M.lookup "ld command" ghcInfo mbArLocation = M.lookup "ar command" ghcInfo mbStripLocation = M.lookup "strip command" ghcInfo ccFlags = getFlags "C compiler flags" gccLinkerFlags = getFlags "Gcc Linker flags" ldLinkerFlags = getFlags "Ld Linker flags" getFlags key = case M.lookup key ghcInfo of Nothing -> [] Just flags -> case reads flags of [(args, "")] -> args _ -> [] -- XXX Should should be an error really configureGcc :: Verbosity -> ConfiguredProgram -> IO ConfiguredProgram configureGcc v gccProg = do gccProg' <- configureGcc' v gccProg return gccProg' { programDefaultArgs = programDefaultArgs gccProg' ++ ccFlags ++ gccLinkerFlags } configureGcc' :: Verbosity -> ConfiguredProgram -> IO ConfiguredProgram configureGcc' | isWindows = \_ gccProg -> case programLocation gccProg of -- if it's found on system then it means we're using the result -- of programFindLocation above rather than a user-supplied path -- Pre GHC 6.12, that meant we should add these flags to tell -- ghc's gcc where it lives and thus where gcc can find its -- various files: FoundOnSystem {} | separateGccMingw implInfo -> return gccProg { programDefaultArgs = ["-B" ++ libDir, "-I" ++ includeDir] } _ -> return gccProg | otherwise = \_ gccProg -> return gccProg configureLd :: Verbosity -> ConfiguredProgram -> IO ConfiguredProgram configureLd v ldProg = do ldProg' <- configureLd' v ldProg return ldProg' { programDefaultArgs = programDefaultArgs ldProg' ++ ldLinkerFlags } -- we need to find out if ld supports the -x flag configureLd' :: Verbosity -> ConfiguredProgram -> IO ConfiguredProgram configureLd' verbosity ldProg = do tempDir <- getTemporaryDirectory ldx <- withTempFile tempDir ".c" $ \testcfile testchnd -> withTempFile tempDir ".o" $ \testofile testohnd -> do hPutStrLn testchnd "int foo() { return 0; }" 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 ldProg { programDefaultArgs = ["-x"] } else return ldProg getLanguages :: Verbosity -> GhcImplInfo -> ConfiguredProgram -> IO [(Language, String)] getLanguages _ implInfo _ -- TODO: should be using --supported-languages rather than hard coding | supportsHaskell2010 implInfo = return [(Haskell98, "-XHaskell98") ,(Haskell2010, "-XHaskell2010")] | otherwise = return [(Haskell98, "")] getGhcInfo :: Verbosity -> GhcImplInfo -> ConfiguredProgram -> IO [(String, String)] getGhcInfo verbosity implInfo ghcProg | flagInfoLanguages implInfo = do xs <- getProgramOutput verbosity (suppressOverrideArgs ghcProg) ["--info"] case reads xs of [(i, ss)] | all isSpace ss -> return i _ -> die "Can't parse --info output of GHC" | otherwise = return [] getExtensions :: Verbosity -> GhcImplInfo -> ConfiguredProgram -> IO [(Extension, String)] getExtensions verbosity implInfo ghcProg | flagInfoLanguages implInfo = do str <- getProgramOutput verbosity (suppressOverrideArgs ghcProg) ["--supported-languages"] let extStrs = if reportsNoExt implInfo then lines str else -- Older GHCs only gave us either Foo or NoFoo, -- so we have to work out the other one ourselves [ extStr'' | extStr <- lines str , let extStr' = case extStr of 'N' : 'o' : xs -> xs _ -> "No" ++ extStr , extStr'' <- [extStr, extStr'] ] let extensions0 = [ (ext, "-X" ++ display ext) | Just ext <- map simpleParse extStrs ] extensions1 = if fakeRecordPuns implInfo then -- ghc-6.8 introduced RecordPuns however it -- should have been NamedFieldPuns. We now -- encourage packages to use NamedFieldPuns -- so for compatibility we fake support for -- it in ghc-6.8 by making it an alias for -- the old RecordPuns extension. (EnableExtension NamedFieldPuns, "-XRecordPuns") : (DisableExtension NamedFieldPuns, "-XNoRecordPuns") : extensions0 else extensions0 extensions2 = if alwaysNondecIndent implInfo then -- ghc-7.2 split NondecreasingIndentation off -- into a proper extension. Before that it -- was always on. (EnableExtension NondecreasingIndentation, "") : (DisableExtension NondecreasingIndentation, "") : extensions1 else extensions1 return extensions2 | otherwise = return oldLanguageExtensions -- | For GHC 6.6.x and earlier, the mapping from supported extensions to flags oldLanguageExtensions :: [(Extension, String)] oldLanguageExtensions = let doFlag (f, (enable, disable)) = [(EnableExtension f, enable), (DisableExtension f, disable)] fglasgowExts = ("-fglasgow-exts", "") -- This is wrong, but we don't want to turn -- all the extensions off when asked to just -- turn one off fFlag flag = ("-f" ++ flag, "-fno-" ++ flag) in concatMap doFlag [(OverlappingInstances , fFlag "allow-overlapping-instances") ,(TypeSynonymInstances , fglasgowExts) ,(TemplateHaskell , fFlag "th") ,(ForeignFunctionInterface , fFlag "ffi") ,(MonomorphismRestriction , fFlag "monomorphism-restriction") ,(MonoPatBinds , fFlag "mono-pat-binds") ,(UndecidableInstances , fFlag "allow-undecidable-instances") ,(IncoherentInstances , fFlag "allow-incoherent-instances") ,(Arrows , fFlag "arrows") ,(Generics , fFlag "generics") ,(ImplicitPrelude , fFlag "implicit-prelude") ,(ImplicitParams , fFlag "implicit-params") ,(CPP , ("-cpp", ""{- Wrong -})) ,(BangPatterns , fFlag "bang-patterns") ,(KindSignatures , fglasgowExts) ,(RecursiveDo , fglasgowExts) ,(ParallelListComp , fglasgowExts) ,(MultiParamTypeClasses , fglasgowExts) ,(FunctionalDependencies , fglasgowExts) ,(Rank2Types , fglasgowExts) ,(RankNTypes , fglasgowExts) ,(PolymorphicComponents , fglasgowExts) ,(ExistentialQuantification , fglasgowExts) ,(ScopedTypeVariables , fFlag "scoped-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 , fFlag "extended-default-rules") ,(UnboxedTuples , fglasgowExts) ,(DeriveDataTypeable , fglasgowExts) ,(ConstrainedClassMethods , fglasgowExts) ] componentCcGhcOptions :: Verbosity -> GhcImplInfo -> LocalBuildInfo -> BuildInfo -> ComponentLocalBuildInfo -> FilePath -> FilePath -> GhcOptions componentCcGhcOptions verbosity implInfo lbi bi clbi pref filename = mempty { ghcOptVerbosity = toFlag verbosity, ghcOptMode = toFlag GhcModeCompile, ghcOptInputFiles = toNubListR [filename], ghcOptCppIncludePath = toNubListR $ [autogenModulesDir lbi, odir] ++ PD.includeDirs bi, ghcOptPackageDBs = withPackageDB lbi, ghcOptPackages = toNubListR $ mkGhcOptPackages clbi, ghcOptCcOptions = toNubListR $ (case withOptimization lbi of NoOptimisation -> [] _ -> ["-O2"]) ++ (case withDebugInfo lbi of NoDebugInfo -> [] MinimalDebugInfo -> ["-g1"] NormalDebugInfo -> ["-g"] MaximalDebugInfo -> ["-g3"]) ++ PD.ccOptions bi, ghcOptObjDir = toFlag odir } where odir | hasCcOdirBug implInfo = pref takeDirectory filename | otherwise = pref -- ghc 6.4.0 had a bug in -odir handling for C compilations. componentGhcOptions :: Verbosity -> LocalBuildInfo -> BuildInfo -> ComponentLocalBuildInfo -> FilePath -> GhcOptions componentGhcOptions verbosity lbi bi clbi odir = mempty { ghcOptVerbosity = toFlag verbosity, ghcOptHideAllPackages = toFlag True, ghcOptCabal = toFlag True, ghcOptPackageDBs = withPackageDB lbi, ghcOptPackages = toNubListR $ mkGhcOptPackages clbi, ghcOptSplitObjs = toFlag (splitObjs lbi), ghcOptSourcePathClear = toFlag True, ghcOptSourcePath = toNubListR $ [odir] ++ (hsSourceDirs bi) ++ [autogenModulesDir lbi], ghcOptCppIncludePath = toNubListR $ [autogenModulesDir lbi, odir] ++ PD.includeDirs bi, ghcOptCppOptions = toNubListR $ cppOptions bi, ghcOptCppIncludes = toNubListR $ [autogenModulesDir lbi cppHeaderName], ghcOptFfiIncludes = toNubListR $ PD.includes bi, ghcOptObjDir = toFlag odir, ghcOptHiDir = toFlag odir, ghcOptStubDir = toFlag odir, ghcOptOutputDir = toFlag odir, ghcOptOptimisation = toGhcOptimisation (withOptimization lbi), ghcOptDebugInfo = toGhcDebugInfo (withDebugInfo lbi), ghcOptExtra = toNubListR $ hcOptions GHC bi, ghcOptLanguage = toFlag (fromMaybe Haskell98 (defaultLanguage bi)), -- Unsupported extensions have already been checked by configure ghcOptExtensions = toNubListR $ usedExtensions bi, ghcOptExtensionMap = M.fromList . compilerExtensions $ (compiler lbi) } where toGhcOptimisation NoOptimisation = mempty --TODO perhaps override? toGhcOptimisation NormalOptimisation = toFlag GhcNormalOptimisation toGhcOptimisation MaximumOptimisation = toFlag GhcMaximumOptimisation -- GHC doesn't support debug info levels yet. toGhcDebugInfo NoDebugInfo = mempty toGhcDebugInfo MinimalDebugInfo = toFlag True toGhcDebugInfo NormalDebugInfo = toFlag True toGhcDebugInfo MaximalDebugInfo = toFlag True -- | Strip out flags that are not supported in ghci filterGhciFlags :: [String] -> [String] filterGhciFlags = filter supported where supported ('-':'O':_) = False supported "-debug" = False supported "-threaded" = False supported "-ticky" = False supported "-eventlog" = False supported "-prof" = False supported "-unreg" = False supported _ = True mkGHCiLibName :: LibraryName -> String mkGHCiLibName (LibraryName lib) = lib <.> "o" ghcLookupProperty :: String -> Compiler -> Bool ghcLookupProperty prop comp = case M.lookup prop (compilerProperties comp) of Just "YES" -> True _ -> False -- when using -split-objs, we need to search for object files in the -- Module_split directory for each module. getHaskellObjects :: GhcImplInfo -> Library -> LocalBuildInfo -> FilePath -> String -> Bool -> IO [FilePath] getHaskellObjects implInfo lib lbi pref wanted_obj_ext allow_split_objs | splitObjs lbi && allow_split_objs = do let splitSuffix = if noExtInSplitSuffix implInfo then "_split" else "_" ++ wanted_obj_ext ++ "_split" dirs = [ pref (ModuleName.toFilePath x ++ splitSuffix) | x <- libModules lib ] 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 lib ] mkGhcOptPackages :: ComponentLocalBuildInfo -> [(InstalledPackageId, PackageId, ModuleRenaming)] mkGhcOptPackages clbi = map (\(i,p) -> (i,p,lookupRenaming p (componentPackageRenaming clbi))) (componentPackageDeps clbi) 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 -- Cabal does not use the environment variable GHC{,JS}_PACKAGE_PATH; let -- users know that this is the case. See ticket #335. Simply ignoring it is -- not a good idea, since then ghc and cabal are looking at different sets -- of package DBs and chaos is likely to ensue. -- -- An exception to this is when running cabal from within a `cabal exec` -- environment. In this case, `cabal exec` will set the -- CABAL_SANDBOX_PACKAGE_PATH to the same value that it set -- GHC{,JS}_PACKAGE_PATH to. If that is the case it is OK to allow -- GHC{,JS}_PACKAGE_PATH. checkPackageDbEnvVar :: String -> String -> IO () checkPackageDbEnvVar compilerName packagePathEnvVar = do mPP <- lookupEnv packagePathEnvVar when (isJust mPP) $ do mcsPP <- lookupEnv "CABAL_SANDBOX_PACKAGE_PATH" unless (mPP == mcsPP) abort where lookupEnv :: String -> IO (Maybe String) lookupEnv name = (Just `fmap` getEnv name) `catchIO` const (return Nothing) abort = die $ "Use of " ++ compilerName ++ "'s environment variable " ++ packagePathEnvVar ++ " is incompatible with Cabal. Use the " ++ "flag --package-db to specify a package database (it can be " ++ "used multiple times)."