-- |
--
-- 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
import           Distribution.Types.ExeDependency
import           Distribution.Types.LegacyExeDependency
import           Distribution.Types.UnqualComponentName

-- | 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
  ]