-- | One of the challenges of using the GHC API for external tooling -- is handling integration with Cabal. This library provides a simple -- interface for configuring GHC's 'DynFlags' as Cabal would have, -- allowing seamless tooling use on Cabal projects. -- -- A typical usage might look like, -- -- > import GHC -- > import qualified GHC.Paths -- > import qualified Distribution.Verbosity as Verbosity -- > import GHC.Cabal -- > -- > main = runGhc (Just GHC.Paths.libdir) $ do -- > dflags <- GHC.getSessionDynFlags -- > -- Use default DynFlags if we aren't in a Cabal project -- > dflags' <- fromMaybe dflags <$> liftIO (initCabalDynFlags Verbosity.normal dflags) -- > GHC.setSessionDynFlags dflags' -- > -- > -- Standard GHC API usage goes here -- module GHC.Cabal ( -- * Initializing GHC DynFlags for Cabal packages initCabalDynFlags ) where import Control.Monad (guard, msum, mzero) import Control.Monad.Trans.Maybe (MaybeT, runMaybeT) import Control.Monad.Trans.Class import Control.Applicative ((<|>)) import Distribution.Verbosity import Distribution.Simple.Utils (defaultPackageDesc, warn, debug, findPackageDesc) import Distribution.Simple.Program (defaultProgramConfiguration) import qualified Distribution.Simple.Setup as Setup import Distribution.PackageDescription as PD import Distribution.PackageDescription.Parse as PD import Distribution.PackageDescription.Configuration (finalizePackageDescription) import Distribution.Simple.LocalBuildInfo as LBI import qualified Distribution.Simple.Configure as Configure import qualified Distribution.Simple.Compiler as Compiler import qualified Distribution.Simple.GHC as CGHC import qualified Distribution.Simple.Program.GHC as CGHC import DynFlags (DynFlags, parseDynamicFlagsCmdLine) import qualified SrcLoc data CabalDetails = CabalDetails { cdLocalBuildInfo :: LocalBuildInfo } -- | Modify a set of 'DynFlags' to match what Cabal would produce. initCabalDynFlags :: Verbosity -> DynFlags -> IO (Maybe DynFlags) initCabalDynFlags verbosity dflags0 = runMaybeT $ do let warnNoCabal _err = lift (warn verbosity "Couldn't find cabal file") >> mzero pdfile <- either warnNoCabal pure =<< lift (findPackageDesc ".") gpkg_descr <- lift $ PD.readPackageDescription verbosity pdfile lbi <- lift $ Configure.getPersistBuildConfig Setup.defaultDistPref let programsConfig = defaultProgramConfiguration (comp, compPlatform, programsConfig') <- lift $ Configure.configCompilerEx (Just Compiler.GHC) Nothing Nothing (withPrograms lbi) (lessVerbose verbosity) -- TODO: is any of this correct? let pkg_descr = case finalizePackageDescription [] (const True) compPlatform (Compiler.compilerInfo comp) [] gpkg_descr of Right (pd,_) -> pd -- This shouldn't happen since we claim dependencies can always be satisfied Left err -> error "missing dependencies" let comp :: Maybe (PD.BuildInfo, LBI.ComponentLocalBuildInfo) comp = msum [libraryComp, executableComp] libraryComp = do lib <- PD.library pkg_descr let bi = PD.libBuildInfo lib guard $ PD.buildable bi return (bi, getComponentLocalBuildInfo lbi CLibName) executableComp = msum $ flip map (PD.executables pkg_descr) $ \exec->do let bi = PD.buildInfo exec guard $ PD.buildable bi return (bi, getComponentLocalBuildInfo lbi (LBI.CExeName $ PD.exeName exec)) case comp of Just (bi, clbi) -> lift $ initCabalDynFlags' verbosity lbi bi clbi dflags0 Nothing -> do lift $ warn verbosity $ "Found no buildable components in "++pdfile mzero initCabalDynFlags' :: Verbosity -> LocalBuildInfo -> BuildInfo -> ComponentLocalBuildInfo -> DynFlags -> IO DynFlags initCabalDynFlags' verbosity lbi bi clbi dflags0 = do debug verbosity $ "initCabalDynFlags': Flags = "++show rendered (dflags, leftovers, warnings) <- DynFlags.parseDynamicFlagsCmdLine dflags0 (map SrcLoc.noLoc rendered) putStrLn $ unlines $ map SrcLoc.unLoc warnings return dflags where baseOpts = CGHC.componentGhcOptions verbosity lbi bi clbi (buildDir lbi) rendered = CGHC.renderGhcOptions (LBI.compiler lbi) baseOpts