Search code examples
haskellghcghc-api

Finding cabal packages when using the GHC API


I'm trying to make a program that typechecks haskell files for me using the GHC API. I've gotten the type checking to work for local files, but I have a specific cabal package that I need to be have available as well (the same package this executable will be a part of). How do add this import dependency?

I also tried compiling with ghc command line to figure this out, using ghc -package PKG-VER --make Test.hs -v but it only seems to look in the local directory for imports.

My current code looks like this:

import           Control.Exception
import           Control.Monad
import           Control.Monad.State
import           DynFlags
import           Exception
import           GHC
import           GHC.Paths           (libdir)
typecheck :: MonadIO m => [FilePath] -> FilePath -> m ()
typecheck otherincludes fp =
  liftIO . defaultErrorHandler defaultLogAction . runGhc (Just libdir) $ do
    dynflags <- getSessionDynFlags
    void $ setSessionDynFlags dynflags { includePaths = otherIncludes ++ includePaths dynflags }
    target <- guessTarget fp Nothing
    setTargets [target]
    void $ load LoadAllTargets
    deps <- depanal [] False
    forM_ deps $ \ms -> parseModule ms >>= typecheckModule

Solution

  • I managed to make your code read and typecheck itself as followS:

    package Test where
    import           Control.Exception
    import           Control.Monad
    import           Control.Monad.State
    import           DynFlags
    import           Exception
    import           GHC
    import           GHC.Paths           (libdir)
    typecheck :: MonadIO m => [FilePath] -> FilePath -> m ()
    typecheck otherincludes fp =
      liftIO . defaultErrorHandler defaultLogAction . runGhc (Just libdir) $ do
        dynflags <- getSessionDynFlags
        void $ setSessionDynFlags dynflags {
            includePaths = otherincludes ++ includePaths dynflags,
            packageFlags = [ExposePackage "ghc"]} }
        target <- guessTarget fp Nothing
        setTargets [target]
        void $ load LoadAllTargets
        deps <- depanal [] False
        forM_ deps $ \ms -> parseModule ms >>= typecheckModule
    

    here is how it runs in ghci:

    $ ghci Test.hs -package ghc
    GHCi, version 7.4.1: http://www.haskell.org/ghc/  :? for help
    Loading package ghc-prim ... linking ... done.
    Loading package integer-gmp ... linking ... done.
    Loading package base ... linking ... done.
    Loading package array-0.4.0.0 ... linking ... done.
    Loading package deepseq-1.3.0.0 ... linking ... done.
    Loading package containers-0.4.2.1 ... linking ... done.
    Loading package filepath-1.3.0.0 ... linking ... done.
    Loading package old-locale-1.0.0.4 ... linking ... done.
    Loading package old-time-1.1.0.0 ... linking ... done.
    Loading package bytestring-0.9.2.1 ... linking ... done.
    Loading package unix-2.5.1.0 ... linking ... done.
    Loading package directory-1.1.0.2 ... linking ... done.
    Loading package pretty-1.1.1.0 ... linking ... done.
    Loading package process-1.1.0.1 ... linking ... done.
    Loading package Cabal-1.14.0 ... linking ... done.
    Loading package binary-0.5.1.0 ... linking ... done.
    Loading package bin-package-db-0.0.0.0 ... linking ... done.
    Loading package hoopl-3.8.7.3 ... linking ... done.
    Loading package hpc-0.5.1.1 ... linking ... done.
    Loading package template-haskell ... linking ... done.
    Loading package ghc-7.4.1 ... linking ... done.
    Ok, modules loaded: Test.
    Prelude Test> typecheck [] "Test.hs"
    Loading package transformers-0.3.0.0 ... linking ... done.
    Loading package mtl-2.1.1 ... linking ... done.
    Prelude Test> 
    

    So the trick seems to be to pass the exposed packages in the dynflags argument to setSessionDynFlags. See the DynFlags module for some documentation.