summaryrefslogtreecommitdiff
path: root/hadrian/src/Oracles
diff options
context:
space:
mode:
Diffstat (limited to 'hadrian/src/Oracles')
-rw-r--r--hadrian/src/Oracles/Flag.hs80
-rw-r--r--hadrian/src/Oracles/ModuleFiles.hs160
-rw-r--r--hadrian/src/Oracles/PackageData.hs66
-rw-r--r--hadrian/src/Oracles/Setting.hs236
4 files changed, 542 insertions, 0 deletions
diff --git a/hadrian/src/Oracles/Flag.hs b/hadrian/src/Oracles/Flag.hs
new file mode 100644
index 0000000000..447f0bc076
--- /dev/null
+++ b/hadrian/src/Oracles/Flag.hs
@@ -0,0 +1,80 @@
+module Oracles.Flag (
+ Flag (..), flag, crossCompiling, platformSupportsSharedLibs,
+ ghcWithSMP, ghcWithNativeCodeGen, supportsSplitObjects
+ ) where
+
+import Hadrian.Oracles.TextFile
+
+import Base
+import Oracles.Setting
+
+data Flag = ArSupportsAtFile
+ | CrossCompiling
+ | GccIsClang
+ | GccLt34
+ | GccLt44
+ | GccLt46
+ | GhcUnregisterised
+ | LeadingUnderscore
+ | SolarisBrokenShld
+ | SplitObjectsBroken
+ | WithLibdw
+ | HaveLibMingwEx
+ | UseSystemFfi
+
+-- Note, if a flag is set to empty string we treat it as set to NO. This seems
+-- fragile, but some flags do behave like this, e.g. GccIsClang.
+flag :: Flag -> Action Bool
+flag f = do
+ let key = case f of
+ ArSupportsAtFile -> "ar-supports-at-file"
+ CrossCompiling -> "cross-compiling"
+ GccIsClang -> "gcc-is-clang"
+ GccLt34 -> "gcc-lt-34"
+ GccLt44 -> "gcc-lt-44"
+ GccLt46 -> "gcc-lt-46"
+ GhcUnregisterised -> "ghc-unregisterised"
+ LeadingUnderscore -> "leading-underscore"
+ SolarisBrokenShld -> "solaris-broken-shld"
+ SplitObjectsBroken -> "split-objects-broken"
+ WithLibdw -> "with-libdw"
+ HaveLibMingwEx -> "have-lib-mingw-ex"
+ UseSystemFfi -> "use-system-ffi"
+ value <- lookupValueOrError configFile key
+ when (value `notElem` ["YES", "NO", ""]) . error $ "Configuration flag "
+ ++ quote (key ++ " = " ++ value) ++ " cannot be parsed."
+ return $ value == "YES"
+
+crossCompiling :: Action Bool
+crossCompiling = flag CrossCompiling
+
+platformSupportsSharedLibs :: Action Bool
+platformSupportsSharedLibs = do
+ badPlatform <- anyTargetPlatform [ "powerpc-unknown-linux"
+ , "x86_64-unknown-mingw32"
+ , "i386-unknown-mingw32" ]
+ solaris <- anyTargetPlatform [ "i386-unknown-solaris2" ]
+ solarisBroken <- flag SolarisBrokenShld
+ return $ not (badPlatform || solaris && solarisBroken)
+
+ghcWithSMP :: Action Bool
+ghcWithSMP = do
+ goodArch <- anyTargetArch ["i386", "x86_64", "sparc", "powerpc", "arm"]
+ ghcUnreg <- flag GhcUnregisterised
+ return $ goodArch && not ghcUnreg
+
+ghcWithNativeCodeGen :: Action Bool
+ghcWithNativeCodeGen = do
+ goodArch <- anyTargetArch ["i386", "x86_64", "sparc", "powerpc"]
+ badOs <- anyTargetOs ["ios", "aix"]
+ ghcUnreg <- flag GhcUnregisterised
+ return $ goodArch && not badOs && not ghcUnreg
+
+supportsSplitObjects :: Action Bool
+supportsSplitObjects = do
+ broken <- flag SplitObjectsBroken
+ ghcUnreg <- flag GhcUnregisterised
+ goodArch <- anyTargetArch [ "i386", "x86_64", "powerpc", "sparc" ]
+ goodOs <- anyTargetOs [ "mingw32", "cygwin32", "linux", "darwin", "solaris2"
+ , "freebsd", "dragonfly", "netbsd", "openbsd" ]
+ return $ not broken && not ghcUnreg && goodArch && goodOs
diff --git a/hadrian/src/Oracles/ModuleFiles.hs b/hadrian/src/Oracles/ModuleFiles.hs
new file mode 100644
index 0000000000..c7175dbc1c
--- /dev/null
+++ b/hadrian/src/Oracles/ModuleFiles.hs
@@ -0,0 +1,160 @@
+{-# LANGUAGE TypeFamilies #-}
+module Oracles.ModuleFiles (
+ decodeModule, encodeModule, findGenerator, hsSources, hsObjects, moduleFilesOracle
+ ) where
+
+import qualified Data.HashMap.Strict as Map
+
+import Base
+import Builder
+import Context
+import GHC
+import Oracles.PackageData
+
+newtype ModuleFiles = ModuleFiles (Stage, Package)
+ deriving (Binary, Eq, Hashable, NFData, Show, Typeable)
+type instance RuleResult ModuleFiles = [Maybe FilePath]
+
+newtype Generator = Generator (Stage, Package, FilePath)
+ deriving (Binary, Eq, Hashable, NFData, Show, Typeable)
+type instance RuleResult Generator = Maybe FilePath
+
+-- | We scan for the following Haskell source extensions when looking for module
+-- files. Note, we do not list "*.(l)hs-boot" files here, as they can never
+-- appear by themselves and always have accompanying "*.(l)hs" master files.
+haskellExtensions :: [String]
+haskellExtensions = [".hs", ".lhs"]
+
+-- | Non-Haskell source extensions and corresponding builders.
+otherExtensions :: [(String, Builder)]
+otherExtensions = [ (".x" , Alex )
+ , (".y" , Happy )
+ , (".ly" , Happy )
+ , (".hsc", Hsc2Hs) ]
+
+-- | We match the following file patterns when looking for module files.
+moduleFilePatterns :: [FilePattern]
+moduleFilePatterns = map ("*" ++) $ haskellExtensions ++ map fst otherExtensions
+
+-- | Given a FilePath determine the corresponding builder.
+determineBuilder :: FilePath -> Maybe Builder
+determineBuilder file = lookup (takeExtension file) otherExtensions
+
+-- | Given a module name extract the directory and file name, e.g.:
+--
+-- > decodeModule "Data.Functor.Identity" == ("Data/Functor", "Identity")
+-- > decodeModule "Prelude" == ("", "Prelude")
+decodeModule :: String -> (FilePath, String)
+decodeModule modName = (intercalate "/" (init xs), last xs)
+ where
+ xs = words $ replaceEq '.' ' ' modName
+
+-- | Given the directory and file name find the corresponding module name, e.g.:
+--
+-- > encodeModule "Data/Functor" "Identity.hs" == "Data.Functor.Identity"
+-- > encodeModule "" "Prelude" == "Prelude"
+-- > uncurry encodeModule (decodeModule name) == name
+encodeModule :: FilePath -> String -> String
+encodeModule dir file
+ | dir == "" = takeBaseName file
+ | otherwise = replaceEq '/' '.' dir ++ '.' : takeBaseName file
+
+-- | Find the generator for a given 'Context' and a source file. For example:
+-- findGenerator (Context Stage1 compiler vanilla)
+-- "_build/stage1/compiler/build/Lexer.hs"
+-- == Just ("compiler/parser/Lexer.x", Alex)
+-- findGenerator (Context Stage1 base vanilla)
+-- "_build/stage1/base/build/Prelude.hs"
+-- == Nothing
+findGenerator :: Context -> FilePath -> Action (Maybe (FilePath, Builder))
+findGenerator Context {..} file = do
+ maybeSource <- askOracle $ Generator (stage, package, file)
+ return $ do
+ source <- maybeSource
+ builder <- determineBuilder source
+ return (source, builder)
+
+-- | Find all Haskell source files for a given 'Context'.
+hsSources :: Context -> Action [FilePath]
+hsSources context = do
+ let modFile (m, Nothing ) = generatedFile context m
+ modFile (m, Just file )
+ | takeExtension file `elem` haskellExtensions = return file
+ | otherwise = generatedFile context m
+ mapM modFile =<< contextFiles context
+
+-- | Find all Haskell object files for a given 'Context'. Note: this is a much
+-- simpler function compared to 'hsSources', because all object files live in
+-- the build directory regardless of whether they are generated or not.
+hsObjects :: Context -> Action [FilePath]
+hsObjects context = do
+ path <- buildPath context
+ modules <- pkgDataList (Modules path)
+ -- GHC.Prim module is only for documentation, we do not actually build it.
+ mapM (objectPath context . moduleSource) (filter (/= "GHC.Prim") modules)
+
+-- | Generated module files live in the 'Context' specific build directory.
+generatedFile :: Context -> String -> Action FilePath
+generatedFile context moduleName = do
+ path <- buildPath context
+ return $ path -/- moduleSource moduleName
+
+moduleSource :: String -> FilePath
+moduleSource moduleName = replaceEq '.' '/' moduleName <.> "hs"
+
+-- | Module files for a given 'Context'.
+contextFiles :: Context -> Action [(String, Maybe FilePath)]
+contextFiles context@Context {..} = do
+ path <- buildPath context
+ modules <- fmap sort . pkgDataList $ Modules path
+ zip modules <$> askOracle (ModuleFiles (stage, package))
+
+-- | This is an important oracle whose role is to find and cache module source
+-- files. It takes a 'Stage' and a 'Package', looks up corresponding source
+-- directories @dirs@ and a sorted list of module names @modules@, and for each
+-- module, e.g. @A.B.C@, returns a 'FilePath' of the form @dir/A/B/C.extension@,
+-- such that @dir@ belongs to @dirs@, and file @dir/A/B/C.extension@ exists, or
+-- 'Nothing' if there is no such file. If more than one matching file is found
+-- an error is raised. For example, for 'Stage1' and 'compiler', @dirs@ will
+-- contain ["compiler/codeGen", "compiler/parser"], and @modules@ will contain
+-- ["CodeGen.Platform.ARM", "Config", "Lexer"]; the oracle will produce a list
+-- containing [Just "compiler/codeGen/CodeGen/Platform/ARM.hs", Nothing,
+-- Just "compiler/parser/Lexer.x"]. The oracle ignores @.(l)hs-boot@ files.
+moduleFilesOracle :: Rules ()
+moduleFilesOracle = void $ do
+ void . addOracle $ \(ModuleFiles (stage, package)) -> do
+ let context = vanillaContext stage package
+ path <- buildPath context
+ srcDirs <- pkgDataList $ SrcDirs path
+ modules <- fmap sort . pkgDataList $ Modules path
+ autogen <- autogenPath context
+ let dirs = autogen : map (pkgPath package -/-) srcDirs
+ modDirFiles = groupSort $ map decodeModule modules
+ result <- concatForM dirs $ \dir -> do
+ todo <- filterM (doesDirectoryExist . (dir -/-) . fst) modDirFiles
+ forM todo $ \(mDir, mFiles) -> do
+ let fullDir = unifyPath $ dir -/- mDir
+ files <- getDirectoryFiles fullDir moduleFilePatterns
+ let cmp f = compare (dropExtension f)
+ found = intersectOrd cmp files mFiles
+ return (map (fullDir -/-) found, mDir)
+ let pairs = sort [ (encodeModule d f, f) | (fs, d) <- result, f <- fs ]
+ multi = [ (m, f1, f2) | (m, f1):(n, f2):_ <- tails pairs, m == n ]
+ unless (null multi) $ do
+ let (m, f1, f2) = head multi
+ error $ "Module " ++ m ++ " has more than one source file: "
+ ++ f1 ++ " and " ++ f2 ++ "."
+ return $ lookupAll modules pairs
+
+ -- Optimisation: we discard Haskell files here, because they are never used
+ -- as generators, and hence would be discarded in 'findGenerator' anyway.
+ generators <- newCache $ \(stage, package) -> do
+ let context = vanillaContext stage package
+ files <- contextFiles context
+ list <- sequence [ (,src) <$> generatedFile context modName
+ | (modName, Just src) <- files
+ , takeExtension src `notElem` haskellExtensions ]
+ return $ Map.fromList list
+
+ addOracle $ \(Generator (stage, package, file)) ->
+ Map.lookup file <$> generators (stage, package)
diff --git a/hadrian/src/Oracles/PackageData.hs b/hadrian/src/Oracles/PackageData.hs
new file mode 100644
index 0000000000..cdfe9bfb48
--- /dev/null
+++ b/hadrian/src/Oracles/PackageData.hs
@@ -0,0 +1,66 @@
+module Oracles.PackageData (
+ PackageData (..), PackageDataList (..), pkgData, pkgDataList
+ ) where
+
+import Hadrian.Oracles.TextFile
+
+import Base
+
+newtype PackageData = BuildGhciLib FilePath
+
+data PackageDataList = AsmSrcs FilePath
+ | CcArgs FilePath
+ | CSrcs FilePath
+ | CmmSrcs FilePath
+ | CppArgs FilePath
+ | DepCcArgs FilePath
+ | DepExtraLibs FilePath
+ | DepIds FilePath
+ | DepIncludeDirs FilePath
+ | DepLdArgs FilePath
+ | DepLibDirs FilePath
+ | DepNames FilePath
+ | Deps FilePath
+ | HiddenModules FilePath
+ | HsArgs FilePath
+ | IncludeDirs FilePath
+ | LdArgs FilePath
+ | Modules FilePath
+ | SrcDirs FilePath
+
+askPackageData :: FilePath -> String -> Action String
+askPackageData path = lookupValueOrEmpty (path -/- "package-data.mk")
+
+-- | For each @PackageData path@ the file 'path/package-data.mk' contains a line
+-- of the form 'path_VERSION = 1.2.3.4'. @pkgData (PackageData path)@ is an
+-- Action that consults the file and returns "1.2.3.4".
+pkgData :: PackageData -> Action String
+pkgData packageData = case packageData of
+ BuildGhciLib path -> askPackageData path "BUILD_GHCI_LIB"
+
+-- | @PackageDataList path@ is used for multiple string options separated by
+-- spaces, such as @path_MODULES = Data.Array Data.Array.Base ...@.
+-- @pkgListData Modules@ therefore returns ["Data.Array", "Data.Array.Base", ...]
+pkgDataList :: PackageDataList -> Action [String]
+pkgDataList packageData = fmap (map unquote . words) $ case packageData of
+ AsmSrcs path -> askPackageData path "S_SRCS"
+ CcArgs path -> askPackageData path "CC_OPTS"
+ CSrcs path -> askPackageData path "C_SRCS"
+ CmmSrcs path -> askPackageData path "CMM_SRCS"
+ CppArgs path -> askPackageData path "CPP_OPTS"
+ DepCcArgs path -> askPackageData path "DEP_CC_OPTS"
+ DepExtraLibs path -> askPackageData path "DEP_EXTRA_LIBS"
+ DepIds path -> askPackageData path "DEP_IPIDS"
+ DepIncludeDirs path -> askPackageData path "DEP_INCLUDE_DIRS_SINGLE_QUOTED"
+ DepLibDirs path -> askPackageData path "DEP_LIB_DIRS_SINGLE_QUOTED"
+ DepLdArgs path -> askPackageData path "DEP_LD_OPTS"
+ DepNames path -> askPackageData path "DEP_NAMES"
+ Deps path -> askPackageData path "DEPS"
+ HiddenModules path -> askPackageData path "HIDDEN_MODULES"
+ HsArgs path -> askPackageData path "HC_OPTS"
+ IncludeDirs path -> askPackageData path "INCLUDE_DIRS"
+ LdArgs path -> askPackageData path "LD_OPTS"
+ Modules path -> askPackageData path "MODULES"
+ SrcDirs path -> askPackageData path "HS_SRC_DIRS"
+ where
+ unquote = dropWhile (== '\'') . dropWhileEnd (== '\'')
diff --git a/hadrian/src/Oracles/Setting.hs b/hadrian/src/Oracles/Setting.hs
new file mode 100644
index 0000000000..aa49011e1e
--- /dev/null
+++ b/hadrian/src/Oracles/Setting.hs
@@ -0,0 +1,236 @@
+module Oracles.Setting (
+ configFile, Setting (..), SettingList (..), setting, settingList, getSetting,
+ getSettingList, anyTargetPlatform, anyTargetOs, anyTargetArch, anyHostOs,
+ ghcWithInterpreter, ghcEnableTablesNextToCode, useLibFFIForAdjustors,
+ ghcCanonVersion, cmdLineLengthLimit, iosHost, osxHost, windowsHost,
+ topDirectory, relocatableBuild, installDocDir, installGhcLibDir, libsuf
+ ) where
+
+import Hadrian.Expression
+import Hadrian.Oracles.TextFile
+import Hadrian.Oracles.Path
+
+import Base
+
+-- TODO: Reduce the variety of similar flags (e.g. CPP and non-CPP versions).
+-- | Each 'Setting' comes from @system.config@ file, e.g. 'target-os = mingw32'.
+-- @setting TargetOs@ looks up the config file and returns "mingw32".
+-- 'SettingList' is used for multiple string values separated by spaces, such
+-- as @gmp-include-dirs = a b@.
+-- @settingList GmpIncludeDirs@ therefore returns a list of strings ["a", "b"].
+data Setting = BuildArch
+ | BuildOs
+ | BuildPlatform
+ | BuildVendor
+ | CcClangBackend
+ | CcLlvmBackend
+ | DynamicExtension
+ | GhcMajorVersion
+ | GhcMinorVersion
+ | GhcPatchLevel
+ | GhcVersion
+ | GhcSourcePath
+ | HostArch
+ | HostOs
+ | HostPlatform
+ | HostVendor
+ | ProjectGitCommitId
+ | ProjectName
+ | ProjectVersion
+ | ProjectVersionInt
+ | ProjectPatchLevel
+ | ProjectPatchLevel1
+ | ProjectPatchLevel2
+ | TargetArch
+ | TargetOs
+ | TargetPlatform
+ | TargetPlatformFull
+ | TargetVendor
+ | LlvmTarget
+ | FfiIncludeDir
+ | FfiLibDir
+ | GmpIncludeDir
+ | GmpLibDir
+ | IconvIncludeDir
+ | IconvLibDir
+ | CursesLibDir
+ -- Paths to where GHC is installed (ref: mk/install.mk)
+ | InstallPrefix
+ | InstallBinDir
+ | InstallLibDir
+ | InstallDataRootDir
+ -- Command lines for invoking the @install@ utility
+ | Install
+ | InstallData
+ | InstallProgram
+ | InstallScript
+ | InstallDir
+ -- Command line for creating a symbolic link
+ | LnS
+
+data SettingList = ConfCcArgs Stage
+ | ConfCppArgs Stage
+ | ConfGccLinkerArgs Stage
+ | ConfLdLinkerArgs Stage
+ | HsCppArgs
+
+-- | Maps 'Setting's to names in @cfg/system.config.in@.
+setting :: Setting -> Action String
+setting key = lookupValueOrError configFile $ case key of
+ BuildArch -> "build-arch"
+ BuildOs -> "build-os"
+ BuildPlatform -> "build-platform"
+ BuildVendor -> "build-vendor"
+ CcClangBackend -> "cc-clang-backend"
+ CcLlvmBackend -> "cc-llvm-backend"
+ DynamicExtension -> "dynamic-extension"
+ GhcMajorVersion -> "ghc-major-version"
+ GhcMinorVersion -> "ghc-minor-version"
+ GhcPatchLevel -> "ghc-patch-level"
+ GhcVersion -> "ghc-version"
+ GhcSourcePath -> "ghc-source-path"
+ HostArch -> "host-arch"
+ HostOs -> "host-os"
+ HostPlatform -> "host-platform"
+ HostVendor -> "host-vendor"
+ ProjectGitCommitId -> "project-git-commit-id"
+ ProjectName -> "project-name"
+ ProjectVersion -> "project-version"
+ ProjectVersionInt -> "project-version-int"
+ ProjectPatchLevel -> "project-patch-level"
+ ProjectPatchLevel1 -> "project-patch-level1"
+ ProjectPatchLevel2 -> "project-patch-level2"
+ TargetArch -> "target-arch"
+ TargetOs -> "target-os"
+ TargetPlatform -> "target-platform"
+ TargetPlatformFull -> "target-platform-full"
+ TargetVendor -> "target-vendor"
+ LlvmTarget -> "llvm-target"
+ FfiIncludeDir -> "ffi-include-dir"
+ FfiLibDir -> "ffi-lib-dir"
+ GmpIncludeDir -> "gmp-include-dir"
+ GmpLibDir -> "gmp-lib-dir"
+ IconvIncludeDir -> "iconv-include-dir"
+ IconvLibDir -> "iconv-lib-dir"
+ CursesLibDir -> "curses-lib-dir"
+ InstallPrefix -> "install-prefix"
+ InstallBinDir -> "install-bindir"
+ InstallLibDir -> "install-libdir"
+ InstallDataRootDir -> "install-datarootdir"
+ Install -> "install"
+ InstallDir -> "install-dir"
+ InstallProgram -> "install-program"
+ InstallScript -> "install-script"
+ InstallData -> "install-data"
+ LnS -> "ln-s"
+
+settingList :: SettingList -> Action [String]
+settingList key = fmap words $ lookupValueOrError configFile $ case key of
+ ConfCcArgs stage -> "conf-cc-args-" ++ stageString stage
+ ConfCppArgs stage -> "conf-cpp-args-" ++ stageString stage
+ ConfGccLinkerArgs stage -> "conf-gcc-linker-args-" ++ stageString stage
+ ConfLdLinkerArgs stage -> "conf-ld-linker-args-" ++ stageString stage
+ HsCppArgs -> "hs-cpp-args"
+
+-- | Get a configuration setting.
+getSetting :: Setting -> Expr c b String
+getSetting = expr . setting
+
+-- | Get a list of configuration settings.
+getSettingList :: SettingList -> Args c b
+getSettingList = expr . settingList
+
+matchSetting :: Setting -> [String] -> Action Bool
+matchSetting key values = (`elem` values) <$> setting key
+
+anyTargetPlatform :: [String] -> Action Bool
+anyTargetPlatform = matchSetting TargetPlatformFull
+
+anyTargetOs :: [String] -> Action Bool
+anyTargetOs = matchSetting TargetOs
+
+anyTargetArch :: [String] -> Action Bool
+anyTargetArch = matchSetting TargetArch
+
+anyHostOs :: [String] -> Action Bool
+anyHostOs = matchSetting HostOs
+
+iosHost :: Action Bool
+iosHost = anyHostOs ["ios"]
+
+osxHost :: Action Bool
+osxHost = anyHostOs ["darwin"]
+
+windowsHost :: Action Bool
+windowsHost = anyHostOs ["mingw32", "cygwin32"]
+
+ghcWithInterpreter :: Action Bool
+ghcWithInterpreter = do
+ goodOs <- anyTargetOs [ "mingw32", "cygwin32", "linux", "solaris2"
+ , "freebsd", "dragonfly", "netbsd", "openbsd"
+ , "darwin", "kfreebsdgnu" ]
+ goodArch <- anyTargetArch [ "i386", "x86_64", "powerpc", "sparc"
+ , "sparc64", "arm" ]
+ return $ goodOs && goodArch
+
+ghcEnableTablesNextToCode :: Action Bool
+ghcEnableTablesNextToCode = notM $ anyTargetArch ["ia64", "powerpc64", "powerpc64le"]
+
+useLibFFIForAdjustors :: Action Bool
+useLibFFIForAdjustors = notM $ anyTargetArch ["i386", "x86_64"]
+
+-- | Canonicalised GHC version number, used for integer version comparisons. We
+-- expand GhcMinorVersion to two digits by adding a leading zero if necessary.
+ghcCanonVersion :: Action String
+ghcCanonVersion = do
+ ghcMajorVersion <- setting GhcMajorVersion
+ ghcMinorVersion <- setting GhcMinorVersion
+ let leadingZero = [ '0' | length ghcMinorVersion == 1 ]
+ return $ ghcMajorVersion ++ leadingZero ++ ghcMinorVersion
+
+-- ref: https://ghc.haskell.org/trac/ghc/wiki/Building/Installing#HowGHCfindsitsfiles
+-- | On Windows we normally build a relocatable installation, which assumes that
+-- the library directory @libdir@ is in a fixed location relative to the GHC
+-- binary, namely @../lib@.
+relocatableBuild :: Action Bool
+relocatableBuild = windowsHost
+
+installDocDir :: Action String
+installDocDir = do
+ version <- setting ProjectVersion
+ dataDir <- setting InstallDataRootDir
+ return $ dataDir -/- ("doc/ghc-" ++ version)
+
+-- | Path to the GHC source tree.
+topDirectory :: Action FilePath
+topDirectory = fixAbsolutePathOnWindows =<< setting GhcSourcePath
+
+-- ref: mk/install.mk:101
+-- TODO: CroosCompilePrefix
+-- | Unix: override @libdir@ and @datadir@ to put GHC-specific files in a
+-- subdirectory with the version number included.
+installGhcLibDir :: Action String
+installGhcLibDir = do
+ rBuild <- relocatableBuild
+ libdir <- setting InstallLibDir
+ if rBuild then return libdir
+ else do
+ version <- setting ProjectVersion
+ return $ libdir -/- ("ghc-" ++ version)
+
+-- TODO: find out why we need version number in the dynamic suffix
+-- The current theory: dynamic libraries are eventually placed in a single
+-- giant directory in the load path of the dynamic linker, and hence we must
+-- distinguish different versions of GHC. In contrast static libraries live
+-- in their own per-package directory and hence do not need a unique filename.
+-- We also need to respect the system's dynamic extension, e.g. .dll or .so.
+libsuf :: Way -> Action String
+libsuf way =
+ if not (wayUnit Dynamic way)
+ then return $ waySuffix way ++ ".a" -- e.g., _p.a
+ else do
+ extension <- setting DynamicExtension -- e.g., .dll or .so
+ version <- setting ProjectVersion -- e.g., 7.11.20141222
+ let prefix = wayPrefix $ removeWayUnit Dynamic way
+ -- e.g., p_ghc7.11.20141222.dll (the result)
+ return $ prefix ++ "-ghc" ++ version ++ extension