{-# LANGUAGE TemplateHaskell, ScopedTypeVariables, TupleSections, OverloadedStrings #-} -- | Invoke the executable that calls cabal functions and communicate -- with it via RPC. module IdeSession.ExeCabalClient ( invokeExeCabal ) where import Data.Monoid ((<>)) import qualified Data.Text as Text import System.Exit (ExitCode(..)) import IdeSession.Cabal import IdeSession.Config import IdeSession.GHC.API import IdeSession.RPC.Client (RpcServer, RpcConversation(..), forkRpcServer, rpcConversation, shutdown, findProgram) import IdeSession.State import IdeSession.Types.Progress import IdeSession.Types.Public (UpdateStatus(..)) import IdeSession.Util -- | Invoke the executable that processes our custom functions that use -- the machinery of the cabal library. invokeExeCabal :: IdeStaticInfo -> IdeCallbacks -> ExeCabalRequest -> (UpdateStatus -> IO ()) -> IO ExitCode invokeExeCabal ideStaticInfo@IdeStaticInfo{..} ideCallbacks args callback = do let logFunc = ideCallbacksLogFunc ideCallbacks mLoc <- findProgram logFunc searchPath ide_backend_exe_cabal case mLoc of Nothing -> fail $ "Could not find ide-backend-exe-cabal" Just prog -> do env <- envWithPathOverride configExtraPathDirs let cwd = case args of ReqExeDoc{} -> ideSessionDir _ -> ideDataDir ideStaticInfo server <- forkRpcServer prog [] (Just cwd) env exitCode <- rpcRunExeCabal server args callback shutdown server -- not long-running return exitCode where (searchPath,ide_backend_exe_cabal) = configIdeBackendExeCabal SessionConfig{..} = ideConfig rpcRunExeCabal :: RpcServer -> ExeCabalRequest -> (UpdateStatus -> IO ()) -> IO ExitCode rpcRunExeCabal server req callback = rpcConversation server $ \RpcConversation{..} -> do put req let go = do response <- get case response of ExeCabalProgress pcounter -> do callback (UpdateStatusProgress pcounter) go ExeCabalDone ec@ExitSuccess -> do callback UpdateStatusDone return ec ExeCabalDone ec@(ExitFailure code) -> do callback $ UpdateStatusFailed $ "Cabal exited with code " <> Text.pack (show code) return ec go