Distribution/Types/HookedBuildInfo.hs

    {-# 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 = (Nothing, [])