Commit 47cc3de6 authored by Chris Wong's avatar Chris Wong
Browse files

Merge branch 'master' into reports++

Conflicts:
	cabal-install/Distribution/Client/Install.hs
parents b22d5ce5 6cc46998
......@@ -127,7 +127,7 @@ source-repository head
library
build-depends:
base >= 4 && < 5,
base >= 4.2 && < 5,
deepseq >= 1.3 && < 1.4,
filepath >= 1 && < 1.4,
directory >= 1 && < 1.3,
......@@ -154,6 +154,7 @@ library
Distribution.License
Distribution.Make
Distribution.ModuleName
Distribution.ModuleExport
Distribution.Package
Distribution.PackageDescription
Distribution.PackageDescription.Check
......@@ -233,7 +234,9 @@ library
test-suite unit-tests
type: exitcode-stdio-1.0
hs-source-dirs: tests
other-modules: UnitTests.Distribution.Compat.ReadP
other-modules:
UnitTests.Distribution.Compat.CreatePipe
UnitTests.Distribution.Compat.ReadP
main-is: UnitTests.hs
build-depends:
base,
......
{-# LANGUAGE CPP, ForeignFunctionInterface #-}
module Distribution.Compat.CreatePipe (createPipe) where
import System.IO (Handle)
import System.IO (Handle, hSetEncoding, localeEncoding)
-- The mingw32_HOST_OS CPP macro is GHC-specific
#if mingw32_HOST_OS
......@@ -31,6 +31,8 @@ createPipe = do
return (readfd, writefd)
(do readh <- fdToHandle readfd ReadMode
writeh <- fdToHandle writefd WriteMode
hSetEncoding readh localeEncoding
hSetEncoding writeh localeEncoding
return (readh, writeh)) `onException` (close readfd >> close writefd)
where
fdToHandle :: CInt -> IOMode -> IO Handle
......@@ -51,5 +53,7 @@ createPipe = do
(readfd, writefd) <- Posix.createPipe
readh <- fdToHandle readfd
writeh <- fdToHandle writefd
hSetEncoding readh localeEncoding
hSetEncoding writeh localeEncoding
return (readh, writeh)
#endif
......@@ -77,11 +77,13 @@ import Distribution.License ( License(..) )
import Distribution.Package
( PackageName(..), PackageIdentifier(..)
, PackageId, InstalledPackageId(..)
, packageName, packageVersion )
, packageName, packageVersion, PackageKey(..) )
import qualified Distribution.Package as Package
( Package(..) )
import Distribution.ModuleName
( ModuleName )
import Distribution.ModuleExport
( ModuleExport(..) )
import Distribution.Version
( Version(..) )
import Distribution.Text
......@@ -90,11 +92,13 @@ import Distribution.Text
-- -----------------------------------------------------------------------------
-- The InstalledPackageInfo type
data InstalledPackageInfo_ m
= InstalledPackageInfo {
-- these parts are exactly the same as PackageDescription
installedPackageId :: InstalledPackageId,
sourcePackageId :: PackageId,
packageKey :: PackageKey,
license :: License,
copyright :: String,
maintainer :: String,
......@@ -108,6 +112,7 @@ data InstalledPackageInfo_ m
-- these parts are required by an installed package only:
exposed :: Bool,
exposedModules :: [m],
reexportedModules :: [ModuleExport m],
hiddenModules :: [m],
trusted :: Bool,
importDirs :: [FilePath], -- contain sources in case of Hugs
......@@ -138,6 +143,8 @@ emptyInstalledPackageInfo
= InstalledPackageInfo {
installedPackageId = InstalledPackageId "",
sourcePackageId = PackageIdentifier (PackageName "") noVersion,
packageKey = OldPackageKey (PackageIdentifier
(PackageName "") noVersion),
license = AllRightsReserved,
copyright = "",
maintainer = "",
......@@ -150,6 +157,7 @@ emptyInstalledPackageInfo
category = "",
exposed = False,
exposedModules = [],
reexportedModules = [],
hiddenModules = [],
trusted = False,
importDirs = [],
......@@ -208,6 +216,9 @@ basicFieldDescrs =
, simpleField "id"
disp parse
installedPackageId (\ipid pkg -> pkg{installedPackageId=ipid})
, simpleField "key"
disp parse
packageKey (\ipid pkg -> pkg{packageKey=ipid})
, simpleField "license"
disp parseLicenseQ
license (\l pkg -> pkg{license=l})
......@@ -247,6 +258,9 @@ installedFieldDescrs = [
, listField "exposed-modules"
disp parseModuleNameQ
exposedModules (\xs pkg -> pkg{exposedModules=xs})
, listField "reexported-modules"
disp parse
reexportedModules (\xs pkg -> pkg{reexportedModules=xs})
, listField "hidden-modules"
disp parseModuleNameQ
hiddenModules (\xs pkg -> pkg{hiddenModules=xs})
......
{-# LANGUAGE DeriveDataTypeable #-}
module Distribution.ModuleExport(ModuleExport(..)) where
import Distribution.Text
( Text(disp, parse) )
import Distribution.Compat.ReadP
( (+++) )
import Distribution.Package
( PackageName, InstalledPackageId )
import qualified Distribution.Compat.ReadP as Parse
import qualified Text.PrettyPrint as Disp
import Text.PrettyPrint ((<+>),(<>))
import Data.Data
-- | Defines a reexport of module 'exportOrigName' from package
-- 'exportOrigPackageId' as new module name 'exportName'. This data type has an
-- interesting invariant: in the installed package database, a ModuleExport is
-- guaranteed to point to the original module which defined the module. Of
-- course, when a user writes a ModuleExport, it may not have this property.
-- ghc-pkg is responsible for enforcing this invariant.
data ModuleExport m = ModuleExport {
-- | Original package name of the reexported module, or Nothing if
-- the user wants us to figure it out automatically. (Note: this package
-- could have reexported the module itself.)
exportOrigPackageName :: Maybe PackageName,
-- | Original module name of reexported module.
exportOrigName :: m,
-- | New module name of reexported module, available to clients
-- of this package.
exportName :: m,
-- | A hack! When ghc-pkg processes 'ModuleExport', it is able to resolve
-- the true, original location an identifier lived in (this cannot be done
-- without consulting the package database), it fills it in here so that
-- GHC can use it. When we get GHC to stop using 'InstalledPackageInfo',
-- this hack can go away.
exportCachedTrueOrig :: Maybe (InstalledPackageId, m)
} deriving (Read, Show, Eq, Data, Typeable)
-- Handy when we need to convert from one ModuleName representation to
-- another (it's used in GHC.)
instance Functor ModuleExport where
fmap f (ModuleExport pnm m m' c) = ModuleExport pnm (f m) (f m')
(fmap (\(x,y)->(x,f y)) c)
instance (Eq m, Text m) => Text (ModuleExport m) where
disp ModuleExport{ exportOrigPackageName = mpnm
, exportOrigName = m
, exportName = m'
, exportCachedTrueOrig = c }
= (maybe Disp.empty (\pnm -> disp pnm <> Disp.char ':') mpnm)
<> disp m
<+> (if m == m'
then Disp.empty
else Disp.text "as" <+> disp m')
<+> (maybe Disp.empty (\(c_ipid, c_m) ->
Disp.parens (disp c_m <> Disp.char '@' <> disp c_ipid)) c)
parse = do Parse.skipSpaces
mpnm <- (do pnm <- parse
_ <- Parse.char ':'
return (Just pnm)
+++ return Nothing)
m <- parse
m' <- (do Parse.skipSpaces
_ <- Parse.string "as"
Parse.skipSpaces
parse)
+++ return m
c <- (do Parse.skipSpaces
_ <- Parse.char '('
c_m <- parse
_ <- Parse.char '@'
c_ipid <- parse
_ <- Parse.char ')'
return (Just (c_ipid, c_m))
+++ return Nothing)
return ModuleExport{ exportOrigPackageName = mpnm
, exportOrigName = m
, exportName = m'
, exportCachedTrueOrig = c }
......@@ -22,6 +22,10 @@ module Distribution.Package (
-- * Installed package identifiers
InstalledPackageId(..),
-- * Package keys (used for linker symbols)
PackageKey(..),
mkPackageKey,
-- * Package source dependencies
Dependency(..),
thisPackageVersion,
......@@ -43,12 +47,16 @@ import Distribution.Compat.ReadP ((<++))
import qualified Text.PrettyPrint as Disp
import Text.PrettyPrint ((<>), (<+>), text)
import Control.DeepSeq (NFData(..))
import qualified Data.Char as Char ( isDigit, isAlphaNum )
import Data.List ( intercalate )
import qualified Data.Char as Char
( isDigit, isAlphaNum, isUpper, isLower, ord, chr )
import Data.List ( intercalate, sort, foldl' )
import Data.Data ( Data )
import Data.Typeable ( Typeable )
import GHC.Fingerprint ( Fingerprint(..), fingerprintString )
import Data.Word ( Word64 )
import Numeric ( showIntAtBase )
newtype PackageName = PackageName String
newtype PackageName = PackageName { unPackageName :: String }
deriving (Read, Show, Eq, Ord, Typeable, Data)
instance Text PackageName where
......@@ -107,6 +115,114 @@ instance Text InstalledPackageId where
parse = InstalledPackageId `fmap` Parse.munch1 abi_char
where abi_char c = Char.isAlphaNum c || c `elem` ":-_."
-- ------------------------------------------------------------
-- * Package Keys
-- ------------------------------------------------------------
-- | A 'PackageKey' is the notion of "package ID" which is visible to the
-- compiler. Why is this not a 'PackageId'? The 'PackageId' is a user-visible
-- concept written explicity in Cabal files; on the other hand, a 'PackageKey'
-- may contain, for example, information about the transitive dependency
-- tree of a package. Why is this not an 'InstalledPackageId'? A 'PackageKey'
-- affects the ABI because it is used for linker symbols; however, an
-- 'InstalledPackageId' can be used to distinguish two ABI-compatible versions
-- of a library.
data PackageKey
-- | Modern package key which is a hash of the PackageId and the transitive
-- dependency key. Manually inline it here so we can get the instances
-- we need. Also contains a short informative string
= PackageKey !String {-# UNPACK #-} !Word64 {-# UNPACK #-} !Word64
-- | Old-style package key which is just a 'PackageId'. Required because
-- old versions of GHC assume that the 'sourcePackageId' recorded for an
-- installed package coincides with the package key it was compiled with.
| OldPackageKey !PackageId
deriving (Read, Show, Eq, Ord, Typeable, Data)
-- | Convenience function which converts a fingerprint into a new-style package
-- key.
fingerprintPackageKey :: String -> Fingerprint -> PackageKey
fingerprintPackageKey s (Fingerprint a b) = PackageKey s a b
-- | Generates a 'PackageKey' from a 'PackageId', sorted package keys of the
-- immediate dependencies.
mkPackageKey :: Bool -- are modern style package keys supported?
-> PackageId
-> [PackageKey] -- dependencies
-> PackageKey
mkPackageKey True pid deps = fingerprintPackageKey stubName
. fingerprintString
. ((show pid ++ "\n") ++)
$ show (sort deps)
where stubName = take 5 (filter (/= '-') (unPackageName (pkgName pid)))
mkPackageKey False pid _ = OldPackageKey pid
-- The base-62 code is based off of 'locators'
-- ((c) Operational Dynamics Consulting, BSD3 licensed)
-- Note: Instead of base-62 encoding a single 128-bit integer
-- (ceil(21.49) characters), we'll base-62 a pair of 64-bit integers
-- (2 * ceil(10.75) characters). Luckily for us, it's the same number of
-- characters! In the long term, this should go in GHC.Fingerprint,
-- but not now...
-- | Size of a 64-bit word when written as a base-62 string
word64Base62Len :: Int
word64Base62Len = 11
-- | Converts a 64-bit word into a base-62 string
toBase62 :: Word64 -> String
toBase62 w = pad ++ str
where
pad = replicate len '0'
len = word64Base62Len - length str -- 11 == ceil(64 / lg 62)
str = showIntAtBase 62 represent w ""
represent :: Int -> Char
represent x
| x < 10 = Char.chr (48 + x)
| x < 36 = Char.chr (65 + x - 10)
| x < 62 = Char.chr (97 + x - 36)
| otherwise = error ("represent (base 62): impossible!")
-- | Parses a base-62 string into a 64-bit word
fromBase62 :: String -> Word64
fromBase62 ss = foldl' multiply 0 ss
where
value :: Char -> Int
value c
| Char.isDigit c = Char.ord c - 48
| Char.isUpper c = Char.ord c - 65 + 10
| Char.isLower c = Char.ord c - 97 + 36
| otherwise = error ("value (base 62): impossible!")
multiply :: Word64 -> Char -> Word64
multiply acc c = acc * 62 + (fromIntegral $ value c)
-- | Parses a base-62 string into a fingerprint.
readBase62Fingerprint :: String -> Fingerprint
readBase62Fingerprint s = Fingerprint w1 w2
where (s1,s2) = splitAt word64Base62Len s
w1 = fromBase62 s1
w2 = fromBase62 (take word64Base62Len s2)
instance Text PackageKey where
disp (PackageKey prefix w1 w2) = Disp.text prefix <> Disp.char '_'
<> Disp.text (toBase62 w1) <> Disp.text (toBase62 w2)
disp (OldPackageKey pid) = disp pid
parse = parseNew <++ parseOld
where parseNew = do
prefix <- Parse.munch1 (\c -> Char.isAlphaNum c || c `elem` "-")
_ <- Parse.char '_' -- if we use '-' it's ambiguous
fmap (fingerprintPackageKey prefix . readBase62Fingerprint)
. Parse.count (word64Base62Len * 2)
$ Parse.satisfy Char.isAlphaNum
parseOld = do pid <- parse
return (OldPackageKey pid)
instance NFData PackageKey where
rnf (PackageKey prefix _ _) = rnf prefix
rnf (OldPackageKey pid) = rnf pid
-- ------------------------------------------------------------
-- * Package source dependencies
-- ------------------------------------------------------------
......
......@@ -109,6 +109,7 @@ import Distribution.Package
( PackageName(PackageName), PackageIdentifier(PackageIdentifier)
, Dependency, Package(..) )
import Distribution.ModuleName ( ModuleName )
import Distribution.ModuleExport ( ModuleExport )
import Distribution.Version
( Version(Version), VersionRange, anyVersion, orLaterVersion
, asVersionIntervals, LowerBound(..) )
......@@ -269,6 +270,7 @@ instance Text BuildType where
data Library = Library {
exposedModules :: [ModuleName],
reexportedModules :: [ModuleExport ModuleName],
libExposed :: Bool, -- ^ Is the lib to be exposed by default?
libBuildInfo :: BuildInfo
}
......@@ -277,11 +279,13 @@ data Library = Library {
instance Monoid Library where
mempty = Library {
exposedModules = mempty,
reexportedModules = mempty,
libExposed = True,
libBuildInfo = mempty
}
mappend a b = Library {
exposedModules = combine exposedModules,
reexportedModules = combine reexportedModules,
libExposed = libExposed a && libExposed b, -- so False propagates
libBuildInfo = combine libBuildInfo
}
......@@ -308,6 +312,8 @@ withLib pkg_descr f =
maybe (return ()) f (maybeHasLibs pkg_descr)
-- | Get all the module names from the library (exposed and internal modules)
-- which need to be compiled. (This does not include reexports, which
-- do not need to be compiled.)
libModules :: Library -> [ModuleName]
libModules lib = exposedModules lib
++ otherModules (libBuildInfo lib)
......
......@@ -65,6 +65,8 @@ import Distribution.Version
import Distribution.Package
( PackageName(PackageName), packageName, packageVersion
, Dependency(..), pkgName )
import Distribution.ModuleExport
( ModuleExport(..) )
import Distribution.Text
( display, disp )
......@@ -221,7 +223,8 @@ checkLibrary _pkg lib =
]
where
moduleDuplicates = dups (libModules lib)
moduleDuplicates = dups (libModules lib ++
map exportName (reexportedModules lib))
checkExecutable :: PackageDescription -> Executable -> [PackageCheck]
checkExecutable pkg exe =
......@@ -903,6 +906,12 @@ checkCabalVersion pkg =
++ "different modules then list the other ones in the "
++ "'other-languages' field."
-- check use of reexported-modules sections
, checkVersion [1,21] (maybe False (not.null.reexportedModules) (library pkg)) $
PackageDistInexcusable $
"To use the 'reexported-module' field the package needs to specify "
++ "at least 'cabal-version: >= 1.21'."
-- check use of default-extensions field
-- don't need to do the equivalent check for other-extensions
, checkVersion [1,10] (any (not . null) (buildInfoField defaultExtensions)) $
......
......@@ -181,6 +181,9 @@ libFieldDescrs =
[ listFieldWithSep vcat "exposed-modules" disp parseModuleNameQ
exposedModules (\mods lib -> lib{exposedModules=mods})
, commaListFieldWithSep vcat "reexported-modules" disp parse
reexportedModules (\mods lib -> lib{reexportedModules=mods})
, boolField "exposed"
libExposed (\val lib -> lib{libExposed=val})
] ++ map biToLib binfoFieldDescrs
......
......@@ -123,6 +123,6 @@ benchOption pkg_descr lbi bm template =
fromPathTemplate $ substPathTemplate env template
where
env = initialPathTemplateEnv
(PD.package pkg_descr) (compilerId $ LBI.compiler lbi)
(LBI.hostPlatform lbi) ++
(PD.package pkg_descr) (LBI.pkgKey lbi)
(compilerId $ LBI.compiler lbi) (LBI.hostPlatform lbi) ++
[(BenchmarkNameVar, toPathTemplate $ PD.benchmarkName bm)]
......@@ -36,10 +36,10 @@ import qualified Distribution.Simple.Build.PathsModule as Build.PathsModule
import Distribution.Package
( Package(..), PackageName(..), PackageIdentifier(..)
, Dependency(..), thisPackageVersion )
, Dependency(..), thisPackageVersion, mkPackageKey )
import Distribution.Simple.Compiler
( Compiler, CompilerFlavor(..), compilerFlavor
, PackageDB(..), PackageDBStack )
, PackageDB(..), PackageDBStack, packageKeySupported )
import Distribution.PackageDescription
( PackageDescription(..), BuildInfo(..), Library(..), Executable(..)
, TestSuite(..), TestSuiteInterface(..), Benchmark(..)
......@@ -55,7 +55,7 @@ import Distribution.Simple.BuildTarget
import Distribution.Simple.PreProcess
( preprocessComponent, PPSuffixHandler )
import Distribution.Simple.LocalBuildInfo
( LocalBuildInfo(compiler, buildDir, withPackageDB, withPrograms)
( LocalBuildInfo(compiler, buildDir, withPackageDB, withPrograms, pkgKey)
, Component(..), componentName, getComponent, componentBuildInfo
, ComponentLocalBuildInfo(..), pkgEnabledComponents
, withComponentsInBuildOrder, componentsInBuildOrder
......@@ -226,7 +226,7 @@ buildComponent verbosity numJobs pkg_descr lbi suffixes
buildExe verbosity numJobs pkg_descr lbi exe clbi
buildComponent verbosity numJobs pkg_descr lbi suffixes
buildComponent verbosity numJobs pkg_descr lbi0 suffixes
comp@(CTest
test@TestSuite { testInterface = TestSuiteLibV09{} })
clbi -- This ComponentLocalBuildInfo corresponds to a detailed
......@@ -236,8 +236,8 @@ buildComponent verbosity numJobs pkg_descr lbi suffixes
-- built.
distPref = do
pwd <- getCurrentDirectory
let (pkg, lib, libClbi, ipi, exe, exeClbi) =
testSuiteLibV09AsLibAndExe pkg_descr lbi test clbi distPref pwd
let (pkg, lib, libClbi, lbi, ipi, exe, exeClbi) =
testSuiteLibV09AsLibAndExe pkg_descr test clbi lbi0 distPref pwd
preprocessComponent pkg_descr comp lbi False verbosity suffixes
info verbosity $ "Building test suite " ++ testName test ++ "..."
buildLib verbosity numJobs pkg lbi lib libClbi
......@@ -293,13 +293,13 @@ replComponent verbosity pkg_descr lbi suffixes
replExe verbosity pkg_descr lbi exe clbi
replComponent verbosity pkg_descr lbi suffixes
replComponent verbosity pkg_descr lbi0 suffixes
comp@(CTest
test@TestSuite { testInterface = TestSuiteLibV09{} })
clbi distPref = do
pwd <- getCurrentDirectory
let (pkg, lib, libClbi, _, _, _) =
testSuiteLibV09AsLibAndExe pkg_descr lbi test clbi distPref pwd
let (pkg, lib, libClbi, lbi, _, _, _) =
testSuiteLibV09AsLibAndExe pkg_descr test clbi lbi0 distPref pwd
preprocessComponent pkg_descr comp lbi False verbosity suffixes
replLib verbosity pkg lbi lib libClbi
......@@ -339,23 +339,25 @@ testSuiteExeV10AsExe TestSuite{} = error "testSuiteExeV10AsExe: wrong kind"
-- | Translate a lib-style 'TestSuite' component into a lib + exe for building
testSuiteLibV09AsLibAndExe :: PackageDescription
-> LocalBuildInfo
-> TestSuite
-> ComponentLocalBuildInfo
-> LocalBuildInfo
-> FilePath
-> FilePath
-> (PackageDescription,
Library, ComponentLocalBuildInfo,
LocalBuildInfo,
IPI.InstalledPackageInfo_ ModuleName,
Executable, ComponentLocalBuildInfo)
testSuiteLibV09AsLibAndExe pkg_descr lbi
testSuiteLibV09AsLibAndExe pkg_descr
test@TestSuite { testInterface = TestSuiteLibV09 _ m }
clbi distPref pwd =
(pkg, lib, libClbi, ipi, exe, exeClbi)
clbi lbi distPref pwd =
(pkg, lib, libClbi, lbi', ipi, exe, exeClbi)
where
bi = testBuildInfo test
lib = Library {
exposedModules = [ m ],
reexportedModules = [],
libExposed = True,
libBuildInfo = bi
}
......@@ -372,6 +374,14 @@ testSuiteLibV09AsLibAndExe pkg_descr lbi
, testSuites = []
, library = Just lib
}
-- Hack to make the library compile with the right package key.
-- Probably the "right" way to do this is move this information to
-- the ComponentLocalBuildInfo, but it seems odd that a single package
-- can define multiple actual packages.
lbi' = lbi {
pkgKey = mkPackageKey (packageKeySupported (compiler lbi))
(package pkg) []
}
ipi = (inplaceInstalledPackageInfo pwd distPref pkg lib lbi libClbi) {
IPI.installedPackageId = inplacePackageId $ packageId ipi
}
......@@ -396,7 +406,7 @@ testSuiteLibV09AsLibAndExe pkg_descr lbi
in name == "Cabal" || name == "base")
(componentPackageDeps clbi))
}
testSuiteLibV09AsLibAndExe _ _ TestSuite{} _ _ _ = error "testSuiteLibV09AsLibAndExe: wrong kind"
testSuiteLibV09AsLibAndExe _ TestSuite{} _ _ _ _ = error "testSuiteLibV09AsLibAndExe: wrong kind"
-- | Translate a exe-style 'Benchmark' component into an exe for building
......
......@@ -66,7 +66,8 @@ import Distribution.Text
import Distribution.ParseUtils
import Distribution.ReadE
import Distribution.Simple.Utils (die, intercalate)
import Text.PrettyPrint ( punctuate, cat, comma, text, empty)
import Text.PrettyPrint ( punctuate, cat, comma, text )
import Text.PrettyPrint as PP ( empty )
data CommandUI flags = CommandUI {
-- | The name of the command as it would be entered on the command line.
......@@ -258,15 +259,15 @@ viewAsFieldDescr (OptionField n dd) = FieldDescr n get set
(cat . punctuate comma . map text . ppr) t
OptArg _ _ _ _ _ ppr ->
case ppr t of [] -> empty
case ppr t of [] -> PP.empty
(Nothing : _) -> text "True"
(Just a : _) -> text a
ChoiceOpt alts ->
fromMaybe empty $ listToMaybe
fromMaybe PP.empty $ listToMaybe
[ text lf | (_,(_,lf:_), _,enabled) <- alts, enabled t]
BoolOpt _ _ _ _ enabled -> (maybe empty disp . enabled) t
BoolOpt _ _ _ _ enabled -> (maybe PP.empty disp . enabled) t
-- set :: LineNo -> String -> a -> ParseResult a
set line val a =
......
......@@ -41,7 +41,9 @@ module Distribution.Simple.Compiler (
unsupportedLanguages,
extensionsToFlags,
unsupportedExtensions,
parmakeSupported
parmakeSupported,
reexportedModulesSupported,
packageKeySupported
) where
import Distribution.Compiler
......@@ -189,9 +191,21 @@ extensionToFlag comp ext = lookup ext (compilerExtensions comp)
-- | Does this compiler support parallel --make mode?
parmakeSupported :: Compiler -> Bool
parmakeSupported comp =
parmakeSupported = ghcSupported "Support parallel --make"
-- | Does this compiler support reexported-modules?
reexportedModulesSupported :: Compiler -> Bool
reexportedModulesSupported = ghcSupported "Support reexported-modules"
-- | Does this compiler support package keys?
packageKeySupported :: Compiler -> Bool
packageKeySupported = ghcSupported "Uses package keys"
-- | Utility function for GHC only features