Skip to content
Snippets Groups Projects
Forked from Glasgow Haskell Compiler / GHC
This fork has diverged from the upstream repository.
  • Rodrigo Mesquita's avatar
    3fdb18f8
    Hardwire a better unit-id for ghc · 3fdb18f8
    Rodrigo Mesquita authored and Marge Bot's avatar Marge Bot committed
    Previously, the unit-id of ghc-the-library was fixed as `ghc`.
    This was done primarily because the compiler must know the unit-id of
    some packages (including ghc) a-priori to define wired-in names.
    
    However, as seen in #20742, a reinstallable `ghc` whose unit-id is fixed
    to `ghc` might result in subtle bugs when different ghc's interact.
    
    A good example of this is having GHC_A load a plugin compiled by GHC_B,
    where GHC_A and GHC_B are linked to ghc-libraries that are ABI
    incompatible. Without a distinction between the unit-id of the ghc library
    GHC_A is linked against and the ghc library the plugin it is loading was
    compiled against, we can't check compatibility.
    
    This patch gives a slightly better unit-id to ghc (ghc-version) by
    (1) Not setting -this-unit-id to ghc, but rather to the new unit-id (modulo stage0)
    (2) Adding a definition to `GHC.Settings.Config` whose value is the new unit-id.
        (2.1) `GHC.Settings.Config` is generated by Hadrian
        (2.2) and also by cabal through `compiler/Setup.hs`
    This unit-id definition is imported by `GHC.Unit.Types` and used to
    set the wired-in unit-id of "ghc", which was previously fixed to "ghc"
    
    The commits following this one will improve the unit-id with a
    cabal-style package hash and check compatibility when loading plugins.
    
    Note that we also ensure that ghc's unit key matches unit id both when
    hadrian or cabal builds ghc, and in this way we no longer need to add
    `ghc` to the WiringMap.
    3fdb18f8
    History
    Hardwire a better unit-id for ghc
    Rodrigo Mesquita authored and Marge Bot's avatar Marge Bot committed
    Previously, the unit-id of ghc-the-library was fixed as `ghc`.
    This was done primarily because the compiler must know the unit-id of
    some packages (including ghc) a-priori to define wired-in names.
    
    However, as seen in #20742, a reinstallable `ghc` whose unit-id is fixed
    to `ghc` might result in subtle bugs when different ghc's interact.
    
    A good example of this is having GHC_A load a plugin compiled by GHC_B,
    where GHC_A and GHC_B are linked to ghc-libraries that are ABI
    incompatible. Without a distinction between the unit-id of the ghc library
    GHC_A is linked against and the ghc library the plugin it is loading was
    compiled against, we can't check compatibility.
    
    This patch gives a slightly better unit-id to ghc (ghc-version) by
    (1) Not setting -this-unit-id to ghc, but rather to the new unit-id (modulo stage0)
    (2) Adding a definition to `GHC.Settings.Config` whose value is the new unit-id.
        (2.1) `GHC.Settings.Config` is generated by Hadrian
        (2.2) and also by cabal through `compiler/Setup.hs`
    This unit-id definition is imported by `GHC.Unit.Types` and used to
    set the wired-in unit-id of "ghc", which was previously fixed to "ghc"
    
    The commits following this one will improve the unit-id with a
    cabal-style package hash and check compatibility when loading plugins.
    
    Note that we also ensure that ghc's unit key matches unit id both when
    hadrian or cabal builds ghc, and in this way we no longer need to add
    `ghc` to the WiringMap.
Setup.hs 6.19 KiB
{-# LANGUAGE RecordWildCards #-}
module Main where

import Distribution.Simple
import Distribution.Simple.BuildPaths
import Distribution.Types.ComponentLocalBuildInfo
import Distribution.Types.ComponentName (ComponentName(CLibName))
import Distribution.Types.LocalBuildInfo
import Distribution.Types.LibraryName (LibraryName(LMainLibName))
import Distribution.Verbosity
import Distribution.Simple.Program
import Distribution.Simple.Utils
import Distribution.Simple.Setup

import System.IO
import System.Process
import System.Directory
import System.FilePath
import Control.Monad
import Data.Char
import qualified Data.Map as Map
import GHC.ResponseFile
import System.Environment

main :: IO ()
main = defaultMainWithHooks ghcHooks
  where
    ghcHooks = simpleUserHooks
      { postConf = \args cfg pd lbi -> do
          let verbosity = fromFlagOrDefault minBound (configVerbosity cfg)
          ghcAutogen verbosity lbi
          postConf simpleUserHooks args cfg pd lbi
      }

-- Mapping from primop-*.hs-incl file to command
primopIncls :: [(String,String)]
primopIncls =
    [ ("primop-data-decl.hs-incl"         , "--data-decl")
    , ("primop-tag.hs-incl"               , "--primop-tag")
    , ("primop-list.hs-incl"              , "--primop-list")
    , ("primop-has-side-effects.hs-incl"  , "--has-side-effects")
    , ("primop-out-of-line.hs-incl"       , "--out-of-line")
    , ("primop-commutable.hs-incl"        , "--commutable")
    , ("primop-code-size.hs-incl"         , "--code-size")
    , ("primop-can-fail.hs-incl"          , "--can-fail")
    , ("primop-strictness.hs-incl"        , "--strictness")
    , ("primop-fixity.hs-incl"            , "--fixity")
    , ("primop-primop-info.hs-incl"       , "--primop-primop-info")
    , ("primop-vector-uniques.hs-incl"    , "--primop-vector-uniques")
    , ("primop-vector-tys.hs-incl"        , "--primop-vector-tys")
    , ("primop-vector-tys-exports.hs-incl", "--primop-vector-tys-exports")
    , ("primop-vector-tycons.hs-incl"     , "--primop-vector-tycons")
    , ("primop-docs.hs-incl"              , "--wired-in-docs")
    ]

ghcAutogen :: Verbosity -> LocalBuildInfo -> IO ()
ghcAutogen verbosity lbi@LocalBuildInfo{..} = do
  -- Get compiler/ root directory from the cabal file
  let Just compilerRoot = takeDirectory <$> pkgDescrFile

  -- Require the necessary programs
  (gcc   ,withPrograms) <- requireProgram normal gccProgram withPrograms
  (ghc   ,withPrograms) <- requireProgram normal ghcProgram withPrograms

  settings <- read <$> getProgramOutput normal ghc ["--info"]
  -- We are reinstalling GHC
  -- Write primop-*.hs-incl
  let hsCppOpts = case lookup "Haskell CPP flags" settings of
        Just fs -> unescapeArgs fs
        Nothing -> []
      primopsTxtPP = compilerRoot </> "GHC/Builtin/primops.txt.pp"
      cppOpts = hsCppOpts ++ ["-P","-x","c"]
      cppIncludes = map ("-I"++) [compilerRoot]
  -- Preprocess primops.txt.pp
  primopsStr <- getProgramOutput normal gcc (cppOpts ++ cppIncludes ++ [primopsTxtPP])
  -- Call genprimopcode to generate *.hs-incl
  forM_ primopIncls $ \(file,command) -> do
    contents <- readProcess "genprimopcode" [command] primopsStr
    rewriteFileEx verbosity (buildDir </> file) contents

  -- Write GHC.Platform.Constants
  let platformConstantsPath = autogenPackageModulesDir lbi </> "GHC/Platform/Constants.hs"
      targetOS = case lookup "target os" settings of
        Nothing -> error "no target os in settings"
        Just os -> os
  createDirectoryIfMissingVerbose verbosity True (takeDirectory platformConstantsPath)
  withTempFile (takeDirectory platformConstantsPath) "Constants_tmp.hs" $ \tmp h -> do
    hClose h
    callProcess "deriveConstants" ["--gen-haskell-type","-o",tmp,"--target-os",targetOS]
    renameFile tmp platformConstantsPath

  let cProjectUnitId = case Map.lookup (CLibName LMainLibName) componentNameMap of
                         Just [LibComponentLocalBuildInfo{componentUnitId}] -> unUnitId componentUnitId
                         _ -> error "Couldn't find unique cabal library when building ghc"

  -- Write GHC.Settings.Config
      configHsPath = autogenPackageModulesDir lbi </> "GHC/Settings/Config.hs"
      configHs = generateConfigHs cProjectUnitId settings
  createDirectoryIfMissingVerbose verbosity True (takeDirectory configHsPath)
  rewriteFileEx verbosity configHsPath configHs

getSetting :: [(String,String)] -> String -> String -> Either String String
getSetting settings kh kr = go settings kr
  where
    go settings k =  case lookup k settings of
      Nothing -> Left (show k ++ " not found in settings: " ++ show settings)
      Just v -> Right v

generateConfigHs :: String -- ^ ghc's cabal-generated unit-id, which matches its package-id/key
                 -> [(String,String)] -> String
generateConfigHs cProjectUnitId settings = either error id $ do
    let getSetting' = getSetting $ (("cStage","2"):) settings
    buildPlatform  <- getSetting' "cBuildPlatformString" "Host platform"
    hostPlatform   <- getSetting' "cHostPlatformString" "Target platform"
    cProjectName   <- getSetting' "cProjectName" "Project name"
    cBooterVersion <- getSetting' "cBooterVersion" "Project version"
    cStage         <- getSetting' "cStage" "cStage"
    return $ unlines
        [ "module GHC.Settings.Config"
        , "  ( module GHC.Version"
        , "  , cBuildPlatformString"
        , "  , cHostPlatformString"
        , "  , cProjectName"
        , "  , cBooterVersion"
        , "  , cStage"
        , "  , cProjectUnitId"
        , "  ) where"
        , ""
        , "import GHC.Prelude.Basic"
        , ""
        , "import GHC.Version"
        , ""
        , "cBuildPlatformString :: String"
        , "cBuildPlatformString = " ++ show buildPlatform
        , ""
        , "cHostPlatformString :: String"
        , "cHostPlatformString = " ++ show hostPlatform
        , ""
        , "cProjectName          :: String"
        , "cProjectName          = " ++ show cProjectName
        , ""
        , "cBooterVersion        :: String"
        , "cBooterVersion        = " ++ show cBooterVersion
        , ""
        , "cStage                :: String"
        , "cStage                = show ("++ cStage ++ " :: Int)"
        , ""
        , "cProjectUnitId :: String"
        , "cProjectUnitId = " ++ show cProjectUnitId
        ]