{-# LANGUAGE CPP #-} {-# LANGUAGE ScopedTypeVariables #-} ----------------------------------------------------------------------------- -- -- Module : IDE.Package -- Copyright : (c) Juergen Nicklisch-Franken, Hamish Mackenzie -- License : GNU-GPL -- -- Maintainer : -- Stability : provisional -- Portability : portable -- -- -- | The packages methods of ide. -- --------------------------------------------------------------------------------- module IDE.Package ( packageConfig , packageConfig' , buildPackage , packageDoc , packageDoc' , packageClean , packageClean' , packageCopy , packageCopy' , packageRun , activatePackage , deactivatePackage , packageInstallDependencies , packageRegister , packageRegister' , packageTest , packageTest' , packageSdist , packageOpenDoc , getPackageDescriptionAndPath , getEmptyModuleTemplate , getModuleTemplate , addModuleToPackageDescr , delModuleFromPackageDescr , backgroundBuildToggled , runUnitTestsToggled , makeModeToggled , debugStart , printBindResultFlag , breakOnErrorFlag , breakOnExceptionFlag , printEvldWithShowFlag , tryDebug , tryDebugQuiet , executeDebugCommand , choosePackageFile , idePackageFromPath ) where import Graphics.UI.Gtk import Distribution.Package hiding (depends,packageId) import Distribution.PackageDescription import Distribution.PackageDescription.Parse import Distribution.PackageDescription.Configuration import Distribution.Verbosity import System.FilePath import Control.Concurrent import System.Directory (setCurrentDirectory, doesFileExist) import Prelude hiding (catch) import Data.Maybe (isNothing, isJust, fromJust) import Control.Exception (SomeException(..), catch) import IDE.Core.State import IDE.Utils.GUIUtils import IDE.Pane.Log import IDE.Pane.PackageEditor import IDE.Pane.SourceBuffer import IDE.Pane.PackageFlags (readFlags) import Distribution.Text (display) import IDE.Utils.FileUtils(getConfigFilePathForLoad) import IDE.LogRef import MyMissing (replace) import Distribution.ModuleName (ModuleName(..)) import Data.List (isInfixOf, nub, foldl', delete) import qualified System.IO.UTF8 as UTF8 (readFile) import IDE.Utils.Tool (ToolOutput(..), runTool, newGhci, ToolState(..)) import qualified Data.Set as Set (fromList) import qualified Data.Map as Map (empty, fromList) import System.Exit (ExitCode(..)) import Control.Applicative ((<$>)) import IDE.Utils.Tool (executeGhciCommand, getProcessExitCode, interruptProcessGroupOf, ProcessHandle) import qualified Data.Conduit as C (Sink) import qualified Data.Conduit.List as CL (foldM, fold) import qualified Data.Conduit.Util as CU (zipSinks) import Data.Conduit (($$)) import Control.Monad.Trans.Reader (ask) import Control.Monad.IO.Class (MonadIO(..)) import Control.Monad.Trans.Class (lift) import Control.Monad (when, unless, liftM) #if MIN_VERSION_Cabal(1,10,0) import Distribution.PackageDescription.PrettyPrintCopied (writeGenericPackageDescription) #endif import Debug.Trace (trace) import IDE.Pane.WebKit.Documentation (getDocumentation, loadDoc, reloadDoc) import Text.Printf (printf) import System.Log.Logger (debugM) import System.Process.Vado (getMountPoint, vado, readSettings) import qualified Data.Text as T (pack, isInfixOf) import IDE.Utils.ExternalTool (runExternalTool', runExternalTool, isRunning, interruptBuild) -- | Get the last item sinkLast = CL.fold (\_ a -> Just a) Nothing moduleInfo :: (a -> BuildInfo) -> (a -> [ModuleName]) -> a -> [(ModuleName, BuildInfo)] moduleInfo bi mods a = map (\m -> (m, buildInfo)) $ mods a where buildInfo = bi a #if MIN_VERSION_Cabal(1,8,0) myLibModules pd = case library pd of Nothing -> [] Just l -> moduleInfo libBuildInfo libModules l myExeModules pd = concatMap (moduleInfo buildInfo exeModules) (executables pd) #else myLibModules pd = moduleInfo libModules libBuildInfo pd myExeModules pd = moduleInfo exeModules buildInfo pd #endif #if MIN_VERSION_Cabal(1,10,0) myTestModules pd = concatMap (moduleInfo testBuildInfo (otherModules . testBuildInfo)) (testSuites pd) #endif activatePackage :: Maybe (IDEPackage, Maybe String) -> IDEM () activatePackage (Just (pack, mbExe)) = do liftIO $ debugM "leksah" "activatePackage" modifyIDE_ (\ide -> ide{activePack = Just pack, activeExe = mbExe}) liftIO $ setCurrentDirectory (dropFileName (ipdCabalFile pack)) triggerEventIDE (Sensitivity [(SensitivityProjectActive,True)]) mbWs <- readIDE workspace let wsStr = case mbWs of Nothing -> "" Just ws -> wsName ws let txt = wsStr ++ " > " ++ packageIdentifierToString (ipdPackageId pack) triggerEventIDE (StatusbarChanged [CompartmentPackage txt]) return () activatePackage Nothing = return () deactivatePackage :: IDEAction deactivatePackage = do liftIO $ debugM "leksah" "deactivatePackage" oldActivePack <- readIDE activePack modifyIDE_ (\ide -> ide{activePack = Nothing, activeExe = Nothing}) when (isJust oldActivePack) $ do triggerEventIDE (Sensitivity [(SensitivityProjectActive,False)]) return () mbWs <- readIDE workspace let wsStr = case mbWs of Nothing -> "" Just ws -> wsName ws let txt = wsStr ++ ":" triggerEventIDE (StatusbarChanged [CompartmentPackage txt]) return () packageConfig :: PackageAction packageConfig = do package <- ask lift $ packageConfig' package (\ _ -> return ()) packageConfig' :: IDEPackage -> (Bool -> IDEAction) -> IDEAction packageConfig' package continuation = do prefs <- readIDE prefs let dir = dropFileName (ipdCabalFile package) logLaunch <- getDefaultLogLaunch showDefaultLogLaunch' runExternalTool' (__ "Configuring") (cabalCommand prefs) (["configure"] ++ (ipdConfigFlags package)) dir $ do (mbLastOutput, _) <- CU.zipSinks sinkLast (logOutput logLaunch) lift $ do mbPack <- idePackageFromPath (ipdCabalFile package) case mbPack of Just pack -> do changePackage pack triggerEventIDE $ WorkspaceChanged False True continuation (mbLastOutput == Just (ToolExit ExitSuccess)) return () Nothing -> do postAsyncIDE $ ideMessage Normal (__ "Can't read package file") continuation False return() runCabalBuild :: Bool -> Bool -> Bool -> IDEPackage -> Bool -> (Bool -> IDEAction) -> IDEAction runCabalBuild backgroundBuild jumpToWarnings withoutLinking package shallConfigure continuation = do prefs <- readIDE prefs let dir = dropFileName (ipdCabalFile package) let args = (["build"] ++ if backgroundBuild && withoutLinking then ["--with-ld=false"] else [] ++ ipdBuildFlags package) runExternalTool' (__ "Building") (cabalCommand prefs) args dir $ do (mbLastOutput, (isConfigErr, _)) <- CU.zipSinks sinkLast $ CU.zipSinks isConfigError $ logOutputForBuild package backgroundBuild jumpToWarnings lift $ do errs <- readIDE errorRefs if shallConfigure && isConfigErr then packageConfig' package (\ b -> when b $ runCabalBuild backgroundBuild jumpToWarnings withoutLinking package False continuation) else do continuation (mbLastOutput == Just (ToolExit ExitSuccess)) return () isConfigError :: Monad m => C.Sink ToolOutput m Bool isConfigError = CL.foldM (\a b -> return $ a || isCErr b) False where isCErr (ToolError str) = str1 `T.isInfixOf` str || str2 `T.isInfixOf` str || str3 `T.isInfixOf` str isCErr _ = False str1 = T.pack (__ "Run the 'configure' command first") str2 = T.pack (__ "please re-configure") str3 = T.pack (__ "cannot satisfy -package-id") buildPackage :: Bool -> Bool -> Bool -> IDEPackage -> (Bool -> IDEAction) -> IDEAction buildPackage backgroundBuild jumpToWarnings withoutLinking package continuation = catchIDE (do ideR <- ask prefs <- readIDE prefs maybeDebug <- readIDE debugState case maybeDebug of Nothing -> do alreadyRunning <- isRunning if alreadyRunning then do interruptBuild when (not backgroundBuild) $ liftIO $ do timeoutAddFull (do reflectIDE (do buildPackage backgroundBuild jumpToWarnings withoutLinking package continuation return False) ideR return False) priorityDefaultIdle 1000 return () else runCabalBuild backgroundBuild jumpToWarnings withoutLinking package True continuation Just debug@(_, ghci) -> do -- TODO check debug package matches active package ready <- liftIO $ isEmptyMVar (currentToolCommand ghci) when ready $ do let dir = dropFileName (ipdCabalFile package) when (saveAllBeforeBuild prefs) (do fileSaveAll belongsToWorkspace; return ()) (`runDebug` debug) . executeDebugCommand ":reload" $ do errs <- logOutputForBuild package backgroundBuild jumpToWarnings unless (any isError $ errs) $ do cmd <- lift $ readIDE autoCommand liftIO . postGUISync $ reflectIDE cmd ideR lift $ continuation True ) (\(e :: SomeException) -> sysMessage Normal (show e)) packageDoc :: PackageAction packageDoc = do package <- ask lift $ packageDoc' False True package (\ _ -> return ()) packageDoc' :: Bool -> Bool -> IDEPackage -> (Bool -> IDEAction) -> IDEAction packageDoc' backgroundBuild jumpToWarnings package continuation = do prefs <- readIDE prefs catchIDE (do let dir = dropFileName (ipdCabalFile package) runExternalTool' (__ "Documenting") (cabalCommand prefs) (["haddock"] ++ (ipdHaddockFlags package)) dir $ do (mbLastOutput, _) <- CU.zipSinks sinkLast $ logOutputForBuild package backgroundBuild jumpToWarnings lift $ reloadDoc lift $ continuation (mbLastOutput == Just (ToolExit ExitSuccess))) (\(e :: SomeException) -> putStrLn (show e)) packageClean :: PackageAction packageClean = do package <- ask lift $ packageClean' package (\ _ -> return ()) packageClean' :: IDEPackage -> (Bool -> IDEAction) -> IDEAction packageClean' package continuation = do prefs <- readIDE prefs logLaunch <- getDefaultLogLaunch showDefaultLogLaunch' let dir = dropFileName (ipdCabalFile package) runExternalTool' (__ "Cleaning") (cabalCommand prefs) ["clean"] dir $ do (mbLastOutput, _) <- CU.zipSinks sinkLast (logOutput logLaunch) lift $ continuation (mbLastOutput == Just (ToolExit ExitSuccess)) packageCopy :: PackageAction packageCopy = do package <- ask logLaunch <- lift $ getDefaultLogLaunch lift $ showDefaultLogLaunch' lift $ catchIDE (do prefs <- readIDE prefs window <- getMainWindow mbDir <- liftIO $ chooseDir window (__ "Select the target directory") Nothing case mbDir of Nothing -> return () Just fp -> do let dir = dropFileName (ipdCabalFile package) runExternalTool' (__ "Copying") (cabalCommand prefs) (["copy"] ++ ["--destdir=" ++ fp]) dir (logOutput logLaunch)) (\(e :: SomeException) -> putStrLn (show e)) packageInstallDependencies :: PackageAction packageInstallDependencies = do package <- ask logLaunch <- lift $ getDefaultLogLaunch lift $ showDefaultLogLaunch' lift $ catchIDE (do prefs <- readIDE prefs let dir = dropFileName (ipdCabalFile package) runExternalTool' (__ "Installing") (cabalCommand prefs) ( (if useCabalDev prefs then ["install-deps"] else ["install","--only-dependencies"]) ++ (ipdConfigFlags package) ++ (ipdInstallFlags package)) dir (logOutput logLaunch)) (\(e :: SomeException) -> putStrLn (show e)) packageCopy' :: IDEPackage -> (Bool -> IDEAction) -> IDEAction packageCopy' package continuation = do prefs <- readIDE prefs logLaunch <- getDefaultLogLaunch showDefaultLogLaunch' catchIDE (do let dir = dropFileName (ipdCabalFile package) runExternalTool' (__ "Copying") (cabalCommand prefs) (["copy"] ++ (ipdInstallFlags package)) dir $ do (mbLastOutput, _) <- CU.zipSinks sinkLast (logOutput logLaunch) lift $ continuation (mbLastOutput == Just (ToolExit ExitSuccess))) (\(e :: SomeException) -> putStrLn (show e)) packageRun :: PackageAction packageRun = do package <- ask lift $ catchIDE (do ideR <- ask maybeDebug <- readIDE debugState pd <- liftIO $ readPackageDescription normal (ipdCabalFile package) >>= return . flattenPackageDescription mbExe <- readIDE activeExe let exes = filter (isActiveExe mbExe) $ executables pd case maybeDebug of Nothing -> do case exes of (Executable name _ _):_ -> do (logLaunch,logName) <- buildLogLaunchByName name let path = "dist/build" name name let dir = dropFileName (ipdCabalFile package) IDE.Package.runPackage (addLogLaunchData logName logLaunch) (printf (__ "Running %s") name) path (ipdExeFlags package) dir (logOutput logLaunch) otherwise -> do sysMessage Normal (__ "no executable in selected package") return () Just debug -> do -- TODO check debug package matches active package case exes of (Executable name mainFilePath _):_ -> do (logLaunch,logName) <- buildLogLaunchByName name runDebug (do executeDebugCommand (":module *" ++ (map (\c -> if c == '/' then '.' else c) (takeWhile (/= '.') mainFilePath))) (logOutput logLaunch) executeDebugCommand (":main " ++ (unwords (ipdExeFlags package))) (logOutput logLaunch)) debug otherwise -> do sysMessage Normal (__ "no executable in selected package") return ()) (\(e :: SomeException) -> putStrLn (show e)) where isActiveExe Nothing _ = True isActiveExe (Just selected) (Executable name _ _) = selected == name isActiveExe _ _ = False packageRegister :: PackageAction packageRegister = do package <- ask lift $ packageRegister' package (\ _ -> return ()) packageRegister' :: IDEPackage -> (Bool -> IDEAction) -> IDEAction packageRegister' package continuation = if ipdHasLibs package then do logLaunch <- getDefaultLogLaunch showDefaultLogLaunch' catchIDE (do prefs <- readIDE prefs let dir = dropFileName (ipdCabalFile package) runExternalTool' (__ "Registering") (cabalCommand prefs) (["register"] ++ (ipdRegisterFlags package)) dir $ do (mbLastOutput, _) <- CU.zipSinks sinkLast (logOutput logLaunch) lift $ continuation (mbLastOutput == Just (ToolExit ExitSuccess))) (\(e :: SomeException) -> putStrLn (show e)) else continuation True packageTest :: PackageAction packageTest = do package <- ask lift $ packageTest' package (\ _ -> return ()) packageTest' :: IDEPackage -> (Bool -> IDEAction) -> IDEAction packageTest' package continuation = if "--enable-tests" `elem` ipdConfigFlags package then do logLaunch <- getDefaultLogLaunch showDefaultLogLaunch' catchIDE (do prefs <- readIDE prefs let dir = dropFileName (ipdCabalFile package) runExternalTool' (__ "Testing") (cabalCommand prefs) (["test"] ++ (ipdTestFlags package)) dir $ do (mbLastOutput, _) <- CU.zipSinks sinkLast (logOutput logLaunch) lift $ continuation (mbLastOutput == Just (ToolExit ExitSuccess))) (\(e :: SomeException) -> putStrLn (show e)) else continuation True packageSdist :: PackageAction packageSdist = do package <- ask logLaunch <- lift $ getDefaultLogLaunch lift $ showDefaultLogLaunch' lift $ catchIDE (do prefs <- readIDE prefs let dir = dropFileName (ipdCabalFile package) runExternalTool' (__ "Source Dist") (cabalCommand prefs) (["sdist"] ++ (ipdSdistFlags package)) dir (logOutput logLaunch)) (\(e :: SomeException) -> putStrLn (show e)) packageOpenDoc :: PackageAction packageOpenDoc = do package <- ask logLaunch <- lift $ getDefaultLogLaunch lift $ showDefaultLogLaunch' lift $ do prefs <- readIDE prefs let path = dropFileName (ipdCabalFile package) "dist/doc/html" display (pkgName (ipdPackageId package)) "index.html" dir = dropFileName (ipdCabalFile package) #ifdef WEBKITGTK loadDoc ("file:///" ++ dir path) getDocumentation Nothing >>= \ p -> displayPane p False #else openBrowser path #endif `catchIDE` (\(e :: SomeException) -> putStrLn (show e)) runPackage :: (ProcessHandle -> IDEAction) -> String -> FilePath -> [String] -> FilePath -> C.Sink ToolOutput IDEM () -> IDEAction runPackage = runExternalTool (return True) -- TODO here one could check if package to be run is building/configuring/etc atm -- --------------------------------------------------------------------- -- | * Utility functions/procedures, that have to do with packages -- getPackageDescriptionAndPath :: IDEM (Maybe (PackageDescription,FilePath)) getPackageDescriptionAndPath = do active <- readIDE activePack case active of Nothing -> do ideMessage Normal (__ "No active package") return Nothing Just p -> do ideR <- ask reifyIDE (\ideR -> catch (do pd <- readPackageDescription normal (ipdCabalFile p) return (Just (flattenPackageDescription pd,ipdCabalFile p))) (\(e :: SomeException) -> do reflectIDE (ideMessage Normal ((__ "Can't load package ") ++(show e))) ideR return Nothing)) getEmptyModuleTemplate :: PackageDescription -> String -> IO String getEmptyModuleTemplate pd modName = getModuleTemplate "module" pd modName "" "" getModuleTemplate :: String -> PackageDescription -> String -> String -> String -> IO String getModuleTemplate template pd modName exports body = catch (do dataDir <- getDataDir filePath <- getConfigFilePathForLoad (template ++ leksahTemplateFileExtension) Nothing dataDir template <- UTF8.readFile filePath return (foldl' (\ a (from, to) -> replace from to a) template [ ("@License@" , (display . license) pd) , ("@Maintainer@" , maintainer pd) , ("@Stability@" , stability pd) , ("@Portability@" , "") , ("@Copyright@" , copyright pd) , ("@ModuleName@" , modName) , ("@ModuleExports@", exports) , ("@ModuleBody@" , body)])) (\ (e :: SomeException) -> sysMessage Normal (printf (__ "Couldn't read template file: %s") (show e)) >> return "") #if MIN_VERSION_Cabal(1,10,0) addModuleToPackageDescr :: ModuleName -> Bool -> PackageAction addModuleToPackageDescr moduleName isExposed = do p <- ask lift $ reifyIDE (\ideR -> catch (do gpd <- readPackageDescription normal (ipdCabalFile p) let npd = if isExposed && isJust (condLibrary gpd) then gpd{ condLibrary = Just (addModToLib moduleName (fromJust (condLibrary gpd))), condExecutables = map (addModToBuildInfoExe moduleName) (condExecutables gpd)} else gpd{ condLibrary = case condLibrary gpd of Nothing -> Nothing Just lib -> Just (addModToBuildInfoLib moduleName (fromJust (condLibrary gpd))), condExecutables = map (addModToBuildInfoExe moduleName) (condExecutables gpd)} writeGenericPackageDescription (ipdCabalFile p) npd) (\(e :: SomeException) -> do reflectIDE (ideMessage Normal ((__ "Can't update package ") ++ show e)) ideR return ())) addModToLib :: ModuleName -> CondTree ConfVar [Dependency] Library -> CondTree ConfVar [Dependency] Library addModToLib modName ct@CondNode{condTreeData = lib} = ct{condTreeData = lib{exposedModules = modName : exposedModules lib}} addModToBuildInfoLib :: ModuleName -> CondTree ConfVar [Dependency] Library -> CondTree ConfVar [Dependency] Library addModToBuildInfoLib modName ct@CondNode{condTreeData = lib} = ct{condTreeData = lib{libBuildInfo = (libBuildInfo lib){otherModules = modName : otherModules (libBuildInfo lib)}}} addModToBuildInfoExe :: ModuleName -> (String, CondTree ConfVar [Dependency] Executable) -> (String, CondTree ConfVar [Dependency] Executable) addModToBuildInfoExe modName (str,ct@CondNode{condTreeData = exe}) = (str, ct{condTreeData = exe{buildInfo = (buildInfo exe){otherModules = modName : otherModules (buildInfo exe)}}}) -------------------------------------------------------------------------- delModuleFromPackageDescr :: ModuleName -> PackageAction delModuleFromPackageDescr moduleName = trace ("addModule " ++ show moduleName) $ do p <- ask lift $ reifyIDE (\ideR -> catch (do gpd <- readPackageDescription normal (ipdCabalFile p) let isExposedAndJust = isExposedModule moduleName (condLibrary gpd) let npd = if isExposedAndJust then gpd{ condLibrary = Just (delModFromLib moduleName (fromJust (condLibrary gpd))), condExecutables = map (delModFromBuildInfoExe moduleName) (condExecutables gpd)} else gpd{ condLibrary = case condLibrary gpd of Nothing -> Nothing Just lib -> Just (delModFromBuildInfoLib moduleName (fromJust (condLibrary gpd))), condExecutables = map (delModFromBuildInfoExe moduleName) (condExecutables gpd)} writeGenericPackageDescription (ipdCabalFile p) npd) (\(e :: SomeException) -> do reflectIDE (ideMessage Normal ((__ "Can't update package ") ++ show e)) ideR return ())) delModFromLib :: ModuleName -> CondTree ConfVar [Dependency] Library -> CondTree ConfVar [Dependency] Library delModFromLib modName ct@CondNode{condTreeData = lib} = ct{condTreeData = lib{exposedModules = delete modName (exposedModules lib)}} delModFromBuildInfoLib :: ModuleName -> CondTree ConfVar [Dependency] Library -> CondTree ConfVar [Dependency] Library delModFromBuildInfoLib modName ct@CondNode{condTreeData = lib} = ct{condTreeData = lib{libBuildInfo = (libBuildInfo lib){otherModules = delete modName (otherModules (libBuildInfo lib))}}} delModFromBuildInfoExe :: ModuleName -> (String, CondTree ConfVar [Dependency] Executable) -> (String, CondTree ConfVar [Dependency] Executable) delModFromBuildInfoExe modName (str,ct@CondNode{condTreeData = exe}) = (str, ct{condTreeData = exe{buildInfo = (buildInfo exe){otherModules = delete modName (otherModules (buildInfo exe))}}}) isExposedModule :: ModuleName -> Maybe (CondTree ConfVar [Dependency] Library) -> Bool isExposedModule mn Nothing = False isExposedModule mn (Just CondNode{condTreeData = lib}) = elem mn (exposedModules lib) #else -- Old version to support older Cabal addModuleToPackageDescr :: ModuleName -> Bool -> PackageAction addModuleToPackageDescr moduleName isExposed = do p <- ask lift $ reifyIDE (\ideR -> catch (do gpd <- readPackageDescription normal (ipdCabalFile p) if hasConfigs gpd then do reflectIDE (ideMessage High (__ "Cabal file with configurations can't be automatically updated with the current version of Leksah")) ideR else let pd = flattenPackageDescription gpd npd = if isExposed && isJust (library pd) then pd{library = Just ((fromJust (library pd)){exposedModules = moduleName : exposedModules (fromJust $ library pd)})} else let npd1 = case library pd of Nothing -> pd Just lib -> pd{library = Just (lib{libBuildInfo = addModToBuildInfo (libBuildInfo lib) moduleName})} in npd1{executables = map (\exe -> exe{buildInfo = addModToBuildInfo (buildInfo exe) moduleName}) (executables npd1)} in writePackageDescription (ipdCabalFile p) npd) (\(e :: SomeException) -> do reflectIDE (ideMessage Normal ((__ "Can't upade package ") ++ show e)) ideR return ())) where addModToBuildInfo :: BuildInfo -> ModuleName -> BuildInfo addModToBuildInfo bi mn = bi {otherModules = mn : otherModules bi} -- Old version to support older Cabal delModuleFromPackageDescr :: ModuleName -> PackageAction delModuleFromPackageDescr moduleName = do p <- ask lift $ reifyIDE (\ideR -> catch (do gpd <- readPackageDescription normal (ipdCabalFile p) if hasConfigs gpd then do reflectIDE (ideMessage High (__ "Cabal file with configurations can't be automatically updated with the current version of Leksah")) ideR else let pd = flattenPackageDescription gpd isExposedAndJust = isExposedModule pd moduleName npd = if isExposedAndJust then pd{library = Just ((fromJust (library pd)){exposedModules = delete moduleName (exposedModules (fromJust $ library pd))})} else let npd1 = case library pd of Nothing -> pd Just lib -> pd{library = Just (lib{libBuildInfo = delModFromBuildInfo (libBuildInfo lib) moduleName})} in npd1{executables = map (\exe -> exe{buildInfo = delModFromBuildInfo (buildInfo exe) moduleName}) (executables npd1)} in writePackageDescription (ipdCabalFile p) npd) (\(e :: SomeException) -> do reflectIDE (ideMessage Normal ((__ "Can't update package ") ++ show e)) ideR return ())) where delModFromBuildInfo :: BuildInfo -> ModuleName -> BuildInfo delModFromBuildInfo bi mn = bi {otherModules = delete mn (otherModules bi)} -- Old version to support older Cabal isExposedModule :: PackageDescription -> ModuleName -> Bool isExposedModule pd mn = do if isJust (library pd) then elem mn (exposedModules (fromJust $ library pd)) else False #endif backgroundBuildToggled :: IDEAction backgroundBuildToggled = do toggled <- getBackgroundBuildToggled modifyIDE_ (\ide -> ide{prefs = (prefs ide){backgroundBuild= toggled}}) runUnitTestsToggled :: IDEAction runUnitTestsToggled = do toggled <- getRunUnitTests modifyIDE_ (\ide -> ide{prefs = (prefs ide){runUnitTests= toggled}}) makeModeToggled :: IDEAction makeModeToggled = do toggled <- getMakeModeToggled modifyIDE_ (\ide -> ide{prefs = (prefs ide){makeMode= toggled}}) -- --------------------------------------------------------------------- -- | * Debug code that needs to use the package -- interactiveFlag :: String -> Bool -> String interactiveFlag name f = (if f then "-f" else "-fno-") ++ name printEvldWithShowFlag :: Bool -> String printEvldWithShowFlag = interactiveFlag "print-evld-with-show" breakOnExceptionFlag :: Bool -> String breakOnExceptionFlag = interactiveFlag "break-on-exception" breakOnErrorFlag :: Bool -> String breakOnErrorFlag = interactiveFlag "break-on-error" printBindResultFlag :: Bool -> String printBindResultFlag = interactiveFlag "print-bind-result" interactiveFlags :: Prefs -> [String] interactiveFlags prefs = (printEvldWithShowFlag $ printEvldWithShow prefs) : (breakOnExceptionFlag $ breakOnException prefs) : (breakOnErrorFlag $ breakOnError prefs) : [printBindResultFlag $ printBindResult prefs] debugStart :: PackageAction debugStart = do package <- ask lift $ catchIDE (do ideRef <- ask prefs' <- readIDE prefs maybeDebug <- readIDE debugState case maybeDebug of Nothing -> do let dir = dropFileName (ipdCabalFile package) mbExe <- readIDE activeExe ghci <- reifyIDE $ \ideR -> newGhci dir mbExe (interactiveFlags prefs') $ reflectIDEI (logOutputForBuild package True False >> return ()) ideR modifyIDE_ (\ide -> ide {debugState = Just (package, ghci)}) triggerEventIDE (Sensitivity [(SensitivityInterpreting, True)]) setDebugToggled True -- Fork a thread to wait for the output from the process to close liftIO $ forkIO $ do readMVar (outputClosed ghci) postGUISync . (`reflectIDE` ideRef) $ do setDebugToggled False modifyIDE_ (\ide -> ide {debugState = Nothing, autoCommand = return ()}) triggerEventIDE (Sensitivity [(SensitivityInterpreting, False)]) -- Kick of a build if one is not already due modifiedPacks <- fileCheckAll belongsToPackage let modified = not (null modifiedPacks) prefs <- readIDE prefs when ((not modified) && (backgroundBuild prefs)) $ do -- So although none of the pakages are modified, -- they may have been modified in ghci mode. -- Lets build to make sure the binaries are up to date mbPackage <- readIDE activePack case mbPackage of Just package -> runCabalBuild True False False package True (\ _ -> return ()) Nothing -> return () return () _ -> do sysMessage Normal (__ "Debugger already running") return ()) (\(e :: SomeException) -> putStrLn (show e)) tryDebug :: DebugAction -> PackageAction tryDebug f = do maybeDebug <- lift $ readIDE debugState case maybeDebug of Just debug -> do -- TODO check debug package matches active package lift $ runDebug f debug _ -> do window <- lift $ getMainWindow resp <- liftIO $ do md <- messageDialogNew (Just window) [] MessageQuestion ButtonsCancel (__ "GHCi debugger is not running.") dialogAddButton md (__ "_Start GHCi") (ResponseUser 1) dialogSetDefaultResponse md (ResponseUser 1) set md [ windowWindowPosition := WinPosCenterOnParent ] resp <- dialogRun md widgetDestroy md return resp case resp of ResponseUser 1 -> do debugStart maybeDebug <- lift $ readIDE debugState case maybeDebug of Just debug -> lift $ postAsyncIDE $ runDebug f debug _ -> return () _ -> return () tryDebugQuiet :: DebugAction -> PackageAction tryDebugQuiet f = do maybeDebug <- lift $ readIDE debugState case maybeDebug of Just debug -> do -- TODO check debug package matches active package lift $ runDebug f debug _ -> do return () executeDebugCommand :: String -> (C.Sink ToolOutput IDEM ()) -> DebugAction executeDebugCommand command handler = do (_, ghci) <- ask lift $ do reifyIDE $ \ideR -> do liftIO $ postGUIAsync $ reflectIDE (do triggerEventIDE (StatusbarChanged [CompartmentState command, CompartmentBuild True]) return ()) ideR executeGhciCommand ghci command $ do reflectIDEI handler ideR liftIO $ postGUIAsync $ reflectIDE (do triggerEventIDE (StatusbarChanged [CompartmentState "", CompartmentBuild False]) return ()) ideR return () allBuildInfo' :: PackageDescription -> [BuildInfo] #if MIN_VERSION_Cabal(1,10,0) allBuildInfo' pkg_descr = [ libBuildInfo lib | Just lib <- [library pkg_descr] ] ++ [ buildInfo exe | exe <- executables pkg_descr ] ++ [ testBuildInfo tst | tst <- testSuites pkg_descr ] testMainPath (TestSuiteExeV10 _ f) = [f] testMainPath _ = [] #else allBuildInfo' = allBuildInfo #endif idePackageFromPath :: FilePath -> IDEM (Maybe IDEPackage) idePackageFromPath filePath = do mbPackageD <- reifyIDE (\ideR -> catch (do pd <- readPackageDescription normal filePath return (Just (flattenPackageDescription pd))) (\ (e :: SomeException) -> do reflectIDE (ideMessage Normal ((__ "Can't activate package ") ++(show e))) ideR return Nothing)) case mbPackageD of Nothing -> return Nothing Just packageD -> do let modules = Map.fromList $ myLibModules packageD ++ myExeModules packageD #if MIN_VERSION_Cabal(1,10,0) ++ myTestModules packageD #endif let mainFiles = [ (modulePath exe, buildInfo exe, False) | exe <- executables packageD ] #if MIN_VERSION_Cabal(1,10,0) ++ [ (f, bi, True) | TestSuite _ (TestSuiteExeV10 _ f) bi _ <- testSuites packageD ] #endif let files = Set.fromList $ extraSrcFiles packageD let srcDirs = case (nub $ concatMap hsSourceDirs (allBuildInfo' packageD)) of [] -> [".","src"] l -> l let exes = [ exeName e | e <- executables packageD , buildable (buildInfo e) ] #if MIN_VERSION_Cabal(1,10,0) let exts = nub $ concatMap oldExtensions (allBuildInfo' packageD) let tests = [ testName t | t <- testSuites packageD , buildable (testBuildInfo t) ] #else let exts = nub $ concatMap extensions (allBuildInfo' packageD) let tests = [] #endif let packp = IDEPackage { ipdPackageId = package packageD, ipdCabalFile = filePath, ipdDepends = buildDepends packageD, ipdModules = modules, ipdHasLibs = hasLibs packageD, ipdExes = exes, ipdTests = tests, ipdMain = mainFiles, ipdExtraSrcs = files, ipdSrcDirs = srcDirs, ipdExtensions = exts, ipdConfigFlags = ["--user", "--enable-tests"], ipdBuildFlags = [], ipdTestFlags = [], ipdHaddockFlags = [], ipdExeFlags = [], ipdInstallFlags = [], ipdRegisterFlags = [], ipdUnregisterFlags = [], ipdSdistFlags = []} let pfile = dropExtension filePath pack <- (do flagFileExists <- liftIO $ doesFileExist (pfile ++ leksahFlagFileExtension) if flagFileExists then liftIO $ readFlags (pfile ++ leksahFlagFileExtension) packp else return packp) return (Just pack)