-----------------------------------------------------------------------------
-- |
-- Module      :  Distribution.Simple.Program.Builtin
-- Copyright   :  Isaac Jones 2006, Duncan Coutts 2007-2009
--
-- Maintainer  :  cabal-devel@haskell.org
-- Portability :  portable
--
-- The module defines all the known built-in 'Program's.
--
-- Where possible we try to find their version numbers.
--
module Distribution.Simple.Program.Builtin (

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

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

import Prelude ()
import Distribution.Compat.Prelude

import Distribution.Simple.Program.GHC
import Distribution.Simple.Program.Find
import Distribution.Simple.Program.Internal
import Distribution.Simple.Program.Run
import Distribution.Simple.Program.Types
import Distribution.Simple.Utils
import Distribution.Verbosity
import Distribution.Version

import qualified Data.Map as Map

-- ------------------------------------------------------------
-- * Known programs
-- ------------------------------------------------------------

-- | The default list of programs.
-- These programs are typically used internally to Cabal.
builtinPrograms :: [Program]
builtinPrograms :: [Program]
builtinPrograms =
    [
    -- compilers and related progs
      Program
ghcProgram
    , Program
runghcProgram
    , Program
ghcPkgProgram
    , Program
ghcjsProgram
    , Program
ghcjsPkgProgram
    , Program
haskellSuiteProgram
    , Program
haskellSuitePkgProgram
    , Program
hmakeProgram
    , Program
jhcProgram
    , Program
uhcProgram
    , Program
hpcProgram
    -- preprocessors
    , Program
hscolourProgram
    , Program
doctestProgram
    , Program
haddockProgram
    , Program
happyProgram
    , Program
alexProgram
    , Program
hsc2hsProgram
    , Program
c2hsProgram
    , Program
cpphsProgram
    , Program
greencardProgram
    -- platform toolchain
    , Program
gccProgram
    , Program
arProgram
    , Program
stripProgram
    , Program
ldProgram
    , Program
tarProgram
    -- configuration tools
    , Program
pkgConfigProgram
    ]

ghcProgram :: Program
ghcProgram :: Program
ghcProgram = (String -> Program
simpleProgram String
"ghc") {
    programFindVersion = findProgramVersion "--numeric-version" id,

    -- Workaround for https://gitlab.haskell.org/ghc/ghc/-/issues/8825
    -- (spurious warning on non-english locales)
    programPostConf    = \Verbosity
_verbosity ConfiguredProgram
ghcProg ->
    do let ghcProg' :: ConfiguredProgram
ghcProg' = ConfiguredProgram
ghcProg {
             programOverrideEnv = ("LANGUAGE", Just "en")
                                  : programOverrideEnv ghcProg
             }
           -- Only the 7.8 branch seems to be affected. Fixed in 7.8.4.
           affectedVersionRange :: VersionRange
affectedVersionRange = VersionRange -> VersionRange -> VersionRange
intersectVersionRanges
                                  (Version -> VersionRange
laterVersion   (Version -> VersionRange) -> Version -> VersionRange
forall a b. (a -> b) -> a -> b
$ [Int] -> Version
mkVersion [Int
7,Int
8,Int
0])
                                  (Version -> VersionRange
earlierVersion (Version -> VersionRange) -> Version -> VersionRange
forall a b. (a -> b) -> a -> b
$ [Int] -> Version
mkVersion [Int
7,Int
8,Int
4])
       ConfiguredProgram -> IO ConfiguredProgram
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (ConfiguredProgram -> IO ConfiguredProgram)
-> ConfiguredProgram -> IO ConfiguredProgram
forall a b. (a -> b) -> a -> b
$ ConfiguredProgram
-> (Version -> ConfiguredProgram)
-> Maybe Version
-> ConfiguredProgram
forall b a. b -> (a -> b) -> Maybe a -> b
maybe ConfiguredProgram
ghcProg
         (\Version
v -> if Version -> VersionRange -> Bool
withinRange Version
v VersionRange
affectedVersionRange
                then ConfiguredProgram
ghcProg' else ConfiguredProgram
ghcProg)
         (ConfiguredProgram -> Maybe Version
programVersion ConfiguredProgram
ghcProg),

    programNormaliseArgs = normaliseGhcArgs
  }

runghcProgram :: Program
runghcProgram :: Program
runghcProgram = (String -> Program
simpleProgram String
"runghc") {
    programFindVersion = findProgramVersion "--version" $ \String
str ->
      case String -> [String]
words String
str of
        -- "runghc 7.10.3"
        (String
_:String
ver:[String]
_) -> String
ver
        [String]
_ -> String
""
  }

ghcPkgProgram :: Program
ghcPkgProgram :: Program
ghcPkgProgram = (String -> Program
simpleProgram String
"ghc-pkg") {
    programFindVersion = findProgramVersion "--version" $ \String
str ->
      -- Invoking "ghc-pkg --version" gives a string like
      -- "GHC package manager version 6.4.1"
      case String -> [String]
words String
str of
        (String
_:String
_:String
_:String
_:String
ver:[String]
_) -> String
ver
        [String]
_               -> String
""
  }

ghcjsProgram :: Program
ghcjsProgram :: Program
ghcjsProgram = (String -> Program
simpleProgram String
"ghcjs") {
    programFindVersion = findProgramVersion "--numeric-ghcjs-version" id
  }

-- note: version is the version number of the GHC version that ghcjs-pkg was built with
ghcjsPkgProgram :: Program
ghcjsPkgProgram :: Program
ghcjsPkgProgram = (String -> Program
simpleProgram String
"ghcjs-pkg") {
    programFindVersion = findProgramVersion "--version" $ \String
str ->
      -- Invoking "ghcjs-pkg --version" gives a string like
      -- "GHCJS package manager version 6.4.1"
      case String -> [String]
words String
str of
        (String
_:String
_:String
_:String
_:String
ver:[String]
_) -> String
ver
        [String]
_               -> String
""
  }

hmakeProgram :: Program
hmakeProgram :: Program
hmakeProgram = (String -> Program
simpleProgram String
"hmake") {
    programFindVersion = findProgramVersion "--version" $ \String
str ->
    -- Invoking "hmake --version" gives a string line
    -- "/usr/local/bin/hmake: 3.13 (2006-11-01)"
      case String -> [String]
words String
str of
        (String
_:String
ver:[String]
_) -> String
ver
        [String]
_         -> String
""
  }

jhcProgram :: Program
jhcProgram :: Program
jhcProgram = (String -> Program
simpleProgram String
"jhc") {
    programFindVersion = findProgramVersion "--version" $ \String
str ->
    -- invoking "jhc --version" gives a string like
    -- "jhc 0.3.20080208 (wubgipkamcep-2)
    -- compiled by ghc-6.8 on a x86_64 running linux"
      case String -> [String]
words String
str of
        (String
_:String
ver:[String]
_) -> String
ver
        [String]
_         -> String
""
  }

uhcProgram :: Program
uhcProgram :: Program
uhcProgram = (String -> Program
simpleProgram String
"uhc") {
    programFindVersion = findProgramVersion "--version-dotted" id
  }

hpcProgram :: Program
hpcProgram :: Program
hpcProgram = (String -> Program
simpleProgram String
"hpc")
    {
        programFindVersion = findProgramVersion "version" $ \String
str ->
            case String -> [String]
words String
str of
                (String
_ : String
_ : String
_ : String
ver : [String]
_) -> String
ver
                [String]
_ -> String
""
    }

-- This represents a haskell-suite compiler. Of course, the compiler
-- itself probably is not called "haskell-suite", so this is not a real
-- program. (But we don't know statically the name of the actual compiler,
-- so this is the best we can do.)
--
-- Having this Program value serves two purposes:
--
-- 1. We can accept options for the compiler in the form of
--
--   --haskell-suite-option(s)=...
--
-- 2. We can find a program later using this static id (with
-- requireProgram).
--
-- The path to the real compiler is found and recorded in the ProgramDb
-- during the configure phase.
haskellSuiteProgram :: Program
haskellSuiteProgram :: Program
haskellSuiteProgram = (String -> Program
simpleProgram String
"haskell-suite") {
    -- pretend that the program exists, otherwise it won't be in the
    -- "configured" state
    programFindLocation = \Verbosity
_verbosity ProgramSearchPath
_searchPath ->
      Maybe (String, [String]) -> IO (Maybe (String, [String]))
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe (String, [String]) -> IO (Maybe (String, [String])))
-> Maybe (String, [String]) -> IO (Maybe (String, [String]))
forall a b. (a -> b) -> a -> b
$ (String, [String]) -> Maybe (String, [String])
forall a. a -> Maybe a
Just (String
"haskell-suite-dummy-location", [])
  }

-- This represent a haskell-suite package manager. See the comments for
-- haskellSuiteProgram.
haskellSuitePkgProgram :: Program
haskellSuitePkgProgram :: Program
haskellSuitePkgProgram = (String -> Program
simpleProgram String
"haskell-suite-pkg") {
    programFindLocation = \Verbosity
_verbosity ProgramSearchPath
_searchPath ->
      Maybe (String, [String]) -> IO (Maybe (String, [String]))
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe (String, [String]) -> IO (Maybe (String, [String])))
-> Maybe (String, [String]) -> IO (Maybe (String, [String]))
forall a b. (a -> b) -> a -> b
$ (String, [String]) -> Maybe (String, [String])
forall a. a -> Maybe a
Just (String
"haskell-suite-pkg-dummy-location", [])
  }


happyProgram :: Program
happyProgram :: Program
happyProgram = (String -> Program
simpleProgram String
"happy") {
    programFindVersion = findProgramVersion "--version" $ \String
str ->
      -- Invoking "happy --version" gives a string like
      -- "Happy Version 1.16 Copyright (c) ...."
      case String -> [String]
words String
str of
        (String
_:String
_:String
ver:[String]
_) -> String
ver
        [String]
_           -> String
""
  }

alexProgram :: Program
alexProgram :: Program
alexProgram = (String -> Program
simpleProgram String
"alex") {
    programFindVersion = findProgramVersion "--version" $ \String
str ->
      -- Invoking "alex --version" gives a string like
      -- "Alex version 2.1.0, (c) 2003 Chris Dornan and Simon Marlow"
      case String -> [String]
words String
str of
        (String
_:String
_:String
ver:[String]
_) -> (Char -> Bool) -> String -> String
forall a. (a -> Bool) -> [a] -> [a]
takeWhile (\Char
x -> Char -> Bool
isDigit Char
x Bool -> Bool -> Bool
|| Char
x Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'.') String
ver
        [String]
_           -> String
""
  }

gccProgram :: Program
gccProgram :: Program
gccProgram = (String -> Program
simpleProgram String
"gcc") {
    programFindVersion = findProgramVersion "-dumpversion" id
  }

arProgram :: Program
arProgram :: Program
arProgram = String -> Program
simpleProgram String
"ar"

stripProgram :: Program
stripProgram :: Program
stripProgram = (String -> Program
simpleProgram String
"strip") {
    programFindVersion = \Verbosity
verbosity ->
      String
-> (String -> String) -> Verbosity -> String -> IO (Maybe Version)
findProgramVersion String
"--version" String -> String
stripExtractVersion (Verbosity -> Verbosity
lessVerbose Verbosity
verbosity)
  }

hsc2hsProgram :: Program
hsc2hsProgram :: Program
hsc2hsProgram = (String -> Program
simpleProgram String
"hsc2hs") {
    programFindVersion =
      findProgramVersion "--version" $ \String
str ->
        -- Invoking "hsc2hs --version" gives a string like "hsc2hs version 0.66"
        case String -> [String]
words String
str of
          (String
_:String
_:String
ver:[String]
_) -> String
ver
          [String]
_           -> String
""
  }

c2hsProgram :: Program
c2hsProgram :: Program
c2hsProgram = (String -> Program
simpleProgram String
"c2hs") {
    programFindVersion = findProgramVersion "--numeric-version" id
  }

cpphsProgram :: Program
cpphsProgram :: Program
cpphsProgram = (String -> Program
simpleProgram String
"cpphs") {
    programFindVersion = findProgramVersion "--version" $ \String
str ->
      -- Invoking "cpphs --version" gives a string like "cpphs 1.3"
      case String -> [String]
words String
str of
        (String
_:String
ver:[String]
_) -> String
ver
        [String]
_         -> String
""
  }

hscolourProgram :: Program
hscolourProgram :: Program
hscolourProgram = (String -> Program
simpleProgram String
"hscolour") {
    programFindLocation = \Verbosity
v ProgramSearchPath
p -> Verbosity
-> ProgramSearchPath -> String -> IO (Maybe (String, [String]))
findProgramOnSearchPath Verbosity
v ProgramSearchPath
p String
"HsColour",
    programFindVersion  = findProgramVersion "-version" $ \String
str ->
      -- Invoking "HsColour -version" gives a string like "HsColour 1.7"
      case String -> [String]
words String
str of
        (String
_:String
ver:[String]
_) -> String
ver
        [String]
_         -> String
""
  }

-- TODO: Ensure that doctest is built against the same GHC as the one
--       that's being used.  Same for haddock.  @phadej pointed this out.
doctestProgram :: Program
doctestProgram :: Program
doctestProgram = (String -> Program
simpleProgram String
"doctest") {
    programFindLocation = \Verbosity
v ProgramSearchPath
p -> Verbosity
-> ProgramSearchPath -> String -> IO (Maybe (String, [String]))
findProgramOnSearchPath Verbosity
v ProgramSearchPath
p String
"doctest"
  , programFindVersion  = findProgramVersion "--version" $ \String
str ->
         -- "doctest version 0.11.2"
         case String -> [String]
words String
str of
           (String
_:String
_:String
ver:[String]
_) -> String
ver
           [String]
_           -> String
""
  }

haddockProgram :: Program
haddockProgram :: Program
haddockProgram = (String -> Program
simpleProgram String
"haddock") {
    programFindVersion = findProgramVersion "--version" $ \String
str ->
      -- Invoking "haddock --version" gives a string like
      -- "Haddock version 0.8, (c) Simon Marlow 2006"
      case String -> [String]
words String
str of
        (String
_:String
_:String
ver:[String]
_) -> (Char -> Bool) -> String -> String
forall a. (a -> Bool) -> [a] -> [a]
takeWhile (Char -> String -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` (Char
'.'Char -> String -> String
forall a. a -> [a] -> [a]
:[Char
'0'..Char
'9'])) String
ver
        [String]
_           -> String
"",

    programNormaliseArgs = \Maybe Version
_ PackageDescription
_ [String]
args -> [String]
args
  }

greencardProgram :: Program
greencardProgram :: Program
greencardProgram = String -> Program
simpleProgram String
"greencard"

ldProgram :: Program
ldProgram :: Program
ldProgram = String -> Program
simpleProgram String
"ld"

tarProgram :: Program
tarProgram :: Program
tarProgram = (String -> Program
simpleProgram String
"tar") {
  -- See #1901. Some versions of 'tar' (OpenBSD, NetBSD, ...) don't support the
  -- '--format' option.
  programPostConf = \Verbosity
verbosity ConfiguredProgram
tarProg -> do
     String
tarHelpOutput <- Verbosity -> ProgramInvocation -> IO String
getProgramInvocationOutput
                      Verbosity
verbosity (ConfiguredProgram -> [String] -> ProgramInvocation
programInvocation ConfiguredProgram
tarProg [String
"--help"])
                      -- Some versions of tar don't support '--help'.
                      IO String -> (IOException -> IO String) -> IO String
forall a. IO a -> (IOException -> IO a) -> IO a
`catchIO` (\IOException
_ -> String -> IO String
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return String
"")
     let k :: String
k = String
"Supports --format"
         v :: String
v = if (String
"--format" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isInfixOf` String
tarHelpOutput) then String
"YES" else String
"NO"
         m :: Map String String
m = String -> String -> Map String String -> Map String String
forall k a. Ord k => k -> a -> Map k a -> Map k a
Map.insert String
k String
v (ConfiguredProgram -> Map String String
programProperties ConfiguredProgram
tarProg)
     ConfiguredProgram -> IO ConfiguredProgram
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (ConfiguredProgram -> IO ConfiguredProgram)
-> ConfiguredProgram -> IO ConfiguredProgram
forall a b. (a -> b) -> a -> b
$ ConfiguredProgram
tarProg { programProperties = m }
  }

cppProgram :: Program
cppProgram :: Program
cppProgram = String -> Program
simpleProgram String
"cpp"

pkgConfigProgram :: Program
pkgConfigProgram :: Program
pkgConfigProgram = (String -> Program
simpleProgram String
"pkg-config") {
    programFindVersion = findProgramVersion "--version" id
  }