shellmate 0.3.4.2 → 0.3.4.3
raw patch · 5 files changed
+46/−8 lines, 5 filesdep ~basedep ~temporaryPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependency ranges changed: base, temporary
API changes (from Hackage documentation)
+ Control.Shell: (<$!>) :: Monad m => (a -> b) -> m a -> m b
+ Control.Shell: (<=<) :: Monad m => (b -> m c) -> (a -> m b) -> a -> m c
+ Control.Shell: (=<<) :: Monad m => (a -> m b) -> m a -> m b
+ Control.Shell: (>=>) :: Monad m => (a -> m b) -> (b -> m c) -> a -> m c
+ Control.Shell: (>>) :: Monad m => m a -> m b -> m b
+ Control.Shell: (>>=) :: Monad m => m a -> (a -> m b) -> m b
+ Control.Shell: -- | The type of the guard's return value, if it succeeds.
+ Control.Shell: ap :: Monad m => m (a -> b) -> m a -> m b
+ Control.Shell: class Functor (f :: Type -> Type)
+ Control.Shell: class Applicative m => Monad (m :: Type -> Type)
+ Control.Shell: class (Alternative m, Monad m) => MonadPlus (m :: Type -> Type)
+ Control.Shell: fail :: Monad m => String -> m a
+ Control.Shell: filterM :: Applicative m => (a -> m Bool) -> [a] -> m [a]
+ Control.Shell: fmap :: Functor f => (a -> b) -> f a -> f b
+ Control.Shell: foldM :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b
+ Control.Shell: foldM_ :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m ()
+ Control.Shell: forM :: (Traversable t, Monad m) => t a -> (a -> m b) -> m (t b)
+ Control.Shell: forM_ :: (Foldable t, Monad m) => t a -> (a -> m b) -> m ()
+ Control.Shell: forever :: Applicative f => f a -> f b
+ Control.Shell: infixl 1 >>
+ Control.Shell: infixl 4 <$!>
+ Control.Shell: infixr 1 =<<
+ Control.Shell: join :: Monad m => m (m a) -> m a
+ Control.Shell: liftM :: Monad m => (a1 -> r) -> m a1 -> m r
+ Control.Shell: liftM2 :: Monad m => (a1 -> a2 -> r) -> m a1 -> m a2 -> m r
+ Control.Shell: liftM3 :: Monad m => (a1 -> a2 -> a3 -> r) -> m a1 -> m a2 -> m a3 -> m r
+ Control.Shell: liftM4 :: Monad m => (a1 -> a2 -> a3 -> a4 -> r) -> m a1 -> m a2 -> m a3 -> m a4 -> m r
+ Control.Shell: liftM5 :: Monad m => (a1 -> a2 -> a3 -> a4 -> a5 -> r) -> m a1 -> m a2 -> m a3 -> m a4 -> m a5 -> m r
+ Control.Shell: mapAndUnzipM :: Applicative m => (a -> m (b, c)) -> [a] -> m ([b], [c])
+ Control.Shell: mapM :: (Traversable t, Monad m) => (a -> m b) -> t a -> m (t b)
+ Control.Shell: mapM_ :: (Foldable t, Monad m) => (a -> m b) -> t a -> m ()
+ Control.Shell: mfilter :: MonadPlus m => (a -> Bool) -> m a -> m a
+ Control.Shell: mplus :: MonadPlus m => m a -> m a -> m a
+ Control.Shell: msum :: (Foldable t, MonadPlus m) => t (m a) -> m a
+ Control.Shell: mzero :: MonadPlus m => m a
+ Control.Shell: replicateM :: Applicative m => Int -> m a -> m [a]
+ Control.Shell: replicateM_ :: Applicative m => Int -> m a -> m ()
+ Control.Shell: return :: Monad m => a -> m a
+ Control.Shell: sequence :: (Traversable t, Monad m) => t (m a) -> m (t a)
+ Control.Shell: sequence_ :: (Foldable t, Monad m) => t (m a) -> m ()
+ Control.Shell: void :: Functor f => f a -> f ()
+ Control.Shell: zipWithM :: Applicative m => (a -> b -> m c) -> [a] -> [b] -> m [c]
+ Control.Shell: zipWithM_ :: Applicative m => (a -> b -> m c) -> [a] -> [b] -> m ()
- Control.Shell: class Guard guard where type Result guard where {
+ Control.Shell: class Guard guard where {
- Control.Shell: class Monad m => MonadIO (m :: * -> *)
+ Control.Shell: class Monad m => MonadIO (m :: Type -> Type)
- Control.Shell: data BufferMode :: *
+ Control.Shell: data BufferMode
- Control.Shell: data Handle :: *
+ Control.Shell: data Handle
- Control.Shell: data IOMode :: *
+ Control.Shell: data IOMode
- Control.Shell.Concurrent: data ThreadId :: *
+ Control.Shell.Concurrent: data ThreadId
- Control.Shell.Posix: data CMode :: *
+ Control.Shell.Posix: data CMode
Files
- Control/Shell/Base.hs +2/−2
- Control/Shell/Control.hs +1/−1
- Control/Shell/Directory.hs +15/−1
- Control/Shell/Internal.hs +23/−1
- shellmate.cabal +5/−3
Control/Shell/Base.hs view
@@ -58,8 +58,8 @@ wd <- Dir.getCurrentDirectory writeIORef globalEnv env Dir.setCurrentDirectory (envWorkDir env)- mapM_ Env.unsetEnv (map fst evs)- mapM_ (uncurry Env.setEnv) (envEnvVars env)+ mapM_ Env.unsetEnv (filter (not . null) $ map fst evs)+ mapM_ (uncurry Env.setEnv) (filter (not . null . fst) $ envEnvVars env) return $ Env IO.stdin IO.stdout IO.stderr wd evs -- | Get the current global shell environment, including standard input,
Control/Shell/Control.hs view
@@ -52,7 +52,7 @@ guard = assert "Guard failed!" -- | Perform the given computation if the given guard passes, otherwise do--- nothing.The guard raising an error counts as failure as far as this+-- nothing. The guard raising an error counts as failure as far as this -- function is concerned. -- Corresponds to 'CM.when'. when :: Guard g => g -> Shell () -> Shell ()
Control/Shell/Directory.hs view
@@ -122,7 +122,21 @@ rmdir :: FilePath -> Shell () rmdir dir = do e <- getEnv- unsafeLiftIO $ Dir.removeDirectoryRecursive (absPath e dir)+ unsafeLiftIO $ do+ let p = absPath e dir+ makeWritableRecursive p+ Dir.removeDirectoryRecursive p++-- Needed to recursively remove directories with read-only files on Windows.+-- Thanks to Ruud @ https://stackoverflow.com/questions/38926895/haskell-removedirectoryrecursive-permission-denied-on-windows+makeWritableRecursive :: FilePath -> IO ()+makeWritableRecursive path = do+ permissions <- Dir.getPermissions path+ Dir.setPermissions path (Dir.setOwnerWritable True permissions)+ isDirectory <- Dir.doesDirectoryExist path+ Control.Monad.when isDirectory $ do+ contents <- Dir.getDirectoryContents path+ forM_ [path </> item | item <- contents, item /= "." && item /= ".."] makeWritableRecursive -- | Do something with the user's home directory. withHomeDirectory :: (FilePath -> Shell a) -> Shell a
Control/Shell/Internal.hs view
@@ -7,13 +7,17 @@ , exit, run, try, getEnv, inEnv, unsafeLiftIO, (|>) ) where import Control.Monad (when, ap, forM)+import Control.Monad.Fail import qualified Control.Concurrent as Conc import qualified Control.Exception as Ex+import qualified Data.IORef as IORef import qualified System.Exit as Exit import qualified System.Process as Proc import qualified System.IO as IO+import qualified System.IO.Unsafe as IO import qualified System.Directory as Dir (getCurrentDirectory) import qualified System.Environment as Env (getEnvironment)+import qualified System.Info as Info (os) -- | A command name plus a ProcessHandle. data Pid = PID !String !Proc.ProcessHandle@@ -59,6 +63,9 @@ (>>=) = Bind fail = Fail +instance MonadFail Shell where+ fail = Fail+ -- | Lift an IO computation into a shell. The lifted computation is not -- thread-safe, and should thus absolutely not use environment variables, -- relative paths or standard input/output.@@ -69,11 +76,20 @@ data ExitReason = Success | Failure !String deriving (Show, Eq) +{-# NOINLINE warningRef #-}+warningRef :: IORef.IORef Bool+warningRef = IO.unsafePerformIO $ IORef.newIORef False+ -- | Run a shell computation. If part of the computation fails, the whole -- computation fails. The computation's environment is initially that of the -- whole process. shell :: Shell a -> IO (Either ExitReason a) shell m = do+ alreadyPrintedWarning <- IORef.atomicModifyIORef warningRef $ \x -> (True, x)+ when (not Conc.rtsSupportsBoundThreads && not alreadyPrintedWarning) $ do+ IO.hPutStrLn IO.stderr "WARNING: your program is not linked against the threaded GHC runtime."+ IO.hPutStrLn IO.stderr "You should REALLY build your program with -threaded,"+ IO.hPutStrLn IO.stderr "or you may experience deadlocks." evs <- Env.getEnvironment wd <- Dir.getCurrentDirectory runSh (env wd evs) m@@ -92,12 +108,15 @@ (\(Ex.SomeException e) -> pure $ Left (Failure (show e))) runSh env (Pipe p) = flip Ex.catch except $ do steps <- mkEnvs env p- pids <- mapM (uncurry (runStep True)) steps+ pids <- mapM (uncurry (runStep closeFDs)) steps ma <- waitPids pids case ma of Failure err -> pure $ Left (Failure err) _ -> pure $ Right () where+ closeFDs+ | Info.os == "mingw32" = False+ | otherwise = True except = \(Ex.SomeException e) -> pure $ Left (Failure (show e)) runSh _ Done = do return $ Left Success@@ -143,6 +162,9 @@ , Proc.new_session = False , Proc.child_group = Nothing , Proc.child_user = Nothing+#endif+#if MIN_VERSION_process(1,5,0)+ , Proc.use_process_jobs = False #endif } runStep closefds env (Internal cmd) = do
shellmate.cabal view
@@ -1,7 +1,9 @@ name: shellmate-version: 0.3.4.2+version: 0.3.4.3 synopsis: Simple interface for shell scripting in Haskell. description: Monadic EDSL for writing cross-platform shell scripts in Haskell.+ Note that programs using shellmate should be built with+ the -threaded flag to avoid deadlocks. homepage: https://github.com/valderman/shellmate license: BSD3 license-file: LICENSE@@ -36,13 +38,13 @@ Control.Shell.Directory Control.Shell.Color build-depends:- base >=4.8 && <5,+ base >=4.9 && <5, transformers >=0.3 && <0.6, bytestring >=0.10 && <0.11, filepath >=1.3 && <1.5, process >=1.1 && <1.7, directory >=1.1 && <1.4,- temporary >=1.1 && <1.3+ temporary >=1.1 && <1.4 default-language: Haskell2010 ghc-options: