buildwrapper-0.5.0: src/Language/Haskell/BuildWrapper/API.hs
{-# LANGUAGE CPP, ScopedTypeVariables #-}
-- |
-- Module : Language.Haskell.BuildWrapper.API
-- Author : JP Moresmau
-- Copyright : (c) JP Moresmau 2011
-- License : BSD3
--
-- Maintainer : jpmoresmau@gmail.com
-- Stability : beta
-- Portability : portable
--
-- API entry point, with all exposed methods
module Language.Haskell.BuildWrapper.API where
import Language.Haskell.BuildWrapper.Base
import Language.Haskell.BuildWrapper.Cabal
import qualified Language.Haskell.BuildWrapper.GHC as BwGHC
import Language.Haskell.BuildWrapper.GHCStorage
import Language.Haskell.BuildWrapper.Src
import qualified Data.Text as T
import Prelude hiding (readFile, writeFile)
import System.IO.UTF8
import Control.Monad.State
import Language.Haskell.Exts.Annotated
import Language.Preprocessor.Cpphs
import Data.Maybe
import System.Directory
import System.FilePath
import GHC (TypecheckedSource)
-- | copy all files from the project to the temporary folder
synchronize :: Bool -- ^ if true copy all files, if false only copy files newer than their corresponding temp files
-> BuildWrapper(OpResult [FilePath]) -- ^ return the list of files copied
synchronize force =do
cf<-gets cabalFile
m<-copyFromMain force $ takeFileName cf
(fileList,ns)<-getFilesToCopy
m1<-mapM (copyFromMain force)(
"Setup.hs":
"Setup.lhs":
fileList)
return (catMaybes (m : m1), ns)
-- | synchronize one file only
synchronize1 :: Bool -- ^ always copy the file, if false only copy the file if it is newer than its corresponding temp file
-> FilePath -- ^ the source file in the project folder
-> BuildWrapper(Maybe FilePath) -- ^ return Nothing if no copy or Just file if copied
synchronize1 force fp = do
m1<-mapM (copyFromMain force) [fp]
return $ head m1
-- | write contents to temporary file
write :: FilePath -- ^ the source file in the project folder
-> String -- ^ the contents
-> BuildWrapper()
write fp s= do
real<-getTargetPath fp
--liftIO $ putStrLn ("contents:"++s)
liftIO $ writeFile real s
-- | run cabal configure
configure :: WhichCabal -- ^ use the source or temp cabal
-> BuildWrapper (OpResult Bool) -- ^ True if configure succeeded
configure which= do
(mlbi,msgs)<-cabalConfigure which
return (isJust mlbi,msgs)
-- | run cabal build
build :: Bool -- ^ do we want output (True) or just compilation without linking?
-> WhichCabal -- ^ use original cabal or temp cabal file
-> BuildWrapper (OpResult BuildResult)
build = cabalBuild
-- | build one source file in GHC
build1 :: FilePath -- ^ the source file
-> BuildWrapper (OpResult Bool) -- ^ True if build is successful
build1 fp=do
(mtm,msgs)<-getGHCAST fp
return (isJust mtm,msgs)
-- | preprocess a file
preproc :: BuildFlags -- ^ the build flags
-> FilePath -- ^ the file to preprocess
-> IO String -- ^ the resulting code
preproc bf tgt= do
inputOrig<-readFile tgt
let epo=parseOptions $ bf_preproc bf
case epo of
Right opts2->runCpphs opts2 tgt inputOrig
Left _->return inputOrig
-- | get the build flags for a source file
getBuildFlags :: FilePath -- ^ the source file
-> BuildWrapper (OpResult BuildFlags)
getBuildFlags fp=do
tgt<-getTargetPath fp
src<-getCabalFile Source
modSrc<-liftIO $ getModificationTime src
mbf<-liftIO $ readBuildFlagsInfo tgt modSrc
case mbf of
Just bf-> return bf
Nothing -> do
(mcbi,bwns)<-getBuildInfo fp
ret<-case mcbi of
Just cbi->do
(_,opts2)<-fileGhcOptions cbi
let
(modName,opts)=cabalExtensions $ snd cbi
lit=".lhs" == takeExtension fp
cppo=fileCppOptions (snd cbi) ++ ["-D__GLASGOW_HASKELL__=" ++ show (__GLASGOW_HASKELL__ :: Int)] ++ ["--unlit" | lit]
modS=moduleToString modName
return (BuildFlags (opts ++ opts2) cppo (Just modS),bwns)
Nothing -> return (BuildFlags knownExtensionNames [] Nothing,[])
liftIO $ storeBuildFlagsInfo tgt ret
return ret
-- | get haskell-src-exts commented AST for source file
getAST :: FilePath -- ^ the source file
-> BuildWrapper (OpResult (Maybe (ParseResult (Module SrcSpanInfo, [Comment]))))
getAST fp=do
(bf,ns)<-getBuildFlags fp
tgt<-getTargetPath fp
input<-liftIO $ preproc bf tgt
pr<- liftIO $ getHSEAST input (bf_ast bf)
return (Just pr,ns)
-- | get GHC typechecked AST for source file
getGHCAST :: FilePath -- ^ the source file
-> BuildWrapper (OpResult (Maybe TypecheckedSource))
getGHCAST fp = withGHCAST' fp (\_->BwGHC.getAST)
-- | perform an action on the GHC AST
withGHCAST :: FilePath -- ^ the source file
-> (FilePath -- ^ the source file
-> FilePath -- ^ the base directory
-> String -- ^ the module name
-> [String] -- ^ the GHC options
-> IO a)
-> BuildWrapper (OpResult (Maybe a))
withGHCAST fp f=withGHCAST' fp (\n a b c d->do
r<- f a b c d
return (Just r,n))
withGHCAST' :: FilePath -- ^ the source file
-> ([BWNote] -- ^ the notes from getting the flags
-> FilePath -- ^ the source file
-> FilePath -- ^ the base directory
-> String -- ^ the module name
-> [String] -- ^ the GHC options
-> IO (OpResult (Maybe a))) -> BuildWrapper (OpResult (Maybe a))
withGHCAST' fp f= do
(bf,ns)<-getBuildFlags fp
case bf of
(BuildFlags opts _ (Just modS))-> do
tgt<-getTargetPath fp
temp<-getFullTempDir
liftIO $ do
cd<-getCurrentDirectory
setCurrentDirectory temp
(pr,bwns2)<- f [] tgt temp modS opts
setCurrentDirectory cd
return (pr,ns ++ bwns2)
_ -> return (Nothing,ns)
-- | get outline for source file
getOutline :: FilePath -- ^ source file
-> BuildWrapper (OpResult OutlineResult)
getOutline fp=do
(mast,bwns)<-getAST fp
case mast of
Just (ParseOk ast)->do
liftIO $ Prelude.print ast
let ods=getHSEOutline ast
let (es,is)=getHSEImportExport ast
return (OutlineResult ods es is,bwns)
Just (ParseFailed failLoc err)->return (OutlineResult [] [] [],BWNote BWError err (BWLocation fp (srcLine failLoc) (srcColumn failLoc)) :bwns)
_ -> return (OutlineResult [] [] [],bwns)
-- | get lexer token types for source file
getTokenTypes :: FilePath -- ^ the source file
-> BuildWrapper (OpResult [TokenDef])
getTokenTypes fp=do
tgt<-getTargetPath fp
ett<-liftIO $ do
input<-readFile tgt
BwGHC.tokenTypesArbitrary tgt input (".lhs" == takeExtension fp) knownExtensionNames
case ett of
Right tt->return (tt,[])
Left bw -> return ([],[bw])
-- ^ get all occurrences of a token in the file
getOccurrences :: FilePath -- ^ the source file
-> String -- ^ the token to search for
-> BuildWrapper (OpResult [TokenDef])
getOccurrences fp query=do
(BuildFlags opts _ _, _)<-getBuildFlags fp
tgt<-getTargetPath fp
input<-liftIO $ readFile tgt
ett<-liftIO $ BwGHC.occurrences tgt input (T.pack query) (".lhs" == takeExtension fp) opts
case ett of
Right tt->return (tt,[])
Left bw -> return ([],[bw])
-- | get thing at point
getThingAtPoint :: FilePath -- ^ the source file
-> Int -- ^ the line
-> Int -- ^ the column
-- -> Bool -- ^ do we want the result qualified?
-- -> Bool -- ^ do we want the result typed?
-> BuildWrapper (OpResult (Maybe ThingAtPoint))
getThingAtPoint fp line col=do
mm<-withGHCAST fp $ BwGHC.getThingAtPointJSON line col
return $ case mm of
(Just m,ns)->(m,ns)
(Nothing,ns)-> (Nothing,ns)
-- | get all names in scope (GHC API)
getNamesInScope :: FilePath-> BuildWrapper (OpResult (Maybe [String]))
getNamesInScope fp=withGHCAST fp BwGHC.getGhcNamesInScope
-- | get cabal dependencies
getCabalDependencies :: BuildWrapper (OpResult [(FilePath,[CabalPackage])])
getCabalDependencies = cabalDependencies
-- | get cabal components
getCabalComponents :: BuildWrapper (OpResult [CabalComponent])
getCabalComponents = cabalComponents