ghcide-0.0.4: src/Development/IDE/GHC/Util.hs
-- Copyright (c) 2019 The DAML Authors. All rights reserved.
-- SPDX-License-Identifier: Apache-2.0
{-# OPTIONS_GHC -Wno-missing-fields #-} -- to enable prettyPrint
{-# LANGUAGE CPP #-}
#include "ghc-api-version.h"
-- | GHC utility functions. Importantly, code using our GHC should never:
--
-- * Call runGhc, use runGhcFast instead. It's faster and doesn't require config we don't have.
--
-- * Call setSessionDynFlags, use modifyDynFlags instead. It's faster and avoids loading packages.
module Development.IDE.GHC.Util(
lookupPackageConfig,
modifyDynFlags,
fakeDynFlags,
prettyPrint,
runGhcEnv,
textToStringBuffer,
moduleImportPath,
HscEnvEq, hscEnv, newHscEnvEq
) where
import Config
import Data.List.Extra
#if MIN_GHC_API_VERSION(8,6,0)
import Fingerprint
#endif
import GHC
import GhcMonad
import GhcPlugins hiding (Unique)
import Data.IORef
import Control.Exception
import FileCleanup
import Platform
import Data.Unique
import Development.Shake.Classes
import qualified Data.Text as T
import StringBuffer
import System.FilePath
import Development.IDE.Types.Location
----------------------------------------------------------------------
-- GHC setup
modifyDynFlags :: GhcMonad m => (DynFlags -> DynFlags) -> m ()
modifyDynFlags f = do
newFlags <- f <$> getSessionDynFlags
-- We do not use setSessionDynFlags here since we handle package
-- initialization separately.
modifySession $ \h ->
h { hsc_dflags = newFlags, hsc_IC = (hsc_IC h) {ic_dflags = newFlags} }
lookupPackageConfig :: UnitId -> HscEnv -> Maybe PackageConfig
lookupPackageConfig unitId env =
lookupPackage' False pkgConfigMap unitId
where
pkgConfigMap =
-- For some weird reason, the GHC API does not provide a way to get the PackageConfigMap
-- from PackageState so we have to wrap it in DynFlags first.
getPackageConfigMap $ hsc_dflags env
-- would be nice to do this more efficiently...
textToStringBuffer :: T.Text -> StringBuffer
textToStringBuffer = stringToStringBuffer . T.unpack
prettyPrint :: Outputable a => a -> String
prettyPrint = showSDoc fakeDynFlags . ppr
runGhcEnv :: HscEnv -> Ghc a -> IO a
runGhcEnv env act = do
filesToClean <- newIORef emptyFilesToClean
dirsToClean <- newIORef mempty
let dflags = (hsc_dflags env){filesToClean=filesToClean, dirsToClean=dirsToClean, useUnicode=True}
ref <- newIORef env{hsc_dflags=dflags}
unGhc act (Session ref) `finally` do
cleanTempFiles dflags
cleanTempDirs dflags
-- Fake DynFlags which are mostly undefined, but define enough to do a
-- little bit.
fakeDynFlags :: DynFlags
fakeDynFlags = defaultDynFlags settings mempty
where
settings = Settings
{ sTargetPlatform = platform
, sPlatformConstants = platformConstants
, sProgramName = "ghc"
, sProjectVersion = cProjectVersion
#if MIN_GHC_API_VERSION(8,6,0)
, sOpt_P_fingerprint = fingerprint0
#endif
}
platform = Platform
{ platformWordSize=8
, platformOS=OSUnknown
, platformUnregisterised=True
}
platformConstants = PlatformConstants
{ pc_DYNAMIC_BY_DEFAULT=False
, pc_WORD_SIZE=8
}
moduleImportPath :: NormalizedFilePath -> GHC.ParsedModule -> Maybe FilePath
-- The call to takeDirectory is required since DAML does not require that
-- the file name matches the module name in the last component.
-- Once that has changed we can get rid of this.
moduleImportPath (takeDirectory . fromNormalizedFilePath -> pathDir) pm
-- This happens for single-component modules since takeDirectory "A" == "."
| modDir == "." = Just pathDir
| otherwise = dropTrailingPathSeparator <$> stripSuffix modDir pathDir
where
ms = GHC.pm_mod_summary pm
mod' = GHC.ms_mod ms
-- A for module A.B
modDir =
takeDirectory $
fromNormalizedFilePath $ toNormalizedFilePath $
moduleNameSlashes $ GHC.moduleName mod'
-- | An HscEnv with equality.
data HscEnvEq = HscEnvEq Unique HscEnv
hscEnv :: HscEnvEq -> HscEnv
hscEnv (HscEnvEq _ x) = x
newHscEnvEq :: HscEnv -> IO HscEnvEq
newHscEnvEq e = do u <- newUnique; return $ HscEnvEq u e
instance Show HscEnvEq where
show (HscEnvEq a _) = "HscEnvEq " ++ show (hashUnique a)
instance Eq HscEnvEq where
HscEnvEq a _ == HscEnvEq b _ = a == b
instance NFData HscEnvEq where
rnf (HscEnvEq a b) = rnf (hashUnique a) `seq` b `seq` ()