@@ -14,7 +14,6 @@ module Main (main) where
1414import Control.Exception
1515import qualified Control.Exception.Lifted as EL
1616import Control.Monad hiding (mapM , forM )
17- import Control.Monad.Catch (MonadThrow )
1817import Control.Monad.IO.Class
1918import Control.Monad.Logger
2019import Control.Monad.Reader (ask , asks , runReaderT )
@@ -47,7 +46,7 @@ import GHC.IO.Encoding (mkTextEncoding, textEncodingName)
4746import Network.HTTP.Client
4847import Options.Applicative
4948import Options.Applicative.Args
50- import Options.Applicative.Help (errorHelp ,stringChunk )
49+ import Options.Applicative.Help (errorHelp , stringChunk , vcatChunks )
5150import Options.Applicative.Builder.Extra
5251import Options.Applicative.Complicated
5352#ifdef USE_GIT_INFO
@@ -94,7 +93,7 @@ import qualified System.Directory as Directory (findExecutable)
9493import System.Environment (getEnvironment , getProgName , getArgs , withArgs )
9594import System.Exit
9695import System.FileLock (lockFile , tryLockFile , unlockFile , SharedExclusive (Exclusive ), FileLock )
97- import System.FilePath (searchPathSeparator )
96+ import System.FilePath (pathSeparator , searchPathSeparator )
9897import System.IO (hIsTerminalDevice , stderr , stdin , stdout , hSetBuffering , BufferMode (.. ), hPutStrLn , Handle , hGetEncoding , hSetEncoding )
9998import System.Process.Read
10099
@@ -167,6 +166,12 @@ main = do
167166 printExceptionStderr e
168167 exitFailure
169168
169+ -- Vertically combine only the error component of the first argument with the
170+ -- error component of the second.
171+ vcatErrorHelp :: ParserHelp -> ParserHelp -> ParserHelp
172+ vcatErrorHelp (ParserHelp e1 _ _ _ _) (ParserHelp e2 h2 u2 b2 f2) =
173+ ParserHelp (vcatChunks [e2, e1]) h2 u2 b2 f2
174+
170175commandLineHandler
171176 :: String
172177 -> Bool
@@ -181,19 +186,29 @@ commandLineHandler progName isInterpreter = complicatedOptions
181186 (addCommands (globalOpts True ) isInterpreter)
182187 where
183188 failureCallback f args =
184- case stripPrefix " Invalid argument" (fst (renderFailure f " " )) of
185- Just _ -> if isInterpreter
186- then handleParseResult (Failure f)
187- else secondaryCommandHandler args
188- >>= maybe (interpreterHandler f args) id
189- Nothing -> handleParseResult (Failure f)
189+ case stripPrefix " Invalid argument" (fst (renderFailure f " " )) of
190+ Just _ -> if isInterpreter
191+ then parseResultHandler args f
192+ else secondaryCommandHandler args f
193+ >>= interpreterHandler args
194+ Nothing -> parseResultHandler args f
195+
196+ parseResultHandler args f =
197+ if isInterpreter
198+ then do
199+ let hlp = errorHelp $ stringChunk
200+ (unwords [" Error executing interpreter command:"
201+ , progName
202+ , unwords args])
203+ handleParseResult (overFailure (vcatErrorHelp hlp) (Failure f))
204+ else handleParseResult (Failure f)
190205
191206 globalOpts hide =
192- extraHelpOption hide progName (Docker. dockerCmdName ++ " *" ) Docker. dockerHelpOptName <*>
193- extraHelpOption hide progName (Nix. nixCmdName ++ " *" ) Nix. nixHelpOptName <*>
194- globalOptsParser hide (if isInterpreter
195- then Just $ LevelOther " silent"
196- else Nothing )
207+ extraHelpOption hide progName (Docker. dockerCmdName ++ " *" ) Docker. dockerHelpOptName <*>
208+ extraHelpOption hide progName (Nix. nixCmdName ++ " *" ) Nix. nixHelpOptName <*>
209+ globalOptsParser hide (if isInterpreter
210+ then Just $ LevelOther " silent"
211+ else Nothing )
197212
198213globalFooter :: String
199214globalFooter = " Run 'stack --help' for global options that apply to all subcommands."
@@ -442,42 +457,66 @@ addCommands globalOpts isInterpreter = do
442457 addSubCommands cmd title globalFooter globalOpts
443458
444459secondaryCommandHandler
445- :: ( MonadIO m , MonadThrow m , MonadBaseControl IO m )
446- => [ String ]
447- -> IO (Maybe ( m a ) )
460+ :: [ String ]
461+ -> ParserFailure ParserHelp
462+ -> IO (ParserFailure ParserHelp )
448463
449464-- fall-through to external executables in `git` style if they exist
450465-- (i.e. `stack something` looks for `stack-something` before
451466-- failing with "Invalid argument `something'")
452- secondaryCommandHandler args = do
467+ secondaryCommandHandler args f =
468+ -- don't even try when the argument looks like a path
469+ if elem pathSeparator cmd
470+ then return f
471+ else do
472+ mExternalExec <- Directory. findExecutable cmd
473+ case mExternalExec of
474+ Just ex -> do
475+ menv <- getEnvOverride buildPlatform
476+ -- TODO show the command in verbose mode
477+ -- hPutStrLn stderr $ unwords $
478+ -- ["Running", "[" ++ ex, unwords (tail args) ++ "]"]
479+ _ <- runNoLoggingT (exec menv ex (tail args))
480+ return f
481+ Nothing -> return $ fmap (vcatErrorHelp (noSuchCmd cmd)) f
482+ where
453483 -- FIXME this is broken when any options are specified before the command
454484 -- e.g. stack --verbosity silent cmd
455- mExternalExec <- Directory. findExecutable (" stack-" ++ head args)
456- case mExternalExec of
457- Just ex -> do
458- menv <- getEnvOverride buildPlatform
459- return (Just $ runNoLoggingT (exec menv ex (tail args)))
460- Nothing -> return Nothing
485+ cmd = stackProgName ++ " -" ++ (head args)
486+ noSuchCmd name = errorHelp $ stringChunk
487+ (" Auxiliary command not found in path `" ++ name ++ " '" )
461488
462489interpreterHandler
463490 :: Monoid t
464- => ParserFailure ParserHelp
465- -> [ String ]
491+ => [ String ]
492+ -> ParserFailure ParserHelp
466493 -> IO (GlobalOptsMonoid , (GlobalOpts -> IO () , t ))
467- interpreterHandler f args = do
468- let file = head args
494+ interpreterHandler args f = do
469495 isFile <- doesFileExist file
470496 if isFile
471497 then runInterpreterCommand file
472- else parseResultHandler (flip mappend (noSuchFile file))
498+ else parseResultHandler (errorCombine (noSuchFile file))
473499 where
500+ file = head args
501+
502+ -- if the filename contains a path separator then we know that it is not a
503+ -- command it is a file to be interpreted. In that case we only show the
504+ -- interpreter error message and exclude the command related error messages.
505+ errorCombine =
506+ if elem pathSeparator file
507+ then overrideErrorHelp
508+ else vcatErrorHelp
509+
510+ overrideErrorHelp (ParserHelp e1 _ _ _ _) (ParserHelp _ h2 u2 b2 f2) =
511+ ParserHelp e1 h2 u2 b2 f2
512+
474513 parseResultHandler fn = handleParseResult (overFailure fn (Failure f))
475514 noSuchFile name = errorHelp $ stringChunk
476- (" \n No such source file to interpret `" ++ name ++ " \ '" )
515+ (" File does not exist or is not a regular file `" ++ name ++ " '" )
477516
478- runInterpreterCommand file = do
517+ runInterpreterCommand path = do
479518 progName <- getProgName
480- iargs <- getInterpreterArgs file
519+ iargs <- getInterpreterArgs path
481520 let parseCmdLine = commandLineHandler progName True
482521 let cmdArgs = iargs ++ " --" : args
483522 -- TODO show the command in verbose mode
0 commit comments