module GhcVersionChaser
( findGhcVersions
) where
import Distribution.Version
import Distribution.Compiler
import Distribution.Types.CondTree
import Distribution.Types.Condition
import Distribution.Types.GenericPackageDescription
import Data.Ord (comparing, Down(..))
import Data.List (nub, sortBy)
import Data.Maybe (mapMaybe, maybeToList)
import Data.Function (on)
import GhcDatabase
import VabalContext
type GhcVersionRangeCondition = VersionRange
type VersionRangeConditionTruthAssignment = VersionRange
type GhcAssignment = (VersionRange, Version)
extractGhcConditions :: CondTree ConfVar c a -> [GhcVersionRangeCondition]
extractGhcConditions =
foldMap (analyzeCond . condBranchCondition) . condTreeComponents
where analyzeCond :: Condition ConfVar -> [GhcVersionRangeCondition]
analyzeCond (Lit _) = []
analyzeCond (Var (Impl GHC vr)) = [vr]
analyzeCond (Var _) = []
analyzeCond (CNot c) = analyzeCond c
analyzeCond (COr c1 c2) = analyzeCond c1 ++ analyzeCond c2
analyzeCond (CAnd c1 c2) = analyzeCond c1 ++ analyzeCond c2
ghcConditions :: GenericPackageDescription -> [GhcVersionRangeCondition]
ghcConditions pkgDescr = concat
[ concatMap extractGhcConditions $ maybeToList (condLibrary pkgDescr)
, concatMap (extractGhcConditions . snd) $ condSubLibraries pkgDescr
, concatMap (extractGhcConditions . snd) $ condForeignLibs pkgDescr
, concatMap (extractGhcConditions . snd) $ condExecutables pkgDescr
, concatMap (extractGhcConditions . snd) $ condTestSuites pkgDescr
, concatMap (extractGhcConditions . snd) $ condBenchmarks pkgDescr
]
makeVersionRangeConditionTruthAssignment :: [VersionRange]
-> VersionRangeConditionTruthAssignment
makeVersionRangeConditionTruthAssignment = foldr intersectVersionRanges anyVersion
complementaryVersionRange :: VersionRange -> VersionRange
complementaryVersionRange = foldVersionRange noVersion
notThisVersion
orEarlierVersion
orLaterVersion
intersectVersionRanges
unionVersionRanges
allTruthAssignments :: [GhcVersionRangeCondition] -> [VersionRangeConditionTruthAssignment]
allTruthAssignments vars =
let vars' = map (\v -> [v, complementaryVersionRange v]) vars
in map makeVersionRangeConditionTruthAssignment $ foldr addAssignment [[]] vars'
where addAssignment :: [GhcVersionRangeCondition]
-> [[VersionRange]]
-> [[VersionRange]]
addAssignment alternatives assignments = do
newAssignment <- alternatives
otherAssignments <- assignments
return (newAssignment : otherAssignments)
truthAssignmentToGhcVersion :: GhcToBaseMap
-> VersionRangeConditionTruthAssignment
-> Maybe Version
truthAssignmentToGhcVersion = newestGhcVersionIn
truthAssignmentToBaseVersionRange :: GhcToBaseMap
-> VersionRangeConditionTruthAssignment
-> VersionRange
truthAssignmentToBaseVersionRange gtb versionRangeAssignment =
let baseVersions = map thisVersion $ baseVersionsIn gtb versionRangeAssignment
in foldr unionVersionRanges noVersion baseVersions
truthAssignmentToGhcAssignment :: GhcToBaseMap
-> VersionRangeConditionTruthAssignment
-> Maybe GhcAssignment
truthAssignmentToGhcAssignment gtb ass =
case truthAssignmentToGhcVersion gtb ass of
Nothing -> Nothing
Just ghcVer -> let baseVerRange = truthAssignmentToBaseVersionRange gtb ass
in Just (baseVerRange, ghcVer)
ghcAssignments :: GhcToBaseMap
-> VersionRange
-> [GhcVersionRangeCondition]
-> [GhcAssignment]
ghcAssignments gtb otherBaseConstraints =
sortBy (comparing Down `on` snd)
. mapMaybe (truthAssignmentToGhcAssignment gtb)
. map (otherBaseConstraints `intersectVersionRanges`)
. allTruthAssignments
. nub
{-# ANN ghcAssignments "HLint: ignore Fuse mapMaybe/map" #-}
findGhcVersions :: GhcToBaseMap
-> VersionRange
-> GenericPackageDescription
-> [GhcAssignment]
findGhcVersions gtb otherBaseConstraints = ghcAssignments gtb otherBaseConstraints
. ghcConditions