module Stack2nix.Util
( assertMinVer
, extractVersion
, mapPool
, logDebug
, ensureExecutableExists
, ensureExecutable
) where
import Control.Concurrent.Async
import Control.Concurrent.MSem
import Control.Exception (onException)
import Control.Monad (unless)
import Data.Maybe (listToMaybe)
import qualified Data.Traversable as T
import Data.Version (Version (..), parseVersion,
showVersion)
import Data.Text (pack, strip, unpack)
import GHC.Exts (sortWith)
import Stack2nix.External.Util (runCmd)
import Stack2nix.Types (Args, argVerbose)
import System.Directory (findExecutable)
import System.Environment (getEnv, setEnv)
import System.Exit (ExitCode (..))
import System.IO (hPutStrLn, stderr)
import System.Process (readProcessWithExitCode)
import Text.ParserCombinators.ReadP (readP_to_S)
import Text.Regex.PCRE (AllTextMatches (..),
getAllTextMatches, (=~))
mapPool :: T.Traversable t => Int -> (a -> IO b) -> t a -> IO (t b)
mapPool max' f xs = do
sem <- new max'
mapConcurrently (with sem . f) xs
extractVersion :: String -> Maybe Version
extractVersion str = ver
where
firstLine = head . lines $ str
candidateVers = getAllTextMatches (firstLine =~ "[\\d\\.]+" :: AllTextMatches [] String)
bestMatch = head . reverse . sortWith length $ candidateVers
ver = fmap fst . listToMaybe . reverse . readP_to_S parseVersion $ bestMatch
assertMinVer :: String -> String -> IO ()
assertMinVer prog minVer = do
hPutStrLn stderr $ unwords ["Ensuring", prog, "version is >=", minVer, "..."]
result <- runCmd prog ["--version"] `onException` error ("Failed to run " ++ prog ++ ". Not found in PATH.")
case result of
(ExitSuccess, out, _) ->
let ver = extractVersion out in
unless (ver >= extractVersion minVer) $ error $ unwords ["ERROR:", prog, "version must be", minVer, "or higher. Current version:", maybe "[parse failure]" showVersion ver]
(ExitFailure _, _, err) -> error err
logDebug :: Args -> String -> IO ()
logDebug args msg
| argVerbose args = hPutStrLn stderr msg
| otherwise = return ()
ensureExecutableExists :: String -> String -> IO ()
ensureExecutableExists executable nixAttr = do
exec <- findExecutable executable
case exec of
Just _ -> return ()
Nothing -> ensureExecutable nixAttr
ensureExecutable :: String -> IO ()
ensureExecutable nixAttr = do
(exitcode2, stdout, err2) <- readProcessWithExitCode "nix-build" ["-A", nixAttr, "<nixpkgs>", "--no-build-output", "--no-out-link"] mempty
case exitcode2 of
ExitSuccess -> do
hPutStrLn stderr $ err2
path <- getEnv "PATH"
setEnv "PATH" (unpack (strip (pack stdout)) ++ "/bin" ++ ":" ++ path)
ExitFailure _ -> error $ nixAttr ++ " failed to build via nix"