    -- |
    --
    -- This modules provides functions for working with both the legacy
    -- "build-tools" field, and its replacement, "build-tool-depends". Prefer using
    -- the functions contained to access those fields directly.
    module Distribution.Simple.BuildToolDepends where

    import           Prelude ()
    import           Distribution.Compat.Prelude

    import qualified Data.Map as Map

    import           Distribution.Package
    import           Distribution.PackageDescription

    -- | Desugar a "build-tools" entry into proper a executable dependency if
    -- possible.
    --
    -- An entry can be so desguared in two cases:
    --
    -- 1. The name in build-tools matches a locally defined executable.  The
    --    executable dependency produced is on that exe in the current package.
    --
    -- 2. The name in build-tools matches a hard-coded set of known tools.  For now,
    --    the executable dependency produced is one an executable in a package of
    --    the same, but the hard-coding could just as well be per-key.
    --
    -- The first cases matches first.
    desugarBuildTool :: PackageDescription
                     -> LegacyExeDependency
                     -> Maybe ExeDependency
    desugarBuildTool :: PackageDescription -> LegacyExeDependency -> Maybe ExeDependency
    desugarBuildTool PackageDescription
    pkg LegacyExeDependency
    led =
      if Bool
    foundLocal
      then ExeDependency -> Maybe ExeDependency
    forall a. a -> Maybe a
    Just (ExeDependency -> Maybe ExeDependency)
    -> ExeDependency -> Maybe ExeDependency
    forall a b. (a -> b) -> a -> b
    $ PackageName -> UnqualComponentName -> VersionRange -> ExeDependency
    ExeDependency (PackageDescription -> PackageName
    forall pkg. Package pkg => pkg -> PackageName
    packageName PackageDescription
    pkg) UnqualComponentName
    toolName VersionRange
    reqVer
      else String -> Map String ExeDependency -> Maybe ExeDependency
    forall k a. Ord k => k -> Map k a -> Maybe a
    Map.lookup String
    name Map String ExeDependency
    whiteMap
      where
        LegacyExeDependency String
    name VersionRange
    reqVer = LegacyExeDependency
    led
        toolName :: UnqualComponentName
    toolName = String -> UnqualComponentName
    mkUnqualComponentName String
    name
        foundLocal :: Bool
    foundLocal = UnqualComponentName
    toolName UnqualComponentName -> [UnqualComponentName] -> Bool
    forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
    `elem` (Executable -> UnqualComponentName)
    -> [Executable] -> [UnqualComponentName]
    forall a b. (a -> b) -> [a] -> [b]
    map Executable -> UnqualComponentName
    exeName (PackageDescription -> [Executable]
    executables PackageDescription
    pkg)
        whitelist :: [String]
    whitelist = [ String
    "hscolour", String
    "haddock", String
    "happy", String
    "alex", String
    "hsc2hs", String
    "c2hs"
                    , String
    "cpphs", String
    "greencard", String
    "hspec-discover"
                    ]
        whiteMap :: Map String ExeDependency
    whiteMap  = [(String, ExeDependency)] -> Map String ExeDependency
    forall k a. Ord k => [(k, a)] -> Map k a
    Map.fromList ([(String, ExeDependency)] -> Map String ExeDependency)
    -> [(String, ExeDependency)] -> Map String ExeDependency
    forall a b. (a -> b) -> a -> b
    $ ((String -> (String, ExeDependency))
     -> [String] -> [(String, ExeDependency)])
    -> [String]
    -> (String -> (String, ExeDependency))
    -> [(String, ExeDependency)]
    forall a b c. (a -> b -> c) -> b -> a -> c
    flip (String -> (String, ExeDependency))
    -> [String] -> [(String, ExeDependency)]
    forall a b. (a -> b) -> [a] -> [b]
    map [String]
    whitelist ((String -> (String, ExeDependency)) -> [(String, ExeDependency)])
    -> (String -> (String, ExeDependency)) -> [(String, ExeDependency)]
    forall a b. (a -> b) -> a -> b
    $ \String
    n ->
          (String
    n, PackageName -> UnqualComponentName -> VersionRange -> ExeDependency
    ExeDependency (String -> PackageName
    mkPackageName String
    n) (String -> UnqualComponentName
    mkUnqualComponentName String
    n) VersionRange
    reqVer)

    -- | Get everything from "build-tool-depends", along with entries from
    -- "build-tools" that we know how to desugar.
    --
    -- This should almost always be used instead of just accessing the
    -- `buildToolDepends` field directly.
    getAllToolDependencies :: PackageDescription
                           -> BuildInfo
                           -> [ExeDependency]
    getAllToolDependencies :: PackageDescription -> BuildInfo -> [ExeDependency]
    getAllToolDependencies PackageDescription
    pkg BuildInfo
    bi =
      BuildInfo -> [ExeDependency]
    buildToolDepends BuildInfo
    bi [ExeDependency] -> [ExeDependency] -> [ExeDependency]
    forall a. [a] -> [a] -> [a]
    ++ (LegacyExeDependency -> Maybe ExeDependency)
    -> [LegacyExeDependency] -> [ExeDependency]
    forall a b. (a -> Maybe b) -> [a] -> [b]
    mapMaybe (PackageDescription -> LegacyExeDependency -> Maybe ExeDependency
    desugarBuildTool PackageDescription
    pkg) (BuildInfo -> [LegacyExeDependency]
    buildTools BuildInfo
    bi)

    -- | Does the given executable dependency map to this current package?
    --
    -- This is a tiny function, but used in a number of places.
    --
    -- This function is only sound to call on `BuildInfo`s from the given package
    -- description. This is because it just filters the package names of each
    -- dependency, and does not check whether version bounds in fact exclude the
    -- current package, or the referenced components in fact exist in the current
    -- package.
    --
    -- This is OK because when a package is loaded, it is checked (in
    -- `Distribution.Package.Check`) that dependencies matching internal components
    -- do indeed have version bounds accepting the current package, and any
    -- depended-on component in the current package actually exists. In fact this
    -- check is performed by gathering the internal tool dependencies of each
    -- component of the package according to this module, and ensuring those
    -- properties on each so-gathered dependency.
    --
    -- version bounds and components of the package are unchecked. This is because
    -- we sanitize exe deps so that the matching name implies these other
    -- conditions.
    isInternal :: PackageDescription -> ExeDependency -> Bool
    isInternal :: PackageDescription -> ExeDependency -> Bool
    isInternal PackageDescription
    pkg (ExeDependency PackageName
    n UnqualComponentName
    _ VersionRange
    _) = PackageName
    n PackageName -> PackageName -> Bool
    forall a. Eq a => a -> a -> Bool
    == PackageDescription -> PackageName
    forall pkg. Package pkg => pkg -> PackageName
    packageName PackageDescription
    pkg

    -- | Get internal "build-tool-depends", along with internal "build-tools"
    --
    -- This is a tiny function, but used in a number of places. The same
    -- restrictions that apply to `isInternal` also apply to this function.
    getAllInternalToolDependencies :: PackageDescription
                                   -> BuildInfo
                                   -> [UnqualComponentName]
    getAllInternalToolDependencies :: PackageDescription -> BuildInfo -> [UnqualComponentName]
    getAllInternalToolDependencies PackageDescription
    pkg BuildInfo
    bi =
      [ UnqualComponentName
    toolname
      | dep :: ExeDependency
    dep@(ExeDependency PackageName
    _ UnqualComponentName
    toolname VersionRange
    _) <- PackageDescription -> BuildInfo -> [ExeDependency]
    getAllToolDependencies PackageDescription
    pkg BuildInfo
    bi
      , PackageDescription -> ExeDependency -> Bool
    isInternal PackageDescription
    pkg ExeDependency
    dep
      ]