{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveGeneric #-}

module Distribution.Types.HookedBuildInfo (
    HookedBuildInfo,
    emptyHookedBuildInfo,
  ) where

-- import Distribution.Compat.Prelude
import Distribution.Types.BuildInfo
import Distribution.Types.UnqualComponentName

-- | 'HookedBuildInfo' is mechanism that hooks can use to
-- override the 'BuildInfo's inside packages.  One example
-- use-case (which is used in core libraries today) is as
-- a way of passing flags which are computed by a configure
-- script into Cabal.  In this case, the autoconf build type adds
-- hooks to read in a textual 'HookedBuildInfo' format prior
-- to doing any operations.
--
-- Quite honestly, this mechanism is a massive hack since we shouldn't
-- be editing the 'PackageDescription' data structure (it's easy
-- to assume that this data structure shouldn't change and
-- run into bugs, see for example 1c20a6328579af9e37677d507e2e9836ef70ab9d).
-- But it's a bit convenient, because there isn't another data
-- structure that allows adding extra 'BuildInfo' style things.
--
-- In any case, a lot of care has to be taken to make sure the
-- 'HookedBuildInfo' is applied to the 'PackageDescription'.  In
-- general this process occurs in "Distribution.Simple", which is
-- responsible for orchestrating the hooks mechanism.  The
-- general strategy:
--
--      1. We run the pre-hook, which produces a 'HookedBuildInfo'
--         (e.g., in the Autoconf case, it reads it out from a file).
--      2. We sanity-check the hooked build info with
--         'sanityCheckHookedBuildInfo'.
--      3. We update our 'PackageDescription' (either freshly read
--         or cached from 'LocalBuildInfo') with 'updatePackageDescription'.
--
--         In principle, we are also supposed to update the copy of
--         the 'PackageDescription' stored in 'LocalBuildInfo'
--         at 'localPkgDescr'.  Unfortunately, in practice, there
--         are lots of Custom setup scripts which fail to update
--         'localPkgDescr' so you really shouldn't rely on it.
--         It's not DEPRECATED because there are legitimate uses
--         for it, but... yeah.  Sharp knife.  See
--         <https://github.com/haskell/cabal/issues/3606>
--         for more information on the issue.
--
-- It is not well-specified whether or not a 'HookedBuildInfo' applied
-- at configure time is persistent to the 'LocalBuildInfo'.  The
-- fact that 'HookedBuildInfo' is passed to 'confHook' MIGHT SUGGEST
-- that the 'HookedBuildInfo' is applied at this time, but actually
-- since 9317b67e6122ab14e53f81b573bd0ecb388eca5a it has been ONLY used
-- to create a modified package description that we check for problems:
-- it is never actually saved to the LBI.  Since 'HookedBuildInfo' is
-- applied monoidally to the existing build infos (and it is not an
-- idempotent monoid), it could break things to save it, since we
-- are obligated to apply any new 'HookedBuildInfo' and then we'd
-- get the effect twice.  But this does mean we have to re-apply
-- it every time. Hey, it's more flexibility.
type HookedBuildInfo = (Maybe BuildInfo, [(UnqualComponentName, BuildInfo)])

emptyHookedBuildInfo :: HookedBuildInfo
emptyHookedBuildInfo :: HookedBuildInfo
emptyHookedBuildInfo = (forall a. Maybe a
Nothing, [])