forked from commercialhaskell/stack
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathStack.hs
More file actions
143 lines (130 loc) · 5.26 KB
/
Stack.hs
File metadata and controls
143 lines (130 loc) · 5.26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE OverloadedStrings #-}
{-|
Module : Stack
Description : Main Stack tool entry point.
License : BSD-3-Clause
Main Stack tool entry point.
-}
module Stack
( main
) where
import Control.Monad.Extra ( whenJust )
import GHC.IO.Encoding ( mkTextEncoding, textEncodingName )
import Options.Applicative.Builder.Extra ( execExtraHelp )
import Path ( parseAbsFile )
import Stack.BuildInfo ( versionString' )
import Stack.CLI ( commandLineHandler )
import Stack.Constants ( stackProgName )
import Stack.Docker ( dockerCmdName, dockerHelpOptName )
import Stack.Nix ( nixCmdName, nixHelpOptName )
import Stack.Options.DockerParser ( dockerOptsParser )
import Stack.Options.GlobalParser ( globalOptsFromMonoid )
import Stack.Options.NixParser ( nixOptsParser )
import Stack.Prelude
import Stack.Runners
( ShouldReexec (..), withConfig, withRunnerGlobal )
import Stack.Types.GlobalOpts ( GlobalOpts (..) )
import Stack.Types.Runner ( Runner )
import Stack.Types.Version
( VersionCheck (..), checkVersion, showStackVersion
, stackVersion
)
import System.Directory ( getCurrentDirectory )
import System.Environment ( executablePath, getArgs, getProgName )
import System.IO ( hGetEncoding, hPutStrLn, hSetEncoding )
import System.Terminal ( hIsTerminalDeviceOrMinTTY )
-- | Type representing exceptions thrown by functions in the "Stack" module.
data StackException
= InvalidReExecVersion String String
deriving Show
instance Exception StackException where
displayException (InvalidReExecVersion expected actual) = concat
[ "Error: [S-2186]\n"
, "When re-executing '"
, stackProgName
, "' in a container, the incorrect version was found\nExpected: "
, expected
, "; found: "
, actual
]
-- | Main Stack tool entry point.
main :: IO ()
main = do
-- Line buffer the output by default, particularly for non-terminal runs.
-- See https://github.com/commercialhaskell/stack/pull/360
hSetBuffering stdout LineBuffering
hSetBuffering stdin LineBuffering
hSetBuffering stderr LineBuffering
hSetTranslit stdout
hSetTranslit stderr
args <- getArgs
progName <- getProgName
mExecutableFilePath <- fromMaybe (pure Nothing) executablePath
let mExecutablePath = mExecutableFilePath >>= parseAbsFile
isTerminal <- hIsTerminalDeviceOrMinTTY stdout
execExtraHelp
args
dockerHelpOptName
(dockerOptsParser False)
("Only showing --" ++ dockerCmdName ++ "* options.")
execExtraHelp
args
nixHelpOptName
(nixOptsParser False)
("Only showing --" ++ nixCmdName ++ "* options.")
currentDir <- getCurrentDirectory
try (commandLineHandler currentDir progName mExecutablePath False) >>= \case
Left (exitCode :: ExitCode) ->
throwIO exitCode
Right (globalMonoid, run) -> do
global <-
globalOptsFromMonoid progName mExecutablePath isTerminal globalMonoid
when (global.logLevel == LevelDebug) $
hPutStrLn stderr versionString'
whenJust global.reExecVersion $ \expectVersion -> do
expectVersion' <- parseVersionThrowing expectVersion
unless (checkVersion MatchMinor expectVersion' stackVersion) $
throwIO $ InvalidReExecVersion expectVersion showStackVersion
withRunnerGlobal global $ run `catches`
[ Handler handleExitCode
, Handler handlePrettyException
, Handler handlePantryException
, Handler handleSomeException
]
-- | Change the character encoding of the given Handle to transliterate on
-- unsupported characters instead of throwing an exception
hSetTranslit :: Handle -> IO ()
hSetTranslit h = do
menc <- hGetEncoding h
whenJust (fmap textEncodingName menc) $ \name ->
unless ('/' `elem` name) $ do
enc' <- mkTextEncoding $ name ++ "//TRANSLIT"
hSetEncoding h enc'
-- | Handle ExitCode exceptions.
handleExitCode :: ExitCode -> RIO Runner a
handleExitCode = exitWith
-- | Handle PrettyException exceptions.
handlePrettyException :: PrettyException -> RIO Runner a
handlePrettyException = handleAnyPrettyException
-- | Handle (pretty) PantryException exceptions.
handlePantryException :: PantryException -> RIO Runner a
handlePantryException = handleAnyPrettyException
-- | Handle any pretty exception.
handleAnyPrettyException :: (Exception e, Pretty e) => e -> RIO Runner a
handleAnyPrettyException e = do
-- The code below loads the entire Stack configuration, when all that is
-- needed are the Stack colours. A tailored approach may be better.
tryAny (withConfig NoReexec $ prettyError $ pretty e) >>= \case
-- Falls back to the command line's Stack colours if there is any error in
-- loading the entire Stack configuration.
Left _ -> prettyError $ pretty e
Right _ -> pure ()
exitFailure
-- | Handle SomeException exceptions. This special handler stops "stack: " from
-- being printed before the exception.
handleSomeException :: SomeException -> RIO Runner a
handleSomeException (SomeException e) = do
logError $ fromString $ displayException e
exitFailure