Fix race condition in stack support code

This commit is contained in:
Daniel Gröber 2015-09-11 03:48:52 +02:00
parent 12d65ba11f
commit 211b957451
9 changed files with 74 additions and 65 deletions

View File

@ -3,7 +3,7 @@
module Language.Haskell.GhcMod ( module Language.Haskell.GhcMod (
-- * Cradle -- * Cradle
Cradle(..) Cradle(..)
, ProjectType(..) , Project(..)
, findCradle , findCradle
-- * Options -- * Options
, Options(..) , Options(..)

View File

@ -20,7 +20,6 @@ module Language.Haskell.GhcMod.CabalHelper
( getComponents ( getComponents
, getGhcMergedPkgOptions , getGhcMergedPkgOptions
, getCabalPackageDbStack , getCabalPackageDbStack
, getStackPackageDbStack
, prepareCabalHelper , prepareCabalHelper
) )
#endif #endif
@ -44,7 +43,6 @@ import Language.Haskell.GhcMod.Logging
import Language.Haskell.GhcMod.Output import Language.Haskell.GhcMod.Output
import Language.Haskell.GhcMod.CustomPackageDb import Language.Haskell.GhcMod.CustomPackageDb
import System.FilePath import System.FilePath
import System.Directory (findExecutable)
import System.Process import System.Process
import System.Exit import System.Exit
import Prelude hiding ((.)) import Prelude hiding ((.))
@ -136,30 +134,18 @@ prepareCabalHelper = do
let projdir = cradleRootDir crdl let projdir = cradleRootDir crdl
distdir = projdir </> cradleDistDir crdl distdir = projdir </> cradleDistDir crdl
readProc <- gmReadProcess readProc <- gmReadProcess
when (cradleProjectType crdl == CabalProject || cradleProjectType crdl == StackProject) $ when (isCabalHelperProject $ cradleProject crdl) $
withCabal $ liftIO $ prepare readProc projdir distdir withCabal $ liftIO $ prepare readProc projdir distdir
getStackPackageDbStack :: IOish m => m [GhcPkgDb]
getStackPackageDbStack = do
mstack <- liftIO $ findExecutable "stack"
case mstack of
Nothing -> return []
Just stack -> do
snapshotDb <- liftIO $ readProcess stack ["path", "--snapshot-pkg-db"] ""
localDb <- liftIO $ readProcess stack ["path", "--local-pkg-db"] ""
return $ map (PackageDb . takeWhile (/='\n')) [snapshotDb, localDb]
patchStackPrograms :: (IOish m, GmOut m) => Cradle -> Programs -> m Programs patchStackPrograms :: (IOish m, GmOut m) => Cradle -> Programs -> m Programs
patchStackPrograms crdl progs patchStackPrograms Cradle { cradleProject = (StackProject senv) } progs = do
| cradleProjectType crdl /= StackProject = return progs Just ghc <- getStackGhcPath senv
patchStackPrograms crdl progs = do Just ghcPkg <- getStackGhcPkgPath senv
let projdir = cradleRootDir crdl
Just ghc <- getStackGhcPath projdir
Just ghcPkg <- getStackGhcPkgPath projdir
return $ progs { return $ progs {
ghcProgram = ghc ghcProgram = ghc
, ghcPkgProgram = ghcPkg , ghcPkgProgram = ghcPkg
} }
patchStackPrograms _crdl progs = return progs
withCabal :: (IOish m, GmEnv m, GmOut m, GmLog m) => m a -> m a withCabal :: (IOish m, GmEnv m, GmOut m, GmLog m) => m a -> m a
withCabal action = do withCabal action = do
@ -188,7 +174,7 @@ withCabal action = do
Nothing -> return False Nothing -> return False
projType <- cradleProjectType <$> cradle proj <- cradleProject <$> cradle
when (isSetupConfigOutOfDate mCabalFile mCabalConfig) $ when (isSetupConfigOutOfDate mCabalFile mCabalConfig) $
gmLog GmDebug "" $ strDoc $ "setup configuration is out of date, reconfiguring Cabal project." gmLog GmDebug "" $ strDoc $ "setup configuration is out of date, reconfiguring Cabal project."
@ -202,14 +188,14 @@ withCabal action = do
when ( isSetupConfigOutOfDate mCabalFile mCabalConfig when ( isSetupConfigOutOfDate mCabalFile mCabalConfig
|| pkgDbStackOutOfSync || pkgDbStackOutOfSync
|| isSetupConfigOutOfDate mCabalSandboxConfig mCabalConfig) $ || isSetupConfigOutOfDate mCabalSandboxConfig mCabalConfig) $
case projType of case proj of
CabalProject -> CabalProject ->
cabalReconfigure readProc (optPrograms opts) crdl projdir distdir cabalReconfigure readProc (optPrograms opts) crdl projdir distdir
StackProject -> StackProject {} ->
stackReconfigure crdl (optPrograms opts) stackReconfigure crdl (optPrograms opts)
_ -> _ ->
error $ "withCabal: unsupported project type: " ++ show projType error $ "withCabal: unsupported project type: " ++ show proj
action action

View File

@ -69,7 +69,7 @@ cabalCradle wdir = do
let cabalDir = takeDirectory cabalFile let cabalDir = takeDirectory cabalFile
return Cradle { return Cradle {
cradleProjectType = CabalProject cradleProject = CabalProject
, cradleCurrentDir = wdir , cradleCurrentDir = wdir
, cradleRootDir = cabalDir , cradleRootDir = cabalDir
, cradleTempDir = error "tmpDir" , cradleTempDir = error "tmpDir"
@ -89,22 +89,22 @@ stackCradle wdir = do
-- rather than stack, or maybe that's just me ;) -- rather than stack, or maybe that's just me ;)
whenM (liftIO $ doesFileExist $ setupConfigPath "dist") $ mzero whenM (liftIO $ doesFileExist $ setupConfigPath "dist") $ mzero
distDir <- MaybeT $ getStackDistDir cabalDir senv <- MaybeT $ getStackEnv cabalDir
return Cradle { return Cradle {
cradleProjectType = StackProject cradleProject = StackProject senv
, cradleCurrentDir = wdir , cradleCurrentDir = wdir
, cradleRootDir = cabalDir , cradleRootDir = cabalDir
, cradleTempDir = error "tmpDir" , cradleTempDir = error "tmpDir"
, cradleCabalFile = Just cabalFile , cradleCabalFile = Just cabalFile
, cradleDistDir = distDir , cradleDistDir = seDistDir senv
} }
sandboxCradle :: IOish m => FilePath -> MaybeT m Cradle sandboxCradle :: IOish m => FilePath -> MaybeT m Cradle
sandboxCradle wdir = do sandboxCradle wdir = do
sbDir <- MaybeT $ liftIO $ findCabalSandboxDir wdir sbDir <- MaybeT $ liftIO $ findCabalSandboxDir wdir
return Cradle { return Cradle {
cradleProjectType = SandboxProject cradleProject = SandboxProject
, cradleCurrentDir = wdir , cradleCurrentDir = wdir
, cradleRootDir = sbDir , cradleRootDir = sbDir
, cradleTempDir = error "tmpDir" , cradleTempDir = error "tmpDir"
@ -115,7 +115,7 @@ sandboxCradle wdir = do
plainCradle :: IOish m => FilePath -> MaybeT m Cradle plainCradle :: IOish m => FilePath -> MaybeT m Cradle
plainCradle wdir = do plainCradle wdir = do
return $ Cradle { return $ Cradle {
cradleProjectType = PlainProject cradleProject = PlainProject
, cradleCurrentDir = wdir , cradleCurrentDir = wdir
, cradleRootDir = wdir , cradleRootDir = wdir
, cradleTempDir = error "tmpDir" , cradleTempDir = error "tmpDir"

View File

@ -26,9 +26,9 @@ debugInfo = do
Cradle {..} <- cradle Cradle {..} <- cradle
cabal <- cabal <-
case cradleProjectType of case cradleProject of
CabalProject -> cabalDebug CabalProject -> cabalDebug
StackProject -> (++) <$> stackPaths <*> cabalDebug StackProject {} -> (++) <$> stackPaths <*> cabalDebug
_ -> return [] _ -> return []
pkgOpts <- packageGhcOptions pkgOpts <- packageGhcOptions
@ -45,9 +45,9 @@ debugInfo = do
stackPaths :: IOish m => GhcModT m [String] stackPaths :: IOish m => GhcModT m [String]
stackPaths = do stackPaths = do
Cradle {..} <- cradle Cradle { cradleProject = StackProject senv } <- cradle
Just ghc <- getStackGhcPath cradleRootDir ghc <- getStackGhcPath senv
Just ghcPkg <- getStackGhcPkgPath cradleRootDir ghcPkg <- getStackGhcPkgPath senv
return $ return $
[ "Stack ghc executable: " ++ show ghc [ "Stack ghc executable: " ++ show ghc
, "Stack ghc-pkg executable:" ++ show ghcPkg , "Stack ghc-pkg executable:" ++ show ghcPkg
@ -64,7 +64,7 @@ cabalDebug = do
return $ return $
[ "Cabal file: " ++ show cradleCabalFile [ "Cabal file: " ++ show cradleCabalFile
, "Cabal Project Type: " ++ show cradleProjectType , "Project: " ++ show cradleProject
, "Cabal entrypoints:\n" ++ render (nest 4 $ , "Cabal entrypoints:\n" ++ render (nest 4 $
mapDoc gmComponentNameDoc smpDoc entrypoints) mapDoc gmComponentNameDoc smpDoc entrypoints)
, "Cabal components:\n" ++ render (nest 4 $ , "Cabal components:\n" ++ render (nest 4 $

View File

@ -65,9 +65,9 @@ getGhcPkgProgram :: IOish m => GhcModT m FilePath
getGhcPkgProgram = do getGhcPkgProgram = do
crdl <- cradle crdl <- cradle
progs <- optPrograms <$> options progs <- optPrograms <$> options
case cradleProjectType crdl of case cradleProject crdl of
StackProject -> do (StackProject senv) -> do
Just ghcPkg <- getStackGhcPkgPath (cradleRootDir crdl) Just ghcPkg <- getStackGhcPkgPath senv
return ghcPkg return ghcPkg
_ -> _ ->
return $ ghcPkgProgram progs return $ ghcPkgProgram progs
@ -77,7 +77,7 @@ getPackageDbStack :: IOish m => GhcModT m [GhcPkgDb]
getPackageDbStack = do getPackageDbStack = do
crdl <- cradle crdl <- cradle
mCusPkgStack <- getCustomPkgDbStack mCusPkgStack <- getCustomPkgDbStack
stack <- case cradleProjectType crdl of stack <- case cradleProject crdl of
PlainProject -> PlainProject ->
return [GlobalDb, UserDb] return [GlobalDb, UserDb]
SandboxProject -> do SandboxProject -> do
@ -85,8 +85,8 @@ getPackageDbStack = do
return $ [GlobalDb, db] return $ [GlobalDb, db]
CabalProject -> CabalProject ->
getCabalPackageDbStack getCabalPackageDbStack
StackProject -> (StackProject StackEnv {..}) ->
getStackPackageDbStack return $ map PackageDb [seSnapshotPkgDb, seLocalPkgDb]
return $ fromMaybe stack mCusPkgStack return $ fromMaybe stack mCusPkgStack
getPackageCachePaths :: IOish m => FilePath -> GhcModT m [FilePath] getPackageCachePaths :: IOish m => FilePath -> GhcModT m [FilePath]

View File

@ -27,6 +27,7 @@ import Control.Monad
import Control.Monad.Trans.Maybe import Control.Monad.Trans.Maybe
import Control.Monad.Trans.Class import Control.Monad.Trans.Class
import Data.List import Data.List
import Data.List.Split
import Data.Char import Data.Char
import Data.Maybe import Data.Maybe
import Data.Traversable hiding (mapM) import Data.Traversable hiding (mapM)
@ -85,22 +86,29 @@ findStackConfigFile dir = do
Just (d, Just a) -> return $ Just $ d </> a Just (d, Just a) -> return $ Just $ d </> a
Just (_, Nothing) -> error "findStackConfigFile" Just (_, Nothing) -> error "findStackConfigFile"
getStackDistDir :: (IOish m, GmOut m) => FilePath -> m (Maybe FilePath) getStackEnv :: (IOish m, GmOut m) => FilePath -> m (Maybe StackEnv)
getStackDistDir projdir = U.withDirectory_ projdir $ runMaybeT $ do getStackEnv projdir = U.withDirectory_ projdir $ runMaybeT $ do
takeWhile (/='\n') <$> readStack ["path", "--dist-dir"] env <- map (liToTup . splitOn ": ") . lines <$> readStack ["path"]
let look k = fromJust $ lookup k env
return StackEnv {
seDistDir = look "dist-dir"
, seBinPath = splitSearchPath $ look "bin-path"
, seSnapshotPkgDb = look "snapshot-pkg-db"
, seLocalPkgDb = look "local-pkg-db"
}
where
liToTup [k,v] = (k,v)
liToTup _ = error "getStackEnv"
getStackGhcPath :: (IOish m, GmOut m) => FilePath -> m (Maybe FilePath) getStackGhcPath :: IOish m => StackEnv -> m (Maybe FilePath)
getStackGhcPath = findExecutablesInStackBinPath "ghc" getStackGhcPath = findExecutablesInStackBinPath "ghc"
getStackGhcPkgPath :: (IOish m, GmOut m) => FilePath -> m (Maybe FilePath) getStackGhcPkgPath :: IOish m => StackEnv -> m (Maybe FilePath)
getStackGhcPkgPath = findExecutablesInStackBinPath "ghc-pkg" getStackGhcPkgPath = findExecutablesInStackBinPath "ghc-pkg"
findExecutablesInStackBinPath :: (IOish m, GmOut m) => String -> FilePath -> m (Maybe FilePath) findExecutablesInStackBinPath :: IOish m => String -> StackEnv -> m (Maybe FilePath)
findExecutablesInStackBinPath exe projdir = findExecutablesInStackBinPath exe StackEnv {..} =
U.withDirectory_ projdir $ runMaybeT $ do liftIO $ listToMaybe <$> findExecutablesInDirectories' seBinPath exe
path <- splitSearchPath . takeWhile (/='\n')
<$> readStack ["path", "--bin-path"]
MaybeT $ liftIO $ listToMaybe <$> findExecutablesInDirectories' path exe
findExecutablesInDirectories' :: [FilePath] -> String -> IO [FilePath] findExecutablesInDirectories' :: [FilePath] -> String -> IO [FilePath]
findExecutablesInDirectories' path binary = findExecutablesInDirectories' path binary =

View File

@ -150,10 +150,10 @@ targetGhcOptions :: forall m. IOish m
targetGhcOptions crdl sefnmn = do targetGhcOptions crdl sefnmn = do
when (Set.null sefnmn) $ error "targetGhcOptions: no targets given" when (Set.null sefnmn) $ error "targetGhcOptions: no targets given"
case cradleProjectType crdl of case cradleProject crdl of
CabalProject -> cabalOpts crdl proj
StackProject -> cabalOpts crdl | isCabalHelperProject proj -> cabalOpts crdl
_ -> sandboxOpts crdl | otherwise -> sandboxOpts crdl
where where
zipMap f l = l `zip` (f `map` l) zipMap f l = l `zip` (f `map` l)
@ -267,10 +267,10 @@ packageGhcOptions :: (Applicative m, IOish m, Gm m)
=> m [GHCOption] => m [GHCOption]
packageGhcOptions = do packageGhcOptions = do
crdl <- cradle crdl <- cradle
case cradleProjectType crdl of case cradleProject crdl of
CabalProject -> getGhcMergedPkgOptions proj
StackProject -> getGhcMergedPkgOptions | isCabalHelperProject proj -> getGhcMergedPkgOptions
_ -> sandboxOpts crdl | otherwise -> sandboxOpts crdl
-- also works for plain projects! -- also works for plain projects!
sandboxOpts :: (IOish m, GmEnv m) => Cradle -> m [String] sandboxOpts :: (IOish m, GmEnv m) => Cradle -> m [String]

View File

@ -139,12 +139,27 @@ defaultOptions = Options {
---------------------------------------------------------------- ----------------------------------------------------------------
data ProjectType = CabalProject | SandboxProject | PlainProject | StackProject data Project = CabalProject
deriving (Eq, Show) | SandboxProject
| PlainProject
| StackProject StackEnv
deriving (Eq, Show)
isCabalHelperProject :: Project -> Bool
isCabalHelperProject StackProject {} = True
isCabalHelperProject CabalProject {} = True
isCabalHelperProject _ = False
data StackEnv = StackEnv {
seDistDir :: FilePath
, seBinPath :: [FilePath]
, seSnapshotPkgDb :: FilePath
, seLocalPkgDb :: FilePath
} deriving (Eq, Show)
-- | The environment where this library is used. -- | The environment where this library is used.
data Cradle = Cradle { data Cradle = Cradle {
cradleProjectType:: ProjectType cradleProject :: Project
-- | The directory where this library is executed. -- | The directory where this library is executed.
, cradleCurrentDir :: FilePath , cradleCurrentDir :: FilePath
-- | The project root directory. -- | The project root directory.

View File

@ -679,7 +679,7 @@ nukeCaches = do
chdir <- liftIO $ (</> "cabal-helper") <$> getAppUserDataDirectory "ghc-mod" chdir <- liftIO $ (</> "cabal-helper") <$> getAppUserDataDirectory "ghc-mod"
c <- cradle c <- cradle
when (cradleProjectType c == CabalProject || cradleProjectType c == StackProject) $ do when (isCabalHelperProject $ cradleProject c) $ do
let root = cradleRootDir c let root = cradleRootDir c
let dist = cradleDistDir c let dist = cradleDistDir c
liftIO $ (trySome . removeDirectoryRecursive) `mapM_` [chdir, root </> dist] liftIO $ (trySome . removeDirectoryRecursive) `mapM_` [chdir, root </> dist]