Distribution/Simple/BuildToolDepends.hs

    -- |
    --
    -- 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 pkg led =
      if foundLocal
      then Just $ ExeDependency (packageName pkg) toolName reqVer
      else Map.lookup name whiteMap
      where
        LegacyExeDependency name reqVer = led
        toolName = mkUnqualComponentName name
        foundLocal = toolName `elem` map exeName (executables pkg)
        whitelist = [ "hscolour", "haddock", "happy", "alex", "hsc2hs", "c2hs"
                    , "cpphs", "greencard", "hspec-discover"
                    ]
        whiteMap  = Map.fromList $ flip map whitelist $ \n ->
          (n, ExeDependency (mkPackageName n) (mkUnqualComponentName n) 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 pkg bi =
      buildToolDepends bi ++ mapMaybe (desugarBuildTool pkg) (buildTools 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 pkg (ExeDependency n _ _) = n == packageName 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 pkg bi =
      [ toolname
      | dep@(ExeDependency _ toolname _) <- getAllToolDependencies pkg bi
      , isInternal pkg dep
      ]