packages feed

blucontrol-0.3.1.0: src/Blucontrol/Main/CLI.hs

module Blucontrol.Main.CLI (
  launch
) where

import Control.DeepSeq
import Control.Monad (when)
import Data.Version (showVersion)
import GHC.Generics
import System.Console.GetOpt
import System.Directory (XdgDirectory (..), createDirectoryIfMissing, doesFileExist, getModificationTime, getXdgDirectory)
import System.Environment (getArgs, getProgName)
import System.Exit (ExitCode (..), exitFailure, exitSuccess)
import System.FilePath ((</>))
import System.Info (arch, compilerName, compilerVersion, os)
import System.Posix.Process (executeFile)
import System.Process (runProcess, waitForProcess)

import Blucontrol.Main.GHC.Internal
import Paths_blucontrol (version)

data Flag = Help
          | Version
          | IgnoreConfig
  deriving (Eq, Generic, Ord, Read, Show)

instance NFData Flag

options :: [OptDescr Flag]
options = [ Option ['h'] ["help"] (NoArg Help) "Explain CLI usage"
          , Option ['v'] ["version"] (NoArg Version) "Display version"
          , Option ['i'] ["ignore-config"] (NoArg IgnoreConfig) "Use default configuration"
          ]

launch :: IO ()
launch = do
  args <- getArgs
  case getOpt Permute options args of
    (optArgs, [], []) -> controlOptions optArgs
    _ -> do printUsage
            exitFailure

controlOptions :: [Flag] -> IO ()
controlOptions flags
  | Help `elem` flags = do printUsage
                           exitSuccess
  | otherwise = case flags of
    [] -> build
    [Version] -> do printVersion
                    exitSuccess
    [IgnoreConfig] -> return ()
    _ -> do printUsage
            exitFailure

printUsage :: IO ()
printUsage = putStr $ usageInfo header options
  where header = "Usage: blucontrol [OPTIONS]"

printVersion :: IO ()
printVersion = putStrLn $ "blucontrol-" <> showVersion version <> " compiled with " <> compiler
  where compiler = compilerName <> "-" <> showVersion compilerVersion

getXdgDir :: XdgDirectory -> IO FilePath
getXdgDir = flip getXdgDirectory "blucontrol"

build :: IO ()
build = do
  configPath <- (</> configLeafname) <$> getXdgDir XdgConfig
  configExists <- doesFileExist configPath
  when configExists $ do
    progName <- getProgName
    compiledConfigPath <- (</> compiledConfigLeafname) <$> getXdgDir XdgCache
    if progName == compiledConfigLeafname
       then do configTime <- getModificationTime configPath -- TODO: getModificationTime can fail
               compiledConfigTime <- getModificationTime compiledConfigPath
               when (configTime > compiledConfigTime) $ do
                 compile
                 executeFile compiledConfigPath False [] Nothing
       else do compile
               executeFile compiledConfigPath False [] Nothing

compile :: IO ()
compile = do
  configDir <- getXdgDir XdgConfig
  cacheDir <- getXdgDir XdgCache
  createDirectoryIfMissing False cacheDir
  let ghcFlags = [ "--make"
                 , configLeafname
                 , "-main-is", "main"
                 , "-v0"
                 , "-o", cacheDir </> compiledConfigLeafname
                 ] <> ghcAdditionalFlags
  status <- waitForProcess =<<
    runProcess ghcBinary ghcFlags (Just configDir) Nothing Nothing Nothing Nothing
  case status of
    ExitSuccess -> return ()
    ExitFailure _ -> exitFailure

compiledConfigLeafname :: FilePath
compiledConfigLeafname = "blucontrol-" <> arch <> "-" <> os

configLeafname :: FilePath
configLeafname = "blucontrol.hs"