packages feed

stack-3.11.1: src/Stack/Types/Dependency.hs

{-# LANGUAGE NoImplicitPrelude   #-}
{-# LANGUAGE NoFieldSelectors    #-}
{-# LANGUAGE OverloadedRecordDot #-}

{-|
Module      : Stack.Types.Dependency
License     : BSD-3-Clause
-}

module Stack.Types.Dependency
  ( DepValue (..)
  , DepType (..)
  , DepLibrary (..)
  , cabalToStackDep
  , cabalExeToStackDep
  , cabalSetupDepsToStackDep
  , libraryDepFromVersionRange
  , isDepTypeLibrary
  , getDepSublib
  , depValueToTarget
  ) where

import qualified Data.Map as Map
import qualified Data.Set as Set
import qualified Distribution.PackageDescription as Cabal
import           Distribution.Types.VersionRange ( VersionRange )
import           Stack.Prelude
import           Stack.Types.ComponentUtils
                   ( StackUnqualCompName (..), fromCabalName )
import           Stack.Types.NamedComponent ( NamedComponent(..) )
import           Stack.Types.SourceMap ( PackageType (..), Target (..) )

-- | The value for a map from dependency name. This contains both the version
-- range and the type of dependency.
data DepValue = DepValue
  { versionRange :: !VersionRange
  , depType :: !DepType
  }
  deriving Show

-- | Is this package being used as a library, or just as a build tool? If the
-- former, we need to ensure that a library actually exists. See
-- <https://github.com/commercialhaskell/stack/issues/2195>
data DepType
  = AsLibrary !DepLibrary
    -- ^ Dependency is used as a library.
  | AsBuildTool
    -- ^ Dependency is used only to provide a build tool.
  deriving (Eq, Show)

-- | Type repesenting dependency packages used as a library.
data DepLibrary = DepLibrary
  { main :: !Bool
    -- ^ Is the dependency on a main (unnamed) library component?
  , subLib :: Set StackUnqualCompName
    -- ^ A set (which may be empty) of dependencies on sub-library components.
  }
  deriving (Eq, Show)

-- | A function to yield the set (which may be empty) of dependencies on
-- sub-library components. Yields 'Nothing' if the dependency is used only to
-- provide a build tool.
getDepSublib :: DepValue -> Maybe (Set StackUnqualCompName)
getDepSublib val = case val.depType of
  AsLibrary libVal -> Just libVal.subLib
  _ -> Nothing

-- | Represents a dependency only on a main (unnamed) library component.
defaultDepLibrary :: DepLibrary
defaultDepLibrary = DepLibrary True mempty

-- | Test whether the dependency is being used as a library.
isDepTypeLibrary :: DepType -> Bool
isDepTypeLibrary AsLibrary{} = True
isDepTypeLibrary AsBuildTool = False

-- | Given a 'Cabal.Dependency', yield the Stack equivalent.
cabalToStackDep :: Cabal.Dependency -> DepValue
cabalToStackDep (Cabal.Dependency _ verRange libNameSet) =
  DepValue { versionRange = verRange, depType = AsLibrary depLibrary }
 where
  depLibrary = DepLibrary finalHasMain filteredItems

  (finalHasMain, filteredItems) = foldr' iterator (False, mempty) libNameSet
   where
    iterator LMainLibName (_, newLibNameSet) = (True, newLibNameSet)
    iterator (LSubLibName libName) (hasMain, newLibNameSet) =
      (hasMain, Set.insert (fromCabalName libName) newLibNameSet)

-- | Given an 'Cabal.ExeDependency', yield the Stack equivalent.
cabalExeToStackDep :: Cabal.ExeDependency -> DepValue
cabalExeToStackDep (Cabal.ExeDependency _ _name verRange) =
  DepValue { versionRange = verRange, depType = AsBuildTool }

cabalSetupDepsToStackDep :: Cabal.SetupBuildInfo -> Map PackageName DepValue
cabalSetupDepsToStackDep setupInfo =
  foldr' inserter mempty (Cabal.setupDepends setupInfo)
 where
  inserter d@(Cabal.Dependency packageName _ _) =
    Map.insert packageName (cabalToStackDep d)

libraryDepFromVersionRange :: VersionRange -> DepValue
libraryDepFromVersionRange range = DepValue
  { versionRange = range
  , depType = AsLibrary defaultDepLibrary
  }

depValueToTarget :: DepValue -> Target
depValueToTarget dv = case dv.depType of
  AsLibrary dlib -> TargetComps (completeSet dlib)
  AsBuildTool -> TargetAll PTDependency
 where
  completeSet dlib =
    (if dlib.main then Set.insert CLib else id) $ sublibSet dlib
  sublibSet dlib = Set.mapMonotonic CSubLib dlib.subLib