module Main where import Control.Applicative ((<$>)) import Control.Monad (unless) import qualified Data.ByteString.Char8 as ByteString import Data.Conduit import qualified Data.Conduit.Binary as Conduit.Binary import qualified Data.Conduit.List as Conduit.List import Data.Conduit.Process import Data.List import Data.List.Utils import Data.Maybe import Distribution.PackageDescription import Distribution.PackageDescription.Parse import System.Console.ANSI import System.Directory import System.Directory.ProjectRoot import System.Environment import System.Exit import System.FilePath import System.IO usage :: String usage = unlines [ "" , " Usage: stack run [name]" , "" , " Commands:" , "" , " stack run [name] Compiles and runs the executable specified or the default" , " stack run set-default Sets the default executable to run" , " stack run -- Like stack run, but will never match a sub-command" , "" ] setDefault :: String -> IO () setDefault name = do pr <- fromMaybe (error "No project root found") <$> getProjectRootCurrent writeFile (pr ".stack-work" ".stack-run-default") name findDefault :: IO String findDefault = do pr <- fromMaybe (error "No project root found") <$> getProjectRootCurrent e <- doesFileExist (pr ".stack-work" ".stack-run-default") if e then readFile (pr ".stack-work" ".stack-run-default") else findDefault' pr where findDefault' pr = do cfp <- fromMaybe (error "No cabal file found") <$> (find ((== ".cabal") . takeExtension) <$> getDirectoryContents pr) getPackageDescription cfp >>= getDefaultExecutable where getPackageDescription p = parsePackageDescription <$> readFile p getDefaultExecutable (ParseFailed _) = error "Failed to parse cabal file" getDefaultExecutable (ParseOk _ gpd) = case condExecutables gpd of [] -> error "No executable found" ((d, _):_) -> return d stackRun :: String -> [String] -> IO b stackRun name as = do pr <- fromMaybe (error "No project root found") <$> getProjectRootCurrent stackYmlExists <- doesFileExist (pr "stack.yaml") unless stackYmlExists $ do ec <- prettyRunCommand "stack init" case ec of ExitSuccess -> return () f -> exitWith f ec <- prettyRunCommand "stack build" case ec of ExitSuccess -> setSGR [Reset] >> hFlush stdout >> runCommand ("stack exec " ++ name ++ " -- " ++ join " " as) >>= waitForProcess >>= exitWith f -> exitWith f prettyRunCommand :: String -> IO ExitCode prettyRunCommand cmd = do setSGR [ SetColor Foreground Vivid White , SetConsoleIntensity BoldIntensity ] putStr "$ " setSGR [Reset] setSGR [SetColor Foreground Vivid Cyan] putStrLn cmd setSGR [Reset] (Inherited, out, err, cph) <- streamingProcess (shell cmd) out =$= Conduit.Binary.lines $$ Conduit.List.mapM_ putLineGray err =$= Conduit.Binary.lines $$ Conduit.List.mapM_ putLineRed waitForStreamingProcess cph where putLineSGR sgr b = do setSGR sgr putStr " " ByteString.putStrLn b setSGR [Reset] putLineGray = putLineSGR [SetColor Foreground Dull White] putLineRed = putLineSGR [SetColor Foreground Vivid Black] main :: IO () main = do args <- getArgs case args of ("set-default":name:_) -> setDefault name ("set-default":_) -> do hPutStrLn stderr "Missing required parameter: " exitFailure ("get-default":_) -> putStrLn =<< findDefault ("help":_) -> do putStrLn usage exitSuccess ("--":name:as) -> stackRun name as (name:as) -> stackRun name as [] -> flip stackRun [] =<< findDefault