{- FUTUREWORK:
 -
 - Currently the logic in this module is not tested.
 -
 - Ideally, a set of unit tests that check whether certain
 - flags trigger recompilation should be added.
 - -}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE RankNTypes #-}

-----------------------------------------------------------------------------

-- |
-- Module      :  Distribution.Simple.Program
-- Copyright   :  Isaac Jones 2006, Duncan Coutts 2007-2009
--
-- Maintainer  :  cabal-devel@haskell.org
-- Portability :  portable
--
-- This provides an abstraction which deals with configuring and running
-- programs. A 'Program' is a static notion of a known program. A
-- 'ConfiguredProgram' is a 'Program' that has been found on the current
-- machine and is ready to be run (possibly with some user-supplied default
-- args). Configuring a program involves finding its location and if necessary
-- finding its version. There is also a 'ProgramDb' type which holds
-- configured and not-yet configured programs. It is the parameter to lots of
-- actions elsewhere in Cabal that need to look up and run programs. If we had
-- a Cabal monad, the 'ProgramDb' would probably be a reader or
-- state component of it.
--
-- The module also defines all the known built-in 'Program's and the
-- 'defaultProgramDb' which contains them all.
--
-- One nice thing about using it is that any program that is
-- registered with Cabal will get some \"configure\" and \".cabal\"
-- helpers like --with-foo-args --foo-path= and extra-foo-args.
--
-- There's also good default behavior for trying to find \"foo\" in
-- PATH, being able to override its location, etc.
--
-- There's also a hook for adding programs in a Setup.lhs script.  See
-- hookedPrograms in 'Distribution.Simple.UserHooks'.  This gives a
-- hook user the ability to get the above flags and such so that they
-- don't have to write all the PATH logic inside Setup.lhs.
module Distribution.Simple.Program
  ( -- * Program and functions for constructing them
    Program (..)
  , ProgramSearchPath
  , ProgramSearchPathEntry (..)
  , simpleProgram
  , findProgramOnSearchPath
  , defaultProgramSearchPath
  , findProgramVersion

    -- * Configured program and related functions
  , ConfiguredProgram (..)
  , programPath
  , ProgArg
  , ProgramLocation (..)
  , runProgram
  , runProgramCwd
  , getProgramOutput
  , suppressOverrideArgs

    -- * Program invocations
  , ProgramInvocation (..)
  , emptyProgramInvocation
  , simpleProgramInvocation
  , programInvocation
  , runProgramInvocation
  , getProgramInvocationOutput
  , getProgramInvocationLBS

    -- * The collection of unconfigured and configured programs
  , builtinPrograms

    -- * The collection of configured programs we can run
  , ProgramDb
  , defaultProgramDb
  , emptyProgramDb
  , restoreProgramDb
  , addKnownProgram
  , addKnownPrograms
  , lookupKnownProgram
  , knownPrograms
  , getProgramSearchPath
  , setProgramSearchPath
  , userSpecifyPath
  , userSpecifyPaths
  , userMaybeSpecifyPath
  , userSpecifyArgs
  , userSpecifyArgss
  , userSpecifiedArgs
  , lookupProgram
  , lookupProgramVersion
  , updateProgram
  , configureProgram
  , configureAllKnownPrograms
  , reconfigurePrograms
  , requireProgram
  , requireProgramVersion
  , needProgram
  , runDbProgram
  , runDbProgramCwd
  , getDbProgramOutput
  , getDbProgramOutputCwd

    -- * Programs that Cabal knows about
  , ghcProgram
  , ghcPkgProgram
  , ghcjsProgram
  , ghcjsPkgProgram
  , hmakeProgram
  , jhcProgram
  , uhcProgram
  , gccProgram
  , arProgram
  , stripProgram
  , happyProgram
  , alexProgram
  , hsc2hsProgram
  , c2hsProgram
  , cpphsProgram
  , hscolourProgram
  , doctestProgram
  , haddockProgram
  , greencardProgram
  , ldProgram
  , tarProgram
  , cppProgram
  , pkgConfigProgram
  , hpcProgram
  ) where

import Distribution.Compat.Prelude
import Prelude ()

import Distribution.Simple.Errors
import Distribution.Simple.Program.Builtin
import Distribution.Simple.Program.Db
import Distribution.Simple.Program.Find
import Distribution.Simple.Program.Run
import Distribution.Simple.Program.Types
import Distribution.Simple.Utils
import Distribution.Utils.Path
import Distribution.Verbosity

-- | Runs the given configured program.
runProgram
  :: Verbosity
  -- ^ Verbosity
  -> ConfiguredProgram
  -- ^ The program to run
  -> [ProgArg]
  -- ^ Any /extra/ arguments to add
  -> IO ()
runProgram :: Verbosity -> ConfiguredProgram -> [ProgArg] -> IO ()
runProgram Verbosity
verbosity ConfiguredProgram
prog [ProgArg]
args =
  Verbosity -> ProgramInvocation -> IO ()
runProgramInvocation Verbosity
verbosity (ConfiguredProgram -> [ProgArg] -> ProgramInvocation
programInvocation ConfiguredProgram
prog [ProgArg]
args)

-- | Runs the given configured program.
runProgramCwd
  :: Verbosity
  -- ^ Verbosity
  -> Maybe (SymbolicPath CWD (Dir to))
  -- ^ Working directory
  -> ConfiguredProgram
  -- ^ The program to run
  -> [ProgArg]
  -- ^ Any /extra/ arguments to add
  -> IO ()
runProgramCwd :: forall to.
Verbosity
-> Maybe (SymbolicPath CWD ('Dir to))
-> ConfiguredProgram
-> [ProgArg]
-> IO ()
runProgramCwd Verbosity
verbosity Maybe (SymbolicPath CWD ('Dir to))
mbWorkDir ConfiguredProgram
prog [ProgArg]
args =
  Verbosity -> ProgramInvocation -> IO ()
runProgramInvocation Verbosity
verbosity (Maybe (SymbolicPath CWD ('Dir to))
-> ConfiguredProgram -> [ProgArg] -> ProgramInvocation
forall to.
Maybe (SymbolicPath CWD ('Dir to))
-> ConfiguredProgram -> [ProgArg] -> ProgramInvocation
programInvocationCwd Maybe (SymbolicPath CWD ('Dir to))
mbWorkDir ConfiguredProgram
prog [ProgArg]
args)

-- | Runs the given configured program and gets the output.
getProgramOutput
  :: Verbosity
  -- ^ Verbosity
  -> ConfiguredProgram
  -- ^ The program to run
  -> [ProgArg]
  -- ^ Any /extra/ arguments to add
  -> IO String
getProgramOutput :: Verbosity -> ConfiguredProgram -> [ProgArg] -> IO ProgArg
getProgramOutput Verbosity
verbosity ConfiguredProgram
prog [ProgArg]
args =
  Verbosity -> ProgramInvocation -> IO ProgArg
getProgramInvocationOutput Verbosity
verbosity (ConfiguredProgram -> [ProgArg] -> ProgramInvocation
programInvocation ConfiguredProgram
prog [ProgArg]
args)

-- | Looks up the given program in the program database and runs it.
runDbProgram
  :: Verbosity
  -- ^ verbosity
  -> Program
  -- ^ The program to run
  -> ProgramDb
  -- ^ look up the program here
  -> [ProgArg]
  -- ^ Any /extra/ arguments to add
  -> IO ()
runDbProgram :: Verbosity -> Program -> ProgramDb -> [ProgArg] -> IO ()
runDbProgram Verbosity
verbosity Program
prog ProgramDb
progDb [ProgArg]
args =
  Verbosity
-> Maybe (SymbolicPath CWD ('Dir (ZonkAny 1)))
-> Program
-> ProgramDb
-> [ProgArg]
-> IO ()
forall to.
Verbosity
-> Maybe (SymbolicPath CWD ('Dir to))
-> Program
-> ProgramDb
-> [ProgArg]
-> IO ()
runDbProgramCwd Verbosity
verbosity Maybe (SymbolicPath CWD ('Dir (ZonkAny 1)))
forall a. Maybe a
Nothing Program
prog ProgramDb
progDb [ProgArg]
args

-- | Looks up the given program in the program database and runs it.
runDbProgramCwd
  :: Verbosity
  -- ^ verbosity
  -> Maybe (SymbolicPath CWD (Dir to))
  -- ^ working directory
  -> Program
  -- ^ The program to run
  -> ProgramDb
  -- ^ look up the program here
  -> [ProgArg]
  -- ^ Any /extra/ arguments to add
  -> IO ()
runDbProgramCwd :: forall to.
Verbosity
-> Maybe (SymbolicPath CWD ('Dir to))
-> Program
-> ProgramDb
-> [ProgArg]
-> IO ()
runDbProgramCwd Verbosity
verbosity Maybe (SymbolicPath CWD ('Dir to))
mbWorkDir Program
prog ProgramDb
programDb [ProgArg]
args =
  case Program -> ProgramDb -> Maybe ConfiguredProgram
lookupProgram Program
prog ProgramDb
programDb of
    Maybe ConfiguredProgram
Nothing ->
      Verbosity -> CabalException -> IO ()
forall a1 a.
(HasCallStack, Show a1, Typeable a1,
 Exception (VerboseException a1)) =>
Verbosity -> a1 -> IO a
dieWithException Verbosity
verbosity (CabalException -> IO ()) -> CabalException -> IO ()
forall a b. (a -> b) -> a -> b
$ ProgArg -> CabalException
ProgramNotFound (Program -> ProgArg
programName Program
prog)
    Just ConfiguredProgram
configuredProg -> Verbosity
-> Maybe (SymbolicPath CWD ('Dir to))
-> ConfiguredProgram
-> [ProgArg]
-> IO ()
forall to.
Verbosity
-> Maybe (SymbolicPath CWD ('Dir to))
-> ConfiguredProgram
-> [ProgArg]
-> IO ()
runProgramCwd Verbosity
verbosity Maybe (SymbolicPath CWD ('Dir to))
mbWorkDir ConfiguredProgram
configuredProg [ProgArg]
args

-- | Looks up the given program in the program database and runs it.
getDbProgramOutput
  :: Verbosity
  -- ^ verbosity
  -> Program
  -- ^ The program to run
  -> ProgramDb
  -- ^ look up the program here
  -> [ProgArg]
  -- ^ Any /extra/ arguments to add
  -> IO String
getDbProgramOutput :: Verbosity -> Program -> ProgramDb -> [ProgArg] -> IO ProgArg
getDbProgramOutput Verbosity
verb Program
prog ProgramDb
progDb [ProgArg]
args =
  Verbosity
-> Maybe (SymbolicPath CWD ('Dir (ZonkAny 0)))
-> Program
-> ProgramDb
-> [ProgArg]
-> IO ProgArg
forall to.
Verbosity
-> Maybe (SymbolicPath CWD ('Dir to))
-> Program
-> ProgramDb
-> [ProgArg]
-> IO ProgArg
getDbProgramOutputCwd Verbosity
verb Maybe (SymbolicPath CWD ('Dir (ZonkAny 0)))
forall a. Maybe a
Nothing Program
prog ProgramDb
progDb [ProgArg]
args

-- | Looks up the given program in the program database and runs it.
getDbProgramOutputCwd
  :: Verbosity
  -- ^ verbosity
  -> Maybe (SymbolicPath CWD (Dir to))
  -- ^ working directory
  -> Program
  -- ^ The program to run
  -> ProgramDb
  -- ^ look up the program here
  -> [ProgArg]
  -- ^ Any /extra/ arguments to add
  -> IO String
getDbProgramOutputCwd :: forall to.
Verbosity
-> Maybe (SymbolicPath CWD ('Dir to))
-> Program
-> ProgramDb
-> [ProgArg]
-> IO ProgArg
getDbProgramOutputCwd Verbosity
verbosity Maybe (SymbolicPath CWD ('Dir to))
mbWorkDir Program
prog ProgramDb
programDb [ProgArg]
args =
  case Program -> ProgramDb -> Maybe ConfiguredProgram
lookupProgram Program
prog ProgramDb
programDb of
    Maybe ConfiguredProgram
Nothing -> Verbosity -> CabalException -> IO ProgArg
forall a1 a.
(HasCallStack, Show a1, Typeable a1,
 Exception (VerboseException a1)) =>
Verbosity -> a1 -> IO a
dieWithException Verbosity
verbosity (CabalException -> IO ProgArg) -> CabalException -> IO ProgArg
forall a b. (a -> b) -> a -> b
$ ProgArg -> CabalException
ProgramNotFound (Program -> ProgArg
programName Program
prog)
    Just ConfiguredProgram
configuredProg ->
      Verbosity -> ProgramInvocation -> IO ProgArg
getProgramInvocationOutput Verbosity
verbosity (ProgramInvocation -> IO ProgArg)
-> ProgramInvocation -> IO ProgArg
forall a b. (a -> b) -> a -> b
$
        Maybe (SymbolicPath CWD ('Dir to))
-> ConfiguredProgram -> [ProgArg] -> ProgramInvocation
forall to.
Maybe (SymbolicPath CWD ('Dir to))
-> ConfiguredProgram -> [ProgArg] -> ProgramInvocation
programInvocationCwd Maybe (SymbolicPath CWD ('Dir to))
mbWorkDir ConfiguredProgram
configuredProg [ProgArg]
args