Starting to switch to Cabal format
Getty Ritter
7 years ago
| 1 | 1 | name: hatch |
| 2 |
version: 0.1 |
|
| 2 | version: 0.1 | |
| 3 | 3 | synopsis: A Haskell toolchain manager |
| 4 |
|
|
| 4 | description: | |
| 5 | The @hatch@ program allows for project-, user-, and system-level | |
| 6 | switching between versions of GHC | |
| 5 | 7 | license: BSD3 |
| 6 | 8 | author: Getty Ritter <hatch@infinitenegativeutility.com> |
| 7 | 9 | maintainer: Getty Ritter <hatch@infinitenegativeutility.com> |
| 8 | 10 | copyright: @2018 Getty Ritter |
| 9 | 11 | category: Build |
| 10 | 12 | build-type: Simple |
| 11 |
cabal-version: >= |
|
| 13 | cabal-version: >=2.0 | |
| 12 | 14 | |
| 13 | 15 | library |
| 14 | 16 | hs-source-dirs: src |
| 22 | 24 | , directory |
| 23 | 25 | , filepath |
| 24 | 26 | , unix |
| 27 | , Cabal | |
| 25 | 28 | default-language: Haskell2010 |
| 26 | 29 | default-extensions: ScopedTypeVariables |
| 27 | 30 | exposed-modules: Hatch |
| 28 | 31 | other-modules: Types |
| 29 | 32 | Config |
| 33 | Util | |
| 30 | 34 | |
| 31 | 35 | executable hatch |
| 32 | 36 | hs-source-dirs: hatch |
| 3 | 3 | |
| 4 | 4 | module Config |
| 5 | 5 | ( ConfigFile |
| 6 |
, |
|
| 6 | , readConfig | |
| 7 | , readProjectConfig | |
| 7 | 8 | , Ini.iniValueL |
| 8 | 9 | ) where |
| 9 | 10 | |
| 13 | 14 | import qualified Data.Text as T |
| 14 | 15 | import qualified Data.Text.IO as T |
| 15 | 16 | import Data.Monoid ((<>)) |
| 17 | import qualified Distribution.ParseUtils as Cabal | |
| 16 | 18 | import qualified System.Directory as Sys |
| 17 | 19 | import System.FilePath ((</>)) |
| 20 | import qualified System.FilePath as Sys | |
| 18 | 21 | import qualified System.Exit as Sys |
| 19 | 22 | import qualified System.Environment.XDG.BaseDir as Sys |
| 20 | 23 | import Text.Read (readMaybe) |
| 21 | 24 | |
| 22 | 25 | import Types |
| 26 | import Util | |
| 23 | 27 | |
| 24 | 28 | type ConfigFile = Ini.Ini Config |
| 29 | ||
| 25 | 30 | |
| 26 | 31 | defaultConfig :: IO Config |
| 27 | 32 | defaultConfig = do |
| 29 | 34 | let _configCurrentCompiler = Nothing |
| 30 | 35 | return Config { .. } |
| 31 | 36 | |
| 37 | ||
| 32 | 38 | configSpec :: Ini.IniSpec Config () |
| 33 | 39 | configSpec = do |
| 34 | 40 | Ini.section "hatch" $ do |
| 35 | 41 | configInstallPath .= Ini.field "path" Ini.string |
| 36 | 42 | & Ini.optional |
| 37 | 43 | configCurrentCompiler .=? Ini.field "current" versionField |
| 44 | ||
| 38 | 45 | |
| 39 | 46 | versionField :: Ini.FieldValue Compiler |
| 40 | 47 | versionField = Ini.FieldValue { .. } |
| 49 | 56 | | otherwise = Left ("Bad GHC version: " ++ show t) |
| 50 | 57 | fvEmit = T.pack . compilerString |
| 51 | 58 | |
| 59 | ||
| 52 | 60 | locateConfig :: FilePath -> IO (Maybe FilePath) |
| 53 | 61 | locateConfig filename = do |
| 54 | 62 | xdgLocs <- Sys.getAllConfigFiles "hatch" filename |
| 60 | 68 | [] -> return Nothing |
| 61 | 69 | ((fp, _):_) -> return (Just fp) |
| 62 | 70 | |
| 63 | fetchConfig :: IO (Ini.Ini Config) | |
| 64 | fetchConfig = do | |
| 71 | ||
| 72 | readProjectConfig :: IO ([Cabal.Field]) | |
| 73 | readProjectConfig = Sys.getCurrentDirectory >>= go | |
| 74 | where go "/" = return [] | |
| 75 | go path = do | |
| 76 | exists <- Sys.doesFileExist (path </> ".hatch") | |
| 77 | if exists | |
| 78 | then do | |
| 79 | content <- readFile (path </> ".hatch") | |
| 80 | case Cabal.readFields content of | |
| 81 | Cabal.ParseOk _ rs -> return rs | |
| 82 | _ -> return [] | |
| 83 | else go (Sys.takeDirectory path) | |
| 84 | ||
| 85 | ||
| 86 | readConfig :: IO (Ini.Ini Config) | |
| 87 | readConfig = do | |
| 65 | 88 | def <- defaultConfig |
| 66 | 89 | let ini = Ini.ini def configSpec |
| 67 | 90 | confLocation <- locateConfig "config.ini" |
| 72 | 95 | content <- T.readFile fp |
| 73 | 96 | case Ini.parseIni content ini of |
| 74 | 97 | Left err -> do |
| 98 | printErr err | |
| 75 | 99 | Sys.die err |
| 76 | 100 | Right x -> return x |
| 2 | 2 | ( main |
| 3 | 3 | ) where |
| 4 | 4 | |
| 5 |
import Lens.Family |
|
| 5 | import Lens.Family ((^.)) | |
| 6 | 6 | import qualified System.Environment as Sys |
| 7 | 7 | import System.FilePath ((</>)) |
| 8 | 8 | import qualified System.FilePath as Sys |
| 11 | 11 | |
| 12 | 12 | import Config |
| 13 | 13 | import Types |
| 14 | import Util | |
| 14 | 15 | |
| 15 | 16 | main :: IO () |
| 16 | 17 | main = do |
| 17 |
conf <- |
|
| 18 | conf <- readConfig | |
| 18 | 19 | print (conf^.iniValueL) |
| 19 | 20 | programName <- Sys.takeFileName `fmap` Sys.getProgName |
| 20 | 21 | if programName == "hatch" |
| 36 | 37 | Sys.executeFile progn False args Nothing |
| 37 | 38 | |
| 38 | 39 | |
| 40 | data HatchCommand | |
| 41 | = SwitchCompiler | |
| 42 | | FetchCompiler | |
| 43 | deriving (Eq, Show) | |
| 44 | ||
| 39 | 45 | runAsHatch :: ConfigFile -> IO () |
| 40 | 46 | runAsHatch _ = do |
| 47 | printErr "testing" | |
| 41 | 48 | putStrLn "Invoking as Hatch!" |
| 1 | 1 | {-# LANGUAGE TemplateHaskell #-} |
| 2 | {-# OPTIONS_GHC -Wno-missing-signatures #-} | |
| 2 | 3 | |
| 3 |
module Types |
|
| 4 | module Types | |
| 5 | ( Compiler(..) | |
| 6 | , compilerVersion | |
| 7 | , Config(..) | |
| 8 | , configInstallPath | |
| 9 | , configCurrentCompiler | |
| 10 | , compilerString | |
| 11 | ) where | |
| 4 | 12 | |
| 5 | import qualified Data.Text as T | |
| 6 | 13 | import qualified Lens.Family.TH as Lens |
| 7 | 14 | |
| 8 | 15 | data Compiler = Compiler |
| 1 | {-# LANGUAGE OverloadedStrings #-} | |
| 2 | ||
| 3 | module Util | |
| 4 | ( printErr | |
| 5 | ) where | |
| 6 | ||
| 7 | import Control.Monad (when) | |
| 8 | import qualified System.Posix.Terminal as Unix | |
| 9 | import qualified System.Posix.IO as Unix | |
| 10 | ||
| 11 | printErr :: String -> IO () | |
| 12 | printErr msg = do | |
| 13 | isTTY <- Unix.queryTerminal Unix.stdOutput | |
| 14 | when isTTY $ putStr "\x1b[91m" | |
| 15 | putStr msg | |
| 16 | when isTTY $ putStr "\x1b[39m" | |
| 17 | putStrLn "" |