packages feed

shh 0.1.0.1 → 0.1.1.0

raw patch · 7 files changed

+74/−25 lines, 7 filesdep ~asyncdep ~basedep ~deepseqPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: async, base, deepseq, directory, filepath, mtl, process, split, template-haskell, unix

API changes (from Hackage documentation)

+ Shh: load :: [String] -> Q [Dec]
+ Shh: loadAnnotated :: (String -> String) -> [String] -> Q [Dec]
+ Shh.Internal: checkExecutable :: FilePath -> IO Bool
+ Shh.Internal: load :: [String] -> Q [Dec]
+ Shh.Internal: loadAnnotated :: (String -> String) -> [String] -> Q [Dec]

Files

ChangeLog.md view
@@ -1,5 +1,11 @@ # Revision history for execpath +## 0.1.1.0 -- 2019-01-22++* Generate an IO action to check for missing dependencies at runtime.++* Add ability to use TH to load a list of specific executables.+ ## 0.1.0.1 -- 2019-01-22  * Force output of lazy read functions to normal form to prevent
README.md view
@@ -1,12 +1,16 @@ # Shh +[![Hackage](https://img.shields.io/hackage/v/shh.svg?colorB=%23999)](http://hackage.haskell.org/package/shh)+[![Build Status](https://travis-ci.org/luke-clifton/shh.svg?branch=master)](https://travis-ci.org/luke-clifton/shh)+ Shh is a library to enable convinient shell-like programming in Haskell. It works well in scripts, and from GHCi, allowing you to use GHCi as a shell.  It supports   * Automatically defining a function for each executable on your `$PATH`-   using template Haskell.+   using template Haskell, as well as a runtime check to ensure they all+   exist on startup.   * Redirction of stdout and stderr        @@ -104,6 +108,10 @@  You now have all your executables available as simple to read Haskell functions.++If you want to check that all the dependenies still exist, you can use+`missingExecutables :: IO [String]`, which will tell you if anything is+missing.  ### Usage in GHCi 
app/Example.hs view
@@ -11,5 +11,11 @@  $(loadEnv) +-- We could also have been a little more explicit about it.+-- load ["sleep", "echo", "cat"]+ main :: IO ()-main = (sleep 1 >> echo "Hello" >> sleep 2) |> cat+main = do+    -- Crash the program if we are missing any executables.+    [] <- missingExecutables+    (sleep 1 >> echo "Hello" >> sleep 2) |> cat
shh.cabal view
@@ -1,5 +1,5 @@ name:                shh-version:             0.1.0.1+version:             0.1.1.0 synopsis:            Simple shell scripting from Haskell description:         Provides a shell scripting environment for Haskell. It                      helps you all external binaries, and allows you to@@ -22,16 +22,17 @@ library   exposed-modules:     Shh                      , Shh.Internal-  build-depends:       base >=4.9 && <4.13-                     , async-                     , deepseq-                     , directory-                     , filepath-                     , mtl-                     , process-                     , split-                     , template-haskell-                     , unix+  build-depends:+    base             >= 4.11 && < 4.13,+    async            >= 2.2.1 && < 2.3,+    deepseq          >= 1.4.3 && < 1.5,+    directory        >= 1.3.1 && < 1.4,+    filepath         >= 1.4.2 && < 1.5,+    mtl              >= 2.2.2 && < 2.3,+    process          >= 1.6.3 && < 1.7,+    split            >= 0.2.3 && < 0.3,+    template-haskell >= 2.13.0 && < 2.15,+    unix             >= 2.7.2 && < 2.8   hs-source-dirs:      src   default-language:    Haskell2010   ghc-options: -Wall
src/Shh.hs view
@@ -53,7 +53,9 @@     -- | == Constructing Arguments     , ExecArg(..)     -- | == Template Haskell helpers+    , load     , loadEnv+    , loadAnnotated     , loadAnnotatedEnv     , loadExe     , loadExeAs
src/Shh/Internal.hs view
@@ -4,6 +4,7 @@ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE GADTs #-} @@ -19,9 +20,9 @@ import Data.Char (isLower, isSpace, isAlphaNum) import Data.List (nub, dropWhileEnd, intercalate) import Data.List.Split (endBy, splitOn)-import Data.Maybe (mapMaybe)+import Data.Maybe (mapMaybe, isJust) import Language.Haskell.TH-import System.Directory (doesDirectoryExist, getDirectoryContents)+import qualified System.Directory as Dir import System.Environment (getEnv) import System.Exit (ExitCode(..)) import System.IO@@ -379,8 +380,9 @@ pathBins :: IO [FilePath] pathBins = do     pathsVar <- splitOn ":" <$> getEnv "PATH"-    paths <- filterM doesDirectoryExist pathsVar-    nub . concat <$> mapM getDirectoryContents paths+    paths <- filterM Dir.doesDirectoryExist pathsVar+    ps <- nub . concat <$> mapM Dir.getDirectoryContents paths+    filterM checkExecutable ps  -- | Execute the given command. Further arguments can be passed in. --@@ -409,6 +411,8 @@         typn = mkName "a"         typ = SigD name (ForallT [PlainTV typn] [AppT (ConT ''Unit) (VarT typn), AppT (ConT ''ExecArgs) (VarT typn)] (VarT typn))     in do+        isExe <- runIO $ checkExecutable executable+        when (not isExe) $ error $ "Attempted to load '" ++ executable ++ "', but it isn't executable"         i <- impl         return $ [typ,i] @@ -428,23 +432,45 @@  -- | Scans your '$PATH' environment variable and creates a function for each -- executable found. Binaries that would not create valid Haskell identifiers--- are ignored.+-- are ignored. It also creates the IO action @missingExecutables@ which will+-- do a runtime check to ensure all the executables that were found at+-- compile time still exist. loadEnv :: Q [Dec] loadEnv = loadAnnotatedEnv id --- | Like `loadEnv`, but allows you to modify the function name that would--- be generated.-loadAnnotatedEnv :: (String -> String) -> Q [Dec]-loadAnnotatedEnv f = do-    bins <- runIO pathBins+-- | Test to see if an executable can be found either on the $PATH or absolute.+checkExecutable :: FilePath -> IO Bool+checkExecutable = fmap isJust . Dir.findExecutable++-- | Load the given executables into the program, checking their executability+-- and creating a function @missingExecutables@ to do a runtime check for their+-- availability.+load :: [String] -> Q [Dec]+load = loadAnnotated id++-- | Same as `load`, but allows you to modify the function names.+loadAnnotated :: (String -> String) -> [String] -> Q [Dec]+loadAnnotated f bins = do     let pairs = mapMaybe getAnnotation bins-    fmap join $ mapM (uncurry loadExeAs) pairs+    ds <- fmap join $ mapM (uncurry loadExeAs) pairs+    d <- valD (varP (mkName "missingExecutables")) (normalB [|+                filterM (fmap not . checkExecutable) bins+            |]) [] +    pure (d:ds)+     where         getAnnotation :: String -> Maybe (String,String)         getAnnotation s             | validIdentifier (f s) = Just (f s, s)             | otherwise             = Nothing++-- | Like `loadEnv`, but allows you to modify the function name that would+-- be generated.+loadAnnotatedEnv :: (String -> String) -> Q [Dec]+loadAnnotatedEnv f = do+    bins <- runIO pathBins+    loadAnnotated f bins  -- | Function that splits '\0' seperated list of strings. Useful in conjuction -- with @find . "-print0"@.
test/Test.hs view
@@ -55,7 +55,7 @@         r @?= "test\n"     , testCase "Lazy read" $ do         withRead (cat "/dev/urandom" |> xxd) $ \s -> do-            take 10 s @?= "00000000: "+            take 6 s @?= "000000"     , testCase "Multiple outputs" $ do         l <- readProc $ (echo (1 :: Int) >> echo (2 :: Int)) |> cat         l @?= "1\n2\n"