packages feed

cabal-install 1.24.0.2 → 2.0.0.0

raw patch · 348 files changed

+32468/−20066 lines, 348 filesdep +deepseqdep +echodep +edit-distancedep −regex-posixdep ~Cabaldep ~HTTPdep ~Win32setup-changed

Dependencies added: deepseq, echo, edit-distance, pretty-show, tracetree

Dependencies removed: regex-posix

Dependency ranges changed: Cabal, HTTP, Win32, array, async, base, base16-bytestring, binary, bytestring, bytestring-builder, containers, cryptohash-sha256, directory, filepath, hackage-security, hashable, mtl, network, network-uri, old-time, pretty, process, random, stm, tar, time, unix, zlib

Files

Distribution/Client/BuildReports/Anonymous.hs view
@@ -27,18 +27,18 @@   ) where  import qualified Distribution.Client.Types as BR-         ( BuildResult, BuildFailure(..), BuildSuccess(..)+         ( BuildOutcome, BuildFailure(..), BuildResult(..)          , DocsResult(..), TestsResult(..) ) import Distribution.Client.Utils          ( mergeBy, MergeResult(..) ) import qualified Paths_cabal_install (version)  import Distribution.Package-         ( PackageIdentifier(..), PackageName(..) )+         ( PackageIdentifier(..), mkPackageName ) import Distribution.PackageDescription-         ( FlagName(..), FlagAssignment )---import Distribution.Version---         ( Version )+         ( FlagName, mkFlagName, unFlagName, FlagAssignment )+import Distribution.Version+         ( mkVersion' ) import Distribution.System          ( OS, Arch ) import Distribution.Compiler@@ -120,7 +120,7 @@   deriving Eq  new :: OS -> Arch -> CompilerId -> PackageIdentifier -> FlagAssignment-    -> [PackageIdentifier] -> BR.BuildResult -> BuildReport+    -> [PackageIdentifier] -> BR.BuildOutcome -> BuildReport new os' arch' comp pkgid flags deps result =   BuildReport {     package               = pkgid,@@ -145,21 +145,22 @@       Left  (BR.BuildFailed     _) -> BuildFailed       Left  (BR.TestsFailed     _) -> TestsFailed       Left  (BR.InstallFailed   _) -> InstallFailed-      Right (BR.BuildOk       _ _ _) -> InstallOk+      Right (BR.BuildResult _ _ _) -> InstallOk     convertDocsOutcome = case result of-      Left _                                -> NotTried-      Right (BR.BuildOk BR.DocsNotTried _ _)  -> NotTried-      Right (BR.BuildOk BR.DocsFailed _ _)    -> Failed-      Right (BR.BuildOk BR.DocsOk _ _)        -> Ok+      Left _                                      -> NotTried+      Right (BR.BuildResult BR.DocsNotTried _ _)  -> NotTried+      Right (BR.BuildResult BR.DocsFailed _ _)    -> Failed+      Right (BR.BuildResult BR.DocsOk _ _)        -> Ok     convertTestsOutcome = case result of-      Left  (BR.TestsFailed _)              -> Failed-      Left _                                -> NotTried-      Right (BR.BuildOk _ BR.TestsNotTried _) -> NotTried-      Right (BR.BuildOk _ BR.TestsOk _)       -> Ok+      Left  (BR.TestsFailed _)                    -> Failed+      Left _                                      -> NotTried+      Right (BR.BuildResult _ BR.TestsNotTried _) -> NotTried+      Right (BR.BuildResult _ BR.TestsOk _)       -> Ok  cabalInstallID :: PackageIdentifier cabalInstallID =-  PackageIdentifier (PackageName "cabal-install") Paths_cabal_install.version+  PackageIdentifier (mkPackageName "cabal-install")+                    (mkVersion' Paths_cabal_install.version)  -- ------------------------------------------------------------ -- * External format@@ -263,15 +264,15 @@ sortedFieldDescrs = sortBy (comparing fieldName) fieldDescrs  dispFlag :: (FlagName, Bool) -> Disp.Doc-dispFlag (FlagName name, True)  =                  Disp.text name-dispFlag (FlagName name, False) = Disp.char '-' <> Disp.text name+dispFlag (fname, True)  =                  Disp.text (unFlagName fname)+dispFlag (fname, False) = Disp.char '-' <> Disp.text (unFlagName fname)  parseFlag :: Parse.ReadP r (FlagName, Bool) parseFlag = do   name <- Parse.munch1 (\c -> Char.isAlphaNum c || c == '_' || c == '-')   case name of-    ('-':flag) -> return (FlagName flag, False)-    flag       -> return (FlagName flag, True)+    ('-':flag) -> return (mkFlagName flag, False)+    flag       -> return (mkFlagName flag, True)  instance Text.Text InstallOutcome where   disp PlanningFailed  = Disp.text "PlanningFailed"
Distribution/Client/BuildReports/Storage.hs view
@@ -28,10 +28,12 @@  import Distribution.Client.Types import qualified Distribution.Client.InstallPlan as InstallPlan-import qualified Distribution.Client.ComponentDeps as CD import Distribution.Client.InstallPlan          ( InstallPlan ) +import qualified Distribution.Solver.Types.ComponentDeps as CD+import           Distribution.Solver.Types.SourcePackage+ import Distribution.Package          ( PackageId, packageId ) import Distribution.PackageDescription@@ -103,7 +105,7 @@         fromPathTemplate (substPathTemplate env template)       where env = initialPathTemplateEnv                     (BuildReport.package  report)-                    -- ToDo: In principle, we can support $pkgkey, but only+                    -- TODO: In principle, we can support $pkgkey, but only                     -- if the configure step succeeds.  So add a Maybe field                     -- to the build report, and either use that or make up                     -- a fake identifier if it's not available.@@ -121,38 +123,35 @@  fromInstallPlan :: Platform -> CompilerId                 -> InstallPlan+                -> BuildOutcomes                 -> [(BuildReport, Maybe Repo)]-fromInstallPlan platform comp plan =+fromInstallPlan platform comp plan buildOutcomes =      catMaybes-   . map (fromPlanPackage platform comp)+   . map (\pkg -> fromPlanPackage+                    platform comp pkg+                    (InstallPlan.lookupBuildOutcome pkg buildOutcomes))    . InstallPlan.toList    $ plan  fromPlanPackage :: Platform -> CompilerId                 -> InstallPlan.PlanPackage+                -> Maybe BuildOutcome                 -> Maybe (BuildReport, Maybe Repo)-fromPlanPackage (Platform arch os) comp planPackage = case planPackage of-  InstallPlan.Installed (ReadyPackage (ConfiguredPackage srcPkg flags _ _) deps)-                         _ result-    -> Just $ ( BuildReport.new os arch comp-                                (packageId srcPkg) flags-                                (map packageId (CD.nonSetupDeps deps))-                                (Right result)-              , extractRepo srcPkg)--  InstallPlan.Failed (ConfiguredPackage srcPkg flags _ deps) result-    -> Just $ ( BuildReport.new os arch comp-                                (packageId srcPkg) flags-                                (map confSrcId (CD.nonSetupDeps deps))-                                (Left result)-              , extractRepo srcPkg )--  _ -> Nothing-+fromPlanPackage (Platform arch os) comp+                (InstallPlan.Configured (ConfiguredPackage _ srcPkg flags _ deps))+                (Just buildResult) =+      Just ( BuildReport.new os arch comp+                             (packageId srcPkg) flags+                             (map packageId (CD.nonSetupDeps deps))+                             buildResult+           , extractRepo srcPkg)   where     extractRepo (SourcePackage { packageSource = RepoTarballPackage repo _ _ })                   = Just repo     extractRepo _ = Nothing++fromPlanPackage _ _ _ _ = Nothing+  fromPlanningFailure :: Platform -> CompilerId     -> [PackageId] -> FlagAssignment -> [(BuildReport, Maybe Repo)]
Distribution/Client/BuildReports/Upload.hs view
@@ -25,7 +25,7 @@ import Distribution.Client.BuildReports.Anonymous (BuildReport) import Distribution.Text (display) import Distribution.Verbosity (Verbosity)-import Distribution.Simple.Utils (die)+import Distribution.Simple.Utils (die') import Distribution.Client.HttpUtils import Distribution.Client.Setup          ( RepoContext(..) )@@ -48,7 +48,7 @@   res <- postHttp transport verbosity fullURI (BuildReport.show buildReport) (Just auth)   case res of     (303, redir) -> return $ undefined redir --TODO parse redir-    _ -> die "unrecognized response" -- give response+    _ -> die' verbosity "unrecognized response" -- give response  {-   setAllowRedirects False@@ -89,4 +89,4 @@   res <- postHttp transport verbosity fullURI buildLog (Just auth)   case res of     (200, _) -> return ()-    _ -> die "unrecognized response" -- give response+    _ -> die' verbosity "unrecognized response" -- give response
− Distribution/Client/BuildTarget.hs
@@ -1,1623 +0,0 @@-{-# LANGUAGE CPP, DeriveGeneric, DeriveFunctor #-}--------------------------------------------------------------------------------- |--- Module      :  Distribution.Client.BuildTargets--- Copyright   :  (c) Duncan Coutts 2012, 2015--- License     :  BSD-like------ Maintainer  :  duncan@community.haskell.org------ Handling for user-specified build targets-------------------------------------------------------------------------------module Distribution.Client.BuildTarget (--    -- * Build targets-    BuildTarget(..),-    --showBuildTarget,-    QualLevel(..),-    buildTargetPackage,-    buildTargetComponentName,--    -- * Top level convenience-    readUserBuildTargets,-    resolveUserBuildTargets,--    -- * Parsing user build targets-    UserBuildTarget,-    parseUserBuildTargets,-    showUserBuildTarget,-    UserBuildTargetProblem(..),-    reportUserBuildTargetProblems,--    -- * Resolving build targets-    resolveBuildTargets,-    BuildTargetProblem(..),-    reportBuildTargetProblems,-  ) where--import Distribution.Package-         ( Package(..), PackageId, PackageName, packageName )-import Distribution.Client.Types-         ( PackageLocation(..) )--import Distribution.PackageDescription-         ( PackageDescription-         , Executable(..)-         , TestSuite(..), TestSuiteInterface(..), testModules-         , Benchmark(..), BenchmarkInterface(..), benchmarkModules-         , BuildInfo(..), libModules, exeModules )-import Distribution.ModuleName-         ( ModuleName, toFilePath )-import Distribution.Simple.LocalBuildInfo-         ( Component(..), ComponentName(..)-         , pkgComponents, componentName, componentBuildInfo )--import Distribution.Text-         ( display, simpleParse )-import Distribution.Simple.Utils-         ( die, lowercase )-import Distribution.Client.Utils-         ( makeRelativeToCwd )--import Data.List-         ( nub, nubBy, stripPrefix, partition, intercalate, sortBy, groupBy )-import Data.Maybe-         ( listToMaybe, maybeToList )-import Data.Either-         ( partitionEithers )-import Data.Function-         ( on )-import GHC.Generics (Generic)-#if MIN_VERSION_containers(0,5,0)-import qualified Data.Map.Lazy   as Map.Lazy-import qualified Data.Map.Strict as Map-import Data.Map.Strict (Map)-#else-import qualified Data.Map as Map.Lazy-import qualified Data.Map as Map-import Data.Map (Map)-#endif-import Control.Monad-#if __GLASGOW_HASKELL__ < 710-import Control.Applicative (Applicative(..), (<$>))-#endif-import Control.Applicative (Alternative(..))-import qualified Distribution.Compat.ReadP as Parse-import Distribution.Compat.ReadP-         ( (+++), (<++) )-import Data.Char-         ( isSpace, isAlphaNum )-import System.FilePath as FilePath-         ( takeExtension, dropExtension, addTrailingPathSeparator-         , splitDirectories, joinPath, splitPath )-import System.Directory-         ( doesFileExist, doesDirectoryExist, canonicalizePath-         , getCurrentDirectory )-import System.FilePath-         ( (</>), (<.>), normalise )----- --------------------------------------------------------------- * User build targets--- ---------------------------------------------------------------- | Various ways that a user may specify a build target.------ The main general form has lots of optional parts:------ > [ package name | package dir | package .cabal file ]--- > [ [lib:|exe:] component name ]--- > [ module name | source file ]------ There's also a special case of a package tarball. It doesn't take part in--- the main general form since we always build a tarball package as a whole.------ > [package tar.gz file]----data UserBuildTarget =--     -- | A simple target specified by a single part. This is any of the-     -- general forms that can be expressed using one part, which are:-     ---     -- > cabal build foo                      -- package name-     -- > cabal build ../bar ../bar/bar.cabal  -- package dir or package file-     -- > cabal build foo                      -- component name-     -- > cabal build Data.Foo                 -- module name-     -- > cabal build Data/Foo.hs bar/Main.hsc -- file name-     ---     -- It can also be a package tarball.-     ---     -- > cabal build bar.tar.gz-     ---     UserBuildTarget1 String--     -- | A qualified target with two parts. This is any of the general-     -- forms that can be expressed using two parts, which are:-     ---     -- > cabal build foo:foo              -- package : component-     -- > cabal build foo:Data.Foo         -- package : module-     -- > cabal build foo:Data/Foo.hs      -- package : filename-     ---     -- > cabal build ./foo:foo            -- package dir : component-     -- > cabal build ./foo:Data.Foo       -- package dir : module-     ---     -- > cabal build ./foo.cabal:foo      -- package file : component-     -- > cabal build ./foo.cabal:Data.Foo -- package file : module-     -- > cabal build ./foo.cabal:Main.hs  -- package file : filename-     ---     -- > cabal build lib:foo exe:foo      -- namespace : component-     -- > cabal build foo:Data.Foo         -- component : module-     -- > cabal build foo:Data/Foo.hs      -- component : filename-     ---   | UserBuildTarget2 String String--     -- A (very) qualified target with three parts. This is any of the general-     -- forms that can be expressed using three parts, which are:-     ---     -- > cabal build foo:lib:foo          -- package : namespace : component-     -- > cabal build foo:foo:Data.Foo     -- package : component : module-     -- > cabal build foo:foo:Data/Foo.hs  -- package : component : filename-     ---     -- > cabal build foo/:lib:foo         -- pkg dir : namespace : component-     -- > cabal build foo/:foo:Data.Foo    -- pkg dir : component : module-     -- > cabal build foo/:foo:Data/Foo.hs -- pkg dir : component : filename-     ---     -- > cabal build foo.cabal:lib:foo         -- pkg file : namespace : component-     -- > cabal build foo.cabal:foo:Data.Foo    -- pkg file : component : module-     -- > cabal build foo.cabal:foo:Data/Foo.hs -- pkg file : component : filename-     ---     -- > cabal build lib:foo:Data.Foo     -- namespace : component : module-     -- > cabal build lib:foo:Data/Foo.hs  -- namespace : component : filename-     ---   | UserBuildTarget3 String String String--     -- A (rediculously) qualified target with four parts. This is any of the-     -- general forms that can be expressed using all four parts, which are:-     ---     -- > cabal build foo:lib:foo:Data.Foo     -- package : namespace : component : module-     -- > cabal build foo:lib:foo:Data/Foo.hs  -- package : namespace : component : filename-     ---     -- > cabal build foo/:lib:foo:Data.Foo    -- pkg dir : namespace : component : module-     -- > cabal build foo/:lib:foo:Data/Foo.hs -- pkg dir : namespace : component : filename-     ---     -- > cabal build foo.cabal:lib:foo:Data.Foo    -- pkg file : namespace : component : module-     -- > cabal build foo.cabal:lib:foo:Data/Foo.hs -- pkg file : namespace : component : filename-     ---   | UserBuildTarget4 String String String String-  deriving (Show, Eq, Ord)----- --------------------------------------------------------------- * Resolved build targets--- ---------------------------------------------------------------- | A fully resolved build target.----data BuildTarget pkg =--     -- | A package as a whole -     ---     BuildTargetPackage pkg--     -- | A specific component-     ---   | BuildTargetComponent pkg ComponentName--     -- | A specific module within a specific component.-     ---   | BuildTargetModule pkg ComponentName ModuleName--     -- | A specific file within a specific component.-     ---   | BuildTargetFile pkg ComponentName FilePath-  deriving (Eq, Ord, Functor, Show, Generic)----- | Get the package that the 'BuildTarget' is referring to.----buildTargetPackage :: BuildTarget pkg -> pkg-buildTargetPackage (BuildTargetPackage   p)         = p-buildTargetPackage (BuildTargetComponent p _cn)     = p-buildTargetPackage (BuildTargetModule    p _cn _mn) = p-buildTargetPackage (BuildTargetFile      p _cn _fn) = p----- | Get the 'ComponentName' that the 'BuildTarget' is referring to, if any.--- The 'BuildTargetPackage' target kind doesn't refer to any individual--- component, while the component, module and file kinds do.----buildTargetComponentName :: BuildTarget pkg -> Maybe ComponentName-buildTargetComponentName (BuildTargetPackage   _p)        = Nothing-buildTargetComponentName (BuildTargetComponent _p cn)     = Just cn-buildTargetComponentName (BuildTargetModule    _p cn _mn) = Just cn-buildTargetComponentName (BuildTargetFile      _p cn _fn) = Just cn----- --------------------------------------------------------------- * Top level, do everything--- ----------------------------------------------------------------- | Parse a bunch of command line args as user build targets, failing with an--- error if any targets are unrecognised.----readUserBuildTargets :: [String] -> IO [UserBuildTarget]-readUserBuildTargets targetStrs = do-    let (uproblems, utargets) = parseUserBuildTargets targetStrs-    reportUserBuildTargetProblems uproblems-    return utargets----- | A 'UserBuildTarget's is just a semi-structured string. We sill have quite--- a bit of work to do to figure out which targets they refer to (ie packages,--- components, file locations etc).------ The possible targets are based on the available packages (and their--- locations). It fails with an error if any user string cannot be matched to--- a valid target.----resolveUserBuildTargets :: [(PackageDescription, PackageLocation a)]-                        -> [UserBuildTarget] -> IO [BuildTarget PackageName]-resolveUserBuildTargets pkgs utargets = do-    utargets' <- mapM getUserTargetFileStatus utargets-    pkgs'     <- mapM (uncurry selectPackageInfo) pkgs-    pwd       <- getCurrentDirectory-    let (primaryPkg, otherPkgs) = selectPrimaryLocalPackage pwd pkgs'-        (bproblems,  btargets)  = resolveBuildTargets-                                    primaryPkg otherPkgs utargets''-        -- default local dir target if there's no given target-        utargets''-          | not (null primaryPkg)-          , null utargets       = [UserBuildTargetFileStatus1 "./"-                                     (FileStatusExistsDir pwd)]-          | otherwise           = utargets'--    reportBuildTargetProblems bproblems-    return (map (fmap packageName) btargets)-  where-    selectPrimaryLocalPackage :: FilePath-                              -> [PackageInfo]-                              -> ([PackageInfo], [PackageInfo])-    selectPrimaryLocalPackage pwd pkgs' =-        let (primary, others) = partition isPrimary pkgs'-         in (primary, others)-      where-        isPrimary PackageInfo { pinfoDirectory = Just (dir,_) }-          | dir == pwd = True-        isPrimary _    = False----- --------------------------------------------------------------- * Checking if targets exist as files--- --------------------------------------------------------------data UserBuildTargetFileStatus =-     UserBuildTargetFileStatus1 String FileStatus-   | UserBuildTargetFileStatus2 String FileStatus String-   | UserBuildTargetFileStatus3 String FileStatus String String-   | UserBuildTargetFileStatus4 String FileStatus String String String-  deriving (Eq, Ord, Show)--data FileStatus = FileStatusExistsFile FilePath -- the canonicalised filepath-                | FileStatusExistsDir  FilePath -- the canonicalised filepath-                | FileStatusNotExists  Bool -- does the parent dir exist even?-  deriving (Eq, Ord, Show)--getUserTargetFileStatus :: UserBuildTarget -> IO UserBuildTargetFileStatus-getUserTargetFileStatus t =-    case t of-      UserBuildTarget1 s1 ->-        (\f1 -> UserBuildTargetFileStatus1 s1 f1)          <$> fileStatus s1-      UserBuildTarget2 s1 s2 ->-        (\f1 -> UserBuildTargetFileStatus2 s1 f1 s2)       <$> fileStatus s1-      UserBuildTarget3 s1 s2 s3 ->-        (\f1 -> UserBuildTargetFileStatus3 s1 f1 s2 s3)    <$> fileStatus s1-      UserBuildTarget4 s1 s2 s3 s4 ->-        (\f1 -> UserBuildTargetFileStatus4 s1 f1 s2 s3 s4) <$> fileStatus s1-  where-    fileStatus f = do-      fexists <- doesFileExist f-      dexists <- doesDirectoryExist f-      case splitPath f of-        _ | fexists -> FileStatusExistsFile <$> canonicalizePath f-          | dexists -> FileStatusExistsDir  <$> canonicalizePath f-        (d:_)       -> FileStatusNotExists  <$> doesDirectoryExist d-        _           -> error "getUserTargetFileStatus: empty path"--forgetFileStatus :: UserBuildTargetFileStatus -> UserBuildTarget-forgetFileStatus t = case t of-    UserBuildTargetFileStatus1 s1 _          -> UserBuildTarget1 s1-    UserBuildTargetFileStatus2 s1 _ s2       -> UserBuildTarget2 s1 s2-    UserBuildTargetFileStatus3 s1 _ s2 s3    -> UserBuildTarget3 s1 s2 s3-    UserBuildTargetFileStatus4 s1 _ s2 s3 s4 -> UserBuildTarget4 s1 s2 s3 s4----- --------------------------------------------------------------- * Parsing user targets--- ----------------------------------------------------------------- | Parse a bunch of 'UserBuildTarget's (purely without throwing exceptions).----parseUserBuildTargets :: [String] -> ([UserBuildTargetProblem]-                                     ,[UserBuildTarget])-parseUserBuildTargets = partitionEithers . map parseUserBuildTarget--parseUserBuildTarget :: String -> Either UserBuildTargetProblem-                                         UserBuildTarget-parseUserBuildTarget targetstr =-    case readPToMaybe parseTargetApprox targetstr of-      Nothing  -> Left  (UserBuildTargetUnrecognised targetstr)-      Just tgt -> Right tgt--  where-    parseTargetApprox :: Parse.ReadP r UserBuildTarget-    parseTargetApprox =-          (do a <- tokenQ-              return (UserBuildTarget1 a))-      +++ (do a <- tokenQ-              _ <- Parse.char ':'-              b <- tokenQ-              return (UserBuildTarget2 a b))-      +++ (do a <- tokenQ-              _ <- Parse.char ':'-              b <- tokenQ-              _ <- Parse.char ':'-              c <- tokenQ-              return (UserBuildTarget3 a b c))-      +++ (do a <- tokenQ-              _ <- Parse.char ':'-              b <- token-              _ <- Parse.char ':'-              c <- tokenQ-              _ <- Parse.char ':'-              d <- tokenQ-              return (UserBuildTarget4 a b c d))--    token  = Parse.munch1 (\x -> not (isSpace x) && x /= ':')-    tokenQ = parseHaskellString <++ token-    parseHaskellString :: Parse.ReadP r String-    parseHaskellString = Parse.readS_to_P reads--    readPToMaybe :: Parse.ReadP a a -> String -> Maybe a-    readPToMaybe p str = listToMaybe [ r | (r,s) <- Parse.readP_to_S p str-                                         , all isSpace s ]---- | Syntax error when trying to parse a 'UserBuildTarget'.-data UserBuildTargetProblem-   = UserBuildTargetUnrecognised String-  deriving Show---- | Throw an exception with a formatted message if there are any problems.----reportUserBuildTargetProblems :: [UserBuildTargetProblem] -> IO ()-reportUserBuildTargetProblems problems = do-    case [ target | UserBuildTargetUnrecognised target <- problems ] of-      []     -> return ()-      target ->-        die $ unlines-                [ "Unrecognised build target syntax for '" ++ name ++ "'."-                | name <- target ]-           ++ "Syntax:\n"-           ++ " - build [package]\n"-           ++ " - build [package:]component\n"-           ++ " - build [package:][component:]module\n"-           ++ " - build [package:][component:]file\n"-           ++ " where\n"-           ++ "  package is a package name, package dir or .cabal file\n\n"-           ++ "Examples:\n"-           ++ " - build foo            -- package name\n"-           ++ " - build tests          -- component name\n"-           ++ "    (name of library, executable, test-suite or benchmark)\n"-           ++ " - build Data.Foo       -- module name\n"-           ++ " - build Data/Foo.hsc   -- file name\n\n"-           ++ "An ambigious target can be qualified by package, component\n"-           ++ "and/or component kind (lib|exe|test|bench)\n"-           ++ " - build foo:tests      -- component qualified by package\n"-           ++ " - build tests:Data.Foo -- module qualified by component\n"-           ++ " - build lib:foo        -- component qualified by kind"----- | Render a 'UserBuildTarget' back as the external syntax. This is mainly for--- error messages.----showUserBuildTarget :: UserBuildTarget -> String-showUserBuildTarget = intercalate ":" . components-  where-    components (UserBuildTarget1 s1)          = [s1]-    components (UserBuildTarget2 s1 s2)       = [s1,s2]-    components (UserBuildTarget3 s1 s2 s3)    = [s1,s2,s3]-    components (UserBuildTarget4 s1 s2 s3 s4) = [s1,s2,s3,s4]--showBuildTarget :: QualLevel -> BuildTarget PackageInfo -> String-showBuildTarget ql = showUserBuildTarget . forgetFileStatus-                   . head . renderBuildTarget ql----- --------------------------------------------------------------- * Resolving user targets to build targets--- ----------------------------------------------------------------- | Given a bunch of user-specified targets, try to resolve what it is they--- refer to.----resolveBuildTargets :: [PackageInfo]     -- any primary pkg, e.g. cur dir-                    -> [PackageInfo]     -- all the other local packages-                    -> [UserBuildTargetFileStatus]-                    -> ([BuildTargetProblem], [BuildTarget PackageInfo])-resolveBuildTargets ppinfo opinfo =-    partitionEithers-  . map (resolveBuildTarget ppinfo opinfo)--resolveBuildTarget :: [PackageInfo] -> [PackageInfo]-                   -> UserBuildTargetFileStatus-                   -> Either BuildTargetProblem (BuildTarget PackageInfo)-resolveBuildTarget ppinfo opinfo userTarget =-    case findMatch (matcher userTarget) of-      Unambiguous          target  -> Right target-      None                 errs    -> Left (classifyMatchErrors errs)-      Ambiguous exactMatch targets ->-        case disambiguateBuildTargets-               matcher userTarget exactMatch-               targets of-          Right targets'   -> Left (BuildTargetAmbiguous  userTarget' targets')-          Left ((m, ms):_) -> Left (MatchingInternalError userTarget' m ms)-          Left []          -> internalError "resolveBuildTarget"-  where-    matcher = matchBuildTarget ppinfo opinfo--    userTarget' = forgetFileStatus userTarget--    classifyMatchErrors errs-      | not (null expected)-      = let (things, got:_) = unzip expected in-        BuildTargetExpected userTarget' things got--      | not (null nosuch)-      = BuildTargetNoSuch userTarget' nosuch--      | otherwise-      = internalError $ "classifyMatchErrors: " ++ show errs-      where-        expected = [ (thing, got) -                   | (_, MatchErrorExpected thing got)-                           <- map (innerErr Nothing) errs ]-        nosuch   = [ (inside, thing, got, alts)-                   | (inside, MatchErrorNoSuch thing got alts)-                           <- map (innerErr Nothing) errs ]--        innerErr _ (MatchErrorIn kind thing m)-                     = innerErr (Just (kind,thing)) m-        innerErr c m = (c,m)----- | The various ways that trying to resolve a 'UserBuildTarget' to a--- 'BuildTarget' can fail.----data BuildTargetProblem-   = BuildTargetExpected   UserBuildTarget [String]  String-     -- ^  [expected thing] (actually got)-   | BuildTargetNoSuch     UserBuildTarget-                           [(Maybe (String, String), String, String, [String])]-     -- ^ [([in thing], no such thing,  actually got, alternatives)]-   | BuildTargetAmbiguous  UserBuildTarget-                           [(UserBuildTarget, BuildTarget PackageInfo)]--   | MatchingInternalError UserBuildTarget (BuildTarget PackageInfo)-                           [(UserBuildTarget, [BuildTarget PackageInfo])]---disambiguateBuildTargets-  :: (UserBuildTargetFileStatus -> Match (BuildTarget PackageInfo))-  -> UserBuildTargetFileStatus -> Bool-  -> [BuildTarget PackageInfo]-  -> Either [(BuildTarget PackageInfo,-              [(UserBuildTarget, [BuildTarget PackageInfo])])]-            [(UserBuildTarget, BuildTarget PackageInfo)]-disambiguateBuildTargets matcher matchInput exactMatch matchResults =-    case partitionEithers results of-      (errs@(_:_), _) -> Left errs-      ([], ok)        -> Right ok-  where-    -- So, here's the strategy. We take the original match results, and make a-    -- table of all their renderings at all qualification levels.-    -- Note there can be multiple renderings at each qualification level.-    matchResultsRenderings :: [(BuildTarget PackageInfo, [UserBuildTargetFileStatus])]-    matchResultsRenderings =-      [ (matchResult, matchRenderings)-      | matchResult <- matchResults-      , let matchRenderings =-              [ rendering-              | ql <- [QL1 .. QL4]-              , rendering <- renderBuildTarget ql matchResult ]-      ]--    -- Of course the point is that we're looking for renderings that are-    -- unambiguous matches. So we build another memo table of all the matches-    -- for all of those renderings. So by looking up in this table we can see-    -- if we've got an unambiguous match.--    memoisedMatches :: Map UserBuildTargetFileStatus-                           (Match (BuildTarget PackageInfo))-    memoisedMatches =-        -- avoid recomputing the main one if it was an exact match-        (if exactMatch then Map.insert matchInput (ExactMatch 0 matchResults)-                       else id)-      $ Map.Lazy.fromList-          [ (rendering, matcher rendering)-          | rendering <- concatMap snd matchResultsRenderings ]--    -- Finally, for each of the match results, we go through all their-    -- possible renderings (in order of qualification level, though remember-    -- there can be multiple renderings per level), and find the first one-    -- that has an unambiguous match.-    results :: [Either (BuildTarget PackageInfo,-                        [(UserBuildTarget, [BuildTarget PackageInfo])])-                       (UserBuildTarget, BuildTarget PackageInfo)]-    results =-      [ case findUnambiguous originalMatch matchRenderings of-          Just unambiguousRendering ->-            Right ( forgetFileStatus unambiguousRendering-                  , originalMatch)--          -- This case is an internal error, but we bubble it up and report it-          Nothing ->-            Left  ( originalMatch-                  , [ (forgetFileStatus rendering, matches)-                    | rendering <- matchRenderings-                    , let (ExactMatch _ matches) =-                            memoisedMatches Map.! rendering -                    ] )--      | (originalMatch, matchRenderings) <- matchResultsRenderings ]--    findUnambiguous :: BuildTarget PackageInfo -> [UserBuildTargetFileStatus]-                    -> Maybe UserBuildTargetFileStatus-    findUnambiguous _ []     = Nothing-    findUnambiguous t (r:rs) =-      case memoisedMatches Map.! r of-        ExactMatch _ [t'] | fmap packageName t == fmap packageName t'-                         -> Just r-        ExactMatch _  _  -> findUnambiguous t rs-        InexactMatch _ _ -> internalError "InexactMatch"-        NoMatch      _ _ -> internalError "NoMatch"--internalError :: String -> a-internalError msg =-  error $ "BuildTargets: internal error: " ++ msg---data QualLevel = QL1 | QL2 | QL3 | QL4-  deriving (Enum, Show)--renderBuildTarget :: QualLevel -> BuildTarget PackageInfo-                  -> [UserBuildTargetFileStatus]-renderBuildTarget ql t =-    case t of-      BuildTargetPackage p ->-        case ql of-          QL1 -> [t1 (dispP p)]-          QL2 -> [t1' pf fs | (pf, fs) <- dispPF p]-          QL3 -> []-          QL4 -> []--      BuildTargetComponent p c ->-        case ql of-          QL1 -> [t1                     (dispC p c)]-          QL2 -> [t2 (dispP p)           (dispC p c),-                  t2           (dispK c) (dispC p c)]-          QL3 -> [t3 (dispP p) (dispK c) (dispC p c)]-          QL4 -> []--      BuildTargetModule p c m ->-        case ql of-          QL1 -> [t1                                 (dispM m)]-          QL2 -> [t2 (dispP p)                       (dispM m),-                  t2                     (dispC p c) (dispM m)]-          QL3 -> [t3 (dispP p)           (dispC p c) (dispM m),-                  t3           (dispK c) (dispC p c) (dispM m)]-          QL4 -> [t4 (dispP p) (dispK c) (dispC p c) (dispM m)]--      BuildTargetFile p c f ->-        case ql of-          QL1 -> [t1                                 f]-          QL2 -> [t2 (dispP p)                       f,-                  t2                     (dispC p c) f]-          QL3 -> [t3 (dispP p)           (dispC p c) f,-                  t3           (dispK c) (dispC p c) f]-          QL4 -> [t4 (dispP p) (dispK c) (dispC p c) f]-  where-    t1  s1 = UserBuildTargetFileStatus1 s1 none-    t1' s1 = UserBuildTargetFileStatus1 s1-    t2  s1 = UserBuildTargetFileStatus2 s1 none-    t3  s1 = UserBuildTargetFileStatus3 s1 none-    t4  s1 = UserBuildTargetFileStatus4 s1 none-    none   = FileStatusNotExists False--    dispP = display . packageName-    dispC = componentStringName . packageName-    dispK = showComponentKindShort . componentKind-    dispM = display--    dispPF p = [ (addTrailingPathSeparator drel, FileStatusExistsDir dabs)-               | PackageInfo { pinfoDirectory   = Just (dabs,drel) } <- [p] ]-            ++ [ (frel, FileStatusExistsFile fabs)-               | PackageInfo { pinfoPackageFile = Just (fabs,frel) } <- [p] ]----- | Throw an exception with a formatted message if there are any problems.----reportBuildTargetProblems :: [BuildTargetProblem] -> IO ()-reportBuildTargetProblems problems = do--    case [ (t, m, ms) | MatchingInternalError t m ms <- problems ] of-      [] -> return ()-      ((target, originalMatch, renderingsAndMatches):_) ->-        die $ "Internal error in build target matching. It should always be "-           ++ "possible to find a syntax that's sufficiently qualified to "-           ++ "give an unambigious match. However when matching '"-           ++ showUserBuildTarget target ++ "'  we found "-           ++ showBuildTarget QL1 originalMatch-           ++ " (" ++ showBuildTargetKind originalMatch ++ ") which does not "-           ++ "have an unambigious syntax. The possible syntax and the "-           ++ "targets they match are as follows:\n"-           ++ unlines-                [ "'" ++ showUserBuildTarget rendering ++ "' which matches "-                  ++ intercalate ", "-                       [ showBuildTarget QL1 match ++-                         " (" ++ showBuildTargetKind match ++ ")"-                       | match <- matches ]-                | (rendering, matches) <- renderingsAndMatches ]--    case [ (t, e, g) | BuildTargetExpected t e g <- problems ] of-      []      -> return ()-      targets ->-        die $ unlines-          [    "Unrecognised build target '" ++ showUserBuildTarget target-            ++ "'.\n"-            ++ "Expected a " ++ intercalate " or " expected-            ++ ", rather than '" ++ got ++ "'."-          | (target, expected, got) <- targets ]--    case [ (t, e) | BuildTargetNoSuch t e <- problems ] of-      []      -> return ()-      targets ->-        die $ unlines-          [ "Unknown build target '" ++ showUserBuildTarget target ++-            "'.\n" ++ unlines-            [    (case inside of-                    Just (kind, thing)-                            -> "The " ++ kind ++ " " ++ thing ++ " has no "-                    Nothing -> "There is no ")-              ++ intercalate " or " [ mungeThing thing ++ " '" ++ got ++ "'"-                                    | (thing, got, _alts) <- nosuch' ] ++ "."-              ++ if null alternatives then "" else-                 "\nPerhaps you meant " ++ intercalate ";\nor "-                 [ "the " ++ thing ++ " " ++ intercalate " or " alts-                 | (thing, alts) <- alternatives ]-            | (inside, nosuch') <- groupByContainer nosuch-            , let alternatives =-                    [ (thing, take 10 alts) --TODO: select best ones-                    | (thing,_got,alts@(_:_)) <- nosuch' ]-            ]-          | (target, nosuch) <- targets-          , let groupByContainer =-                    map (\g@((inside,_,_,_):_) ->-                            (inside, [   (thing,got,alts)-                                     | (_,thing,got,alts) <- g ]))-                  . groupBy ((==)    `on` (\(x,_,_,_) -> x))-                  . sortBy  (compare `on` (\(x,_,_,_) -> x))-          ]-        where-          mungeThing "file" = "file target"-          mungeThing thing  = thing--    case [ (t, ts) | BuildTargetAmbiguous t ts <- problems ] of-      []      -> return ()-      targets ->-        die $ unlines-          [    "Ambiguous build target '" ++ showUserBuildTarget target-            ++ "'. It could be:\n "-            ++ unlines [ "   "++ showUserBuildTarget ut ++-                         " (" ++ showBuildTargetKind bt ++ ")"-                       | (ut, bt) <- amb ]-          | (target, amb) <- targets ]--  where-    showBuildTargetKind (BuildTargetPackage   _    ) = "package"-    showBuildTargetKind (BuildTargetComponent _ _  ) = "component"-    showBuildTargetKind (BuildTargetModule    _ _ _) = "module"-    showBuildTargetKind (BuildTargetFile      _ _ _) = "file"---------------------------------------- Top level BuildTarget matcher-----matchBuildTarget :: [PackageInfo] -> [PackageInfo]-                 -> UserBuildTargetFileStatus-                 -> Match (BuildTarget PackageInfo)-matchBuildTarget ppinfo opinfo = \utarget ->-    nubMatchesBy ((==) `on` (fmap packageName)) $-    case utarget of-      UserBuildTargetFileStatus1 str1 fstatus1 ->-        matchBuildTarget1 ppinfo opinfo str1 fstatus1--      UserBuildTargetFileStatus2 str1 fstatus1 str2 ->-        matchBuildTarget2 pinfo str1 fstatus1 str2--      UserBuildTargetFileStatus3 str1 fstatus1 str2 str3 ->-        matchBuildTarget3 pinfo str1 fstatus1 str2 str3--      UserBuildTargetFileStatus4 str1 fstatus1 str2 str3 str4 ->-        matchBuildTarget4 pinfo str1 fstatus1 str2 str3 str4-  where-    pinfo  = ppinfo ++ opinfo-    --TODO: sort this out---matchBuildTarget1 :: [PackageInfo] -> [PackageInfo]-                  -> String -> FileStatus -> Match (BuildTarget PackageInfo)-matchBuildTarget1 ppinfo opinfo = \str1 fstatus1 ->-         match1Cmp pcinfo str1-    <//> match1Pkg pinfo  str1 fstatus1-    <//> match1Cmp ocinfo str1-    <//> match1Mod cinfo  str1-    <//> match1Fil pinfo  str1 fstatus1-  where-    pinfo  = ppinfo ++ opinfo-    cinfo  = concatMap pinfoComponents pinfo-    pcinfo = concatMap pinfoComponents ppinfo-    ocinfo = concatMap pinfoComponents opinfo---matchBuildTarget2 :: [PackageInfo] -> String -> FileStatus -> String-                  -> Match (BuildTarget PackageInfo)-matchBuildTarget2 pinfo str1 fstatus1 str2 =-        match2PkgCmp pinfo str1 fstatus1 str2-   <|>  match2KndCmp cinfo str1          str2-   <//> match2PkgMod pinfo str1 fstatus1 str2-   <//> match2CmpMod cinfo str1          str2-   <//> match2PkgFil pinfo str1 fstatus1 str2-   <//> match2CmpFil cinfo str1          str2-  where-    cinfo = concatMap pinfoComponents pinfo-    --TODO: perhaps we actually do want to prioritise local/primary components---matchBuildTarget3 :: [PackageInfo] -> String -> FileStatus -> String -> String-                  -> Match (BuildTarget PackageInfo)-matchBuildTarget3 pinfo str1 fstatus1 str2 str3 =-        match3PkgKndCmp pinfo str1 fstatus1 str2 str3 -   <//> match3PkgCmpMod pinfo str1 fstatus1 str2 str3-   <//> match3PkgCmpFil pinfo str1 fstatus1 str2 str3-   <//> match3KndCmpMod cinfo str1          str2 str3-   <//> match3KndCmpFil cinfo str1          str2 str3-  where-    cinfo = concatMap pinfoComponents pinfo---matchBuildTarget4 :: [PackageInfo]-                  -> String -> FileStatus -> String -> String -> String-                  -> Match (BuildTarget PackageInfo)-matchBuildTarget4 pinfo str1 fstatus1 str2 str3 str4 =-        match4PkgKndCmpMod pinfo str1 fstatus1 str2 str3 str4-   <//> match4PkgKndCmpFil pinfo str1 fstatus1 str2 str3 str4------------------------------------------ Individual BuildTarget matchers-----match1Pkg :: [PackageInfo] -> String -> FileStatus-          -> Match (BuildTarget PackageInfo)-match1Pkg pinfo = \str1 fstatus1 -> do-    guardPackage            str1 fstatus1-    p <- matchPackage pinfo str1 fstatus1-    return (BuildTargetPackage p)--match1Cmp :: [ComponentInfo] -> String -> Match (BuildTarget PackageInfo)-match1Cmp cs = \str1 -> do-    guardComponentName str1-    c <- matchComponentName cs str1-    return (BuildTargetComponent (cinfoPackage c) (cinfoName c))--match1Mod :: [ComponentInfo] -> String -> Match (BuildTarget PackageInfo)-match1Mod cs = \str1 -> do-    guardModuleName str1-    let ms = [ (m,c) | c <- cs, m <- cinfoModules c ]-    (m,c) <- matchModuleNameAnd ms str1-    return (BuildTargetModule (cinfoPackage c) (cinfoName c) m)--match1Fil :: [PackageInfo] -> String -> FileStatus-          -> Match (BuildTarget PackageInfo)-match1Fil ps str1 fstatus1 =-    expecting "file" str1 $ do-    (pkgfile, p) <- matchPackageDirectoryPrefix ps fstatus1-    orNoThingIn "package" (display (packageName p)) $ do-      (filepath, c) <- matchComponentFile (pinfoComponents p) pkgfile-      return (BuildTargetFile p (cinfoName c) filepath)-------match2PkgCmp :: [PackageInfo]-             -> String -> FileStatus -> String-             -> Match (BuildTarget PackageInfo)-match2PkgCmp ps = \str1 fstatus1 str2 -> do-    guardPackage         str1 fstatus1-    guardComponentName   str2-    p <- matchPackage ps str1 fstatus1-    orNoThingIn "package" (display (packageName p)) $ do-      c <- matchComponentName (pinfoComponents p) str2-      return (BuildTargetComponent p (cinfoName c))-    --TODO: the error here ought to say there's no component by that name in-    -- this package, and name the package--match2KndCmp :: [ComponentInfo] -> String -> String-             -> Match (BuildTarget PackageInfo)-match2KndCmp cs = \str1 str2 -> do-    ckind <- matchComponentKind str1-    guardComponentName str2-    c <- matchComponentKindAndName cs ckind str2-    return (BuildTargetComponent (cinfoPackage c) (cinfoName c))--match2PkgMod :: [PackageInfo] -> String -> FileStatus -> String-             -> Match (BuildTarget PackageInfo)-match2PkgMod ps = \str1 fstatus1 str2 -> do-    guardPackage         str1 fstatus1-    guardModuleName      str2-    p <- matchPackage ps str1 fstatus1-    orNoThingIn "package" (display (packageName p)) $ do-      let ms = [ (m,c) | c <- pinfoComponents p, m <- cinfoModules c ]-      (m,c) <- matchModuleNameAnd ms str2-      return (BuildTargetModule p (cinfoName c) m)--match2CmpMod :: [ComponentInfo] -> String -> String-             -> Match (BuildTarget PackageInfo)-match2CmpMod cs = \str1 str2 -> do-    guardComponentName str1-    guardModuleName    str2-    c <- matchComponentName cs str1-    orNoThingIn "component" (cinfoStrName c) $ do-      let ms = cinfoModules c-      m <- matchModuleName ms str2-      return (BuildTargetModule (cinfoPackage c) (cinfoName c) m)--match2PkgFil :: [PackageInfo] -> String -> FileStatus -> String-             -> Match (BuildTarget PackageInfo)-match2PkgFil ps str1 fstatus1 str2 = do-    guardPackage         str1 fstatus1-    p <- matchPackage ps str1 fstatus1-    orNoThingIn "package" (display (packageName p)) $ do-      (filepath, c) <- matchComponentFile (pinfoComponents p) str2-      return (BuildTargetFile p (cinfoName c) filepath)--match2CmpFil :: [ComponentInfo] -> String -> String-             -> Match (BuildTarget PackageInfo)-match2CmpFil cs str1 str2 = do-    guardComponentName str1-    c <- matchComponentName cs str1-    orNoThingIn "component" (cinfoStrName c) $ do-      (filepath, _) <- matchComponentFile [c] str2-      return (BuildTargetFile (cinfoPackage c) (cinfoName c) filepath)-------match3PkgKndCmp :: [PackageInfo]-                -> String -> FileStatus -> String -> String-                -> Match (BuildTarget PackageInfo)-match3PkgKndCmp ps = \str1 fstatus1 str2 str3 -> do-    guardPackage         str1 fstatus1-    ckind <- matchComponentKind str2-    guardComponentName   str3-    p <- matchPackage ps str1 fstatus1-    orNoThingIn "package" (display (packageName p)) $ do-      c <- matchComponentKindAndName (pinfoComponents p) ckind str3-      return (BuildTargetComponent p (cinfoName c))--match3PkgCmpMod :: [PackageInfo]-                -> String -> FileStatus -> String -> String-                -> Match (BuildTarget PackageInfo)-match3PkgCmpMod ps = \str1 fstatus1 str2 str3 -> do-    guardPackage str1 fstatus1-    guardComponentName str2-    guardModuleName    str3-    p <- matchPackage ps str1 fstatus1-    orNoThingIn "package" (display (packageName p)) $ do-      c <- matchComponentName (pinfoComponents p) str2-      orNoThingIn "component" (cinfoStrName c) $ do-        let ms = cinfoModules c-        m <- matchModuleName ms str3-        return (BuildTargetModule p (cinfoName c) m)--match3KndCmpMod :: [ComponentInfo]-                -> String -> String -> String-                -> Match (BuildTarget PackageInfo)-match3KndCmpMod cs = \str1 str2 str3 -> do-    ckind <- matchComponentKind str1-    guardComponentName str2-    guardModuleName    str3-    c <- matchComponentKindAndName cs ckind str2-    orNoThingIn "component" (cinfoStrName c) $ do-      let ms = cinfoModules c-      m <- matchModuleName ms str3-      return (BuildTargetModule (cinfoPackage c) (cinfoName c) m)--match3PkgCmpFil :: [PackageInfo]-                -> String -> FileStatus -> String -> String-                -> Match (BuildTarget PackageInfo)-match3PkgCmpFil ps = \str1 fstatus1 str2 str3 -> do-    guardPackage         str1 fstatus1-    guardComponentName   str2-    p <- matchPackage ps str1 fstatus1-    orNoThingIn "package" (display (packageName p)) $ do-      c <- matchComponentName (pinfoComponents p) str2-      orNoThingIn "component" (cinfoStrName c) $ do-        (filepath, _) <- matchComponentFile [c] str3-        return (BuildTargetFile p (cinfoName c) filepath)--match3KndCmpFil :: [ComponentInfo] -> String -> String -> String-                -> Match (BuildTarget PackageInfo)-match3KndCmpFil cs = \str1 str2 str3 -> do-    ckind <- matchComponentKind str1-    guardComponentName str2-    c <- matchComponentKindAndName cs ckind str2-    orNoThingIn "component" (cinfoStrName c) $ do-      (filepath, _) <- matchComponentFile [c] str3-      return (BuildTargetFile (cinfoPackage c) (cinfoName c) filepath)------match4PkgKndCmpMod :: [PackageInfo]-                   -> String-> FileStatus -> String -> String -> String-                   -> Match (BuildTarget PackageInfo)-match4PkgKndCmpMod ps = \str1 fstatus1 str2 str3 str4 -> do-    guardPackage         str1 fstatus1-    ckind <- matchComponentKind str2-    guardComponentName   str3-    guardModuleName      str4-    p <- matchPackage ps str1 fstatus1-    orNoThingIn "package" (display (packageName p)) $ do-      c <- matchComponentKindAndName (pinfoComponents p) ckind str3-      orNoThingIn "component" (cinfoStrName c) $ do-        let ms = cinfoModules c-        m <- matchModuleName ms str4-        return (BuildTargetModule p (cinfoName c) m)--match4PkgKndCmpFil :: [PackageInfo]-                   -> String -> FileStatus -> String -> String -> String-                   -> Match (BuildTarget PackageInfo)-match4PkgKndCmpFil ps = \str1 fstatus1 str2 str3 str4 -> do-    guardPackage       str1 fstatus1-    ckind <- matchComponentKind str2-    guardComponentName str3-    p     <- matchPackage ps    str1 fstatus1-    orNoThingIn "package" (display (packageName p)) $ do-      c <- matchComponentKindAndName (pinfoComponents p) ckind str3-      orNoThingIn "component" (cinfoStrName c) $ do-        (filepath,_) <- matchComponentFile [c] str4-        return (BuildTargetFile p (cinfoName c) filepath)------------------------------------- Package and component info-----data PackageInfo = PackageInfo {-       pinfoId          :: PackageId,-       pinfoLocation    :: PackageLocation (),-       pinfoDirectory   :: Maybe (FilePath, FilePath),-       pinfoPackageFile :: Maybe (FilePath, FilePath),-       pinfoComponents  :: [ComponentInfo]-     }--data ComponentInfo = ComponentInfo {-       cinfoName    :: ComponentName,-       cinfoStrName :: ComponentStringName,-       cinfoPackage :: PackageInfo,-       cinfoSrcDirs :: [FilePath],-       cinfoModules :: [ModuleName],-       cinfoHsFiles :: [FilePath],   -- other hs files (like main.hs)-       cinfoCFiles  :: [FilePath],-       cinfoJsFiles :: [FilePath]-     }--type ComponentStringName = String--instance Package PackageInfo where-  packageId = pinfoId----TODO: [required eventually] need the original GenericPackageDescription or--- the flattening thereof because we need to be able to target modules etc--- that are not enabled in the current configuration.-selectPackageInfo :: PackageDescription -> PackageLocation a -> IO PackageInfo-selectPackageInfo pkg loc = do-    (pkgdir, pkgfile) <--      case loc of-        --TODO: local tarballs, remote tarballs etc-        LocalUnpackedPackage dir -> do -          dirabs <- canonicalizePath dir-          dirrel <- makeRelativeToCwd dirabs-          --TODO: ought to get this earlier in project reading-          let fileabs = dirabs </> display (packageName pkg) <.> "cabal"-              filerel = dirrel </> display (packageName pkg) <.> "cabal"-          exists <- doesFileExist fileabs-          return ( Just (dirabs, dirrel)-                 , if exists then Just (fileabs, filerel) else Nothing-                 )-        _ -> return (Nothing, Nothing)-    let pinfo =-          PackageInfo {-            pinfoId          = packageId pkg,-            pinfoLocation    = fmap (const ()) loc,-            pinfoDirectory   = pkgdir,-            pinfoPackageFile = pkgfile,-            pinfoComponents  = selectComponentInfo pinfo pkg-          }-    return pinfo---selectComponentInfo :: PackageInfo -> PackageDescription -> [ComponentInfo]-selectComponentInfo pinfo pkg =-    [ ComponentInfo {-        cinfoName    = componentName c,-        cinfoStrName = componentStringName (packageName pkg) (componentName c),-        cinfoPackage = pinfo,-        cinfoSrcDirs = hsSourceDirs bi,---                       [ pkgroot </> srcdir---                       | (pkgroot,_) <- maybeToList (pinfoDirectory pinfo)---                       , srcdir <- hsSourceDirs bi ],-        cinfoModules = componentModules c,-        cinfoHsFiles = componentHsFiles c,-        cinfoCFiles  = cSources bi,-        cinfoJsFiles = jsSources bi-      }-    | c <- pkgComponents pkg-    , let bi = componentBuildInfo c ]---componentStringName :: PackageName -> ComponentName -> ComponentStringName-componentStringName pkgname CLibName         = display pkgname-componentStringName _      (CExeName  name)  = name-componentStringName _      (CTestName  name) = name-componentStringName _      (CBenchName name) = name--componentModules :: Component -> [ModuleName]-componentModules (CLib   lib)   = libModules lib-componentModules (CExe   exe)   = exeModules exe-componentModules (CTest  test)  = testModules test-componentModules (CBench bench) = benchmarkModules bench--componentHsFiles :: Component -> [FilePath]-componentHsFiles (CExe exe) = [modulePath exe]-componentHsFiles (CTest  TestSuite {-                           testInterface = TestSuiteExeV10 _ mainfile-                         }) = [mainfile]-componentHsFiles (CBench Benchmark {-                           benchmarkInterface = BenchmarkExeV10 _ mainfile-                         }) = [mainfile]-componentHsFiles _          = []------------------------------------ Matching component kinds-----data ComponentKind = LibKind | ExeKind | TestKind | BenchKind-  deriving (Eq, Ord, Show)--componentKind :: ComponentName -> ComponentKind-componentKind CLibName       = LibKind-componentKind (CExeName  _)  = ExeKind-componentKind (CTestName  _) = TestKind-componentKind (CBenchName _) = BenchKind--cinfoKind :: ComponentInfo -> ComponentKind-cinfoKind = componentKind . cinfoName--matchComponentKind :: String -> Match ComponentKind-matchComponentKind s-  | s `elem` ["lib", "library"]            = increaseConfidence >> return LibKind-  | s `elem` ["exe", "executable"]         = increaseConfidence >> return ExeKind-  | s `elem` ["tst", "test", "test-suite"] = increaseConfidence-                                             >> return TestKind-  | s `elem` ["bench", "benchmark"]        = increaseConfidence-                                             >> return BenchKind-  | otherwise                              = matchErrorExpected-                                             "component kind" s--showComponentKind :: ComponentKind -> String-showComponentKind LibKind   = "library"-showComponentKind ExeKind   = "executable"-showComponentKind TestKind  = "test-suite"-showComponentKind BenchKind = "benchmark"--showComponentKindShort :: ComponentKind -> String-showComponentKindShort LibKind   = "lib"-showComponentKindShort ExeKind   = "exe"-showComponentKindShort TestKind  = "test"-showComponentKindShort BenchKind = "bench"----------------------------------- Matching package targets-----guardPackage :: String -> FileStatus -> Match ()-guardPackage str fstatus =-      guardPackageName str-  <|> guardPackageDir  str fstatus-  <|> guardPackageFile str fstatus---guardPackageName :: String -> Match ()-guardPackageName s-  | validPackgageName s = increaseConfidence-  | otherwise           = matchErrorExpected "package name" s-  where--validPackgageName :: String -> Bool-validPackgageName s =-       all validPackgageNameChar s-    && not (null s)-  where-    validPackgageNameChar c = isAlphaNum c || c == '-'---guardPackageDir :: String -> FileStatus -> Match ()-guardPackageDir _ (FileStatusExistsDir _) = increaseConfidence-guardPackageDir str _ = matchErrorExpected "package directory" str---guardPackageFile :: String -> FileStatus -> Match ()-guardPackageFile _ (FileStatusExistsFile file)-                       | takeExtension file == ".cabal"-                       = increaseConfidence-guardPackageFile str _ = matchErrorExpected "package .cabal file" str---matchPackage :: [PackageInfo] -> String -> FileStatus -> Match PackageInfo-matchPackage pinfo = \str fstatus ->-    orNoThingIn "project" "" $-          matchPackageName pinfo str-    <//> (matchPackageDir  pinfo str fstatus-     <|>  matchPackageFile pinfo str fstatus)---matchPackageName :: [PackageInfo] -> String -> Match PackageInfo-matchPackageName ps = \str -> do-    guard (validPackgageName str)-    orNoSuchThing "package" str-                  (map (display . packageName) ps) $-      increaseConfidenceFor $-        matchInexactly caseFold (display . packageName) ps str---matchPackageDir :: [PackageInfo]-                -> String -> FileStatus -> Match PackageInfo-matchPackageDir ps = \str fstatus ->-    case fstatus of-      FileStatusExistsDir canondir -> -        orNoSuchThing "package directory" str (map (snd . fst) dirs) $-          increaseConfidenceFor $-            fmap snd $ matchExactly (fst . fst) dirs canondir-      _ -> mzero-  where-    dirs = [ ((dabs,drel),p)-           | p@PackageInfo{ pinfoDirectory = Just (dabs,drel) } <- ps ]---matchPackageFile :: [PackageInfo] -> String -> FileStatus -> Match PackageInfo-matchPackageFile ps = \str fstatus -> do-    case fstatus of-      FileStatusExistsFile canonfile -> -        orNoSuchThing "package .cabal file" str (map (snd . fst) files) $-          increaseConfidenceFor $-            fmap snd $ matchExactly (fst . fst) files canonfile-      _ -> mzero-  where-    files = [ ((fabs,frel),p)-            | p@PackageInfo{ pinfoPackageFile = Just (fabs,frel) } <- ps ]----TODO: test outcome when dir exists but doesn't match any known one----TODO: perhaps need another distinction, vs no such thing, point is the---      thing is not known, within the project, but could be outside project------------------------------------ Matching component targets------guardComponentName :: String -> Match ()-guardComponentName s-  | all validComponentChar s-    && not (null s)  = increaseConfidence-  | otherwise        = matchErrorExpected "component name" s-  where-    validComponentChar c = isAlphaNum c || c == '.'-                        || c == '_' || c == '-' || c == '\''---matchComponentName :: [ComponentInfo] -> String -> Match ComponentInfo-matchComponentName cs str =-    orNoSuchThing "component" str (map cinfoStrName cs)-  $ increaseConfidenceFor-  $ matchInexactly caseFold cinfoStrName cs str---matchComponentKindAndName :: [ComponentInfo] -> ComponentKind -> String-                          -> Match ComponentInfo-matchComponentKindAndName cs ckind str =-    orNoSuchThing (showComponentKind ckind ++ " component") str-                  (map render cs)-  $ increaseConfidenceFor-  $ matchInexactly (\(ck, cn) -> (ck, caseFold cn))-                   (\c -> (cinfoKind c, cinfoStrName c))-                   cs-                   (ckind, str)-  where-    render c = showComponentKindShort (cinfoKind c) ++ ":" ++ cinfoStrName c------------------------------------ Matching module targets-----guardModuleName :: String -> Match ()-guardModuleName s =-  case simpleParse s :: Maybe ModuleName of-    Just _                   -> increaseConfidence-    _ | all validModuleChar s-        && not (null s)      -> return ()-      | otherwise            -> matchErrorExpected "module name" s-    where-      validModuleChar c = isAlphaNum c || c == '.' || c == '_' || c == '\''---matchModuleName :: [ModuleName] -> String -> Match ModuleName-matchModuleName ms str =-    orNoSuchThing "module" str (map display ms)-  $ increaseConfidenceFor-  $ matchInexactly caseFold display ms str---matchModuleNameAnd :: [(ModuleName, a)] -> String -> Match (ModuleName, a)-matchModuleNameAnd ms str =-    orNoSuchThing "module" str (map (display . fst) ms)-  $ increaseConfidenceFor-  $ matchInexactly caseFold (display . fst) ms str------------------------------------ Matching file targets-----matchPackageDirectoryPrefix :: [PackageInfo] -> FileStatus-                            -> Match (FilePath, PackageInfo)-matchPackageDirectoryPrefix ps (FileStatusExistsFile filepath) =-    increaseConfidenceFor $-      matchDirectoryPrefix pkgdirs filepath-  where-    pkgdirs = [ (dir, p)-              | p@PackageInfo { pinfoDirectory = Just (dir,_) } <- ps ]-matchPackageDirectoryPrefix _ _ = mzero---matchComponentFile :: [ComponentInfo] -> String-                   -> Match (FilePath, ComponentInfo)-matchComponentFile cs str =-    orNoSuchThing "file" str [] $-        matchComponentModuleFile cs str-    <|> matchComponentOtherFile  cs str---matchComponentOtherFile :: [ComponentInfo] -> String-                        -> Match (FilePath, ComponentInfo)-matchComponentOtherFile cs =-    matchFile-      [ (file, c)-      | c    <- cs-      , file <- cinfoHsFiles c-             ++ cinfoCFiles  c-             ++ cinfoJsFiles c-      ]---matchComponentModuleFile :: [ComponentInfo] -> String-                         -> Match (FilePath, ComponentInfo)-matchComponentModuleFile cs str = do-    matchFile-      [ (normalise (d </> toFilePath m), c)-      | c <- cs-      , d <- cinfoSrcDirs c-      , m <- cinfoModules c-      ]-      (dropExtension (normalise str))---- utils--matchFile :: [(FilePath, a)] -> FilePath -> Match (FilePath, a)-matchFile fs =-      increaseConfidenceFor-    . matchInexactly caseFold fst fs--matchDirectoryPrefix :: [(FilePath, a)] -> FilePath -> Match (FilePath, a)-matchDirectoryPrefix dirs filepath =-    tryEach $-      [ (file, x)-      | (dir,x) <- dirs-      , file <- maybeToList (stripDirectory dir) ]-  where-    stripDirectory :: FilePath -> Maybe FilePath-    stripDirectory dir =-      joinPath `fmap` stripPrefix (splitDirectories dir) filepathsplit--    filepathsplit = splitDirectories filepath------------------------------------ Matching monad------- | A matcher embodies a way to match some input as being some recognised--- value. In particular it deals with multiple and ambiguous matches.------ There are various matcher primitives ('matchExactly', 'matchInexactly'),--- ways to combine matchers ('matchPlus', 'matchPlusShadowing') and finally we--- can run a matcher against an input using 'findMatch'.----data Match a = NoMatch      Confidence [MatchError]-             | ExactMatch   Confidence [a]-             | InexactMatch Confidence [a]-  deriving Show--type Confidence = Int--data MatchError = MatchErrorExpected String String            -- thing got-                | MatchErrorNoSuch   String String [String]   -- thing got alts-                | MatchErrorIn       String String MatchError -- kind  thing-  deriving (Show, Eq)---instance Functor Match where-    fmap _ (NoMatch      d ms) = NoMatch      d ms-    fmap f (ExactMatch   d xs) = ExactMatch   d (fmap f xs)-    fmap f (InexactMatch d xs) = InexactMatch d (fmap f xs)--instance Applicative Match where-    pure a = ExactMatch 0 [a]-    (<*>)  = ap--instance Alternative Match where-    empty = NoMatch 0 []-    (<|>) = matchPlus--instance Monad Match where-    return                  = pure-    NoMatch      d ms >>= _ = NoMatch d ms-    ExactMatch   d xs >>= f = addDepth d-                            $ msum (map f xs)-    InexactMatch d xs >>= f = addDepth d . forceInexact-                            $ msum (map f xs)--instance MonadPlus Match where-    mzero = empty-    mplus = matchPlus--(<//>) :: Match a -> Match a -> Match a-(<//>) = matchPlusShadowing--infixl 3 <//>--addDepth :: Confidence -> Match a -> Match a-addDepth d' (NoMatch      d msgs) = NoMatch      (d'+d) msgs-addDepth d' (ExactMatch   d xs)   = ExactMatch   (d'+d) xs-addDepth d' (InexactMatch d xs)   = InexactMatch (d'+d) xs--forceInexact :: Match a -> Match a-forceInexact (ExactMatch d ys) = InexactMatch d ys-forceInexact m                 = m---- | Combine two matchers. Exact matches are used over inexact matches--- but if we have multiple exact, or inexact then the we collect all the--- ambiguous matches.----matchPlus :: Match a -> Match a -> Match a-matchPlus   (ExactMatch   d1 xs)   (ExactMatch   d2 xs') =-  ExactMatch (max d1 d2) (xs ++ xs')-matchPlus a@(ExactMatch   _  _ )   (InexactMatch _  _  ) = a-matchPlus a@(ExactMatch   _  _ )   (NoMatch      _  _  ) = a-matchPlus   (InexactMatch _  _ ) b@(ExactMatch   _  _  ) = b-matchPlus   (InexactMatch d1 xs)   (InexactMatch d2 xs') =-  InexactMatch (max d1 d2) (xs ++ xs')-matchPlus a@(InexactMatch _  _ )   (NoMatch      _  _  ) = a-matchPlus   (NoMatch      _  _ ) b@(ExactMatch   _  _  ) = b-matchPlus   (NoMatch      _  _ ) b@(InexactMatch _  _  ) = b-matchPlus a@(NoMatch      d1 ms) b@(NoMatch      d2 ms')-                                             | d1 >  d2  = a-                                             | d1 <  d2  = b-                                             | otherwise = NoMatch d1 (ms ++ ms')---- | Combine two matchers. This is similar to 'matchPlus' with the--- difference that an exact match from the left matcher shadows any exact--- match on the right. Inexact matches are still collected however.----matchPlusShadowing :: Match a -> Match a -> Match a-matchPlusShadowing a@(ExactMatch _ _) (ExactMatch _ _) = a-matchPlusShadowing a                   b               = matchPlus a b------------------------------------ Various match primitives-----matchErrorExpected :: String -> String -> Match a-matchErrorExpected thing got      = NoMatch 0 [MatchErrorExpected thing got]--matchErrorNoSuch :: String -> String -> [String] -> Match a-matchErrorNoSuch thing got alts = NoMatch 0 [MatchErrorNoSuch thing got alts]--expecting :: String -> String -> Match a -> Match a-expecting thing got (NoMatch 0 _) = matchErrorExpected thing got-expecting _     _   m             = m--orNoSuchThing :: String -> String -> [String] -> Match a -> Match a-orNoSuchThing thing got alts (NoMatch 0 _) = matchErrorNoSuch thing got alts-orNoSuchThing _     _   _    m             = m--orNoThingIn :: String -> String -> Match a -> Match a-orNoThingIn kind name (NoMatch n ms) =-    NoMatch n [ MatchErrorIn kind name m | m <- ms ]-orNoThingIn _ _ m = m--increaseConfidence :: Match ()-increaseConfidence = ExactMatch 1 [()]--increaseConfidenceFor :: Match a -> Match a-increaseConfidenceFor m = m >>= \r -> increaseConfidence >> return r--nubMatchesBy :: (a -> a -> Bool) -> Match a -> Match a-nubMatchesBy _  (NoMatch      d msgs) = NoMatch      d msgs-nubMatchesBy eq (ExactMatch   d xs)   = ExactMatch   d (nubBy eq xs)-nubMatchesBy eq (InexactMatch d xs)   = InexactMatch d (nubBy eq xs)--nubMatchErrors :: Match a -> Match a-nubMatchErrors (NoMatch      d msgs) = NoMatch      d (nub msgs)-nubMatchErrors (ExactMatch   d xs)   = ExactMatch   d xs-nubMatchErrors (InexactMatch d xs)   = InexactMatch d xs---- | Lift a list of matches to an exact match.----exactMatches, inexactMatches :: [a] -> Match a--exactMatches [] = mzero-exactMatches xs = ExactMatch 0 xs--inexactMatches [] = mzero-inexactMatches xs = InexactMatch 0 xs--tryEach :: [a] -> Match a-tryEach = exactMatches------------------------------------ Top level match runner------- | Given a matcher and a key to look up, use the matcher to find all the--- possible matches. There may be 'None', a single 'Unambiguous' match or--- you may have an 'Ambiguous' match with several possibilities.----findMatch :: Match a -> MaybeAmbiguous a-findMatch match =-    case nubMatchErrors match of-      NoMatch    _ msgs -> None msgs-      ExactMatch   _ [x] -> Unambiguous x-      InexactMatch _ [x] -> Unambiguous x-      ExactMatch   _  [] -> error "findMatch: impossible: ExactMatch []"-      InexactMatch _  [] -> error "findMatch: impossible: InexactMatch []"-      ExactMatch   _  xs -> Ambiguous True  xs-      InexactMatch _  xs -> Ambiguous False xs--data MaybeAmbiguous a = None [MatchError] | Unambiguous a | Ambiguous Bool [a]-  deriving Show------------------------------------ Basic matchers------- | A primitive matcher that looks up a value in a finite 'Map'. The--- value must match exactly.----matchExactly :: Ord k => (a -> k) -> [a] -> (k -> Match a)-matchExactly key xs =-    \k -> case Map.lookup k m of-            Nothing -> mzero-            Just ys -> exactMatches ys-  where-    m = Map.fromListWith (++) [ (key x, [x]) | x <- xs ]---- | A primitive matcher that looks up a value in a finite 'Map'. It checks--- for an exact or inexact match. We get an inexact match if the match--- is not exact, but the canonical forms match. It takes a canonicalisation--- function for this purpose.------ So for example if we used string case fold as the canonicalisation--- function, then we would get case insensitive matching (but it will still--- report an exact match when the case matches too).----matchInexactly :: (Ord k, Ord k') => (k -> k') -> (a -> k)-               -> [a] -> (k -> Match a)-matchInexactly cannonicalise key xs =-    \k -> case Map.lookup k m of-            Just ys -> exactMatches ys-            Nothing -> case Map.lookup (cannonicalise k) m' of-                         Just ys -> inexactMatches ys-                         Nothing -> mzero-  where-    m = Map.fromListWith (++) [ (key x, [x]) | x <- xs ]--    -- the map of canonicalised keys to groups of inexact matches-    m' = Map.mapKeysWith (++) cannonicalise m------------------------------------ Utils-----caseFold :: String -> String-caseFold = lowercase------------------------------------ Example inputs-----{--ex1pinfo :: [PackageInfo]-ex1pinfo =-  [ PackageInfo {-      pinfoName        = PackageName "foo",-      pinfoDirectory   = Just "/the/foo",-      pinfoPackageFile = Just "/the/foo/foo.cabal",-      pinfoComponents  = []-    }-  , PackageInfo {-      pinfoName        = PackageName "bar",-      pinfoDirectory   = Just "/the/bar",-      pinfoPackageFile = Just "/the/bar/bar.cabal",-      pinfoComponents  = []-    }-  ]--}-{--stargets =-  [ BuildTargetComponent (CExeName "foo")-  , BuildTargetModule    (CExeName "foo") (mkMn "Foo")-  , BuildTargetModule    (CExeName "tst") (mkMn "Foo")-  ]-    where-    mkMn :: String -> ModuleName-    mkMn  = fromJust . simpleParse--ex_pkgid :: PackageIdentifier-Just ex_pkgid = simpleParse "thelib"--}--{--ex_cs :: [ComponentInfo]-ex_cs =-  [ (mkC (CExeName "foo") ["src1", "src1/src2"] ["Foo", "Src2.Bar", "Bar"])-  , (mkC (CExeName "tst") ["src1", "test"]      ["Foo"])-  ]-    where-    mkC n ds ms = ComponentInfo n (componentStringName pkgid n) ds (map mkMn ms)-    mkMn :: String -> ModuleName-    mkMn  = fromJust . simpleParse-    pkgid :: PackageIdentifier-    Just pkgid = simpleParse "thelib"--}-
Distribution/Client/Check.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE CPP #-} ----------------------------------------------------------------------------- -- | -- Module      :  Distribution.Client.Check@@ -17,8 +18,12 @@  import Control.Monad ( when, unless ) -import Distribution.PackageDescription.Parse-         ( readPackageDescription )+#ifdef CABAL_PARSEC+import Distribution.PackageDescription.Parsec ( readGenericPackageDescription )+#else+import Distribution.PackageDescription.Parse ( readGenericPackageDescription )+#endif+ import Distribution.PackageDescription.Check import Distribution.PackageDescription.Configuration          ( flattenPackageDescription )@@ -30,7 +35,7 @@ check :: Verbosity -> IO Bool check verbosity = do     pdfile <- defaultPackageDesc verbosity-    ppd <- readPackageDescription verbosity pdfile+    ppd <- readGenericPackageDescription verbosity pdfile     -- flatten the generic package description into a regular package     -- description     -- TODO: this may give more warnings than it should give;
+ Distribution/Client/CmdBench.hs view
@@ -0,0 +1,244 @@+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE ViewPatterns   #-}++-- | cabal-install CLI command: bench+--+module Distribution.Client.CmdBench (+    -- * The @bench@ CLI and action+    benchCommand,+    benchAction,++    -- * Internals exposed for testing+    TargetProblem(..),+    selectPackageTargets,+    selectComponentTarget+  ) where++import Distribution.Client.ProjectOrchestration+import Distribution.Client.CmdErrorMessages++import Distribution.Client.Setup+         ( GlobalFlags, ConfigFlags(..), ConfigExFlags, InstallFlags+         , applyFlagDefaults )+import qualified Distribution.Client.Setup as Client+import Distribution.Simple.Setup+         ( HaddockFlags, fromFlagOrDefault )+import Distribution.Simple.Command+         ( CommandUI(..), usageAlternatives )+import Distribution.Text+         ( display )+import Distribution.Verbosity+         ( Verbosity, normal )+import Distribution.Simple.Utils+         ( wrapText, die' )++import Control.Monad (when)+++benchCommand :: CommandUI (ConfigFlags, ConfigExFlags, InstallFlags, HaddockFlags)+benchCommand = Client.installCommand {+  commandName         = "new-bench",+  commandSynopsis     = "Run benchmarks",+  commandUsage        = usageAlternatives "new-bench" [ "[TARGETS] [FLAGS]" ],+  commandDescription  = Just $ \_ -> wrapText $+        "Runs the specified benchmarks, first ensuring they are up to "+     ++ "date.\n\n"++     ++ "Any benchmark in any package in the project can be specified. "+     ++ "A package can be specified in which case all the benchmarks in the "+     ++ "package are run. The default is to run all the benchmarks in the "+     ++ "package in the current directory.\n\n"++     ++ "Dependencies are built or rebuilt as necessary. Additional "+     ++ "configuration flags can be specified on the command line and these "+     ++ "extend the project configuration from the 'cabal.project', "+     ++ "'cabal.project.local' and other files.",+  commandNotes        = Just $ \pname ->+        "Examples:\n"+     ++ "  " ++ pname ++ " new-bench\n"+     ++ "    Run all the benchmarks in the package in the current directory\n"+     ++ "  " ++ pname ++ " new-bench pkgname\n"+     ++ "    Run all the benchmarks in the package named pkgname\n"+     ++ "  " ++ pname ++ " new-bench cname\n"+     ++ "    Run the benchmark named cname\n"+     ++ "  " ++ pname ++ " new-bench cname -O2\n"+     ++ "    Run the benchmark built with '-O2' (including local libs used)\n\n"++     ++ cmdCommonHelpTextNewBuildBeta+   }+++-- | The @build@ command does a lot. It brings the install plan up to date,+-- selects that part of the plan needed by the given or implicit targets and+-- then executes the plan.+--+-- For more details on how this works, see the module+-- "Distribution.Client.ProjectOrchestration"+--+benchAction :: (ConfigFlags, ConfigExFlags, InstallFlags, HaddockFlags)+            -> [String] -> GlobalFlags -> IO ()+benchAction (applyFlagDefaults -> (configFlags, configExFlags, installFlags, haddockFlags))+            targetStrings globalFlags = do++    baseCtx <- establishProjectBaseContext verbosity cliConfig++    targetSelectors <- either (reportTargetSelectorProblems verbosity) return+                   =<< readTargetSelectors (localPackages baseCtx) targetStrings++    buildCtx <-+      runProjectPreBuildPhase verbosity baseCtx $ \elaboratedPlan -> do++            when (buildSettingOnlyDeps (buildSettings baseCtx)) $+              die' verbosity $+                  "The bench command does not support '--only-dependencies'. "+               ++ "You may wish to use 'build --only-dependencies' and then "+               ++ "use 'bench'."++            -- Interpret the targets on the command line as bench targets+            -- (as opposed to say build or haddock targets).+            targets <- either (reportTargetProblems verbosity) return+                     $ resolveTargets+                         selectPackageTargets+                         selectComponentTarget+                         TargetProblemCommon+                         elaboratedPlan+                         targetSelectors++            let elaboratedPlan' = pruneInstallPlanToTargets+                                    TargetActionBuild+                                    targets+                                    elaboratedPlan+            return elaboratedPlan'++    printPlan verbosity baseCtx buildCtx++    buildOutcomes <- runProjectBuildPhase verbosity baseCtx buildCtx+    runProjectPostBuildPhase verbosity baseCtx buildCtx buildOutcomes+  where+    verbosity = fromFlagOrDefault normal (configVerbosity configFlags)+    cliConfig = commandLineFlagsToProjectConfig+                  globalFlags configFlags configExFlags+                  installFlags haddockFlags++-- | This defines what a 'TargetSelector' means for the @bench@ command.+-- It selects the 'AvailableTarget's that the 'TargetSelector' refers to,+-- or otherwise classifies the problem.+--+-- For the @bench@ command we select all buildable benchmarks,+-- or fail if there are no benchmarks or no buildable benchmarks.+--+selectPackageTargets :: TargetSelector PackageId+                     -> [AvailableTarget k] -> Either TargetProblem [k]+selectPackageTargets targetSelector targets++    -- If there are any buildable benchmark targets then we select those+  | not (null targetsBenchBuildable)+  = Right targetsBenchBuildable++    -- If there are benchmarks but none are buildable then we report those+  | not (null targetsBench)+  = Left (TargetProblemNoneEnabled targetSelector targetsBench)++    -- If there are no benchmarks but some other targets then we report that+  | not (null targets)+  = Left (TargetProblemNoBenchmarks targetSelector)++    -- If there are no targets at all then we report that+  | otherwise+  = Left (TargetProblemNoTargets targetSelector)+  where+    targetsBenchBuildable = selectBuildableTargets+                          . filterTargetsKind BenchKind+                          $ targets++    targetsBench          = forgetTargetsDetail+                          . filterTargetsKind BenchKind+                          $ targets+++-- | For a 'TargetComponent' 'TargetSelector', check if the component can be+-- selected.+--+-- For the @bench@ command we just need to check it is a benchmark, in addition+-- to the basic checks on being buildable etc.+--+selectComponentTarget :: PackageId -> ComponentName -> SubComponentTarget+                      -> AvailableTarget k -> Either TargetProblem k+selectComponentTarget pkgid cname subtarget@WholeComponent t+  | CBenchName _ <- availableTargetComponentName t+  = either (Left . TargetProblemCommon) return $+           selectComponentTargetBasic pkgid cname subtarget t+  | otherwise+  = Left (TargetProblemComponentNotBenchmark pkgid cname)++selectComponentTarget pkgid cname subtarget _+  = Left (TargetProblemIsSubComponent pkgid cname subtarget)++-- | The various error conditions that can occur when matching a+-- 'TargetSelector' against 'AvailableTarget's for the @bench@ command.+--+data TargetProblem =+     TargetProblemCommon        TargetProblemCommon++     -- | The 'TargetSelector' matches benchmarks but none are buildable+   | TargetProblemNoneEnabled  (TargetSelector PackageId) [AvailableTarget ()]++     -- | There are no targets at all+   | TargetProblemNoTargets    (TargetSelector PackageId)++     -- | The 'TargetSelector' matches targets but no benchmarks+   | TargetProblemNoBenchmarks (TargetSelector PackageId)++     -- | The 'TargetSelector' refers to a component that is not a benchmark+   | TargetProblemComponentNotBenchmark PackageId ComponentName++     -- | Asking to benchmark an individual file or module is not supported+   | TargetProblemIsSubComponent   PackageId ComponentName SubComponentTarget+  deriving (Eq, Show)++reportTargetProblems :: Verbosity -> [TargetProblem] -> IO a+reportTargetProblems verbosity =+    die' verbosity . unlines . map renderTargetProblem++renderTargetProblem :: TargetProblem -> String+renderTargetProblem (TargetProblemCommon problem) =+    renderTargetProblemCommon "run" problem++renderTargetProblem (TargetProblemNoneEnabled targetSelector targets) =+    renderTargetProblemNoneEnabled "benchmark" targetSelector targets++renderTargetProblem (TargetProblemNoBenchmarks targetSelector) =+    "Cannot run benchmarks for the target '" ++ showTargetSelector targetSelector+ ++ "' which refers to " ++ renderTargetSelector targetSelector+ ++ " because "+ ++ plural (targetSelectorPluralPkgs targetSelector) "it does" "they do"+ ++ " not contain any benchmarks."++renderTargetProblem (TargetProblemNoTargets targetSelector) =+    case targetSelectorFilter targetSelector of+      Just kind | kind /= BenchKind+        -> "The bench command is for running benchmarks, but the target '"+           ++ showTargetSelector targetSelector ++ "' refers to "+           ++ renderTargetSelector targetSelector ++ "."++      _ -> renderTargetProblemNoTargets "benchmark" targetSelector+  where+    targetSelectorFilter (TargetAllPackages  mkfilter) = mkfilter+    targetSelectorFilter (TargetPackage  _ _ mkfilter) = mkfilter+    targetSelectorFilter (TargetComponent _ _ _)       = Nothing++renderTargetProblem (TargetProblemComponentNotBenchmark pkgid cname) =+    "The bench command is for running benchmarks, but the target '"+ ++ showTargetSelector targetSelector ++ "' refers to "+ ++ renderTargetSelector targetSelector ++ " from the package "+ ++ display pkgid ++ "."+  where+    targetSelector = TargetComponent pkgid cname WholeComponent++renderTargetProblem (TargetProblemIsSubComponent pkgid cname subtarget) =+    "The bench command can only run benchmarks as a whole, "+ ++ "not files or modules within them, but the target '"+ ++ showTargetSelector targetSelector ++ "' refers to "+ ++ renderTargetSelector targetSelector ++ "."+  where+    targetSelector = TargetComponent pkgid cname subtarget
Distribution/Client/CmdBuild.hs view
@@ -1,32 +1,71 @@-{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE ViewPatterns #-}  -- | cabal-install CLI command: build -- module Distribution.Client.CmdBuild (+    -- * The @build@ CLI and action+    buildCommand,     buildAction,++    -- * Internals exposed for testing+    TargetProblem(..),+    selectPackageTargets,+    selectComponentTarget   ) where  import Distribution.Client.ProjectOrchestration-         ( PreBuildHooks(..), runProjectPreBuildPhase, selectTargets-         , ProjectBuildContext(..), runProjectBuildPhase-         , printPlan, reportBuildFailures )-import Distribution.Client.ProjectConfig-         ( BuildTimeSettings(..) )-import Distribution.Client.ProjectPlanning-         ( PackageTarget(..) )-import Distribution.Client.BuildTarget-         ( readUserBuildTargets )+import Distribution.Client.CmdErrorMessages  import Distribution.Client.Setup-         ( GlobalFlags, ConfigFlags(..), ConfigExFlags, InstallFlags )+         ( GlobalFlags, ConfigFlags(..), ConfigExFlags, InstallFlags+         , applyFlagDefaults )+import qualified Distribution.Client.Setup as Client import Distribution.Simple.Setup          ( HaddockFlags, fromFlagOrDefault )+import Distribution.Simple.Command+         ( CommandUI(..), usageAlternatives ) import Distribution.Verbosity-         ( normal )+         ( Verbosity, normal )+import Distribution.Simple.Utils+         ( wrapText, die' ) -import Control.Monad (unless)+import qualified Data.Map as Map  +buildCommand :: CommandUI (ConfigFlags, ConfigExFlags, InstallFlags, HaddockFlags)+buildCommand = Client.installCommand {+  commandName         = "new-build",+  commandSynopsis     = "Compile targets within the project.",+  commandUsage        = usageAlternatives "new-build" [ "[TARGETS] [FLAGS]" ],+  commandDescription  = Just $ \_ -> wrapText $+        "Build one or more targets from within the project. The available "+     ++ "targets are the packages in the project as well as individual "+     ++ "components within those packages, including libraries, executables, "+     ++ "test-suites or benchmarks. Targets can be specified by name or "+     ++ "location. If no target is specified then the default is to build "+     ++ "the package in the current directory.\n\n"++     ++ "Dependencies are built or rebuilt as necessary. Additional "+     ++ "configuration flags can be specified on the command line and these "+     ++ "extend the project configuration from the 'cabal.project', "+     ++ "'cabal.project.local' and other files.",+  commandNotes        = Just $ \pname ->+        "Examples:\n"+     ++ "  " ++ pname ++ " new-build\n"+     ++ "    Build the package in the current directory or all packages in the project\n"+     ++ "  " ++ pname ++ " new-build pkgname\n"+     ++ "    Build the package named pkgname in the project\n"+     ++ "  " ++ pname ++ " new-build ./pkgfoo\n"+     ++ "    Build the package in the ./pkgfoo directory\n"+     ++ "  " ++ pname ++ " new-build cname\n"+     ++ "    Build the component named cname in the project\n"+     ++ "  " ++ pname ++ " new-build cname --enable-profiling\n"+     ++ "    Build the component in profiling mode (including dependencies as needed)\n\n"++     ++ cmdCommonHelpTextNewBuildBeta+   }++ -- | The @build@ command does a lot. It brings the install plan up to date, -- selects that part of the plan needed by the given or implicit targets and -- then executes the plan.@@ -36,35 +75,123 @@ -- buildAction :: (ConfigFlags, ConfigExFlags, InstallFlags, HaddockFlags)             -> [String] -> GlobalFlags -> IO ()-buildAction (configFlags, configExFlags, installFlags, haddockFlags)+buildAction (applyFlagDefaults -> (configFlags, configExFlags, installFlags, haddockFlags))             targetStrings globalFlags = do -    userTargets <- readUserBuildTargets targetStrings+    baseCtx <- establishProjectBaseContext verbosity cliConfig -    buildCtx@ProjectBuildContext{buildSettings} <--      runProjectPreBuildPhase-        verbosity-        ( globalFlags, configFlags, configExFlags-        , installFlags, haddockFlags )-        PreBuildHooks {-          hookPrePlanning      = \_ _ _ -> return (),-          hookSelectPlanSubset = selectBuildTargets userTargets-        }+    targetSelectors <- either (reportTargetSelectorProblems verbosity) return+                   =<< readTargetSelectors (localPackages baseCtx) targetStrings -    printPlan verbosity buildCtx+    buildCtx <-+      runProjectPreBuildPhase verbosity baseCtx $ \elaboratedPlan -> do -    unless (buildSettingDryRun buildSettings) $ do-      plan <- runProjectBuildPhase-                verbosity-                buildCtx-      reportBuildFailures plan+            -- Interpret the targets on the command line as build targets+            -- (as opposed to say repl or haddock targets).+            targets <- either (reportTargetProblems verbosity) return+                     $ resolveTargets+                         selectPackageTargets+                         selectComponentTarget+                         TargetProblemCommon+                         elaboratedPlan+                         targetSelectors++            let elaboratedPlan' = pruneInstallPlanToTargets+                                    TargetActionBuild+                                    targets+                                    elaboratedPlan+            elaboratedPlan'' <-+              if buildSettingOnlyDeps (buildSettings baseCtx)+                then either (reportCannotPruneDependencies verbosity) return $+                     pruneInstallPlanToDependencies (Map.keysSet targets)+                                                    elaboratedPlan'+                else return elaboratedPlan'++            return elaboratedPlan''++    printPlan verbosity baseCtx buildCtx++    buildOutcomes <- runProjectBuildPhase verbosity baseCtx buildCtx+    runProjectPostBuildPhase verbosity baseCtx buildCtx buildOutcomes   where     verbosity = fromFlagOrDefault normal (configVerbosity configFlags)+    cliConfig = commandLineFlagsToProjectConfig+                  globalFlags configFlags configExFlags+                  installFlags haddockFlags -    -- When we interpret the targets on the command line, interpret them as-    -- repl targets (as opposed to say repl or haddock targets).-    selectBuildTargets =-      selectTargets-        BuildDefaultComponents-        BuildSpecificComponent+-- | This defines what a 'TargetSelector' means for the @bench@ command.+-- It selects the 'AvailableTarget's that the 'TargetSelector' refers to,+-- or otherwise classifies the problem.+--+-- For the @build@ command select all components except non-buildable and disabled+-- tests\/benchmarks, fail if there are no such components+--+selectPackageTargets :: TargetSelector PackageId+                     -> [AvailableTarget k] -> Either TargetProblem [k]+selectPackageTargets targetSelector targets++    -- If there are any buildable targets then we select those+  | not (null targetsBuildable)+  = Right targetsBuildable++    -- If there are targets but none are buildable then we report those+  | not (null targets)+  = Left (TargetProblemNoneEnabled targetSelector targets')++    -- If there are no targets at all then we report that+  | otherwise+  = Left (TargetProblemNoTargets targetSelector)+  where+    targets'         = forgetTargetsDetail targets+    targetsBuildable = selectBuildableTargetsWith+                         (buildable targetSelector)+                         targets++    -- When there's a target filter like "pkg:tests" then we do select tests,+    -- but if it's just a target like "pkg" then we don't build tests unless+    -- they are requested by default (i.e. by using --enable-tests)+    buildable (TargetPackage _ _  Nothing) TargetNotRequestedByDefault = False+    buildable (TargetAllPackages  Nothing) TargetNotRequestedByDefault = False+    buildable _ _ = True++-- | For a 'TargetComponent' 'TargetSelector', check if the component can be+-- selected.+--+-- For the @build@ command we just need the basic checks on being buildable etc.+--+selectComponentTarget :: PackageId -> ComponentName -> SubComponentTarget+                      -> AvailableTarget k -> Either TargetProblem k+selectComponentTarget pkgid cname subtarget =+    either (Left . TargetProblemCommon) Right+  . selectComponentTargetBasic pkgid cname subtarget+++-- | The various error conditions that can occur when matching a+-- 'TargetSelector' against 'AvailableTarget's for the @build@ command.+--+data TargetProblem =+     TargetProblemCommon       TargetProblemCommon++     -- | The 'TargetSelector' matches targets but none are buildable+   | TargetProblemNoneEnabled (TargetSelector PackageId) [AvailableTarget ()]++     -- | There are no targets at all+   | TargetProblemNoTargets   (TargetSelector PackageId)+  deriving (Eq, Show)++reportTargetProblems :: Verbosity -> [TargetProblem] -> IO a+reportTargetProblems verbosity =+    die' verbosity . unlines . map renderTargetProblem++renderTargetProblem :: TargetProblem -> String+renderTargetProblem (TargetProblemCommon problem) =+    renderTargetProblemCommon "build" problem+renderTargetProblem (TargetProblemNoneEnabled targetSelector targets) =+    renderTargetProblemNoneEnabled "build" targetSelector targets+renderTargetProblem(TargetProblemNoTargets targetSelector) =+    renderTargetProblemNoTargets "build" targetSelector++reportCannotPruneDependencies :: Verbosity -> CannotPruneDependencies -> IO a+reportCannotPruneDependencies verbosity =+    die' verbosity . renderCannotPruneDependencies 
Distribution/Client/CmdConfigure.hs view
@@ -1,20 +1,71 @@+{-# LANGUAGE ViewPatterns #-} -- | cabal-install CLI command: configure -- module Distribution.Client.CmdConfigure (+    configureCommand,     configureAction,   ) where  import Distribution.Client.ProjectOrchestration import Distribution.Client.ProjectConfig+         ( writeProjectLocalExtraConfig )  import Distribution.Client.Setup-         ( GlobalFlags, ConfigFlags(..), ConfigExFlags, InstallFlags )+         ( GlobalFlags, ConfigFlags(..), ConfigExFlags, InstallFlags+         , applyFlagDefaults ) import Distribution.Simple.Setup          ( HaddockFlags, fromFlagOrDefault ) import Distribution.Verbosity          ( normal ) +import Distribution.Simple.Command+         ( CommandUI(..), usageAlternatives )+import Distribution.Simple.Utils+         ( wrapText )+import qualified Distribution.Client.Setup as Client +configureCommand :: CommandUI (ConfigFlags, ConfigExFlags+                              ,InstallFlags, HaddockFlags)+configureCommand = Client.installCommand {+  commandName         = "new-configure",+  commandSynopsis     = "Add extra project configuration",+  commandUsage        = usageAlternatives "new-configure" [ "[FLAGS]" ],+  commandDescription  = Just $ \_ -> wrapText $+        "Adjust how the project is built by setting additional package flags "+     ++ "and other flags.\n\n"++     ++ "The configuration options are written to the 'cabal.project.local' "+     ++ "file (or '$project_file.local', if '--project-file' is specified) "+     ++ "which extends the configuration from the 'cabal.project' file "+     ++ "(if any). This combination is used as the project configuration for "+     ++ "all other commands (such as 'new-build', 'new-repl' etc) though it "+     ++ "can be extended/overridden on a per-command basis.\n\n"++     ++ "The new-configure command also checks that the project configuration "+     ++ "will work. In particular it checks that there is a consistent set of "+     ++ "dependencies for the project as a whole.\n\n"++     ++ "The 'cabal.project.local' file persists across 'new-clean' but is "+     ++ "overwritten on the next use of the 'new-configure' command. The "+     ++ "intention is that the 'cabal.project' file should be kept in source "+     ++ "control but the 'cabal.project.local' should not.\n\n"++     ++ "It is never necessary to use the 'new-configure' command. It is "+     ++ "merely a convenience in cases where you do not want to specify flags "+     ++ "to 'new-build' (and other commands) every time and yet do not want "+     ++ "to alter the 'cabal.project' persistently.",+  commandNotes        = Just $ \pname ->+        "Examples:\n"+     ++ "  " ++ pname ++ " new-configure --with-compiler ghc-7.10.3\n"+     ++ "    Adjust the project configuration to use the given compiler\n"+     ++ "    program and check the resulting configuration works.\n"+     ++ "  " ++ pname ++ " new-configure\n"+     ++ "    Reset the local configuration to empty and check the overall\n"+     ++ "    project configuration works.\n\n"++     ++ cmdCommonHelpTextNewBuildBeta+   }+ -- | To a first approximation, the @configure@ just runs the first phase of -- the @build@ command where we bring the install plan up to date (thus -- checking that it's possible).@@ -27,34 +78,41 @@ -- configureAction :: (ConfigFlags, ConfigExFlags, InstallFlags, HaddockFlags)                 -> [String] -> GlobalFlags -> IO ()-configureAction (configFlags, configExFlags, installFlags, haddockFlags)+configureAction (applyFlagDefaults -> (configFlags, configExFlags, installFlags, haddockFlags))                 _extraArgs globalFlags = do     --TODO: deal with _extraArgs, since flags with wrong syntax end up there +    baseCtx <- establishProjectBaseContext verbosity cliConfig++    -- Write out the @cabal.project.local@ so it gets picked up by the+    -- planning phase.+    writeProjectLocalExtraConfig (distDirLayout baseCtx)+                                 cliConfig+     buildCtx <--      runProjectPreBuildPhase-        verbosity-        ( globalFlags, configFlags, configExFlags-        , installFlags, haddockFlags )-        PreBuildHooks {-          hookPrePlanning = \projectRootDir _ cliConfig ->-            -- Write out the @cabal.project.local@ so it gets picked up by the-            -- planning phase.-            writeProjectLocalExtraConfig projectRootDir cliConfig,+      runProjectPreBuildPhase verbosity baseCtx $ \elaboratedPlan -> do -          hookSelectPlanSubset = return-        }+            -- TODO: Select the same subset of targets as 'CmdBuild' would+            -- pick (ignoring, for example, executables in libraries+            -- we depend on). But we don't want it to fail, so actually we+            -- have to do it slightly differently from build.+            return elaboratedPlan -    --TODO: Hmm, but we don't have any targets. Currently this prints what we-    -- would build if we were to build everything. Could pick implicit target like "."-    --TODO: should we say what's in the project (+deps) as a whole?-    printPlan-      verbosity-      buildCtx {-        buildSettings = (buildSettings buildCtx) {-          buildSettingDryRun = True-        }-      }+    let baseCtx' = baseCtx {+                      buildSettings = (buildSettings baseCtx) {+                        buildSettingDryRun = True+                      }+                    }++    -- TODO: Hmm, but we don't have any targets. Currently this prints+    -- what we would build if we were to build everything. Could pick+    -- implicit target like "."+    --+    -- TODO: should we say what's in the project (+deps) as a whole?+    printPlan verbosity baseCtx' buildCtx   where     verbosity = fromFlagOrDefault normal (configVerbosity configFlags)+    cliConfig = commandLineFlagsToProjectConfig+                  globalFlags configFlags configExFlags+                  installFlags haddockFlags 
+ Distribution/Client/CmdErrorMessages.hs view
@@ -0,0 +1,352 @@+{-# LANGUAGE RecordWildCards, NamedFieldPuns #-}++-- | Utilities to help format error messages for the various CLI commands.+--+module Distribution.Client.CmdErrorMessages (+    module Distribution.Client.CmdErrorMessages,+    module Distribution.Client.TargetSelector,+  ) where++import Distribution.Client.ProjectOrchestration+import Distribution.Client.TargetSelector+         ( componentKind, showTargetSelector )++import Distribution.Package+         ( packageId, packageName )+import Distribution.Types.ComponentName+         ( showComponentName )+import Distribution.Solver.Types.OptionalStanza+         ( OptionalStanza(..) )+import Distribution.Text+         ( display )++import Data.Maybe (isNothing)+import Data.List (sortBy, groupBy, nub)+import Data.Function (on)+++-----------------------+-- Singular or plural+--++-- | A tag used in rendering messages to distinguish singular or plural.+--+data Plural = Singular | Plural++-- | Used to render a singular or plural version of something+--+-- > plural (listPlural theThings) "it is" "they are"+--+plural :: Plural -> a -> a -> a+plural Singular si _pl = si+plural Plural  _si  pl = pl++-- | Singular for singleton lists and plural otherwise.+--+listPlural :: [a] -> Plural+listPlural [_] = Singular+listPlural  _  = Plural+++--------------------+-- Rendering lists+--++-- | Render a list of things in the style @foo, bar and baz@+renderListCommaAnd :: [String] -> String+renderListCommaAnd []     = ""+renderListCommaAnd [x]    = x+renderListCommaAnd [x,x'] = x ++ " and " ++ x'+renderListCommaAnd (x:xs) = x ++ ", " ++ renderListCommaAnd xs++-- | Render a list of things in the style @blah blah; this that; and the other@+renderListSemiAnd :: [String] -> String+renderListSemiAnd []     = ""+renderListSemiAnd [x]    = x+renderListSemiAnd [x,x'] = x ++ "; and " ++ x'+renderListSemiAnd (x:xs) = x ++ "; " ++ renderListSemiAnd xs++-- | When rendering lists of things it often reads better to group related+-- things, e.g. grouping components by package name+--+-- > renderListSemiAnd+-- >   [     "the package " ++ display pkgname ++ " components "+-- >      ++ renderListCommaAnd showComponentName components+-- >   | (pkgname, components) <- sortGroupOn packageName allcomponents ]+--+sortGroupOn :: Ord b => (a -> b) -> [a] -> [(b, [a])]+sortGroupOn key = map (\xs@(x:_) -> (key x, xs))+                . groupBy ((==) `on` key)+                . sortBy  (compare `on` key)+++----------------------------------------------------+-- Renderering for a few project and package types+--++renderTargetSelector :: TargetSelector PackageId -> String+renderTargetSelector (TargetPackage _ pkgid Nothing) =+    "the package " ++ display pkgid++renderTargetSelector (TargetPackage _ pkgid (Just kfilter)) =+    "the " ++ renderComponentKind Plural kfilter+ ++ " in the package " ++ display pkgid++renderTargetSelector (TargetAllPackages Nothing) =+    "all the packages in the project"++renderTargetSelector (TargetAllPackages (Just kfilter)) =+    "all the " ++ renderComponentKind Plural kfilter+ ++ " in the project"++renderTargetSelector (TargetComponent pkgid CLibName WholeComponent) =+    "the library in the package " ++ display pkgid++renderTargetSelector (TargetComponent _pkgid cname WholeComponent) =+    "the " ++ showComponentName cname++renderTargetSelector (TargetComponent _pkgid cname (FileTarget filename)) =+    "the file " ++ filename ++ " in the " ++ showComponentName cname++renderTargetSelector (TargetComponent _pkgid cname (ModuleTarget modname)) =+    "the module " ++ display modname ++ " in the " ++ showComponentName cname+++renderOptionalStanza :: Plural -> OptionalStanza -> String+renderOptionalStanza Singular TestStanzas  = "test suite"+renderOptionalStanza Plural   TestStanzas  = "test suites"+renderOptionalStanza Singular BenchStanzas = "benchmark"+renderOptionalStanza Plural   BenchStanzas = "benchmarks"++-- | The optional stanza type (test suite or benchmark), if it is one.+optionalStanza :: ComponentName -> Maybe OptionalStanza+optionalStanza (CTestName  _) = Just TestStanzas+optionalStanza (CBenchName _) = Just BenchStanzas+optionalStanza _              = Nothing+++-- | Does the 'TargetSelector' potentially refer to one package or many?+--+targetSelectorPluralPkgs :: TargetSelector a -> Plural+targetSelectorPluralPkgs (TargetAllPackages _)     = Plural+targetSelectorPluralPkgs (TargetPackage _ _ _)     = Singular+targetSelectorPluralPkgs (TargetComponent _ _ _)   = Singular++-- | Does the 'TargetSelector' refer to +targetSelectorRefersToPkgs :: TargetSelector a -> Bool+targetSelectorRefersToPkgs (TargetAllPackages  mkfilter) = isNothing mkfilter+targetSelectorRefersToPkgs (TargetPackage  _ _ mkfilter) = isNothing mkfilter+targetSelectorRefersToPkgs (TargetComponent _ _ _)       = False++renderComponentKind :: Plural -> ComponentKind -> String+renderComponentKind Singular ckind = case ckind of+  LibKind   -> "library"  -- internal/sub libs?+  FLibKind  -> "foreign library"+  ExeKind   -> "executable"+  TestKind  -> "test suite"+  BenchKind -> "benchmark"+renderComponentKind Plural ckind = case ckind of+  LibKind   -> "libraries"  -- internal/sub libs?+  FLibKind  -> "foreign libraries"+  ExeKind   -> "executables"+  TestKind  -> "test suites"+  BenchKind -> "benchmarks"+++-------------------------------------------------------+-- Renderering error messages for TargetProblemCommon+--++renderTargetProblemCommon :: String -> TargetProblemCommon -> String+renderTargetProblemCommon verb (TargetNotInProject pkgname) =+    "Cannot " ++ verb ++ " the package " ++ display pkgname ++ ", it is not "+ ++ "in this project (either directly or indirectly). If you want to add it "+ ++ "to the project then edit the cabal.project file."++renderTargetProblemCommon verb (TargetComponentNotProjectLocal pkgid cname _) =+    "Cannot " ++ verb ++ " the " ++ showComponentName cname ++ " because the "+ ++ "package " ++ display pkgid ++ " is not local to the project, and cabal "+ ++ "does not currently support building test suites or benchmarks of "+ ++ "non-local dependencies. To run test suites or benchmarks from "+ ++ "dependencies you can unpack the package locally and adjust the "+ ++ "cabal.project file to include that package directory."++renderTargetProblemCommon verb (TargetComponentNotBuildable pkgid cname _) =+    "Cannot " ++ verb ++ " the " ++ showComponentName cname ++ " because it is "+ ++ "marked as 'buildable: False' within the '" ++ display (packageName pkgid)+ ++ ".cabal' file (at least for the current configuration). If you believe it "+ ++ "should be buildable then check the .cabal file to see if the buildable "+ ++ "property is conditional on flags. Alternatively you may simply have to "+ ++ "edit the .cabal file to declare it as buildable and fix any resulting "+ ++ "build problems."++renderTargetProblemCommon verb (TargetOptionalStanzaDisabledByUser _ cname _) =+    "Cannot " ++ verb ++ " the " ++ showComponentName cname ++ " because "+ ++ "building " ++ compkinds ++ " has been explicitly disabled in the "+ ++ "configuration. You can adjust this configuration in the "+ ++ "cabal.project{.local} file either for all packages in the project or on "+ ++ "a per-package basis. Note that if you do not explicitly disable "+ ++ compkinds ++ " then the solver will merely try to make a plan with "+ ++ "them available, so you may wish to explicitly enable them which will "+ ++ "require the solver to find a plan with them available or to fail with an "+ ++ "explanation."+   where+     compkinds = renderComponentKind Plural (componentKind cname)++renderTargetProblemCommon verb (TargetOptionalStanzaDisabledBySolver pkgid cname _) =+    "Cannot " ++ verb ++ " the " ++ showComponentName cname ++ " because the "+ ++ "solver did not find a plan that included the " ++ compkinds+ ++ " for " ++ display pkgid ++ ". It is probably worth trying again with "+ ++ compkinds ++ "explicitly enabled in the configuration in the "+ ++ "cabal.project{.local} file. This will ask the solver to find a plan with "+ ++ "the " ++ compkinds ++ " available. It will either fail with an "+ ++ "explanation or find a different plan that uses different versions of some "+ ++ "other packages. Use the '--dry-run' flag to see package versions and "+ ++ "check that you are happy with the choices."+   where+     compkinds = renderComponentKind Plural (componentKind cname)++renderTargetProblemCommon verb (TargetProblemNoSuchPackage pkgid) =+    "Internal error when trying to " ++ verb ++ " the package "+  ++ display pkgid ++ ". The package is not in the set of available targets "+  ++ "for the project plan, which would suggest an inconsistency "+  ++ "between readTargetSelectors and resolveTargets."++renderTargetProblemCommon verb (TargetProblemNoSuchComponent pkgid cname) =+    "Internal error when trying to " ++ verb ++ " the "+  ++ showComponentName cname ++ " from the package " ++ display pkgid+  ++ ". The package,component pair is not in the set of available targets "+  ++ "for the project plan, which would suggest an inconsistency "+  ++ "between readTargetSelectors and resolveTargets."+++------------------------------------------------------------+-- Renderering error messages for TargetProblemNoneEnabled+--++-- | Several commands have a @TargetProblemNoneEnabled@ problem constructor.+-- This renders an error message for those cases.+--+renderTargetProblemNoneEnabled :: String+                               -> TargetSelector PackageId+                               -> [AvailableTarget ()]+                               -> String+renderTargetProblemNoneEnabled verb targetSelector targets =+    "Cannot " ++ verb ++ " " ++ renderTargetSelector targetSelector+ ++ " because none of the components are available to build: "+ ++ renderListSemiAnd+    [ case (status, mstanza) of+        (TargetDisabledByUser, Just stanza) ->+            renderListCommaAnd+              [ "the " ++ showComponentName availableTargetComponentName+              | AvailableTarget {availableTargetComponentName} <- targets' ]+         ++ plural (listPlural targets') " is " " are "+         ++ " not available because building "+         ++ renderOptionalStanza Plural stanza+         ++ " has been disabled in the configuration"+        (TargetDisabledBySolver, Just stanza) ->+            renderListCommaAnd+              [ "the " ++ showComponentName availableTargetComponentName+              | AvailableTarget {availableTargetComponentName} <- targets' ]+         ++ plural (listPlural targets') " is " " are "+         ++ "not available because the solver did not find a plan that "+         ++ "included the " ++ renderOptionalStanza Plural stanza+        (TargetNotBuildable, _) ->+            renderListCommaAnd+              [ "the " ++ showComponentName availableTargetComponentName+              | AvailableTarget {availableTargetComponentName} <- targets' ]+         ++ plural (listPlural targets') " is " " are all "+         ++ "marked as 'buildable: False'"+        (TargetNotLocal, _) ->+            renderListCommaAnd+              [ "the " ++ showComponentName availableTargetComponentName+              | AvailableTarget {availableTargetComponentName} <- targets' ]+         ++ " cannot be built because cabal does not currently support "+         ++ "building test suites or benchmarks of non-local dependencies"+        (TargetBuildable () TargetNotRequestedByDefault, Just stanza) ->+            renderListCommaAnd+              [ "the " ++ showComponentName availableTargetComponentName+              | AvailableTarget {availableTargetComponentName} <- targets' ]+         ++ " will not be built because " ++ renderOptionalStanza Plural stanza+         ++ " are not built by default in the current configuration (but you "+         ++ "can still build them specifically)" --TODO: say how+        _ -> error $ "renderBuildTargetProblem: unexpected status "+                  ++ show (status, mstanza)+    | ((status, mstanza), targets') <- sortGroupOn groupingKey targets+    ]+  where+    groupingKey t =+      ( availableTargetStatus t+      , case availableTargetStatus t of+          TargetNotBuildable -> Nothing+          TargetNotLocal     -> Nothing+          _ -> optionalStanza (availableTargetComponentName t)+      )++------------------------------------------------------------+-- Renderering error messages for TargetProblemNoneEnabled+--++-- | Several commands have a @TargetProblemNoTargets@ problem constructor.+-- This renders an error message for those cases.+--+renderTargetProblemNoTargets :: String -> TargetSelector PackageId -> String+renderTargetProblemNoTargets verb targetSelector =+    "Cannot " ++ verb ++ " " ++ renderTargetSelector targetSelector+ ++ " because " ++ reason targetSelector ++ ". "+ ++ "Check the .cabal "+ ++ plural (targetSelectorPluralPkgs targetSelector)+      "file for the package and make sure that it properly declares "+      "files for the packages and make sure that they properly declare "+ ++ "the components that you expect."+  where+    reason (TargetPackage _ _ Nothing) =+        "it does not contain any components at all"+    reason (TargetPackage _ _ (Just kfilter)) =+        "it does not contain any " ++ renderComponentKind Plural kfilter+    reason (TargetAllPackages Nothing) =+        "none of them contain any components at all"+    reason (TargetAllPackages (Just kfilter)) =+        "none of the packages contain any "+     ++ renderComponentKind Plural kfilter+    reason ts@TargetComponent{} =+        error $ "renderTargetProblemNoTargets: " ++ show ts++-----------------------------------------------------------+-- Renderering error messages for CannotPruneDependencies+--++renderCannotPruneDependencies :: CannotPruneDependencies -> String+renderCannotPruneDependencies (CannotPruneDependencies brokenPackages) =+      "Cannot select only the dependencies (as requested by the "+   ++ "'--only-dependencies' flag), "+   ++ (case pkgids of+          [pkgid] -> "the package " ++ display pkgid ++ " is "+          _       -> "the packages "+                     ++ renderListCommaAnd (map display pkgids) ++ " are ")+   ++ "required by a dependency of one of the other targets."+  where+    -- throw away the details and just list the deps that are needed+    pkgids :: [PackageId]+    pkgids = nub . map packageId . concatMap snd $ brokenPackages++{-+           ++ "Syntax:\n"+           ++ " - build [package]\n"+           ++ " - build [package:]component\n"+           ++ " - build [package:][component:]module\n"+           ++ " - build [package:][component:]file\n"+           ++ " where\n"+           ++ "  package is a package name, package dir or .cabal file\n\n"+           ++ "Examples:\n"+           ++ " - build foo            -- package name\n"+           ++ " - build tests          -- component name\n"+           ++ "    (name of library, executable, test-suite or benchmark)\n"+           ++ " - build Data.Foo       -- module name\n"+           ++ " - build Data/Foo.hsc   -- file name\n\n"+           ++ "An ambigious target can be qualified by package, component\n"+           ++ "and/or component kind (lib|exe|test|bench|flib)\n"+           ++ " - build foo:tests      -- component qualified by package\n"+           ++ " - build tests:Data.Foo -- module qualified by component\n"+           ++ " - build lib:foo        -- component qualified by kind"+-}
+ Distribution/Client/CmdFreeze.hs view
@@ -0,0 +1,243 @@+{-# LANGUAGE CPP, NamedFieldPuns, RecordWildCards, ViewPatterns #-}++-- | cabal-install CLI command: freeze+--+module Distribution.Client.CmdFreeze (+    freezeCommand,+    freezeAction,+  ) where++import Distribution.Client.ProjectOrchestration+import Distribution.Client.ProjectPlanning+import Distribution.Client.ProjectConfig+         ( ProjectConfig(..), ProjectConfigShared(..)+         , writeProjectLocalFreezeConfig )+import Distribution.Client.Targets+         ( UserQualifier(..), UserConstraintScope(..), UserConstraint(..) )+import Distribution.Solver.Types.PackageConstraint+         ( PackageProperty(..) )+import Distribution.Solver.Types.ConstraintSource+         ( ConstraintSource(..) )+import Distribution.Client.DistDirLayout+         ( DistDirLayout(distProjectFile) )+import qualified Distribution.Client.InstallPlan as InstallPlan+++import Distribution.Package+         ( PackageName, packageName, packageVersion )+import Distribution.Version+         ( VersionRange, thisVersion+         , unionVersionRanges, simplifyVersionRange )+import Distribution.PackageDescription+         ( FlagAssignment )+import Distribution.Client.Setup+         ( GlobalFlags, ConfigFlags(..), ConfigExFlags, InstallFlags+         , applyFlagDefaults )+import Distribution.Simple.Setup+         ( HaddockFlags, fromFlagOrDefault )+import Distribution.Simple.Utils+         ( die', notice )+import Distribution.Verbosity+         ( normal )++import Data.Monoid as Monoid+import qualified Data.Map as Map+import Data.Map (Map)+import Control.Monad (unless)++import Distribution.Simple.Command+         ( CommandUI(..), usageAlternatives )+import Distribution.Simple.Utils+         ( wrapText )+import qualified Distribution.Client.Setup as Client+++freezeCommand :: CommandUI (ConfigFlags, ConfigExFlags, InstallFlags, HaddockFlags)+freezeCommand = Client.installCommand {+  commandName         = "new-freeze",+  commandSynopsis     = "Freeze dependencies.",+  commandUsage        = usageAlternatives "new-freeze" [ "[FLAGS]" ],+  commandDescription  = Just $ \_ -> wrapText $+        "The project configuration is frozen so that it will be reproducible "+     ++ "in future.\n\n"++     ++ "The precise dependency configuration for the project is written to "+     ++ "the 'cabal.project.freeze' file (or '$project_file.freeze' if "+     ++ "'--project-file' is specified). This file extends the configuration "+     ++ "from the 'cabal.project' file and thus is used as the project "+     ++ "configuration for all other commands (such as 'new-build', "+     ++ "'new-repl' etc).\n\n"++     ++ "The freeze file can be kept in source control. To make small "+     ++ "adjustments it may be edited manually, or to make bigger changes "+     ++ "you may wish to delete the file and re-freeze. For more control, "+     ++ "one approach is to try variations using 'new-build --dry-run' with "+     ++ "solver flags such as '--constraint=\"pkg < 1.2\"' and once you have "+     ++ "a satisfactory solution to freeze it using the 'new-freeze' command "+     ++ "with the same set of flags.",+  commandNotes        = Just $ \pname ->+        "Examples:\n"+     ++ "  " ++ pname ++ " new-freeze\n"+     ++ "    Freeze the configuration of the current project\n\n"+     ++ "  " ++ pname ++ " new-build --dry-run --constraint=\"aeson < 1\"\n"+     ++ "    Check what a solution with the given constraints would look like\n"+     ++ "  " ++ pname ++ " new-freeze --constraint=\"aeson < 1\"\n"+     ++ "    Freeze a solution using the given constraints\n\n"++     ++ "Note: this command is part of the new project-based system (aka "+     ++ "nix-style\nlocal builds). These features are currently in beta. "+     ++ "Please see\n"+     ++ "http://cabal.readthedocs.io/en/latest/nix-local-build-overview.html "+     ++ "for\ndetails and advice on what you can expect to work. If you "+     ++ "encounter problems\nplease file issues at "+     ++ "https://github.com/haskell/cabal/issues and if you\nhave any time "+     ++ "to get involved and help with testing, fixing bugs etc then\nthat "+     ++ "is very much appreciated.\n"+   }++-- | To a first approximation, the @freeze@ command runs the first phase of+-- the @build@ command where we bring the install plan up to date, and then+-- based on the install plan we write out a @cabal.project.freeze@ config file.+--+-- For more details on how this works, see the module+-- "Distribution.Client.ProjectOrchestration"+--+freezeAction :: (ConfigFlags, ConfigExFlags, InstallFlags, HaddockFlags)+             -> [String] -> GlobalFlags -> IO ()+freezeAction (applyFlagDefaults -> (configFlags, configExFlags, installFlags, haddockFlags))+             extraArgs globalFlags = do++    unless (null extraArgs) $+      die' verbosity $ "'freeze' doesn't take any extra arguments: "+         ++ unwords extraArgs++    ProjectBaseContext {+      distDirLayout,+      cabalDirLayout,+      projectConfig,+      localPackages+    } <- establishProjectBaseContext verbosity cliConfig++    (_, elaboratedPlan, _) <-+      rebuildInstallPlan verbosity+                         distDirLayout cabalDirLayout+                         projectConfig+                         localPackages++    let freezeConfig = projectFreezeConfig elaboratedPlan+    writeProjectLocalFreezeConfig distDirLayout freezeConfig+    notice verbosity $+      "Wrote freeze file: " ++ distProjectFile distDirLayout "freeze"++  where+    verbosity = fromFlagOrDefault normal (configVerbosity configFlags)+    cliConfig = commandLineFlagsToProjectConfig+                  globalFlags configFlags configExFlags+                  installFlags haddockFlags++++-- | Given the install plan, produce a config value with constraints that+-- freezes the versions of packages used in the plan.+--+projectFreezeConfig :: ElaboratedInstallPlan -> ProjectConfig+projectFreezeConfig elaboratedPlan =+    Monoid.mempty {+      projectConfigShared = Monoid.mempty {+        projectConfigConstraints =+          concat (Map.elems (projectFreezeConstraints elaboratedPlan))+      }+    }++-- | Given the install plan, produce solver constraints that will ensure the+-- solver picks the same solution again in future in different environments.+--+projectFreezeConstraints :: ElaboratedInstallPlan+                         -> Map PackageName [(UserConstraint, ConstraintSource)]+projectFreezeConstraints plan =+    --+    -- TODO: [required eventually] this is currently an underapproximation+    -- since the constraints language is not expressive enough to specify the+    -- precise solution. See https://github.com/haskell/cabal/issues/3502.+    --+    -- For the moment we deal with multiple versions in the solution by using+    -- constraints that allow either version. Also, we do not include any+    -- /version/ constraints for packages that are local to the project (e.g.+    -- if the solution has two instances of Cabal, one from the local project+    -- and one pulled in as a setup deps then we exclude all constraints on+    -- Cabal, not just the constraint for the local instance since any+    -- constraint would apply to both instances). We do however keep flag+    -- constraints of local packages.+    --+    deleteLocalPackagesVersionConstraints+      (Map.unionWith (++) versionConstraints flagConstraints)+  where+    versionConstraints :: Map PackageName [(UserConstraint, ConstraintSource)]+    versionConstraints =+      Map.mapWithKey+        (\p v -> [(UserConstraint (UserQualified UserQualToplevel p) (PackagePropertyVersion v),+                   ConstraintSourceFreeze)])+        versionRanges++    versionRanges :: Map PackageName VersionRange+    versionRanges =+      Map.map simplifyVersionRange $+      Map.fromListWith unionVersionRanges $+          [ (packageName pkg, thisVersion (packageVersion pkg))+          | InstallPlan.PreExisting pkg <- InstallPlan.toList plan+          ]+       ++ [ (packageName pkg, thisVersion (packageVersion pkg))+          | InstallPlan.Configured pkg <- InstallPlan.toList plan+          ]++    flagConstraints :: Map PackageName [(UserConstraint, ConstraintSource)]+    flagConstraints =+      Map.mapWithKey+        (\p f -> [(UserConstraint (UserQualified UserQualToplevel p) (PackagePropertyFlags f),+                   ConstraintSourceFreeze)])+        flagAssignments++    flagAssignments :: Map PackageName FlagAssignment+    flagAssignments =+      Map.fromList+        [ (pkgname, flags)+        | InstallPlan.Configured elab <- InstallPlan.toList plan+        , let flags   = elabFlagAssignment elab+              pkgname = packageName elab+        , not (null flags) ]++    -- As described above, remove the version constraints on local packages,+    -- but leave any flag constraints.+    deleteLocalPackagesVersionConstraints+      :: Map PackageName [(UserConstraint, ConstraintSource)]+      -> Map PackageName [(UserConstraint, ConstraintSource)]+    deleteLocalPackagesVersionConstraints =+#if MIN_VERSION_containers(0,5,0)+      Map.mergeWithKey+        (\_pkgname () constraints ->+            case filter (not . isVersionConstraint . fst) constraints of+              []           -> Nothing+              constraints' -> Just constraints')+        (const Map.empty) id+        localPackages+#else+      Map.mapMaybeWithKey+        (\pkgname constraints ->+            if pkgname `Map.member` localPackages+              then case filter (not . isVersionConstraint . fst) constraints of+                     []           -> Nothing+                     constraints' -> Just constraints'+              else Just constraints)+#endif++    isVersionConstraint (UserConstraint _ (PackagePropertyVersion _)) = True+    isVersionConstraint _                                             = False++    localPackages :: Map PackageName ()+    localPackages =+      Map.fromList+        [ (packageName elab, ())+        | InstallPlan.Configured elab <- InstallPlan.toList plan+        , elabLocalToProject elab+        ]+
+ Distribution/Client/CmdHaddock.hs view
@@ -0,0 +1,200 @@+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE ViewPatterns   #-}++-- | cabal-install CLI command: haddock+--+module Distribution.Client.CmdHaddock (+    -- * The @haddock@ CLI and action+    haddockCommand,+    haddockAction,++    -- * Internals exposed for testing+    TargetProblem(..),+    selectPackageTargets,+    selectComponentTarget+  ) where++import Distribution.Client.ProjectOrchestration+import Distribution.Client.CmdErrorMessages++import Distribution.Client.Setup+         ( GlobalFlags, ConfigFlags(..), ConfigExFlags, InstallFlags+         , applyFlagDefaults )+import qualified Distribution.Client.Setup as Client+import Distribution.Simple.Setup+         ( HaddockFlags(..), fromFlagOrDefault, fromFlag )+import Distribution.Simple.Command+         ( CommandUI(..), usageAlternatives )+import Distribution.Verbosity+         ( Verbosity, normal )+import Distribution.Simple.Utils+         ( wrapText, die' )++import Control.Monad (when)+++haddockCommand :: CommandUI (ConfigFlags, ConfigExFlags, InstallFlags+                            ,HaddockFlags)+haddockCommand = Client.installCommand {+  commandName         = "new-haddock",+  commandSynopsis     = "Build Haddock documentation",+  commandUsage        = usageAlternatives "new-haddock" [ "[FLAGS] TARGET" ],+  commandDescription  = Just $ \_ -> wrapText $+        "Build Haddock documentation for the specified packages within the "+     ++ "project.\n\n"++     ++ "Any package in the project can be specified. If no package is "+     ++ "specified, the default is to build the documentation for the package "+     ++ "in the current directory. The default behaviour is to build "+     ++ "documentation for the exposed modules of the library component (if "+     ++ "any). This can be changed with the '--internal', '--executables', "+     ++ "'--tests', '--benchmarks' or '--all' flags.\n\n"++     ++ "Currently, documentation for dependencies is NOT built. This "+     ++ "behavior may change in future.\n\n"++     ++ "Additional configuration flags can be specified on the command line "+     ++ "and these extend the project configuration from the 'cabal.project', "+     ++ "'cabal.project.local' and other files.",+  commandNotes        = Just $ \pname ->+        "Examples:\n"+     ++ "  " ++ pname ++ " new-haddock pkgname"+     ++ "    Build documentation for the package named pkgname\n\n"++     ++ cmdCommonHelpTextNewBuildBeta+   }+   --TODO: [nice to have] support haddock on specific components, not just+   -- whole packages and the silly --executables etc modifiers.++-- | The @haddock@ command is TODO.+--+-- For more details on how this works, see the module+-- "Distribution.Client.ProjectOrchestration"+--+haddockAction :: (ConfigFlags, ConfigExFlags, InstallFlags, HaddockFlags)+                 -> [String] -> GlobalFlags -> IO ()+haddockAction (applyFlagDefaults -> (configFlags, configExFlags, installFlags, haddockFlags))+                targetStrings globalFlags = do++    baseCtx <- establishProjectBaseContext verbosity cliConfig++    targetSelectors <- either (reportTargetSelectorProblems verbosity) return+                   =<< readTargetSelectors (localPackages baseCtx) targetStrings++    buildCtx <-+      runProjectPreBuildPhase verbosity baseCtx $ \elaboratedPlan -> do++            when (buildSettingOnlyDeps (buildSettings baseCtx)) $+              die' verbosity+                "The haddock command does not support '--only-dependencies'."++              -- When we interpret the targets on the command line, interpret them as+              -- haddock targets+            targets <- either (reportTargetProblems verbosity) return+                     $ resolveTargets+                         (selectPackageTargets haddockFlags)+                         selectComponentTarget+                         TargetProblemCommon+                         elaboratedPlan+                         targetSelectors++            let elaboratedPlan' = pruneInstallPlanToTargets+                                    TargetActionHaddock+                                    targets+                                    elaboratedPlan+            return elaboratedPlan'++    printPlan verbosity baseCtx buildCtx++    buildOutcomes <- runProjectBuildPhase verbosity baseCtx buildCtx+    runProjectPostBuildPhase verbosity baseCtx buildCtx buildOutcomes+  where+    verbosity = fromFlagOrDefault normal (configVerbosity configFlags)+    cliConfig = commandLineFlagsToProjectConfig+                  globalFlags configFlags configExFlags+                  installFlags haddockFlags++-- | This defines what a 'TargetSelector' means for the @haddock@ command.+-- It selects the 'AvailableTarget's that the 'TargetSelector' refers to,+-- or otherwise classifies the problem.+--+-- For the @haddock@ command we select all buildable libraries. Additionally,+-- depending on the @--executables@ flag we also select all the buildable exes.+-- We do similarly for test-suites, benchmarks and foreign libs.+--+selectPackageTargets  :: HaddockFlags -> TargetSelector PackageId+                      -> [AvailableTarget k] -> Either TargetProblem [k]+selectPackageTargets haddockFlags targetSelector targets++    -- If there are any buildable targets then we select those+  | not (null targetsBuildable)+  = Right targetsBuildable++    -- If there are targets but none are buildable then we report those+  | not (null targets)+  = Left (TargetProblemNoneEnabled targetSelector targets')++    -- If there are no targets at all then we report that+  | otherwise+  = Left (TargetProblemNoTargets targetSelector)+  where+    targets'         = forgetTargetsDetail    (map disableNotRequested targets)+    targetsBuildable = selectBuildableTargets (map disableNotRequested targets)++    -- When there's a target filter like "pkg:exes" then we do select exes,+    -- but if it's just a target like "pkg" then we don't build docs for exes+    -- unless they are requested by default (i.e. by using --executables)+    disableNotRequested t@(AvailableTarget _ cname (TargetBuildable _ _) _)+      | not (isRequested targetSelector (componentKind cname))+      = t { availableTargetStatus = TargetDisabledByUser }+    disableNotRequested t = t++    isRequested (TargetPackage _ _ (Just _)) _ = True+    isRequested (TargetAllPackages (Just _)) _ = True+    isRequested _ LibKind    = True+--  isRequested _ SubLibKind = True --TODO: what about sublibs?+    isRequested _ FLibKind   = fromFlag (haddockForeignLibs haddockFlags)+    isRequested _ ExeKind    = fromFlag (haddockExecutables haddockFlags)+    isRequested _ TestKind   = fromFlag (haddockTestSuites  haddockFlags)+    isRequested _ BenchKind  = fromFlag (haddockBenchmarks  haddockFlags)+++-- | For a 'TargetComponent' 'TargetSelector', check if the component can be+-- selected.+--+-- For the @haddock@ command we just need the basic checks on being buildable+-- etc.+--+selectComponentTarget :: PackageId -> ComponentName -> SubComponentTarget+                      -> AvailableTarget k -> Either TargetProblem k+selectComponentTarget pkgid cname subtarget =+    either (Left . TargetProblemCommon) Right+  . selectComponentTargetBasic pkgid cname subtarget+++-- | The various error conditions that can occur when matching a+-- 'TargetSelector' against 'AvailableTarget's for the @haddock@ command.+--+data TargetProblem =+     TargetProblemCommon       TargetProblemCommon++     -- | The 'TargetSelector' matches targets but none are buildable+   | TargetProblemNoneEnabled (TargetSelector PackageId) [AvailableTarget ()]++     -- | There are no targets at all+   | TargetProblemNoTargets   (TargetSelector PackageId)+  deriving (Eq, Show)++reportTargetProblems :: Verbosity -> [TargetProblem] -> IO a+reportTargetProblems verbosity =+    die' verbosity . unlines . map renderTargetProblem++renderTargetProblem :: TargetProblem -> String+renderTargetProblem (TargetProblemCommon problem) =+    renderTargetProblemCommon "build documentation for" problem++renderTargetProblem (TargetProblemNoneEnabled targetSelector targets) =+    renderTargetProblemNoneEnabled "build documentation for" targetSelector targets++renderTargetProblem(TargetProblemNoTargets targetSelector) =+    renderTargetProblemNoTargets "build documentation for" targetSelector
Distribution/Client/CmdRepl.hs view
@@ -1,32 +1,81 @@ {-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE ViewPatterns   #-}  -- | cabal-install CLI command: repl -- module Distribution.Client.CmdRepl (+    -- * The @repl@ CLI and action+    replCommand,     replAction,++    -- * Internals exposed for testing+    TargetProblem(..),+    selectPackageTargets,+    selectComponentTarget   ) where  import Distribution.Client.ProjectOrchestration-         ( PreBuildHooks(..), runProjectPreBuildPhase, selectTargets-         , ProjectBuildContext(..), runProjectBuildPhase-         , printPlan, reportBuildFailures )-import Distribution.Client.ProjectConfig-         ( BuildTimeSettings(..) )-import Distribution.Client.ProjectPlanning-         ( PackageTarget(..) )-import Distribution.Client.BuildTarget-         ( readUserBuildTargets )+import Distribution.Client.CmdErrorMessages  import Distribution.Client.Setup-         ( GlobalFlags, ConfigFlags(..), ConfigExFlags, InstallFlags )+         ( GlobalFlags, ConfigFlags(..), ConfigExFlags, InstallFlags+         , applyFlagDefaults )+import qualified Distribution.Client.Setup as Client import Distribution.Simple.Setup          ( HaddockFlags, fromFlagOrDefault )+import Distribution.Simple.Command+         ( CommandUI(..), usageAlternatives )+import Distribution.Package+         ( packageName )+import Distribution.Types.ComponentName+         ( componentNameString )+import Distribution.Text+         ( display ) import Distribution.Verbosity-         ( normal )+         ( Verbosity, normal )+import Distribution.Simple.Utils+         ( wrapText, die', ordNub ) -import Control.Monad (unless)+import qualified Data.Map as Map+import qualified Data.Set as Set+import Control.Monad (when)  +replCommand :: CommandUI (ConfigFlags, ConfigExFlags, InstallFlags, HaddockFlags)+replCommand = Client.installCommand {+  commandName         = "new-repl",+  commandSynopsis     = "Open an interactive session for the given component.",+  commandUsage        = usageAlternatives "new-repl" [ "[TARGET] [FLAGS]" ],+  commandDescription  = Just $ \_ -> wrapText $+        "Open an interactive session for a component within the project. The "+     ++ "available targets are the same as for the 'new-build' command: "+     ++ "individual components within packages in the project, including "+     ++ "libraries, executables, test-suites or benchmarks. Packages can "+     ++ "also be specified in which case the library component in the "+     ++ "package will be used, or the (first listed) executable in the "+     ++ "package if there is no library.\n\n"++     ++ "Dependencies are built or rebuilt as necessary. Additional "+     ++ "configuration flags can be specified on the command line and these "+     ++ "extend the project configuration from the 'cabal.project', "+     ++ "'cabal.project.local' and other files.",+  commandNotes        = Just $ \pname ->+        "Examples, open an interactive session:\n"+     ++ "  " ++ pname ++ " new-repl\n"+     ++ "    for the default component in the package in the current directory\n"+     ++ "  " ++ pname ++ " new-repl pkgname\n"+     ++ "    for the default component in the package named 'pkgname'\n"+     ++ "  " ++ pname ++ " new-repl ./pkgfoo\n"+     ++ "    for the default component in the package in the ./pkgfoo directory\n"+     ++ "  " ++ pname ++ " new-repl cname\n"+     ++ "    for the component named 'cname'\n"+     ++ "  " ++ pname ++ " new-repl pkgname:cname\n"+     ++ "    for the component 'cname' in the package 'pkgname'\n\n"++     ++ cmdCommonHelpTextNewBuildBeta+   }++ -- | The @repl@ command is very much like @build@. It brings the install plan -- up to date, selects that part of the plan needed by the given or implicit -- repl target and then executes the plan.@@ -39,36 +88,205 @@ -- "Distribution.Client.ProjectOrchestration" -- replAction :: (ConfigFlags, ConfigExFlags, InstallFlags, HaddockFlags)-              -> [String] -> GlobalFlags -> IO ()-replAction (configFlags, configExFlags, installFlags, haddockFlags)+           -> [String] -> GlobalFlags -> IO ()+replAction (applyFlagDefaults -> (configFlags, configExFlags, installFlags, haddockFlags))            targetStrings globalFlags = do -    userTargets <- readUserBuildTargets targetStrings+    baseCtx <- establishProjectBaseContext verbosity cliConfig -    buildCtx@ProjectBuildContext{buildSettings} <--      runProjectPreBuildPhase-        verbosity-        ( globalFlags, configFlags, configExFlags-        , installFlags, haddockFlags )-        PreBuildHooks {-          hookPrePlanning      = \_ _ _ -> return (),-          hookSelectPlanSubset = selectReplTargets userTargets-        }+    targetSelectors <- either (reportTargetSelectorProblems verbosity) return+                   =<< readTargetSelectors (localPackages baseCtx) targetStrings -    printPlan verbosity buildCtx+    buildCtx <-+      runProjectPreBuildPhase verbosity baseCtx $ \elaboratedPlan -> do -    unless (buildSettingDryRun buildSettings) $ do-      plan <- runProjectBuildPhase-                verbosity-                buildCtx-      reportBuildFailures plan+            when (buildSettingOnlyDeps (buildSettings baseCtx)) $+              die' verbosity $ "The repl command does not support '--only-dependencies'. "+                 ++ "You may wish to use 'build --only-dependencies' and then "+                 ++ "use 'repl'."++            -- Interpret the targets on the command line as repl targets+            -- (as opposed to say build or haddock targets).+            targets <- either (reportTargetProblems verbosity) return+                     $ resolveTargets+                         selectPackageTargets+                         selectComponentTarget+                         TargetProblemCommon+                         elaboratedPlan+                         targetSelectors++            -- Reject multiple targets, or at least targets in different+            -- components. It is ok to have two module/file targets in the+            -- same component, but not two that live in different components.+            when (Set.size (distinctTargetComponents targets) > 1) $+              reportTargetProblems verbosity+                [TargetProblemMultipleTargets targets]++            let elaboratedPlan' = pruneInstallPlanToTargets+                                    TargetActionRepl+                                    targets+                                    elaboratedPlan+            return elaboratedPlan'++    printPlan verbosity baseCtx buildCtx++    buildOutcomes <- runProjectBuildPhase verbosity baseCtx buildCtx+    runProjectPostBuildPhase verbosity baseCtx buildCtx buildOutcomes   where     verbosity = fromFlagOrDefault normal (configVerbosity configFlags)+    cliConfig = commandLineFlagsToProjectConfig+                  globalFlags configFlags configExFlags+                  installFlags haddockFlags -    -- When we interpret the targets on the command line, interpret them as-    -- repl targets (as opposed to say build or haddock targets).-    selectReplTargets =-      selectTargets-        ReplDefaultComponent-        ReplSpecificComponent+-- | This defines what a 'TargetSelector' means for the @repl@ command.+-- It selects the 'AvailableTarget's that the 'TargetSelector' refers to,+-- or otherwise classifies the problem.+--+-- For repl we select:+--+-- * the library if there is only one and it's buildable; or+--+-- * the exe if there is only one and it's buildable; or+--+-- * any other buildable component.+--+-- Fail if there are no buildable lib\/exe components, or if there are+-- multiple libs or exes.+--+selectPackageTargets  :: TargetSelector PackageId+                      -> [AvailableTarget k] -> Either TargetProblem [k]+selectPackageTargets targetSelector targets++    -- If there is exactly one buildable library then we select that+  | [target] <- targetsLibsBuildable+  = Right [target]++    -- but fail if there are multiple buildable libraries.+  | not (null targetsLibsBuildable)+  = Left (TargetProblemMatchesMultiple targetSelector targetsLibsBuildable')++    -- If there is exactly one buildable executable then we select that+  | [target] <- targetsExesBuildable+  = Right [target]++    -- but fail if there are multiple buildable executables.+  | not (null targetsExesBuildable)+  = Left (TargetProblemMatchesMultiple targetSelector targetsExesBuildable')++    -- If there is exactly one other target then we select that+  | [target] <- targetsBuildable+  = Right [target]++    -- but fail if there are multiple such targets+  | not (null targetsBuildable)+  = Left (TargetProblemMatchesMultiple targetSelector targetsBuildable')++    -- If there are targets but none are buildable then we report those+  | not (null targets)+  = Left (TargetProblemNoneEnabled targetSelector targets')++    -- If there are no targets at all then we report that+  | otherwise+  = Left (TargetProblemNoTargets targetSelector)+  where+    targets'                = forgetTargetsDetail targets+    (targetsLibsBuildable,+     targetsLibsBuildable') = selectBuildableTargets'+                            . filterTargetsKind LibKind+                            $ targets+    (targetsExesBuildable,+     targetsExesBuildable') = selectBuildableTargets'+                            . filterTargetsKind ExeKind+                            $ targets+    (targetsBuildable,+     targetsBuildable')     = selectBuildableTargetsWith'+                                (isRequested targetSelector) targets++    -- When there's a target filter like "pkg:tests" then we do select tests,+    -- but if it's just a target like "pkg" then we don't build tests unless+    -- they are requested by default (i.e. by using --enable-tests)+    isRequested (TargetAllPackages  Nothing) TargetNotRequestedByDefault = False+    isRequested (TargetPackage _ _  Nothing) TargetNotRequestedByDefault = False+    isRequested _ _ = True+++-- | For a 'TargetComponent' 'TargetSelector', check if the component can be+-- selected.+--+-- For the @repl@ command we just need the basic checks on being buildable etc.+--+selectComponentTarget :: PackageId -> ComponentName -> SubComponentTarget+                      -> AvailableTarget k -> Either TargetProblem k+selectComponentTarget pkgid cname subtarget =+    either (Left . TargetProblemCommon) Right+  . selectComponentTargetBasic pkgid cname subtarget+++-- | The various error conditions that can occur when matching a+-- 'TargetSelector' against 'AvailableTarget's for the @repl@ command.+--+data TargetProblem =+     TargetProblemCommon       TargetProblemCommon++     -- | The 'TargetSelector' matches targets but none are buildable+   | TargetProblemNoneEnabled (TargetSelector PackageId) [AvailableTarget ()]++     -- | There are no targets at all+   | TargetProblemNoTargets   (TargetSelector PackageId)++     -- | A single 'TargetSelector' matches multiple targets+   | TargetProblemMatchesMultiple (TargetSelector PackageId) [AvailableTarget ()]++     -- | Multiple 'TargetSelector's match multiple targets+   | TargetProblemMultipleTargets TargetsMap+  deriving (Eq, Show)++reportTargetProblems :: Verbosity -> [TargetProblem] -> IO a+reportTargetProblems verbosity =+    die' verbosity . unlines . map renderTargetProblem++renderTargetProblem :: TargetProblem -> String+renderTargetProblem (TargetProblemCommon problem) =+    renderTargetProblemCommon "open a repl for" problem++renderTargetProblem (TargetProblemMatchesMultiple targetSelector targets) =+    "Cannot open a repl for multiple components at once. The target '"+ ++ showTargetSelector targetSelector ++ "' refers to "+ ++ renderTargetSelector targetSelector ++ " which "+ ++ (if targetSelectorRefersToPkgs targetSelector then "includes " else "are ")+ ++ renderListSemiAnd+      [ "the " ++ renderComponentKind Plural ckind ++ " " +++        renderListCommaAnd+          [ maybe (display pkgname) display (componentNameString cname)+          | t <- ts+          , let cname   = availableTargetComponentName t+                pkgname = packageName (availableTargetPackageId t)+          ]+      | (ckind, ts) <- sortGroupOn availableTargetComponentKind targets+      ]+ ++ ".\n\n" ++ explanationSingleComponentLimitation+  where+    availableTargetComponentKind = componentKind+                                 . availableTargetComponentName++renderTargetProblem (TargetProblemMultipleTargets selectorMap) =+    "Cannot open a repl for multiple components at once. The targets "+ ++ renderListCommaAnd+      [ "'" ++ showTargetSelector ts ++ "'"+      | ts <- ordNub (concatMap snd (concat (Map.elems selectorMap))) ]+ ++ " refer to different components."+ ++ ".\n\n" ++ explanationSingleComponentLimitation++renderTargetProblem (TargetProblemNoneEnabled targetSelector targets) =+    renderTargetProblemNoneEnabled "open a repl for" targetSelector targets++renderTargetProblem (TargetProblemNoTargets targetSelector) =+    renderTargetProblemNoTargets "open a repl for" targetSelector+++explanationSingleComponentLimitation :: String+explanationSingleComponentLimitation =+    "The reason for this limitation is that current versions of ghci do not "+ ++ "support loading multiple components as source. Load just one component "+ ++ "and when you make changes to a dependent component then quit and reload." 
+ Distribution/Client/CmdRun.hs view
@@ -0,0 +1,289 @@+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE ViewPatterns   #-}++-- | cabal-install CLI command: run+--+module Distribution.Client.CmdRun (+    -- * The @run@ CLI and action+    runCommand,+    runAction,++    -- * Internals exposed for testing+    TargetProblem(..),+    selectPackageTargets,+    selectComponentTarget+  ) where++import Distribution.Client.ProjectOrchestration+import Distribution.Client.CmdErrorMessages++import Distribution.Client.Setup+         ( GlobalFlags, ConfigFlags(..), ConfigExFlags, InstallFlags+         , applyFlagDefaults )+import qualified Distribution.Client.Setup as Client+import Distribution.Simple.Setup+         ( HaddockFlags, fromFlagOrDefault )+import Distribution.Simple.Command+         ( CommandUI(..), usageAlternatives )+import Distribution.Types.ComponentName+         ( componentNameString )+import Distribution.Text+         ( display )+import Distribution.Verbosity+         ( Verbosity, normal )+import Distribution.Simple.Utils+         ( wrapText, die', ordNub )++import qualified Data.Map as Map+import qualified Data.Set as Set+import Control.Monad (when)+++runCommand :: CommandUI (ConfigFlags, ConfigExFlags, InstallFlags, HaddockFlags)+runCommand = Client.installCommand {+  commandName         = "new-run",+  commandSynopsis     = "Run an executable.",+  commandUsage        = usageAlternatives "new-run"+                          [ "[TARGET] [FLAGS] [-- EXECUTABLE_FLAGS]" ],+  commandDescription  = Just $ \pname -> wrapText $+        "Runs the specified executable, first ensuring it is up to date.\n\n"++     ++ "Any executable in any package in the project can be specified. "+     ++ "A package can be specified if contains just one executable. "+     ++ "The default is to use the package in the current directory if it "+     ++ "contains just one executable.\n\n"++     ++ "Extra arguments can be passed to the program, but use '--' to "+     ++ "separate arguments for the program from arguments for " ++ pname+     ++ ". The executable is run in an environment where it can find its "+     ++ "data files inplace in the build tree.\n\n"++     ++ "Dependencies are built or rebuilt as necessary. Additional "+     ++ "configuration flags can be specified on the command line and these "+     ++ "extend the project configuration from the 'cabal.project', "+     ++ "'cabal.project.local' and other files.",+  commandNotes        = Just $ \pname ->+        "Examples:\n"+     ++ "  " ++ pname ++ " new-run\n"+     ++ "    Run the executable in the package in the current directory\n"+     ++ "  " ++ pname ++ " new-run foo-tool\n"+     ++ "    Run the named executable (in any package in the project)\n"+     ++ "  " ++ pname ++ " new-run pkgfoo:foo-tool\n"+     ++ "    Run the executable 'foo-tool' in the package 'pkgfoo'\n"+     ++ "  " ++ pname ++ " new-run foo -O2 -- dothing --fooflag\n"+     ++ "    Build with '-O2' and run the program, passing it extra arguments.\n\n"++     ++ cmdCommonHelpTextNewBuildBeta+   }+++-- | The @build@ command does a lot. It brings the install plan up to date,+-- selects that part of the plan needed by the given or implicit targets and+-- then executes the plan.+--+-- For more details on how this works, see the module+-- "Distribution.Client.ProjectOrchestration"+--+runAction :: (ConfigFlags, ConfigExFlags, InstallFlags, HaddockFlags)+          -> [String] -> GlobalFlags -> IO ()+runAction (applyFlagDefaults -> (configFlags, configExFlags, installFlags, haddockFlags))+            targetStrings globalFlags = do++    baseCtx <- establishProjectBaseContext verbosity cliConfig++    targetSelectors <- either (reportTargetSelectorProblems verbosity) return+                   =<< readTargetSelectors (localPackages baseCtx) targetStrings++    buildCtx <-+      runProjectPreBuildPhase verbosity baseCtx $ \elaboratedPlan -> do++            when (buildSettingOnlyDeps (buildSettings baseCtx)) $+              die' verbosity $+                  "The run command does not support '--only-dependencies'. "+               ++ "You may wish to use 'build --only-dependencies' and then "+               ++ "use 'run'."++            -- Interpret the targets on the command line as build targets+            -- (as opposed to say repl or haddock targets).+            targets <- either (reportTargetProblems verbosity) return+                     $ resolveTargets+                         selectPackageTargets+                         selectComponentTarget+                         TargetProblemCommon+                         elaboratedPlan+                         targetSelectors++            -- Reject multiple targets, or at least targets in different+            -- components. It is ok to have two module/file targets in the+            -- same component, but not two that live in different components.+            when (Set.size (distinctTargetComponents targets) > 1) $+              reportTargetProblems verbosity+                [TargetProblemMultipleTargets targets]++            let elaboratedPlan' = pruneInstallPlanToTargets+                                    TargetActionBuild+                                    targets+                                    elaboratedPlan+            return elaboratedPlan'++    printPlan verbosity baseCtx buildCtx++    buildOutcomes <- runProjectBuildPhase verbosity baseCtx buildCtx+    runProjectPostBuildPhase verbosity baseCtx buildCtx buildOutcomes+  where+    verbosity = fromFlagOrDefault normal (configVerbosity configFlags)+    cliConfig = commandLineFlagsToProjectConfig+                  globalFlags configFlags configExFlags+                  installFlags haddockFlags++-- | This defines what a 'TargetSelector' means for the @run@ command.+-- It selects the 'AvailableTarget's that the 'TargetSelector' refers to,+-- or otherwise classifies the problem.+--+-- For the @run@ command we select the exe if there is only one and it's+-- buildable. Fail if there are no or multiple buildable exe components.+--+selectPackageTargets :: TargetSelector PackageId+                     -> [AvailableTarget k] -> Either TargetProblem [k]+selectPackageTargets targetSelector targets++    -- If there is exactly one buildable executable then we select that+  | [target] <- targetsExesBuildable+  = Right [target]++    -- but fail if there are multiple buildable executables.+  | not (null targetsExesBuildable)+  = Left (TargetProblemMatchesMultiple targetSelector targetsExesBuildable')++    -- If there are executables but none are buildable then we report those+  | not (null targetsExes)+  = Left (TargetProblemNoneEnabled targetSelector targetsExes)++    -- If there are no executables but some other targets then we report that+  | not (null targets)+  = Left (TargetProblemNoExes targetSelector)++    -- If there are no targets at all then we report that+  | otherwise+  = Left (TargetProblemNoTargets targetSelector)+  where+    (targetsExesBuildable,+     targetsExesBuildable') = selectBuildableTargets'+                            . filterTargetsKind ExeKind+                            $ targets++    targetsExes             = forgetTargetsDetail+                            . filterTargetsKind ExeKind+                            $ targets+++-- | For a 'TargetComponent' 'TargetSelector', check if the component can be+-- selected.+--+-- For the @run@ command we just need to check it is a executable, in addition+-- to the basic checks on being buildable etc.+--+selectComponentTarget :: PackageId -> ComponentName -> SubComponentTarget+                      -> AvailableTarget k -> Either TargetProblem  k+selectComponentTarget pkgid cname subtarget@WholeComponent t+  | CExeName _ <- availableTargetComponentName t+  = either (Left . TargetProblemCommon) return $+           selectComponentTargetBasic pkgid cname subtarget t+  | otherwise+  = Left (TargetProblemComponentNotExe pkgid cname)++selectComponentTarget pkgid cname subtarget _+  = Left (TargetProblemIsSubComponent pkgid cname subtarget)++-- | The various error conditions that can occur when matching a+-- 'TargetSelector' against 'AvailableTarget's for the @run@ command.+--+data TargetProblem =+     TargetProblemCommon       TargetProblemCommon+     -- | The 'TargetSelector' matches targets but none are buildable+   | TargetProblemNoneEnabled (TargetSelector PackageId) [AvailableTarget ()]++     -- | There are no targets at all+   | TargetProblemNoTargets   (TargetSelector PackageId)++     -- | The 'TargetSelector' matches targets but no executables+   | TargetProblemNoExes      (TargetSelector PackageId)++     -- | A single 'TargetSelector' matches multiple targets+   | TargetProblemMatchesMultiple (TargetSelector PackageId) [AvailableTarget ()]++     -- | Multiple 'TargetSelector's match multiple targets+   | TargetProblemMultipleTargets TargetsMap++     -- | The 'TargetSelector' refers to a component that is not an executable+   | TargetProblemComponentNotExe PackageId ComponentName++     -- | Asking to run an individual file or module is not supported+   | TargetProblemIsSubComponent  PackageId ComponentName SubComponentTarget+  deriving (Eq, Show)++reportTargetProblems :: Verbosity -> [TargetProblem] -> IO a+reportTargetProblems verbosity =+    die' verbosity . unlines . map renderTargetProblem++renderTargetProblem :: TargetProblem -> String+renderTargetProblem (TargetProblemCommon problem) =+    renderTargetProblemCommon "run" problem++renderTargetProblem (TargetProblemNoneEnabled targetSelector targets) =+    renderTargetProblemNoneEnabled "run" targetSelector targets++renderTargetProblem (TargetProblemNoExes targetSelector) =+    "Cannot run the target '" ++ showTargetSelector targetSelector+ ++ "' which refers to " ++ renderTargetSelector targetSelector+ ++ " because "+ ++ plural (targetSelectorPluralPkgs targetSelector) "it does" "they do"+ ++ " not contain any executables."++renderTargetProblem (TargetProblemNoTargets targetSelector) =+    case targetSelectorFilter targetSelector of+      Just kind | kind /= ExeKind+        -> "The run command is for running executables, but the target '"+           ++ showTargetSelector targetSelector ++ "' refers to "+           ++ renderTargetSelector targetSelector ++ "."++      _ -> renderTargetProblemNoTargets "run" targetSelector+  where+    targetSelectorFilter (TargetPackage  _ _ mkfilter) = mkfilter+    targetSelectorFilter (TargetAllPackages  mkfilter) = mkfilter+    targetSelectorFilter (TargetComponent _ _ _)       = Nothing+++renderTargetProblem (TargetProblemMatchesMultiple targetSelector targets) =+    "The run command is for running a single executable at once. The target '"+ ++ showTargetSelector targetSelector ++ "' refers to "+ ++ renderTargetSelector targetSelector ++ " which includes the executables "+ ++ renderListCommaAnd+      [ display name+      | cname@CExeName{} <- map availableTargetComponentName targets+      , let Just name = componentNameString cname+      ]+ ++ "."++renderTargetProblem (TargetProblemMultipleTargets selectorMap) =+    "The run command is for running a single executable at once. The targets "+ ++ renderListCommaAnd [ "'" ++ showTargetSelector ts ++ "'"+                       | ts <- ordNub (concatMap snd (concat (Map.elems selectorMap))) ]+ ++ " refer to different executables."++renderTargetProblem (TargetProblemComponentNotExe pkgid cname) =+    "The run command is for running executables, but the target '"+ ++ showTargetSelector targetSelector ++ "' refers to "+ ++ renderTargetSelector targetSelector ++ " from the package "+ ++ display pkgid ++ "."+  where+    targetSelector = TargetComponent pkgid cname WholeComponent++renderTargetProblem (TargetProblemIsSubComponent pkgid cname subtarget) =+    "The run command can only run an executable as a whole, "+ ++ "not files or modules within them, but the target '"+ ++ showTargetSelector targetSelector ++ "' refers to "+ ++ renderTargetSelector targetSelector ++ "."+  where+    targetSelector = TargetComponent pkgid cname subtarget+
+ Distribution/Client/CmdTest.hs view
@@ -0,0 +1,247 @@+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE ViewPatterns   #-}++-- | cabal-install CLI command: test+--+module Distribution.Client.CmdTest (+    -- * The @test@ CLI and action+    testCommand,+    testAction,++    -- * Internals exposed for testing+    TargetProblem(..),+    selectPackageTargets,+    selectComponentTarget+  ) where++import Distribution.Client.ProjectOrchestration+import Distribution.Client.CmdErrorMessages++import Distribution.Client.Setup+         ( GlobalFlags, ConfigFlags(..), ConfigExFlags, InstallFlags+         , applyFlagDefaults )+import qualified Distribution.Client.Setup as Client+import Distribution.Simple.Setup+         ( HaddockFlags, fromFlagOrDefault )+import Distribution.Simple.Command+         ( CommandUI(..), usageAlternatives )+import Distribution.Text+         ( display )+import Distribution.Verbosity+         ( Verbosity, normal )+import Distribution.Simple.Utils+         ( wrapText, die' )++import Control.Monad (when)+++testCommand :: CommandUI (ConfigFlags, ConfigExFlags, InstallFlags, HaddockFlags)+testCommand = Client.installCommand {+  commandName         = "new-test",+  commandSynopsis     = "Run test-suites",+  commandUsage        = usageAlternatives "new-test" [ "[TARGETS] [FLAGS]" ],+  commandDescription  = Just $ \_ -> wrapText $+        "Runs the specified test-suites, first ensuring they are up to "+     ++ "date.\n\n"++     ++ "Any test-suite in any package in the project can be specified. "+     ++ "A package can be specified in which case all the test-suites in the "+     ++ "package are run. The default is to run all the test-suites in the "+     ++ "package in the current directory.\n\n"++     ++ "Dependencies are built or rebuilt as necessary. Additional "+     ++ "configuration flags can be specified on the command line and these "+     ++ "extend the project configuration from the 'cabal.project', "+     ++ "'cabal.project.local' and other files.",+  commandNotes        = Just $ \pname ->+        "Examples:\n"+     ++ "  " ++ pname ++ " new-test\n"+     ++ "    Run all the test-suites in the package in the current directory\n"+     ++ "  " ++ pname ++ " new-test pkgname\n"+     ++ "    Run all the test-suites in the package named pkgname\n"+     ++ "  " ++ pname ++ " new-test cname\n"+     ++ "    Run the test-suite named cname\n"+     ++ "  " ++ pname ++ " new-test cname --enable-coverage\n"+     ++ "    Run the test-suite built with code coverage (including local libs used)\n\n"++     ++ cmdCommonHelpTextNewBuildBeta+   }+++-- | The @test@ command is very much like @build@. It brings the install plan+-- up to date, selects that part of the plan needed by the given or implicit+-- test target(s) and then executes the plan.+--+-- Compared to @build@ the difference is that there's also test targets+-- which are ephemeral.+--+-- For more details on how this works, see the module+-- "Distribution.Client.ProjectOrchestration"+--+testAction :: (ConfigFlags, ConfigExFlags, InstallFlags, HaddockFlags)+           -> [String] -> GlobalFlags -> IO ()+testAction (applyFlagDefaults -> (configFlags, configExFlags, installFlags, haddockFlags))+           targetStrings globalFlags = do++    baseCtx <- establishProjectBaseContext verbosity cliConfig++    targetSelectors <- either (reportTargetSelectorProblems verbosity) return+                   =<< readTargetSelectors (localPackages baseCtx) targetStrings++    buildCtx <-+      runProjectPreBuildPhase verbosity baseCtx $ \elaboratedPlan -> do++            when (buildSettingOnlyDeps (buildSettings baseCtx)) $+              die' verbosity $+                  "The test command does not support '--only-dependencies'. "+               ++ "You may wish to use 'build --only-dependencies' and then "+               ++ "use 'test'."++            -- Interpret the targets on the command line as test targets+            -- (as opposed to say build or haddock targets).+            targets <- either (reportTargetProblems verbosity) return+                     $ resolveTargets+                         selectPackageTargets+                         selectComponentTarget+                         TargetProblemCommon+                         elaboratedPlan+                         targetSelectors++            let elaboratedPlan' = pruneInstallPlanToTargets+                                    TargetActionTest+                                    targets+                                    elaboratedPlan+            return elaboratedPlan'++    printPlan verbosity baseCtx buildCtx++    buildOutcomes <- runProjectBuildPhase verbosity baseCtx buildCtx+    runProjectPostBuildPhase verbosity baseCtx buildCtx buildOutcomes+  where+    verbosity = fromFlagOrDefault normal (configVerbosity configFlags)+    cliConfig = commandLineFlagsToProjectConfig+                  globalFlags configFlags configExFlags+                  installFlags haddockFlags++-- | This defines what a 'TargetSelector' means for the @test@ command.+-- It selects the 'AvailableTarget's that the 'TargetSelector' refers to,+-- or otherwise classifies the problem.+--+-- For the @test@ command we select all buildable test-suites,+-- or fail if there are no test-suites or no buildable test-suites.+--+selectPackageTargets  :: TargetSelector PackageId+                      -> [AvailableTarget k] -> Either TargetProblem [k]+selectPackageTargets targetSelector targets++    -- If there are any buildable test-suite targets then we select those+  | not (null targetsTestsBuildable)+  = Right targetsTestsBuildable++    -- If there are test-suites but none are buildable then we report those+  | not (null targetsTests)+  = Left (TargetProblemNoneEnabled targetSelector targetsTests)++    -- If there are no test-suite but some other targets then we report that+  | not (null targets)+  = Left (TargetProblemNoTests targetSelector)++    -- If there are no targets at all then we report that+  | otherwise+  = Left (TargetProblemNoTargets targetSelector)+  where+    targetsTestsBuildable = selectBuildableTargets+                          . filterTargetsKind TestKind+                          $ targets++    targetsTests          = forgetTargetsDetail+                          . filterTargetsKind TestKind+                          $ targets+++-- | For a 'TargetComponent' 'TargetSelector', check if the component can be+-- selected.+--+-- For the @test@ command we just need to check it is a test-suite, in addition+-- to the basic checks on being buildable etc.+--+selectComponentTarget :: PackageId -> ComponentName -> SubComponentTarget+                      -> AvailableTarget k -> Either TargetProblem k+selectComponentTarget pkgid cname subtarget@WholeComponent t+  | CTestName _ <- availableTargetComponentName t+  = either (Left . TargetProblemCommon) return $+           selectComponentTargetBasic pkgid cname subtarget t+  | otherwise+  = Left (TargetProblemComponentNotTest pkgid cname)++selectComponentTarget pkgid cname subtarget _+  = Left (TargetProblemIsSubComponent pkgid cname subtarget)++-- | The various error conditions that can occur when matching a+-- 'TargetSelector' against 'AvailableTarget's for the @test@ command.+--+data TargetProblem =+     TargetProblemCommon       TargetProblemCommon++     -- | The 'TargetSelector' matches targets but none are buildable+   | TargetProblemNoneEnabled (TargetSelector PackageId) [AvailableTarget ()]++     -- | There are no targets at all+   | TargetProblemNoTargets   (TargetSelector PackageId)++     -- | The 'TargetSelector' matches targets but no test-suites+   | TargetProblemNoTests     (TargetSelector PackageId)++     -- | The 'TargetSelector' refers to a component that is not a test-suite+   | TargetProblemComponentNotTest PackageId ComponentName++     -- | Asking to test an individual file or module is not supported+   | TargetProblemIsSubComponent   PackageId ComponentName SubComponentTarget+  deriving (Eq, Show)++reportTargetProblems :: Verbosity -> [TargetProblem] -> IO a+reportTargetProblems verbosity =+    die' verbosity . unlines . map renderTargetProblem++renderTargetProblem :: TargetProblem -> String+renderTargetProblem (TargetProblemCommon problem) =+    renderTargetProblemCommon "run" problem++renderTargetProblem (TargetProblemNoneEnabled targetSelector targets) =+    renderTargetProblemNoneEnabled "test" targetSelector targets++renderTargetProblem (TargetProblemNoTests targetSelector) =+    "Cannot run tests for the target '" ++ showTargetSelector targetSelector+ ++ "' which refers to " ++ renderTargetSelector targetSelector+ ++ " because "+ ++ plural (targetSelectorPluralPkgs targetSelector) "it does" "they do"+ ++ " not contain any test suites."++renderTargetProblem (TargetProblemNoTargets targetSelector) =+    case targetSelectorFilter targetSelector of+      Just kind | kind /= TestKind+        -> "The test command is for running test suites, but the target '"+           ++ showTargetSelector targetSelector ++ "' refers to "+           ++ renderTargetSelector targetSelector ++ "."++      _ -> renderTargetProblemNoTargets "test" targetSelector+  where+    targetSelectorFilter (TargetPackage  _ _ mkfilter) = mkfilter+    targetSelectorFilter (TargetAllPackages  mkfilter) = mkfilter+    targetSelectorFilter (TargetComponent _ _ _)       = Nothing++renderTargetProblem (TargetProblemComponentNotTest pkgid cname) =+    "The test command is for running test suites, but the target '"+ ++ showTargetSelector targetSelector ++ "' refers to "+ ++ renderTargetSelector targetSelector ++ " from the package "+ ++ display pkgid ++ "."+  where+    targetSelector = TargetComponent pkgid cname WholeComponent++renderTargetProblem (TargetProblemIsSubComponent pkgid cname subtarget) =+    "The test command can only run test suites as a whole, "+ ++ "not files or modules within them, but the target '"+ ++ showTargetSelector targetSelector ++ "' refers to "+ ++ renderTargetSelector targetSelector ++ "."+  where+    targetSelector = TargetComponent pkgid cname subtarget
+ Distribution/Client/Compat/FileLock.hsc view
@@ -0,0 +1,204 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE InterruptibleFFI #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MultiWayIf #-}+{-# LANGUAGE DeriveDataTypeable #-}++-- | This compat module can be removed once base-4.10 (ghc-8.2) is the minimum+-- required version. Though note that the locking functionality is not in+-- public modules in base-4.10, just in the "GHC.IO.Handle.Lock" module.+module Distribution.Client.Compat.FileLock (+    FileLockingNotSupported(..)+  , LockMode(..)+  , hLock+  , hTryLock+  ) where++#if MIN_VERSION_base(4,10,0)++import GHC.IO.Handle.Lock++#else++-- The remainder of this file is a modified copy+-- of GHC.IO.Handle.Lock from ghc-8.2.x+--+-- The modifications were just to the imports and the CPP, since we do not have+-- access to the HAVE_FLOCK from the ./configure script. We approximate the+-- lack of HAVE_FLOCK with defined(solaris2_HOST_OS) instead since that is the+-- only known major Unix platform lacking flock().++import Control.Exception (Exception)+import Data.Typeable++#if defined(solaris2_HOST_OS)++import Control.Exception (throwIO)+import System.IO (Handle)++#else++import Data.Bits+import Data.Function+import Control.Concurrent.MVar++import Foreign.C.Error+import Foreign.C.Types++import GHC.IO.Handle.Types+import GHC.IO.FD+import GHC.IO.Exception++#if defined(mingw32_HOST_OS)++#if defined(i386_HOST_ARCH)+## define WINDOWS_CCONV stdcall+#elif defined(x86_64_HOST_ARCH)+## define WINDOWS_CCONV ccall+#else+# error Unknown mingw32 arch+#endif++#include <windows.h>++import Foreign.Marshal.Alloc+import Foreign.Marshal.Utils+import Foreign.Ptr+import GHC.Windows++#else /* !defined(mingw32_HOST_OS), so assume unix with flock() */++#include <sys/file.h>++#endif /* !defined(mingw32_HOST_OS) */++#endif /* !defined(solaris2_HOST_OS) */++#endif /* MIN_VERSION_base */+++#if !(MIN_VERSION_base(4,10,0))++-- | Exception thrown by 'hLock' on non-Windows platforms that don't support+-- 'flock'.+data FileLockingNotSupported = FileLockingNotSupported+  deriving (Typeable, Show)++instance Exception FileLockingNotSupported++-- | Indicates a mode in which a file should be locked.+data LockMode = SharedLock | ExclusiveLock++-- | If a 'Handle' references a file descriptor, attempt to lock contents of the+-- underlying file in appropriate mode. If the file is already locked in+-- incompatible mode, this function blocks until the lock is established. The+-- lock is automatically released upon closing a 'Handle'.+--+-- Things to be aware of:+--+-- 1) This function may block inside a C call. If it does, in order to be able+-- to interrupt it with asynchronous exceptions and/or for other threads to+-- continue working, you MUST use threaded version of the runtime system.+--+-- 2) The implementation uses 'LockFileEx' on Windows and 'flock' otherwise,+-- hence all of their caveats also apply here.+--+-- 3) On non-Windows plaftorms that don't support 'flock' (e.g. Solaris) this+-- function throws 'FileLockingNotImplemented'. We deliberately choose to not+-- provide fcntl based locking instead because of its broken semantics.+--+-- @since 4.10.0.0+hLock :: Handle -> LockMode -> IO ()+hLock h mode = lockImpl h "hLock" mode True >> return ()++-- | Non-blocking version of 'hLock'.+--+-- @since 4.10.0.0+hTryLock :: Handle -> LockMode -> IO Bool+hTryLock h mode = lockImpl h "hTryLock" mode False++----------------------------------------++#if defined(solaris2_HOST_OS)++-- | No-op implementation.+lockImpl :: Handle -> String -> LockMode -> Bool -> IO Bool+lockImpl _ _ _ _ = throwIO FileLockingNotSupported++#else /* !defined(solaris2_HOST_OS) */++#if defined(mingw32_HOST_OS)++lockImpl :: Handle -> String -> LockMode -> Bool -> IO Bool+lockImpl h ctx mode block = do+  FD{fdFD = fd} <- handleToFd h+  wh <- throwErrnoIf (== iNVALID_HANDLE_VALUE) ctx $ c_get_osfhandle fd+  allocaBytes sizeof_OVERLAPPED $ \ovrlpd -> do+    fillBytes ovrlpd (fromIntegral sizeof_OVERLAPPED) 0+    let flags = cmode .|. (if block then 0 else #{const LOCKFILE_FAIL_IMMEDIATELY})+    -- We want to lock the whole file without looking up its size to be+    -- consistent with what flock does. According to documentation of LockFileEx+    -- "locking a region that goes beyond the current end-of-file position is+    -- not an error", however e.g. Windows 10 doesn't accept maximum possible+    -- value (a pair of MAXDWORDs) for mysterious reasons. Work around that by+    -- trying 2^32-1.+    fix $ \retry -> c_LockFileEx wh flags 0 0xffffffff 0x0 ovrlpd >>= \case+      True  -> return True+      False -> getLastError >>= \err -> if+        | not block && err == #{const ERROR_LOCK_VIOLATION} -> return False+        | err == #{const ERROR_OPERATION_ABORTED} -> retry+        | otherwise -> failWith ctx err+  where+    sizeof_OVERLAPPED = #{size OVERLAPPED}++    cmode = case mode of+      SharedLock    -> 0+      ExclusiveLock -> #{const LOCKFILE_EXCLUSIVE_LOCK}++-- https://msdn.microsoft.com/en-us/library/aa297958.aspx+foreign import ccall unsafe "_get_osfhandle"+  c_get_osfhandle :: CInt -> IO HANDLE++-- https://msdn.microsoft.com/en-us/library/windows/desktop/aa365203.aspx+foreign import WINDOWS_CCONV interruptible "LockFileEx"+  c_LockFileEx :: HANDLE -> DWORD -> DWORD -> DWORD -> DWORD -> Ptr () -> IO BOOL++#else /* !defined(mingw32_HOST_OS), so assume unix with flock() */++lockImpl :: Handle -> String -> LockMode -> Bool -> IO Bool+lockImpl h ctx mode block = do+  FD{fdFD = fd} <- handleToFd h+  let flags = cmode .|. (if block then 0 else #{const LOCK_NB})+  fix $ \retry -> c_flock fd flags >>= \case+    0 -> return True+    _ -> getErrno >>= \errno -> if+      | not block && errno == eWOULDBLOCK -> return False+      | errno == eINTR -> retry+      | otherwise -> ioException $ errnoToIOError ctx errno (Just h) Nothing+  where+    cmode = case mode of+      SharedLock    -> #{const LOCK_SH}+      ExclusiveLock -> #{const LOCK_EX}++foreign import ccall interruptible "flock"+  c_flock :: CInt -> CInt -> IO CInt++#endif /* !defined(mingw32_HOST_OS) */++-- | Turn an existing Handle into a file descriptor. This function throws an+-- IOError if the Handle does not reference a file descriptor.+handleToFd :: Handle -> IO FD+handleToFd h = case h of+  FileHandle _ mv -> do+    Handle__{haDevice = dev} <- readMVar mv+    case cast dev of+      Just fd -> return fd+      Nothing -> throwErr "not a file descriptor"+  DuplexHandle{} -> throwErr "not a file handle"+  where+    throwErr msg = ioException $ IOError (Just h)+      InappropriateType "handleToFd" msg Nothing Nothing++#endif /* defined(solaris2_HOST_OS) */++#endif /* MIN_VERSION_base */
+ Distribution/Client/Compat/Prelude.hs view
@@ -0,0 +1,35 @@+{-# LANGUAGE CPP #-}++-- to suppress WARNING in "Distribution.Compat.Prelude.Internal"+{-# OPTIONS_GHC -fno-warn-deprecations #-}++-- | This module does two things:+--+-- * Acts as a compatiblity layer, like @base-compat@.+--+-- * Provides commonly used imports.+--+-- This module is a superset of "Distribution.Compat.Prelude" (which+-- this module re-exports)+--+module Distribution.Client.Compat.Prelude+  ( module Distribution.Compat.Prelude.Internal+  , Prelude.IO+  , readMaybe+  ) where++import Prelude (IO)+import Distribution.Compat.Prelude.Internal hiding (IO)++#if MIN_VERSION_base(4,6,0)+import Text.Read+         ( readMaybe )+#endif++#if !MIN_VERSION_base(4,6,0)+-- | An implementation of readMaybe, for compatibility with older base versions.+readMaybe :: Read a => String -> Maybe a+readMaybe s = case reads s of+                [(x,"")] -> Just x+                _        -> Nothing+#endif
Distribution/Client/Compat/Process.hs view
@@ -24,7 +24,7 @@  import           Control.Exception (catch, throw) import           System.Exit       (ExitCode (ExitFailure))-import           System.IO.Error   (isDoesNotExistError)+import           System.IO.Error   (isDoesNotExistError, isPermissionError) import qualified System.Process    as P  -- | @readProcessWithExitCode@ creates an external process, reads its@@ -38,11 +38,12 @@ --   The version from @System.Process@ behaves inconsistently across --   platforms when an executable with the given name is not found: in --   some cases it returns an @ExitFailure@, in others it throws an---   exception.  This variant catches \"does not exist\" exceptions and---   turns them into @ExitFailure@s.+--   exception.  This variant catches \"does not exist\" and+--   \"permission denied\" exceptions and turns them into+--   @ExitFailure@s. readProcessWithExitCode :: FilePath -> [String] -> String -> IO (ExitCode, String, String) readProcessWithExitCode cmd args input =   P.readProcessWithExitCode cmd args input-    `catch` \e -> if isDoesNotExistError e+    `catch` \e -> if isDoesNotExistError e || isPermissionError e                     then return (ExitFailure 127, "", "")                     else throw e
− Distribution/Client/Compat/Time.hs
@@ -1,167 +0,0 @@-{-# LANGUAGE CPP, ForeignFunctionInterface, GeneralizedNewtypeDeriving #-}-module Distribution.Client.Compat.Time-       ( ModTime(..) -- Needed for testing-       , getModTime, getFileAge, getCurTime-       , posixSecondsToModTime )-       where--import Control.Arrow    ( first )-import Data.Int         ( Int64 )-import Data.Word        ( Word64 )-import System.Directory ( getModificationTime )--import Distribution.Compat.Binary ( Binary )--import Data.Time.Clock.POSIX ( POSIXTime, getPOSIXTime )-#if MIN_VERSION_directory(1,2,0)-import Data.Time.Clock.POSIX ( posixDayLength )-import Data.Time             ( diffUTCTime, getCurrentTime )-#else-import System.Time ( getClockTime, diffClockTimes-                   , normalizeTimeDiff, tdDay, tdHour )-#endif--#if defined mingw32_HOST_OS--import Data.Bits          ((.|.), unsafeShiftL)-#if MIN_VERSION_base(4,7,0)-import Data.Bits          (finiteBitSize)-#else-import Data.Bits          (bitSize)-#endif--import Data.Int           ( Int32 )-import Foreign            ( allocaBytes, peekByteOff )-import System.IO.Error    ( mkIOError, doesNotExistErrorType )-import System.Win32.Types ( BOOL, DWORD, LPCTSTR, LPVOID, withTString )--#else--import System.Posix.Files ( FileStatus, getFileStatus )--#if MIN_VERSION_unix(2,6,0)-import System.Posix.Files ( modificationTimeHiRes )-#else-import System.Posix.Files ( modificationTime )-#endif--#endif---- | An opaque type representing a file's modification time, represented--- internally as a 64-bit unsigned integer in the Windows UTC format.-newtype ModTime = ModTime Word64-                deriving (Binary, Bounded, Eq, Ord)--instance Show ModTime where-  show (ModTime x) = show x--instance Read ModTime where-  readsPrec p str = map (first ModTime) (readsPrec p str)---- | Return modification time of the given file. Works around the low clock--- resolution problem that 'getModificationTime' has on GHC < 7.8.------ This is a modified version of the code originally written for Shake by Neil--- Mitchell. See module Development.Shake.FileInfo.-getModTime :: FilePath -> IO ModTime--#if defined mingw32_HOST_OS---- Directly against the Win32 API.-getModTime path = allocaBytes size_WIN32_FILE_ATTRIBUTE_DATA $ \info -> do-  res <- getFileAttributesEx path info-  if not res-    then do-      let err = mkIOError doesNotExistErrorType-                "Distribution.Client.Compat.Time.getModTime"-                Nothing (Just path)-      ioError err-    else do-      dwLow  <- peekByteOff info-                index_WIN32_FILE_ATTRIBUTE_DATA_ftLastWriteTime_dwLowDateTime-      dwHigh <- peekByteOff info-                index_WIN32_FILE_ATTRIBUTE_DATA_ftLastWriteTime_dwHighDateTime-#if MIN_VERSION_base(4,7,0)-      let qwTime =-            (fromIntegral (dwHigh :: DWORD) `unsafeShiftL` finiteBitSize dwHigh)-            .|. (fromIntegral (dwLow :: DWORD))-#else-      let qwTime =-            (fromIntegral (dwHigh :: DWORD) `unsafeShiftL` bitSize dwHigh)-            .|. (fromIntegral (dwLow :: DWORD))-#endif-      return $! ModTime (qwTime :: Word64)--#ifdef x86_64_HOST_ARCH-#define CALLCONV ccall-#else-#define CALLCONV stdcall-#endif--foreign import CALLCONV "windows.h GetFileAttributesExW"-  c_getFileAttributesEx :: LPCTSTR -> Int32 -> LPVOID -> IO BOOL--getFileAttributesEx :: String -> LPVOID -> IO BOOL-getFileAttributesEx path lpFileInformation =-  withTString path $ \c_path ->-      c_getFileAttributesEx c_path getFileExInfoStandard lpFileInformation--getFileExInfoStandard :: Int32-getFileExInfoStandard = 0--size_WIN32_FILE_ATTRIBUTE_DATA :: Int-size_WIN32_FILE_ATTRIBUTE_DATA = 36--index_WIN32_FILE_ATTRIBUTE_DATA_ftLastWriteTime_dwLowDateTime :: Int-index_WIN32_FILE_ATTRIBUTE_DATA_ftLastWriteTime_dwLowDateTime = 20--index_WIN32_FILE_ATTRIBUTE_DATA_ftLastWriteTime_dwHighDateTime :: Int-index_WIN32_FILE_ATTRIBUTE_DATA_ftLastWriteTime_dwHighDateTime = 24--#else---- Directly against the unix library.-getModTime path = do-    st <- getFileStatus path-    return $! (extractFileTime st)--extractFileTime :: FileStatus -> ModTime-#if MIN_VERSION_unix(2,6,0)-extractFileTime x = posixTimeToModTime (modificationTimeHiRes x)-#else-extractFileTime x = posixSecondsToModTime $ fromIntegral $ fromEnum $-                    modificationTime x-#endif--#endif--windowsTick, secToUnixEpoch :: Word64-windowsTick    = 10000000-secToUnixEpoch = 11644473600---- | Convert POSIX seconds to ModTime.-posixSecondsToModTime :: Int64 -> ModTime-posixSecondsToModTime s =-  ModTime $ ((fromIntegral s :: Word64) + secToUnixEpoch) * windowsTick---- | Convert 'POSIXTime' to 'ModTime'.-posixTimeToModTime :: POSIXTime -> ModTime-posixTimeToModTime p = ModTime $ (ceiling $ p * 1e7) -- 100 ns precision-                       + (secToUnixEpoch * windowsTick)---- | Return age of given file in days.-getFileAge :: FilePath -> IO Double-getFileAge file = do-  t0 <- getModificationTime file-#if MIN_VERSION_directory(1,2,0)-  t1 <- getCurrentTime-  return $ realToFrac (t1 `diffUTCTime` t0) / realToFrac posixDayLength-#else-  t1 <- getClockTime-  let dt = normalizeTimeDiff (t1 `diffClockTimes` t0)-  return $ fromIntegral ((24 * tdDay dt) + tdHour dt) / 24.0-#endif---- | Return the current time as 'ModTime'.-getCurTime :: IO ModTime-getCurTime = posixTimeToModTime `fmap` getPOSIXTime -- Uses 'gettimeofday'.
− Distribution/Client/ComponentDeps.hs
@@ -1,161 +0,0 @@--- | Fine-grained package dependencies------ Like many others, this module is meant to be "double-imported":------ > import Distribution.Client.ComponentDeps (--- >     Component--- >   , ComponentDep--- >   , ComponentDeps--- >   )--- > import qualified Distribution.Client.ComponentDeps as CD-{-# LANGUAGE CPP #-}-{-# LANGUAGE DeriveFunctor #-}-{-# LANGUAGE DeriveGeneric #-}-module Distribution.Client.ComponentDeps (-    -- * Fine-grained package dependencies-    Component(..)-  , ComponentDep-  , ComponentDeps -- opaque-    -- ** Constructing ComponentDeps-  , empty-  , fromList-  , singleton-  , insert-  , filterDeps-  , fromLibraryDeps-  , fromSetupDeps-  , fromInstalled-    -- ** Deconstructing ComponentDeps-  , toList-  , flatDeps-  , nonSetupDeps-  , libraryDeps-  , setupDeps-  , select-  ) where--import Data.Map (Map)-import qualified Data.Map as Map-import Distribution.Compat.Binary (Binary)-import Distribution.Compat.Semigroup (Semigroup((<>)))-import GHC.Generics-import Data.Foldable (fold)--#if !MIN_VERSION_base(4,8,0)-import Data.Foldable (Foldable(foldMap))-import Data.Monoid (Monoid(..))-import Data.Traversable (Traversable(traverse))-#endif--{--------------------------------------------------------------------------------  Types--------------------------------------------------------------------------------}---- | Component of a package.-data Component =-    ComponentLib-  | ComponentExe   String-  | ComponentTest  String-  | ComponentBench String-  | ComponentSetup-  deriving (Show, Eq, Ord, Generic)--instance Binary Component---- | Dependency for a single component.-type ComponentDep a = (Component, a)---- | Fine-grained dependencies for a package.------ Typically used as @ComponentDeps [Dependency]@, to represent the list of--- dependencies for each named component within a package.----newtype ComponentDeps a = ComponentDeps { unComponentDeps :: Map Component a }-  deriving (Show, Functor, Eq, Ord, Generic)--instance Semigroup a => Monoid (ComponentDeps a) where-  mempty = ComponentDeps Map.empty-  mappend = (<>)--instance Semigroup a => Semigroup (ComponentDeps a) where-  ComponentDeps d <> ComponentDeps d' =-      ComponentDeps (Map.unionWith (<>) d d')--instance Foldable ComponentDeps where-  foldMap f = foldMap f . unComponentDeps--instance Traversable ComponentDeps where-  traverse f = fmap ComponentDeps . traverse f . unComponentDeps--instance Binary a => Binary (ComponentDeps a)--{--------------------------------------------------------------------------------  Construction--------------------------------------------------------------------------------}--empty :: ComponentDeps a-empty = ComponentDeps $ Map.empty--fromList :: Monoid a => [ComponentDep a] -> ComponentDeps a-fromList = ComponentDeps . Map.fromListWith mappend--singleton :: Component -> a -> ComponentDeps a-singleton comp = ComponentDeps . Map.singleton comp--insert :: Monoid a => Component -> a -> ComponentDeps a -> ComponentDeps a-insert comp a = ComponentDeps . Map.alter aux comp . unComponentDeps-  where-    aux Nothing   = Just a-    aux (Just a') = Just $ a `mappend` a'---- | Keep only selected components (and their associated deps info).-filterDeps :: (Component -> a -> Bool) -> ComponentDeps a -> ComponentDeps a-filterDeps p = ComponentDeps . Map.filterWithKey p . unComponentDeps---- | ComponentDeps containing library dependencies only-fromLibraryDeps :: a -> ComponentDeps a-fromLibraryDeps = singleton ComponentLib---- | ComponentDeps containing setup dependencies only.-fromSetupDeps :: a -> ComponentDeps a-fromSetupDeps = singleton ComponentSetup---- | ComponentDeps for installed packages.------ We assume that installed packages only record their library dependencies.-fromInstalled :: a -> ComponentDeps a-fromInstalled = fromLibraryDeps--{--------------------------------------------------------------------------------  Deconstruction--------------------------------------------------------------------------------}--toList :: ComponentDeps a -> [ComponentDep a]-toList = Map.toList . unComponentDeps---- | All dependencies of a package.------ This is just a synonym for 'fold', but perhaps a use of 'flatDeps' is more--- obvious than a use of 'fold', and moreover this avoids introducing lots of--- @#ifdef@s for 7.10 just for the use of 'fold'.-flatDeps :: Monoid a => ComponentDeps a -> a-flatDeps = fold---- | All dependencies except the setup dependencies.------ Prior to the introduction of setup dependencies in version 1.24 this--- would have been _all_ dependencies.-nonSetupDeps :: Monoid a => ComponentDeps a -> a-nonSetupDeps = select (/= ComponentSetup)---- | Library dependencies proper only.-libraryDeps :: Monoid a => ComponentDeps a -> a-libraryDeps = select (== ComponentLib)---- | Setup dependencies.-setupDeps :: Monoid a => ComponentDeps a -> a-setupDeps = select (== ComponentSetup)---- | Select dependencies satisfying a given predicate.-select :: Monoid a => (Component -> Bool) -> ComponentDeps a -> a-select p = foldMap snd . filter (p . fst) . toList
Distribution/Client/Config.hs view
@@ -48,8 +48,6 @@          ( RemoteRepo(..), Username(..), Password(..), emptyRemoteRepo ) import Distribution.Client.BuildReports.Types          ( ReportLevel(..) )-import Distribution.Client.Dependency.Types-         ( ConstraintSource(..) ) import Distribution.Client.Setup          ( GlobalFlags(..), globalCommand, defaultGlobalFlags          , ConfigExFlags(..), configureExOptions, defaultConfigExFlags@@ -64,10 +62,10 @@          ( DebugInfoLevel(..), OptimisationLevel(..) ) import Distribution.Simple.Setup          ( ConfigFlags(..), configureOptions, defaultConfigFlags-         , AllowNewer(..)+         , AllowNewer(..), AllowOlder(..), RelaxDeps(..)          , HaddockFlags(..), haddockOptions, defaultHaddockFlags          , installDirsOptions, optionDistPref-         , programConfigurationPaths', programConfigurationOptions+         , programDbPaths', programDbOptions          , Flag(..), toFlag, flagToMaybe, fromFlagOrDefault ) import Distribution.Simple.InstallDirs          ( InstallDirs(..), defaultInstallDirs@@ -86,19 +84,21 @@ import qualified Distribution.ParseUtils as ParseUtils          ( Field(..) ) import qualified Distribution.Text as Text-         ( Text(..) )+         ( Text(..), display ) import Distribution.Simple.Command          ( CommandUI(commandOptions), commandDefaultFlags, ShowOrParseArgs(..)          , viewAsFieldDescr ) import Distribution.Simple.Program-         ( defaultProgramConfiguration )+         ( defaultProgramDb ) import Distribution.Simple.Utils-         ( die, notice, warn, lowercase, cabalVersion )+         ( die', notice, warn, lowercase, cabalVersion ) import Distribution.Compiler          ( CompilerFlavor(..), defaultCompilerFlavor ) import Distribution.Verbosity          ( Verbosity, normal ) +import Distribution.Solver.Types.ConstraintSource+ import Data.List          ( partition, find, foldl' ) import Data.Maybe@@ -227,7 +227,8 @@         globalRequireSandbox    = combine globalRequireSandbox,         globalIgnoreSandbox     = combine globalIgnoreSandbox,         globalIgnoreExpiry      = combine globalIgnoreExpiry,-        globalHttpTransport     = combine globalHttpTransport+        globalHttpTransport     = combine globalHttpTransport,+        globalNix               = combine globalNix         }         where           combine        = combine'        savedGlobalFlags@@ -239,37 +240,45 @@         installDryRun                = combine installDryRun,         installMaxBackjumps          = combine installMaxBackjumps,         installReorderGoals          = combine installReorderGoals,+        installCountConflicts        = combine installCountConflicts,         installIndependentGoals      = combine installIndependentGoals,         installShadowPkgs            = combine installShadowPkgs,         installStrongFlags           = combine installStrongFlags,+        installAllowBootLibInstalls  = combine installAllowBootLibInstalls,         installReinstall             = combine installReinstall,         installAvoidReinstalls       = combine installAvoidReinstalls,         installOverrideReinstall     = combine installOverrideReinstall,         installUpgradeDeps           = combine installUpgradeDeps,         installOnly                  = combine installOnly,         installOnlyDeps              = combine installOnlyDeps,+        installIndexState            = combine installIndexState,         installRootCmd               = combine installRootCmd,         installSummaryFile           = lastNonEmptyNL installSummaryFile,         installLogFile               = combine installLogFile,         installBuildReports          = combine installBuildReports,         installReportPlanningFailure = combine installReportPlanningFailure,         installSymlinkBinDir         = combine installSymlinkBinDir,+        installPerComponent          = combine installPerComponent,         installOneShot               = combine installOneShot,         installNumJobs               = combine installNumJobs,+        installKeepGoing             = combine installKeepGoing,         installRunTests              = combine installRunTests,-        installOfflineMode           = combine installOfflineMode+        installOfflineMode           = combine installOfflineMode,+        installProjectFileName       = combine installProjectFileName         }         where           combine        = combine'        savedInstallFlags           lastNonEmptyNL = lastNonEmptyNL' savedInstallFlags        combinedSavedConfigureFlags = ConfigFlags {+        configArgs                = lastNonEmpty configArgs,         configPrograms_           = configPrograms_ . savedConfigureFlags $ b,         -- TODO: NubListify         configProgramPaths        = lastNonEmpty configProgramPaths,         -- TODO: NubListify         configProgramArgs         = lastNonEmpty configProgramArgs,         configProgramPathExtra    = lastNonEmptyNL configProgramPathExtra,+        configInstantiateWith     = lastNonEmpty configInstantiateWith,         configHcFlavor            = combine configHcFlavor,         configHcPath              = combine configHcPath,         configHcPkg               = combine configHcPkg,@@ -298,8 +307,11 @@         configExtraFrameworkDirs  = lastNonEmpty configExtraFrameworkDirs,         -- TODO: NubListify         configExtraIncludeDirs    = lastNonEmpty configExtraIncludeDirs,+        configDeterministic       = combine configDeterministic,         configIPID                = combine configIPID,+        configCID                 = combine configCID,         configDistPref            = combine configDistPref,+        configCabalFilePath       = combine configCabalFilePath,         configVerbosity           = combine configVerbosity,         configUserInstall         = combine configUserInstall,         -- TODO: NubListify@@ -321,6 +333,8 @@         configExactConfiguration  = combine configExactConfiguration,         configFlagError           = combine configFlagError,         configRelocatable         = combine configRelocatable,+        configAllowOlder          = combineMonoid savedConfigureFlags+                                    configAllowOlder,         configAllowNewer          = combineMonoid savedConfigureFlags                                     configAllowNewer         }@@ -350,7 +364,7 @@                                        `mappend` savedGlobalInstallDirs b        combinedSavedUploadFlags = UploadFlags {-        uploadCheck       = combine uploadCheck,+        uploadCandidate   = combine uploadCandidate,         uploadDoc         = combine uploadDoc,         uploadUsername    = combine uploadUsername,         uploadPassword    = combine uploadPassword,@@ -380,6 +394,7 @@         haddockExecutables   = combine haddockExecutables,         haddockTestSuites    = combine haddockTestSuites,         haddockBenchmarks    = combine haddockBenchmarks,+        haddockForeignLibs   = combine haddockForeignLibs,         haddockInternal      = combine haddockInternal,         haddockCss           = combine haddockCss,         haddockHscolour      = combine haddockHscolour,@@ -525,13 +540,9 @@                   remoteRepoKeyThreshold = 0                 } | secure /= Just False             = r {-              --TODO: When we want to switch us from using opt-in to opt-out-              -- security for the central hackage server, uncomment the-              -- following line. That will cause the default (of unspecified)-              -- to get interpreted as if it were "secure: True". For the-              -- moment it means the keys get added but you have to manually-              -- set "secure: True" to opt-in.-              --remoteRepoSecure       = Just True,+                -- Use hackage-security by default unless you opt-out with+                -- secure: False+                remoteRepoSecure       = Just True,                 remoteRepoRootKeys     = defaultHackageRemoteRepoKeys,                 remoteRepoKeyThreshold = defaultHackageRemoteRepoKeyThreshold               }@@ -605,7 +616,7 @@       return conf     Just (ParseFailed err) -> do       let (line, msg) = locatedErrorMsg err-      die $+      die' verbosity $           "Error parsing config file " ++ configFile         ++ maybe "" (\n -> ':' : show n) line ++ ":\n" ++ msg @@ -664,15 +675,17 @@   where     explanation = unlines       ["-- This is the configuration file for the 'cabal' command line tool."-      ,""+      ,"--"       ,"-- The available configuration options are listed below."       ,"-- Some of them have default values listed."-      ,""+      ,"--"       ,"-- Lines (like this one) beginning with '--' are comments."       ,"-- Be careful with spaces and indentation because they are"       ,"-- used to indicate layout for nested sections."-      ,""-      ,"-- Cabal library version: " ++ showVersion cabalVersion+      ,"--"+      ,"-- This config file was generated using the following versions"+      ,"-- of Cabal and cabal-install:"+      ,"-- Cabal library version: " ++ Text.display cabalVersion       ,"-- cabal-install version: " ++ showVersion Paths_cabal_install.version       ,"",""       ]@@ -686,21 +699,38 @@ commentSavedConfig = do   userInstallDirs   <- defaultInstallDirs defaultCompiler True True   globalInstallDirs <- defaultInstallDirs defaultCompiler False True-  return SavedConfig {-    savedGlobalFlags       = defaultGlobalFlags,-    savedInstallFlags      = defaultInstallFlags,-    savedConfigureExFlags  = defaultConfigExFlags,-    savedConfigureFlags    = (defaultConfigFlags defaultProgramConfiguration) {-      configUserInstall    = toFlag defaultUserInstall,-      configAllowNewer     = Just AllowNewerNone-    },-    savedUserInstallDirs   = fmap toFlag userInstallDirs,-    savedGlobalInstallDirs = fmap toFlag globalInstallDirs,-    savedUploadFlags       = commandDefaultFlags uploadCommand,-    savedReportFlags       = commandDefaultFlags reportCommand,-    savedHaddockFlags      = defaultHaddockFlags-  }+  let conf0 = mempty {+        savedGlobalFlags       = defaultGlobalFlags {+            globalRemoteRepos = toNubList [defaultRemoteRepo]+            },+        savedInstallFlags      = defaultInstallFlags,+        savedConfigureExFlags  = defaultConfigExFlags,+        savedConfigureFlags    = (defaultConfigFlags defaultProgramDb) {+            configUserInstall    = toFlag defaultUserInstall,+            configAllowNewer     = Just (AllowNewer RelaxDepsNone),+            configAllowOlder     = Just (AllowOlder RelaxDepsNone)+            },+        savedUserInstallDirs   = fmap toFlag userInstallDirs,+        savedGlobalInstallDirs = fmap toFlag globalInstallDirs,+        savedUploadFlags       = commandDefaultFlags uploadCommand,+        savedReportFlags       = commandDefaultFlags reportCommand,+        savedHaddockFlags      = defaultHaddockFlags +        }+  conf1 <- extendToEffectiveConfig conf0+  let globalFlagsConf1 = savedGlobalFlags conf1+      conf2 = conf1 {+        savedGlobalFlags = globalFlagsConf1 {+            globalRemoteRepos = overNubList (map removeRootKeys)+                                (globalRemoteRepos globalFlagsConf1)+            }+        }+  return conf2+    where+      -- Most people don't want to see default root keys, so don't print them.+      removeRootKeys :: RemoteRepo -> RemoteRepo+      removeRootKeys r = r { remoteRepoRootKeys = [] }+ -- | All config file fields. -- configFieldDescriptions :: ConstraintSource -> [FieldDescr SavedConfig]@@ -721,17 +751,15 @@        [simpleField "compiler"           (fromFlagOrDefault Disp.empty . fmap Text.disp) (optional Text.parse)           configHcFlavor (\v flags -> flags { configHcFlavor = v })-       ,let showAllowNewer Nothing               = mempty-            showAllowNewer (Just AllowNewerNone) = Disp.text "False"-            showAllowNewer (Just _)              = Disp.text "True"--            toAllowNewer True  = Just AllowNewerAll-            toAllowNewer False = Just AllowNewerNone--            pkgs = (Just . AllowNewerSome) `fmap` parseOptCommaList Text.parse-            parseAllowNewer = (toAllowNewer `fmap` Text.parse) Parse.<++ pkgs in+       ,let pkgs = (Just . AllowOlder . RelaxDepsSome) `fmap` parseOptCommaList Text.parse+            parseAllowOlder = ((Just . AllowOlder . toRelaxDeps) `fmap` Text.parse) Parse.<++ pkgs in+        simpleField "allow-older"+        (showRelaxDeps . fmap unAllowOlder) parseAllowOlder+        configAllowOlder (\v flags -> flags { configAllowOlder = v })+       ,let pkgs = (Just . AllowNewer . RelaxDepsSome) `fmap` parseOptCommaList Text.parse+            parseAllowNewer = ((Just . AllowNewer . toRelaxDeps) `fmap` Text.parse) Parse.<++ pkgs in         simpleField "allow-newer"-        showAllowNewer parseAllowNewer+        (showRelaxDeps . fmap unAllowNewer) parseAllowNewer         configAllowNewer (\v flags -> flags { configAllowNewer = v })         -- TODO: The following is a temporary fix. The "optimization"         -- and "debug-info" fields are OptArg, and viewAsFieldDescr@@ -797,7 +825,7 @@    ++ toSavedConfig liftUploadFlag        (commandOptions uploadCommand ParseArgs)-       ["verbose", "check", "documentation"] []+       ["verbose", "check", "documentation", "publish"] []    ++ toSavedConfig liftReportFlag        (commandOptions reportCommand ParseArgs)@@ -831,6 +859,15 @@       , name `notElem` exclusions ]     optional = Parse.option mempty . fmap toFlag ++    showRelaxDeps Nothing              = mempty+    showRelaxDeps (Just RelaxDepsNone) = Disp.text "False"+    showRelaxDeps (Just _)             = Disp.text "True"++    toRelaxDeps True  = RelaxDepsAll+    toRelaxDeps False = RelaxDepsNone++ -- TODO: next step, make the deprecated fields elicit a warning. -- deprecatedFieldDescriptions :: [FieldDescr SavedConfig]@@ -1004,8 +1041,8 @@  showConfigWithComments :: SavedConfig -> SavedConfig -> String showConfigWithComments comment vals = Disp.render $-      case fmap ppRemoteRepoSection . fromNubList . globalRemoteRepos-           . savedGlobalFlags $ vals of+      case fmap (uncurry ppRemoteRepoSection)+           (zip (getRemoteRepos comment) (getRemoteRepos vals)) of         [] -> Disp.text ""         (x:xs) -> foldl' (\ r r' -> r $+$ Disp.text "" $+$ r') x xs   $+$ Disp.text ""@@ -1025,6 +1062,7 @@   $+$ configFlagsSection "program-default-options" withProgramOptionsFields                          configProgramArgs   where+    getRemoteRepos = fromNubList . globalRemoteRepos . savedGlobalFlags     mcomment = Just comment     installDirsSection name field =       ppSection "install-dirs" name installDirsFields@@ -1042,11 +1080,9 @@ installDirsFields :: [FieldDescr (InstallDirs (Flag PathTemplate))] installDirsFields = map viewAsFieldDescr installDirsOptions -ppRemoteRepoSection :: RemoteRepo -> Doc-ppRemoteRepoSection vals = ppSection "repository" (remoteRepoName vals)-        remoteRepoFields def vals-  where-    def = Just (emptyRemoteRepo "ignored") { remoteRepoSecure = Just False }+ppRemoteRepoSection :: RemoteRepo -> RemoteRepo -> Doc+ppRemoteRepoSection def vals = ppSection "repository" (remoteRepoName vals)+        remoteRepoFields (Just def) vals  remoteRepoFields :: [FieldDescr RemoteRepo] remoteRepoFields =@@ -1094,14 +1130,14 @@ withProgramsFields :: [FieldDescr [(String, FilePath)]] withProgramsFields =   map viewAsFieldDescr $-  programConfigurationPaths' (++ "-location") defaultProgramConfiguration+  programDbPaths' (++ "-location") defaultProgramDb                              ParseArgs id (++)  -- | Fields for the 'program-default-options' section. withProgramOptionsFields :: [FieldDescr [(String, [String])]] withProgramOptionsFields =   map viewAsFieldDescr $-  programConfigurationOptions defaultProgramConfiguration ParseArgs id (++)+  programDbOptions defaultProgramDb ParseArgs id (++)  -- | Get the differences (as a pseudo code diff) between the user's -- '~/.cabal/config' and the one that cabal would generate if it didn't exist.
Distribution/Client/Configure.hs view
@@ -15,70 +15,82 @@     configure,     configureSetupScript,     chooseCabalVersion,-    checkConfigExFlags+    checkConfigExFlags,+    -- * Saved configure flags+    readConfigFlagsFrom, readConfigFlags,+    cabalConfigFlagsFile,+    writeConfigFlagsTo, writeConfigFlags,   ) where +import Prelude ()+import Distribution.Client.Compat.Prelude+ import Distribution.Client.Dependency-import Distribution.Client.Dependency.Types-         ( ConstraintSource(..)-         , LabeledPackageConstraint(..), showConstraintSource ) import qualified Distribution.Client.InstallPlan as InstallPlan-import Distribution.Client.InstallPlan (InstallPlan)+import Distribution.Client.SolverInstallPlan (SolverInstallPlan) import Distribution.Client.IndexUtils as IndexUtils          ( getSourcePackages, getInstalledPackages )-import Distribution.Client.PackageIndex ( PackageIndex, elemByPackageName )-import Distribution.Client.PkgConfigDb (PkgConfigDb, readPkgConfigDb) import Distribution.Client.Setup-         ( ConfigExFlags(..), configureCommand, filterConfigureFlags-         , RepoContext(..) )+         ( ConfigExFlags(..), RepoContext(..)+         , configureCommand, configureExCommand, filterConfigureFlags ) import Distribution.Client.Types as Source import Distribution.Client.SetupWrapper          ( setupWrapper, SetupScriptOptions(..), defaultSetupScriptOptions ) import Distribution.Client.Targets          ( userToPackageConstraint, userConstraintPackageName )-import qualified Distribution.Client.ComponentDeps as CD import Distribution.Package (PackageId) import Distribution.Client.JobControl (Lock) +import qualified Distribution.Solver.Types.ComponentDeps as CD+import           Distribution.Solver.Types.Settings+import           Distribution.Solver.Types.ConstraintSource+import           Distribution.Solver.Types.LabeledPackageConstraint+import           Distribution.Solver.Types.OptionalStanza+import           Distribution.Solver.Types.PackageIndex+                   ( PackageIndex, elemByPackageName )+import           Distribution.Solver.Types.PkgConfigDb+                   (PkgConfigDb, readPkgConfigDb)+import           Distribution.Solver.Types.SourcePackage+ import Distribution.Simple.Compiler          ( Compiler, CompilerInfo, compilerInfo, PackageDB(..), PackageDBStack )-import Distribution.Simple.Program (ProgramConfiguration )+import Distribution.Simple.Program (ProgramDb)+import Distribution.Client.SavedFlags ( readCommandFlags, writeCommandFlags ) import Distribution.Simple.Setup-         ( ConfigFlags(..), AllowNewer(..)+         ( ConfigFlags(..), AllowNewer(..), AllowOlder(..), RelaxDeps(..)          , fromFlag, toFlag, flagToMaybe, fromFlagOrDefault ) import Distribution.Simple.PackageIndex          ( InstalledPackageIndex, lookupPackageName ) import Distribution.Simple.Utils          ( defaultPackageDesc )-import qualified Distribution.InstalledPackageInfo as Installed import Distribution.Package-         ( Package(..), UnitId, packageName-         , Dependency(..), thisPackageVersion-         )+         ( Package(..), packageName )+import Distribution.Types.Dependency+         ( Dependency(..), thisPackageVersion ) import qualified Distribution.PackageDescription as PkgDesc+#ifdef CABAL_PARSEC+import Distribution.PackageDescription.Parsec+         ( readGenericPackageDescription )+#else import Distribution.PackageDescription.Parse-         ( readPackageDescription )+         ( readGenericPackageDescription )+#endif import Distribution.PackageDescription.Configuration-         ( finalizePackageDescription )+         ( finalizePD ) import Distribution.Version-         ( anyVersion, thisVersion )+         ( Version, mkVersion, anyVersion, thisVersion+         , VersionRange, orLaterVersion ) import Distribution.Simple.Utils as Utils-         ( warn, notice, debug, die )+         ( warn, notice, debug, die' ) import Distribution.Simple.Setup-         ( isAllowNewer )+         ( isRelaxDeps ) import Distribution.System          ( Platform ) import Distribution.Text ( display ) import Distribution.Verbosity as Verbosity          ( Verbosity )-import Distribution.Version-         ( Version(..), VersionRange, orLaterVersion ) -import Control.Monad (unless)-#if !MIN_VERSION_base(4,8,0)-import Data.Monoid (Monoid(..))-#endif-import Data.Maybe (isJust, fromMaybe)+import System.FilePath ( (</>) )  -- | Choose the Cabal version such that the setup scripts compiled against this -- version will support the given command-line flags.@@ -88,11 +100,13 @@   where     -- Cabal < 1.19.2 doesn't support '--exact-configuration' which is needed     -- for '--allow-newer' to work.-    allowNewer = isAllowNewer-                 (fromMaybe AllowNewerNone $ configAllowNewer configFlags)+    allowNewer = isRelaxDeps+                 (maybe RelaxDepsNone unAllowNewer $ configAllowNewer configFlags)+    allowOlder = isRelaxDeps+                 (maybe RelaxDepsNone unAllowOlder $ configAllowOlder configFlags) -    defaultVersionRange = if allowNewer-                          then orLaterVersion (Version [1,19,2] [])+    defaultVersionRange = if allowOlder || allowNewer+                          then orLaterVersion (mkVersion [1,19,2])                           else anyVersion  -- | Configure the package found in the local directory@@ -101,17 +115,17 @@           -> RepoContext           -> Compiler           -> Platform-          -> ProgramConfiguration+          -> ProgramDb           -> ConfigFlags           -> ConfigExFlags           -> [String]           -> IO ()-configure verbosity packageDBs repoCtxt comp platform conf+configure verbosity packageDBs repoCtxt comp platform progdb   configFlags configExFlags extraArgs = do -  installedPkgIndex <- getInstalledPackages verbosity comp packageDBs conf+  installedPkgIndex <- getInstalledPackages verbosity comp packageDBs progdb   sourcePkgDb       <- getSourcePackages    verbosity repoCtxt-  pkgConfigDb       <- readPkgConfigDb      verbosity conf+  pkgConfigDb       <- readPkgConfigDb      verbosity progdb    checkConfigExFlags verbosity installedPkgIndex                      (packageIndex sourcePkgDb) configExFlags@@ -131,17 +145,18 @@       setupWrapper verbosity (setupScriptOptions installedPkgIndex Nothing)         Nothing configureCommand (const configFlags) extraArgs -    Right installPlan -> case InstallPlan.ready installPlan of+    Right installPlan0 ->+     let installPlan = InstallPlan.configureInstallPlan configFlags installPlan0+     in case fst (InstallPlan.ready installPlan) of       [pkg@(ReadyPackage-             (ConfiguredPackage (SourcePackage _ _ (LocalUnpackedPackage _) _)-                                 _ _ _)-             _)] -> do+              (ConfiguredPackage _ (SourcePackage _ _ (LocalUnpackedPackage _) _)+                                 _ _ _))] -> do         configurePackage verbosity           platform (compilerInfo comp)           (setupScriptOptions installedPkgIndex (Just pkg))           configFlags pkg extraArgs -      _ -> die $ "internal error: configure install plan should have exactly "+      _ -> die' verbosity $ "internal error: configure install plan should have exactly "               ++ "one local ready package."    where@@ -153,7 +168,7 @@         packageDBs         comp         platform-        conf+        progdb         (fromFlagOrDefault            (useDistPref defaultSetupScriptOptions)            (configDistPref configFlags))@@ -168,7 +183,7 @@ configureSetupScript :: PackageDBStack                      -> Compiler                      -> Platform-                     -> ProgramConfiguration+                     -> ProgramDb                      -> FilePath                      -> VersionRange                      -> Maybe Lock@@ -179,7 +194,7 @@ configureSetupScript packageDBs                      comp                      platform-                     conf+                     progdb                      distPref                      cabalVersion                      lock@@ -193,10 +208,11 @@     , usePlatform              = Just platform     , usePackageDB             = packageDBs'     , usePackageIndex          = index'-    , useProgramConfig         = conf+    , useProgramDb             = progdb     , useDistPref              = distPref     , useLoggingHandle         = Nothing     , useWorkingDir            = Nothing+    , useExtraPathEnv          = []     , setupCacheLock           = lock     , useWin32CleanHack        = False     , forceExternalSetupMethod = forceExternal@@ -209,6 +225,7 @@     , useDependencies          = fromMaybe [] explicitSetupDeps     , useDependenciesExclusive = not defaultSetupDeps && isJust explicitSetupDeps     , useVersionMacros         = not defaultSetupDeps && isJust explicitSetupDeps+    , isInteractive            = False     }   where     -- When we are compiling a legacy setup script without an explicit@@ -228,8 +245,8 @@      maybeSetupBuildInfo :: Maybe PkgDesc.SetupBuildInfo     maybeSetupBuildInfo = do-      ReadyPackage (ConfiguredPackage (SourcePackage _ gpkg _ _) _ _ _) _-                 <- mpkg+      ReadyPackage cpkg <- mpkg+      let gpkg = packageDescription (confPkgSource cpkg)       PkgDesc.setupBuildInfo (PkgDesc.packageDescription gpkg)      -- Was a default 'custom-setup' stanza added by 'cabal-install' itself? If@@ -238,16 +255,14 @@     defaultSetupDeps = maybe False PkgDesc.defaultSetupDepends                        maybeSetupBuildInfo -    explicitSetupDeps :: Maybe [(UnitId, PackageId)]+    explicitSetupDeps :: Maybe [(InstalledPackageId, PackageId)]     explicitSetupDeps = do       -- Check if there is an explicit setup stanza.       _buildInfo <- maybeSetupBuildInfo       -- Return the setup dependencies computed by the solver-      ReadyPackage _ deps <- mpkg-      return [ ( Installed.installedUnitId deppkg-               , Installed.sourcePackageId    deppkg-               )-             | deppkg <- CD.setupDeps deps+      ReadyPackage cpkg <- mpkg+      return [ ( cid, srcid )+             | ConfiguredId srcid (Just PkgDesc.CLibName) cid <- CD.setupDeps (confPkgDeps cpkg)              ]  -- | Warn if any constraints or preferences name packages that are not in the@@ -284,17 +299,20 @@                  -> InstalledPackageIndex                  -> SourcePackageDb                  -> PkgConfigDb-                 -> IO (Progress String String InstallPlan)+                 -> IO (Progress String String SolverInstallPlan) planLocalPackage verbosity comp platform configFlags configExFlags   installedPkgIndex (SourcePackageDb _ packagePrefs) pkgConfigDb = do-  pkg <- readPackageDescription verbosity =<< defaultPackageDesc verbosity+  pkg <- readGenericPackageDescription verbosity =<<+            case flagToMaybe (configCabalFilePath configFlags) of+                Nothing -> defaultPackageDesc verbosity+                Just fp -> return fp   solver <- chooseSolver verbosity (fromFlag $ configSolver configExFlags)             (compilerInfo comp)    let -- We create a local package and ask to resolve a dependency on it       localPkg = SourcePackage {         packageInfoId             = packageId pkg,-        Source.packageDescription = pkg,+        packageDescription        = pkg,         packageSource             = LocalUnpackedPackage ".",         packageDescrOverride      = Nothing       }@@ -304,8 +322,10 @@         fromFlagOrDefault False $ configBenchmarks configFlags        resolverParams =-          removeUpperBounds-          (fromMaybe AllowNewerNone $ configAllowNewer configFlags)+          removeLowerBounds+          (fromMaybe (AllowOlder RelaxDepsNone) $ configAllowOlder configFlags)+        . removeUpperBounds+          (fromMaybe (AllowNewer RelaxDepsNone) $ configAllowNewer configFlags)          . addPreferences             -- preferences from the config file or command line@@ -321,22 +341,34 @@          . addConstraints             -- package flags from the config file or command line-            [ let pc = PackageConstraintFlags (packageName pkg)-                       (configConfigurationsFlags configFlags)+            [ let pc = PackageConstraint+                       (scopeToplevel $ packageName pkg)+                       (PackagePropertyFlags $ configConfigurationsFlags configFlags)               in LabeledPackageConstraint pc ConstraintSourceConfigFlagOrTarget             ]          . addConstraints             -- '--enable-tests' and '--enable-benchmarks' constraints from             -- the config file or command line-            [ let pc = PackageConstraintStanzas (packageName pkg) $+            [ let pc = PackageConstraint (scopeToplevel $ packageName pkg) .+                       PackagePropertyStanzas $                        [ TestStanzas  | testsEnabled ] ++                        [ BenchStanzas | benchmarksEnabled ]               in LabeledPackageConstraint pc ConstraintSourceConfigFlagOrTarget             ] +            -- Don't solve for executables, since we use an empty source+            -- package database and executables never show up in the+            -- installed package index+        . setSolveExecutables (SolveExecutables False)++        . setSolverVerbosity verbosity+         $ standardInstallPolicy             installedPkgIndex+            -- NB: We pass in an *empty* source package database,+            -- because cabal configure assumes that all dependencies+            -- have already been installed             (SourcePackageDb mempty packagePrefs)             [SpecificSourcePackage localPkg] @@ -359,34 +391,78 @@                  -> [String]                  -> IO () configurePackage verbosity platform comp scriptOptions configFlags-                 (ReadyPackage (ConfiguredPackage (SourcePackage _ gpkg _ _)-                                                  flags stanzas _)-                               deps)+                 (ReadyPackage (ConfiguredPackage ipid spkg flags stanzas deps))                  extraArgs =    setupWrapper verbosity     scriptOptions (Just pkg) configureCommand configureFlags extraArgs    where+    gpkg = packageDescription spkg     configureFlags   = filterConfigureFlags configFlags {+      configIPID = if isJust (flagToMaybe (configIPID configFlags))+                    -- Make sure cabal configure --ipid works.+                    then configIPID configFlags+                    else toFlag (display ipid),       configConfigurationsFlags = flags,       -- We generate the legacy constraints as well as the new style precise       -- deps.  In the end only one set gets passed to Setup.hs configure,       -- depending on the Cabal version we are talking to.-      configConstraints  = [ thisPackageVersion (packageId deppkg)-                           | deppkg <- CD.nonSetupDeps deps ],-      configDependencies = [ (packageName (Installed.sourcePackageId deppkg),-                              Installed.installedUnitId deppkg)-                           | deppkg <- CD.nonSetupDeps deps ],+      configConstraints  = [ thisPackageVersion srcid+                           | ConfiguredId srcid (Just PkgDesc.CLibName) _uid <- CD.nonSetupDeps deps ],+      configDependencies = [ (packageName srcid, uid)+                           | ConfiguredId srcid (Just PkgDesc.CLibName) uid <- CD.nonSetupDeps deps ],       -- Use '--exact-configuration' if supported.       configExactConfiguration = toFlag True,       configVerbosity          = toFlag verbosity,-      configBenchmarks         = toFlag (BenchStanzas `elem` stanzas),+      -- NB: if the user explicitly specified+      -- --enable-tests/--enable-benchmarks, always respect it.+      -- (But if they didn't, let solver decide.)+      configBenchmarks         = toFlag (BenchStanzas `elem` stanzas)+                                    `mappend` configBenchmarks configFlags,       configTests              = toFlag (TestStanzas `elem` stanzas)+                                    `mappend` configTests configFlags     } -    pkg = case finalizePackageDescription flags+    pkg = case finalizePD flags (enableStanzas stanzas)            (const True)-           platform comp [] (enableStanzas stanzas gpkg) of-      Left _ -> error "finalizePackageDescription ReadyPackage failed"+           platform comp [] gpkg of+      Left _ -> error "finalizePD ReadyPackage failed"       Right (desc, _) -> desc++-- -----------------------------------------------------------------------------+-- * Saved configure environments and flags+-- -----------------------------------------------------------------------------++-- | Read saved configure flags and restore the saved environment from the+-- specified files.+readConfigFlagsFrom :: FilePath  -- ^ path to saved flags file+                    -> IO (ConfigFlags, ConfigExFlags)+readConfigFlagsFrom flags = do+  readCommandFlags flags configureExCommand++-- | The path (relative to @--build-dir@) where the arguments to @configure@+-- should be saved.+cabalConfigFlagsFile :: FilePath -> FilePath+cabalConfigFlagsFile dist = dist </> "cabal-config-flags"++-- | Read saved configure flags and restore the saved environment from the+-- usual location.+readConfigFlags :: FilePath  -- ^ @--build-dir@+                -> IO (ConfigFlags, ConfigExFlags)+readConfigFlags dist =+  readConfigFlagsFrom (cabalConfigFlagsFile dist)++-- | Save the configure flags and environment to the specified files.+writeConfigFlagsTo :: FilePath  -- ^ path to saved flags file+                   -> Verbosity -> (ConfigFlags, ConfigExFlags)+                   -> IO ()+writeConfigFlagsTo file verb flags = do+  writeCommandFlags verb file configureExCommand flags++-- | Save the build flags to the usual location.+writeConfigFlags :: Verbosity+                 -> FilePath  -- ^ @--build-dir@+                 -> (ConfigFlags, ConfigExFlags) -> IO ()+writeConfigFlags verb dist =+  writeConfigFlagsTo (cabalConfigFlagsFile dist) verb
Distribution/Client/Dependency.hs view
@@ -23,13 +23,14 @@     resolveWithoutDependencies,      -- * Constructing resolver policies-    DepResolverParams(..),+    PackageProperty(..),     PackageConstraint(..),+    scopeToplevel,     PackagesPreferenceDefault(..),     PackagePreference(..),-    InstalledPreference(..),      -- ** Standard policy+    basicInstallPolicy,     standardInstallPolicy,     PackageSpecifier(..), @@ -37,8 +38,6 @@     applySandboxInstallPolicy,      -- ** Extra policy options-    dontUpgradeNonUpgradeablePackages,-    hideBrokenInstalledPackages,     upgradeDependencies,     reinstallTargets, @@ -47,60 +46,52 @@     addPreferences,     setPreferenceDefault,     setReorderGoals,+    setCountConflicts,     setIndependentGoals,     setAvoidReinstalls,     setShadowPkgs,     setStrongFlags,+    setAllowBootLibInstalls,     setMaxBackjumps,-    addSourcePackages,-    hideInstalledPackagesSpecificByUnitId,-    hideInstalledPackagesSpecificBySourcePackageId,-    hideInstalledPackagesAllVersions,+    setEnableBackjumping,+    setSolveExecutables,+    setGoalOrder,+    setSolverVerbosity,+    removeLowerBounds,     removeUpperBounds,     addDefaultSetupDependencies,+    addSetupCabalMinVersionConstraint,   ) where -import Distribution.Client.Dependency.TopDown-         ( topDownResolver )-import Distribution.Client.Dependency.Modular+import Distribution.Solver.Modular          ( modularResolver, SolverConfig(..) )-import qualified Distribution.Client.PackageIndex as PackageIndex import Distribution.Simple.PackageIndex (InstalledPackageIndex) import qualified Distribution.Simple.PackageIndex as InstalledPackageIndex-import qualified Distribution.Client.InstallPlan as InstallPlan-import Distribution.Client.InstallPlan (InstallPlan)-import Distribution.Client.PkgConfigDb (PkgConfigDb)+import Distribution.Client.SolverInstallPlan (SolverInstallPlan)+import qualified Distribution.Client.SolverInstallPlan as SolverInstallPlan import Distribution.Client.Types-         ( SourcePackageDb(SourcePackageDb), SourcePackage(..)-         , ConfiguredPackage(..), ConfiguredId(..)-         , OptionalStanza(..), enableStanzas )+         ( SourcePackageDb(SourcePackageDb)+         , UnresolvedPkgLoc, UnresolvedSourcePackage ) import Distribution.Client.Dependency.Types-         ( PreSolver(..), Solver(..), DependencyResolver, ResolverPackage(..)-         , PackageConstraint(..), showPackageConstraint-         , LabeledPackageConstraint(..), unlabelPackageConstraint-         , ConstraintSource(..), showConstraintSource-         , PackagePreferences(..), InstalledPreference(..)-         , PackagesPreferenceDefault(..)-         , Progress(..), foldProgress )+         ( PreSolver(..), Solver(..)+         , PackagesPreferenceDefault(..) ) import Distribution.Client.Sandbox.Types          ( SandboxPackageInfo(..) ) import Distribution.Client.Targets-import Distribution.Client.ComponentDeps (ComponentDeps)-import qualified Distribution.Client.ComponentDeps as CD-import qualified Distribution.InstalledPackageInfo as Installed import Distribution.Package-         ( PackageName(..), PackageIdentifier(PackageIdentifier), PackageId-         , Package(..), packageName, packageVersion-         , UnitId, Dependency(Dependency))+         ( PackageName, mkPackageName, PackageIdentifier(PackageIdentifier), PackageId+         , Package(..), packageName, packageVersion )+import Distribution.Types.Dependency import qualified Distribution.PackageDescription as PD import qualified Distribution.PackageDescription.Configuration as PD import Distribution.PackageDescription.Configuration-         ( finalizePackageDescription )+         ( finalizePD ) import Distribution.Client.PackageUtils          ( externalBuildDepends ) import Distribution.Version-         ( VersionRange, Version(..), anyVersion, orLaterVersion, thisVersion-         , withinRange, simplifyVersionRange )+         ( Version, mkVersion+         , VersionRange, anyVersion, thisVersion, orLaterVersion, withinRange+         , simplifyVersionRange, removeLowerBound, removeUpperBound ) import Distribution.Compiler          ( CompilerInfo(..) ) import Distribution.System@@ -108,20 +99,41 @@ import Distribution.Client.Utils          ( duplicates, duplicatesBy, mergeBy, MergeResult(..) ) import Distribution.Simple.Utils-         ( comparing, warn, info )+         ( comparing ) import Distribution.Simple.Configure          ( relaxPackageDeps ) import Distribution.Simple.Setup-         ( AllowNewer(..) )+         ( asBool, AllowNewer(..), AllowOlder(..), RelaxDeps(..) ) import Distribution.Text          ( display ) import Distribution.Verbosity-         ( Verbosity )+         ( normal, Verbosity )+import qualified Distribution.Compat.Graph as Graph +import           Distribution.Solver.Types.ComponentDeps (ComponentDeps)+import qualified Distribution.Solver.Types.ComponentDeps as CD+import           Distribution.Solver.Types.ConstraintSource+import           Distribution.Solver.Types.DependencyResolver+import           Distribution.Solver.Types.InstalledPreference+import           Distribution.Solver.Types.LabeledPackageConstraint+import           Distribution.Solver.Types.OptionalStanza+import           Distribution.Solver.Types.PackageConstraint+import           Distribution.Solver.Types.PackagePath+import           Distribution.Solver.Types.PackagePreferences+import qualified Distribution.Solver.Types.PackageIndex as PackageIndex+import           Distribution.Solver.Types.PkgConfigDb (PkgConfigDb)+import           Distribution.Solver.Types.Progress+import           Distribution.Solver.Types.ResolverPackage+import           Distribution.Solver.Types.Settings+import           Distribution.Solver.Types.SolverId+import           Distribution.Solver.Types.SolverPackage+import           Distribution.Solver.Types.SourcePackage+import           Distribution.Solver.Types.Variable+ import Data.List          ( foldl', sort, sortBy, nubBy, maximumBy, intercalate, nub ) import Data.Function (on)-import Data.Maybe  (fromMaybe)+import Data.Maybe (fromMaybe) import qualified Data.Map as Map import qualified Data.Set as Set import Data.Set (Set)@@ -138,37 +150,54 @@ -- implemented in terms of adjustments to the parameters. -- data DepResolverParams = DepResolverParams {-       depResolverTargets           :: [PackageName],+       depResolverTargets           :: Set PackageName,        depResolverConstraints       :: [LabeledPackageConstraint],        depResolverPreferences       :: [PackagePreference],        depResolverPreferenceDefault :: PackagesPreferenceDefault,        depResolverInstalledPkgIndex :: InstalledPackageIndex,-       depResolverSourcePkgIndex    :: PackageIndex.PackageIndex SourcePackage,-       depResolverReorderGoals      :: Bool,-       depResolverIndependentGoals  :: Bool,-       depResolverAvoidReinstalls   :: Bool,-       depResolverShadowPkgs        :: Bool,-       depResolverStrongFlags       :: Bool,-       depResolverMaxBackjumps      :: Maybe Int+       depResolverSourcePkgIndex    :: PackageIndex.PackageIndex UnresolvedSourcePackage,+       depResolverReorderGoals      :: ReorderGoals,+       depResolverCountConflicts    :: CountConflicts,+       depResolverIndependentGoals  :: IndependentGoals,+       depResolverAvoidReinstalls   :: AvoidReinstalls,+       depResolverShadowPkgs        :: ShadowPkgs,+       depResolverStrongFlags       :: StrongFlags,++       -- | Whether to allow base and its dependencies to be installed.+       depResolverAllowBootLibInstalls :: AllowBootLibInstalls,++       depResolverMaxBackjumps      :: Maybe Int,+       depResolverEnableBackjumping :: EnableBackjumping,+       -- | Whether or not to solve for dependencies on executables.+       -- This should be true, except in the legacy code path where+       -- we can't tell if an executable has been installed or not,+       -- so we shouldn't solve for them.  See #3875.+       depResolverSolveExecutables  :: SolveExecutables,++       -- | Function to override the solver's goal-ordering heuristics.+       depResolverGoalOrder         :: Maybe (Variable QPN -> Variable QPN -> Ordering),+       depResolverVerbosity         :: Verbosity      }  showDepResolverParams :: DepResolverParams -> String showDepResolverParams p =-     "targets: " ++ intercalate ", " (map display (depResolverTargets p))+     "targets: " ++ intercalate ", " (map display $ Set.toList (depResolverTargets p))   ++ "\nconstraints: "   ++   concatMap (("\n  " ++) . showLabeledConstraint)        (depResolverConstraints p)   ++ "\npreferences: "   ++   concatMap (("\n  " ++) . showPackagePreference)        (depResolverPreferences p)-  ++ "\nstrategy: "          ++ show (depResolverPreferenceDefault p)-  ++ "\nreorder goals: "     ++ show (depResolverReorderGoals      p)-  ++ "\nindependent goals: " ++ show (depResolverIndependentGoals  p)-  ++ "\navoid reinstalls: "  ++ show (depResolverAvoidReinstalls   p)-  ++ "\nshadow packages: "   ++ show (depResolverShadowPkgs        p)-  ++ "\nstrong flags: "      ++ show (depResolverStrongFlags       p)+  ++ "\nstrategy: "          ++ show (depResolverPreferenceDefault        p)+  ++ "\nreorder goals: "     ++ show (asBool (depResolverReorderGoals     p))+  ++ "\ncount conflicts: "   ++ show (asBool (depResolverCountConflicts   p))+  ++ "\nindependent goals: " ++ show (asBool (depResolverIndependentGoals p))+  ++ "\navoid reinstalls: "  ++ show (asBool (depResolverAvoidReinstalls  p))+  ++ "\nshadow packages: "   ++ show (asBool (depResolverShadowPkgs       p))+  ++ "\nstrong flags: "      ++ show (asBool (depResolverStrongFlags      p))+  ++ "\nallow boot library installs: " ++ show (asBool (depResolverAllowBootLibInstalls p))   ++ "\nmax backjumps: "     ++ maybe "infinite" show-                                     (depResolverMaxBackjumps      p)+                                     (depResolverMaxBackjumps             p)   where     showLabeledConstraint :: LabeledPackageConstraint -> String     showLabeledConstraint (LabeledPackageConstraint pc src) =@@ -205,29 +234,35 @@   display pn ++ " " ++ show st  basicDepResolverParams :: InstalledPackageIndex-                       -> PackageIndex.PackageIndex SourcePackage+                       -> PackageIndex.PackageIndex UnresolvedSourcePackage                        -> DepResolverParams basicDepResolverParams installedPkgIndex sourcePkgIndex =     DepResolverParams {-       depResolverTargets           = [],+       depResolverTargets           = Set.empty,        depResolverConstraints       = [],        depResolverPreferences       = [],        depResolverPreferenceDefault = PreferLatestForSelected,        depResolverInstalledPkgIndex = installedPkgIndex,        depResolverSourcePkgIndex    = sourcePkgIndex,-       depResolverReorderGoals      = False,-       depResolverIndependentGoals  = False,-       depResolverAvoidReinstalls   = False,-       depResolverShadowPkgs        = False,-       depResolverStrongFlags       = False,-       depResolverMaxBackjumps      = Nothing+       depResolverReorderGoals      = ReorderGoals False,+       depResolverCountConflicts    = CountConflicts True,+       depResolverIndependentGoals  = IndependentGoals False,+       depResolverAvoidReinstalls   = AvoidReinstalls False,+       depResolverShadowPkgs        = ShadowPkgs False,+       depResolverStrongFlags       = StrongFlags False,+       depResolverAllowBootLibInstalls = AllowBootLibInstalls False,+       depResolverMaxBackjumps      = Nothing,+       depResolverEnableBackjumping = EnableBackjumping True,+       depResolverSolveExecutables  = SolveExecutables True,+       depResolverGoalOrder         = Nothing,+       depResolverVerbosity         = normal      }  addTargets :: [PackageName]            -> DepResolverParams -> DepResolverParams addTargets extraTargets params =     params {-      depResolverTargets = extraTargets ++ depResolverTargets params+      depResolverTargets = Set.fromList extraTargets `Set.union` depResolverTargets params     }  addConstraints :: [LabeledPackageConstraint]@@ -253,42 +288,80 @@       depResolverPreferenceDefault = preferenceDefault     } -setReorderGoals :: Bool -> DepResolverParams -> DepResolverParams-setReorderGoals b params =+setReorderGoals :: ReorderGoals -> DepResolverParams -> DepResolverParams+setReorderGoals reorder params =     params {-      depResolverReorderGoals = b+      depResolverReorderGoals = reorder     } -setIndependentGoals :: Bool -> DepResolverParams -> DepResolverParams-setIndependentGoals b params =+setCountConflicts :: CountConflicts -> DepResolverParams -> DepResolverParams+setCountConflicts count params =     params {-      depResolverIndependentGoals = b+      depResolverCountConflicts = count     } -setAvoidReinstalls :: Bool -> DepResolverParams -> DepResolverParams-setAvoidReinstalls b params =+setIndependentGoals :: IndependentGoals -> DepResolverParams -> DepResolverParams+setIndependentGoals indep params =     params {-      depResolverAvoidReinstalls = b+      depResolverIndependentGoals = indep     } -setShadowPkgs :: Bool -> DepResolverParams -> DepResolverParams-setShadowPkgs b params =+setAvoidReinstalls :: AvoidReinstalls -> DepResolverParams -> DepResolverParams+setAvoidReinstalls avoid params =     params {-      depResolverShadowPkgs = b+      depResolverAvoidReinstalls = avoid     } -setStrongFlags :: Bool -> DepResolverParams -> DepResolverParams-setStrongFlags b params =+setShadowPkgs :: ShadowPkgs -> DepResolverParams -> DepResolverParams+setShadowPkgs shadow params =     params {-      depResolverStrongFlags = b+      depResolverShadowPkgs = shadow     } +setStrongFlags :: StrongFlags -> DepResolverParams -> DepResolverParams+setStrongFlags sf params =+    params {+      depResolverStrongFlags = sf+    }++setAllowBootLibInstalls :: AllowBootLibInstalls -> DepResolverParams -> DepResolverParams+setAllowBootLibInstalls i params =+    params {+      depResolverAllowBootLibInstalls = i+    }+ setMaxBackjumps :: Maybe Int -> DepResolverParams -> DepResolverParams setMaxBackjumps n params =     params {       depResolverMaxBackjumps = n     } +setEnableBackjumping :: EnableBackjumping -> DepResolverParams -> DepResolverParams+setEnableBackjumping b params =+    params {+      depResolverEnableBackjumping = b+    }++setSolveExecutables :: SolveExecutables -> DepResolverParams -> DepResolverParams+setSolveExecutables b params =+    params {+      depResolverSolveExecutables = b+    }++setGoalOrder :: Maybe (Variable QPN -> Variable QPN -> Ordering)+             -> DepResolverParams+             -> DepResolverParams+setGoalOrder order params =+    params {+      depResolverGoalOrder = order+    }++setSolverVerbosity :: Verbosity -> DepResolverParams -> DepResolverParams+setSolverVerbosity verbosity params =+    params {+      depResolverVerbosity = verbosity+    }+ -- | Some packages are specific to a given compiler version and should never be -- upgraded. dontUpgradeNonUpgradeablePackages :: DepResolverParams -> DepResolverParams@@ -297,20 +370,24 @@   where     extraConstraints =       [ LabeledPackageConstraint-        (PackageConstraintInstalled pkgname)+        (PackageConstraint (ScopeAnyQualifier pkgname) PackagePropertyInstalled)         ConstraintSourceNonUpgradeablePackage-      | notElem (PackageName "base") (depResolverTargets params)-      , pkgname <- map PackageName [ "base", "ghc-prim", "integer-gmp"-                                   , "integer-simple" ]+      | Set.notMember (mkPackageName "base") (depResolverTargets params)+      -- If you change this enumeration, make sure to update the list in+      -- "Distribution.Solver.Modular.Solver" as well+      , pkgname <- [ mkPackageName "base"+                   , mkPackageName "ghc-prim"+                   , mkPackageName "integer-gmp"+                   , mkPackageName "integer-simple"+                   , mkPackageName "template-haskell"+                   ]       , isInstalled pkgname ]-    -- TODO: the top down resolver chokes on the base constraints-    -- below when there are no targets and thus no dep on base.-    -- Need to refactor constraints separate from needing packages.+     isInstalled = not . null                 . InstalledPackageIndex.lookupPackageName                                  (depResolverInstalledPkgIndex params) -addSourcePackages :: [SourcePackage]+addSourcePackages :: [UnresolvedSourcePackage]                   -> DepResolverParams -> DepResolverParams addSourcePackages pkgs params =     params {@@ -319,17 +396,6 @@               (depResolverSourcePkgIndex params) pkgs     } -hideInstalledPackagesSpecificByUnitId :: [UnitId]-                                                     -> DepResolverParams-                                                     -> DepResolverParams-hideInstalledPackagesSpecificByUnitId pkgids params =-    --TODO: this should work using exclude constraints instead-    params {-      depResolverInstalledPkgIndex =-        foldl' (flip InstalledPackageIndex.deleteUnitId)-               (depResolverInstalledPkgIndex params) pkgids-    }- hideInstalledPackagesSpecificBySourcePackageId :: [PackageId]                                                   -> DepResolverParams                                                   -> DepResolverParams@@ -352,17 +418,6 @@     }  -hideBrokenInstalledPackages :: DepResolverParams -> DepResolverParams-hideBrokenInstalledPackages params =-    hideInstalledPackagesSpecificByUnitId pkgids params-  where-    pkgids = map Installed.installedUnitId-           . InstalledPackageIndex.reverseDependencyClosure-                            (depResolverInstalledPkgIndex params)-           . map (Installed.installedUnitId . fst)-           . InstalledPackageIndex.brokenPackages-           $ depResolverInstalledPkgIndex params- -- | Remove upper bounds in dependencies using the policy specified by the -- 'AllowNewer' argument (all/some/none). --@@ -371,27 +426,43 @@ -- 'addSourcePackages' won't have upper bounds in dependencies relaxed. -- removeUpperBounds :: AllowNewer -> DepResolverParams -> DepResolverParams-removeUpperBounds AllowNewerNone params = params-removeUpperBounds allowNewer     params =+removeUpperBounds (AllowNewer RelaxDepsNone) params = params+removeUpperBounds (AllowNewer allowNewer)    params =     params {       depResolverSourcePkgIndex = sourcePkgIndex'     }   where     sourcePkgIndex' = fmap relaxDeps $ depResolverSourcePkgIndex params -    relaxDeps :: SourcePackage -> SourcePackage+    relaxDeps :: UnresolvedSourcePackage -> UnresolvedSourcePackage     relaxDeps srcPkg = srcPkg {-      packageDescription = relaxPackageDeps allowNewer+      packageDescription = relaxPackageDeps removeUpperBound allowNewer                            (packageDescription srcPkg)       } +-- | Dual of 'removeUpperBounds'+removeLowerBounds :: AllowOlder -> DepResolverParams -> DepResolverParams+removeLowerBounds (AllowOlder RelaxDepsNone) params = params+removeLowerBounds (AllowOlder allowNewer)    params =+    params {+      depResolverSourcePkgIndex = sourcePkgIndex'+    }+  where+    sourcePkgIndex' = fmap relaxDeps $ depResolverSourcePkgIndex params++    relaxDeps :: UnresolvedSourcePackage -> UnresolvedSourcePackage+    relaxDeps srcPkg = srcPkg {+      packageDescription = relaxPackageDeps removeLowerBound allowNewer+                           (packageDescription srcPkg)+      }+ -- | Supply defaults for packages without explicit Setup dependencies -- -- Note: It's important to apply 'addDefaultSetupDepends' after -- 'addSourcePackages'. Otherwise, the packages inserted by -- 'addSourcePackages' won't have upper bounds in dependencies relaxed. ---addDefaultSetupDependencies :: (SourcePackage -> Maybe [Dependency])+addDefaultSetupDependencies :: (UnresolvedSourcePackage -> Maybe [Dependency])                             -> DepResolverParams -> DepResolverParams addDefaultSetupDependencies defaultSetupDeps params =     params {@@ -399,7 +470,7 @@         fmap applyDefaultSetupDeps (depResolverSourcePkgIndex params)     }   where-    applyDefaultSetupDeps :: SourcePackage -> SourcePackage+    applyDefaultSetupDeps :: UnresolvedSourcePackage -> UnresolvedSourcePackage     applyDefaultSetupDeps srcpkg =         srcpkg {           packageDescription = gpkgdesc {@@ -420,21 +491,38 @@         gpkgdesc = packageDescription srcpkg         pkgdesc  = PD.packageDescription gpkgdesc +-- | If a package has a custom setup then we need to add a setup-depends+-- on Cabal.+--+addSetupCabalMinVersionConstraint :: Version+                                  -> DepResolverParams -> DepResolverParams+addSetupCabalMinVersionConstraint minVersion =+    addConstraints+      [ LabeledPackageConstraint+          (PackageConstraint (ScopeAnySetupQualifier cabalPkgname)+                             (PackagePropertyVersion $ orLaterVersion minVersion))+          ConstraintSetupCabalMinVersion+      ]+  where+    cabalPkgname = mkPackageName "Cabal" + upgradeDependencies :: DepResolverParams -> DepResolverParams upgradeDependencies = setPreferenceDefault PreferAllLatest   reinstallTargets :: DepResolverParams -> DepResolverParams reinstallTargets params =-    hideInstalledPackagesAllVersions (depResolverTargets params) params+    hideInstalledPackagesAllVersions (Set.toList $ depResolverTargets params) params  -standardInstallPolicy :: InstalledPackageIndex-                      -> SourcePackageDb-                      -> [PackageSpecifier SourcePackage]-                      -> DepResolverParams-standardInstallPolicy+-- | A basic solver policy on which all others are built.+--+basicInstallPolicy :: InstalledPackageIndex+                   -> SourcePackageDb+                   -> [PackageSpecifier UnresolvedSourcePackage]+                   -> DepResolverParams+basicInstallPolicy     installedPkgIndex (SourcePackageDb sourcePkgIndex sourcePkgPrefs)     pkgSpecifiers @@ -451,20 +539,35 @@   . hideInstalledPackagesSpecificBySourcePackageId       [ packageId pkg | SpecificSourcePackage pkg <- pkgSpecifiers ] -  . addDefaultSetupDependencies mkDefaultSetupDeps-   . addSourcePackages       [ pkg  | SpecificSourcePackage pkg <- pkgSpecifiers ]    $ basicDepResolverParams       installedPkgIndex sourcePkgIndex ++-- | The policy used by all the standard commands, install, fetch, freeze etc+-- (but not the new-build and related commands).+--+-- It extends the 'basicInstallPolicy' with a policy on setup deps.+--+standardInstallPolicy :: InstalledPackageIndex+                      -> SourcePackageDb+                      -> [PackageSpecifier UnresolvedSourcePackage]+                      -> DepResolverParams+standardInstallPolicy installedPkgIndex sourcePkgDb pkgSpecifiers++  = addDefaultSetupDependencies mkDefaultSetupDeps++  $ basicInstallPolicy+      installedPkgIndex sourcePkgDb pkgSpecifiers+     where       -- Force Cabal >= 1.24 dep when the package is affected by #3199.-      mkDefaultSetupDeps :: SourcePackage -> Maybe [Dependency]+      mkDefaultSetupDeps :: UnresolvedSourcePackage -> Maybe [Dependency]       mkDefaultSetupDeps srcpkg | affected        =-        Just [Dependency (PackageName "Cabal")-              (orLaterVersion $ Version [1,24] [])]+        Just [Dependency (mkPackageName "Cabal")+              (orLaterVersion $ mkVersion [1,24])]                                 | otherwise       = Nothing         where           gpkgdesc = packageDescription srcpkg@@ -503,8 +606,9 @@         (thisVersion (packageVersion pkg)) | pkg <- otherDeps ]    . addConstraints-      [ let pc = PackageConstraintVersion (packageName pkg)-                 (thisVersion (packageVersion pkg))+      [ let pc = PackageConstraint+                 (scopeToplevel $ packageName pkg)+                 (PackagePropertyVersion $ thisVersion (packageVersion pkg))         in LabeledPackageConstraint pc ConstraintSourceModifiedAddSourceDep       | pkg <- modifiedDeps ] @@ -531,19 +635,12 @@ -- ------------------------------------------------------------  chooseSolver :: Verbosity -> PreSolver -> CompilerInfo -> IO Solver-chooseSolver verbosity preSolver _cinfo =+chooseSolver _verbosity preSolver _cinfo =     case preSolver of-      AlwaysTopDown -> do-        warn verbosity "Topdown solver is deprecated"-        return TopDown       AlwaysModular -> do         return Modular-      Choose -> do-        info verbosity "Choosing modular solver."-        return Modular -runSolver :: Solver -> SolverConfig -> DependencyResolver-runSolver TopDown = const topDownResolver -- TODO: warn about unsupported options+runSolver :: Solver -> SolverConfig -> DependencyResolver UnresolvedPkgLoc runSolver Modular = modularResolver  -- | Run the dependency solver.@@ -557,11 +654,11 @@                     -> PkgConfigDb                     -> Solver                     -> DepResolverParams-                    -> Progress String String InstallPlan+                    -> Progress String String SolverInstallPlan      --TODO: is this needed here? see dontUpgradeNonUpgradeablePackages resolveDependencies platform comp _pkgConfigDB _solver params-  | null (depResolverTargets params)+  | Set.null (depResolverTargets params)   = return (validateSolverResult platform comp indGoals [])   where     indGoals = depResolverIndependentGoals params@@ -570,8 +667,10 @@      Step (showDepResolverParams finalparams)   $ fmap (validateSolverResult platform comp indGoals)-  $ runSolver solver (SolverConfig reorderGoals indGoals noReinstalls-                      shadowing strFlags maxBkjumps)+  $ runSolver solver (SolverConfig reordGoals cntConflicts+                      indGoals noReinstalls+                      shadowing strFlags allowBootLibs maxBkjumps enableBj+                      solveExes order verbosity)                      platform comp installedPkgIndex sourcePkgIndex                      pkgConfigDB preferences constraints targets   where@@ -581,23 +680,23 @@       prefs defpref       installedPkgIndex       sourcePkgIndex-      reorderGoals+      reordGoals+      cntConflicts       indGoals       noReinstalls       shadowing       strFlags-      maxBkjumps)     = dontUpgradeNonUpgradeablePackages-                      -- TODO:-                      -- The modular solver can properly deal with broken-                      -- packages and won't select them. So the-                      -- 'hideBrokenInstalledPackages' function should be moved-                      -- into a module that is specific to the top-down solver.-                      . (if solver /= Modular then hideBrokenInstalledPackages-                                              else id)-                      $ params+      allowBootLibs+      maxBkjumps+      enableBj+      solveExes+      order+      verbosity) =+        if asBool (depResolverAllowBootLibInstalls params)+        then params+        else dontUpgradeNonUpgradeablePackages params -    preferences = interpretPackagesPreference-                    (Set.fromList targets) defpref prefs+    preferences = interpretPackagesPreference targets defpref prefs   -- | Give an interpretation to the global 'PackagesPreference' as@@ -648,24 +747,21 @@ -- validateSolverResult :: Platform                      -> CompilerInfo-                     -> Bool-                     -> [ResolverPackage]-                     -> InstallPlan+                     -> IndependentGoals+                     -> [ResolverPackage UnresolvedPkgLoc]+                     -> SolverInstallPlan validateSolverResult platform comp indepGoals pkgs =     case planPackagesProblems platform comp pkgs of-      [] -> case InstallPlan.new indepGoals index of+      [] -> case SolverInstallPlan.new indepGoals graph of               Right plan     -> plan               Left  problems -> error (formatPlanProblems problems)       problems               -> error (formatPkgProblems problems)    where-    index = InstalledPackageIndex.fromList (map toPlanPackage pkgs)--    toPlanPackage (PreExisting pkg) = InstallPlan.PreExisting pkg-    toPlanPackage (Configured  pkg) = InstallPlan.Configured  pkg+    graph = Graph.fromDistinctList pkgs      formatPkgProblems  = formatProblemMessage . map showPlanPackageProblem-    formatPlanProblems = formatProblemMessage . map InstallPlan.showPlanProblem+    formatPlanProblems = formatProblemMessage . map SolverInstallPlan.showPlanProblem      formatProblemMessage problems =       unlines $@@ -673,11 +769,13 @@       : "The proposed (invalid) plan contained the following problems:"       : problems       ++ "Proposed plan:"-      : [InstallPlan.showPlanIndex index]+      : [SolverInstallPlan.showPlanIndex pkgs]   data PlanPackageProblem =-       InvalidConfiguredPackage ConfiguredPackage [PackageProblem]+       InvalidConfiguredPackage (SolverPackage UnresolvedPkgLoc)+                                [PackageProblem]+     | DuplicatePackageSolverId SolverId [ResolverPackage UnresolvedPkgLoc]  showPlanPackageProblem :: PlanPackageProblem -> String showPlanPackageProblem (InvalidConfiguredPackage pkg packageProblems) =@@ -685,15 +783,20 @@   ++ " has an invalid configuration, in particular:\n"   ++ unlines [ "  " ++ showPackageProblem problem              | problem <- packageProblems ]+showPlanPackageProblem (DuplicatePackageSolverId pid dups) =+     "Package " ++ display (packageId pid) ++ " has "+  ++ show (length dups) ++ " duplicate instances."  planPackagesProblems :: Platform -> CompilerInfo-                     -> [ResolverPackage]+                     -> [ResolverPackage UnresolvedPkgLoc]                      -> [PlanPackageProblem] planPackagesProblems platform cinfo pkgs =      [ InvalidConfiguredPackage pkg packageProblems      | Configured pkg <- pkgs      , let packageProblems = configuredPackageProblems platform cinfo pkg      , not (null packageProblems) ]+  ++ [ DuplicatePackageSolverId (Graph.nodeKey (head dups)) dups+     | dups <- duplicatesBy (comparing Graph.nodeKey) pkgs ]  data PackageProblem = DuplicateFlag PD.FlagName                     | MissingFlag   PD.FlagName@@ -704,14 +807,14 @@                     | InvalidDep    Dependency PackageId  showPackageProblem :: PackageProblem -> String-showPackageProblem (DuplicateFlag (PD.FlagName flag)) =-  "duplicate flag in the flag assignment: " ++ flag+showPackageProblem (DuplicateFlag flag) =+  "duplicate flag in the flag assignment: " ++ PD.unFlagName flag -showPackageProblem (MissingFlag (PD.FlagName flag)) =-  "missing an assignment for the flag: " ++ flag+showPackageProblem (MissingFlag flag) =+  "missing an assignment for the flag: " ++ PD.unFlagName flag -showPackageProblem (ExtraFlag (PD.FlagName flag)) =-  "extra flag given that is not used by the package: " ++ flag+showPackageProblem (ExtraFlag flag) =+  "extra flag given that is not used by the package: " ++ PD.unFlagName flag  showPackageProblem (DuplicateDeps pkgids) =      "duplicate packages specified as selected dependencies: "@@ -736,9 +839,9 @@ -- dependencies are satisfied by the specified packages. -- configuredPackageProblems :: Platform -> CompilerInfo-                          -> ConfiguredPackage -> [PackageProblem]+                          -> SolverPackage UnresolvedPkgLoc -> [PackageProblem] configuredPackageProblems platform cinfo-  (ConfiguredPackage pkg specifiedFlags stanzas specifiedDeps') =+  (SolverPackage pkg specifiedFlags stanzas specifiedDeps' _specifiedExeDeps') =      [ DuplicateFlag flag | ((flag,_):_) <- duplicates specifiedFlags ]   ++ [ MissingFlag flag | OnlyInLeft  flag <- mergedFlags ]   ++ [ ExtraFlag   flag | OnlyInRight flag <- mergedFlags ]@@ -749,9 +852,10 @@   ++ [ ExtraDep       pkgid | OnlyInRight     pkgid <- mergedDeps ]   ++ [ InvalidDep dep pkgid | InBoth      dep pkgid <- mergedDeps                             , not (packageSatisfiesDependency pkgid dep) ]+  -- TODO: sanity tests on executable deps   where     specifiedDeps :: ComponentDeps [PackageId]-    specifiedDeps = fmap (map confSrcId) specifiedDeps'+    specifiedDeps = fmap (map solverSrcId) specifiedDeps'      mergedFlags = mergeBy compare       (sort $ map PD.flagName (PD.genPackageFlags (packageDescription pkg)))@@ -785,12 +889,13 @@     -- of the `nubOn` in `mergeDeps`.     requiredDeps :: [Dependency]     requiredDeps =-      --TODO: use something lower level than finalizePackageDescription-      case finalizePackageDescription specifiedFlags+      --TODO: use something lower level than finalizePD+      case finalizePD specifiedFlags+         (enableStanzas stanzas)          (const True)          platform cinfo          []-         (enableStanzas stanzas $ packageDescription pkg) of+         (packageDescription pkg) of         Right (resolvedPkg, _) ->              externalBuildDepends resolvedPkg           ++ maybe [] PD.setupDepends (PD.setupBuildInfo resolvedPkg)@@ -817,14 +922,15 @@ -- It simply means preferences for installed packages will be ignored. -- resolveWithoutDependencies :: DepResolverParams-                           -> Either [ResolveNoDepsError] [SourcePackage]+                           -> Either [ResolveNoDepsError] [UnresolvedSourcePackage] resolveWithoutDependencies (DepResolverParams targets constraints                               prefs defpref installedPkgIndex sourcePkgIndex-                              _reorderGoals _indGoals _avoidReinstalls-                              _shadowing _strFlags _maxBjumps) =-    collectEithers (map selectPackage targets)+                              _reorderGoals _countConflicts _indGoals _avoidReinstalls+                              _shadowing _strFlags _maxBjumps _enableBj+                              _solveExes _allowBootLibInstalls _order _verbosity) =+    collectEithers $ map selectPackage (Set.toList targets)   where-    selectPackage :: PackageName -> Either ResolveNoDepsError SourcePackage+    selectPackage :: PackageName -> Either ResolveNoDepsError UnresolvedSourcePackage     selectPackage pkgname       | null choices = Left  $! ResolveUnsatisfiable pkgname requiredVersions       | otherwise    = Right $! maximumBy bestByPrefs choices@@ -856,12 +962,12 @@       Map.findWithDefault anyVersion pkgname packageVersionConstraintMap     packageVersionConstraintMap =       let pcs = map unlabelPackageConstraint constraints-      in Map.fromList [ (name, range)-                      | PackageConstraintVersion name range <- pcs ]+      in Map.fromList [ (scopeToPackageName scope, range)+                      | PackageConstraint+                          scope (PackagePropertyVersion range) <- pcs ]      packagePreferences :: PackageName -> PackagePreferences-    packagePreferences = interpretPackagesPreference-                           (Set.fromList targets) defpref prefs+    packagePreferences = interpretPackagesPreference targets defpref prefs   collectEithers :: [Either a b] -> Either [a] [b]
− Distribution/Client/Dependency/Modular.hs
@@ -1,59 +0,0 @@-module Distribution.Client.Dependency.Modular-         ( modularResolver, SolverConfig(..)) where---- Here, we try to map between the external cabal-install solver--- interface and the internal interface that the solver actually--- expects. There are a number of type conversions to perform: we--- have to convert the package indices to the uniform index used--- by the solver; we also have to convert the initial constraints;--- and finally, we have to convert back the resulting install--- plan.--import Data.Map as M-         ( fromListWith )-import Distribution.Client.Dependency.Modular.Assignment-         ( Assignment, toCPs )-import Distribution.Client.Dependency.Modular.Dependency-         ( RevDepMap )-import Distribution.Client.Dependency.Modular.ConfiguredConversion-         ( convCP )-import Distribution.Client.Dependency.Modular.IndexConversion-         ( convPIs )-import Distribution.Client.Dependency.Modular.Log-         ( logToProgress )-import Distribution.Client.Dependency.Modular.Package-         ( PN )-import Distribution.Client.Dependency.Modular.Solver-         ( SolverConfig(..), solve )-import Distribution.Client.Dependency.Types-         ( DependencyResolver, ResolverPackage-         , PackageConstraint(..), unlabelPackageConstraint )-import Distribution.System-         ( Platform(..) )---- | Ties the two worlds together: classic cabal-install vs. the modular--- solver. Performs the necessary translations before and after.-modularResolver :: SolverConfig -> DependencyResolver-modularResolver sc (Platform arch os) cinfo iidx sidx pkgConfigDB pprefs pcs pns =-  fmap (uncurry postprocess)      $ -- convert install plan-  logToProgress (maxBackjumps sc) $ -- convert log format into progress format-  solve sc cinfo idx pkgConfigDB pprefs gcs pns-    where-      -- Indices have to be converted into solver-specific uniform index.-      idx    = convPIs os arch cinfo (shadowPkgs sc) (strongFlags sc) iidx sidx-      -- Constraints have to be converted into a finite map indexed by PN.-      gcs    = M.fromListWith (++) (map pair pcs)-        where-          pair lpc = (pcName $ unlabelPackageConstraint lpc, [lpc])--      -- Results have to be converted into an install plan.-      postprocess :: Assignment -> RevDepMap -> [ResolverPackage]-      postprocess a rdm = map (convCP iidx sidx) (toCPs a rdm)--      -- Helper function to extract the PN from a constraint.-      pcName :: PackageConstraint -> PN-      pcName (PackageConstraintVersion   pn _) = pn-      pcName (PackageConstraintInstalled pn  ) = pn-      pcName (PackageConstraintSource    pn  ) = pn-      pcName (PackageConstraintFlags     pn _) = pn-      pcName (PackageConstraintStanzas   pn _) = pn
− Distribution/Client/Dependency/Modular/Assignment.hs
@@ -1,150 +0,0 @@-module Distribution.Client.Dependency.Modular.Assignment-    ( Assignment(..)-    , FAssignment-    , SAssignment-    , PreAssignment(..)-    , extend-    , toCPs-    ) where--import Control.Applicative-import Control.Monad-import Data.Array as A-import Data.List as L-import Data.Map as M-import Data.Maybe-import Prelude hiding (pi)--import Language.Haskell.Extension (Extension, Language)--import Distribution.PackageDescription (FlagAssignment) -- from Cabal-import Distribution.Client.Types (OptionalStanza)-import Distribution.Client.Utils.LabeledGraph-import Distribution.Client.ComponentDeps (ComponentDeps, Component)-import qualified Distribution.Client.ComponentDeps as CD--import Distribution.Client.Dependency.Modular.Configured-import Distribution.Client.Dependency.Modular.Dependency-import Distribution.Client.Dependency.Modular.Flag-import Distribution.Client.Dependency.Modular.Package-import Distribution.Client.Dependency.Modular.Version---- | A (partial) package assignment. Qualified package names--- are associated with instances.-type PAssignment    = Map QPN I---- | A (partial) package preassignment. Qualified package names--- are associated with constrained instances. Constrained instances--- record constraints about the instances that can still be chosen,--- and in the extreme case fix a concrete instance.-type PPreAssignment = Map QPN (CI QPN)-type FAssignment    = Map QFN Bool-type SAssignment    = Map QSN Bool---- | A (partial) assignment of variables.-data Assignment = A PAssignment FAssignment SAssignment-  deriving (Show, Eq)---- | A preassignment comprises knowledge about variables, but not--- necessarily fixed values.-data PreAssignment = PA PPreAssignment FAssignment SAssignment---- | Extend a package preassignment.------ Takes the variable that causes the new constraints, a current preassignment--- and a set of new dependency constraints.------ We're trying to extend the preassignment with each dependency one by one.--- Each dependency is for a particular variable. We check if we already have--- constraints for that variable in the current preassignment. If so, we're--- trying to merge the constraints.------ Either returns a witness of the conflict that would arise during the merge,--- or the successfully extended assignment.-extend :: (Extension -> Bool) -- ^ is a given extension supported-       -> (Language  -> Bool) -- ^ is a given language supported-       -> (PN -> VR  -> Bool) -- ^ is a given pkg-config requirement satisfiable-       -> Var QPN-       -> PPreAssignment -> [Dep QPN] -> Either (ConflictSet QPN, [Dep QPN]) PPreAssignment-extend extSupported langSupported pkgPresent var = foldM extendSingle-  where--    extendSingle :: PPreAssignment -> Dep QPN-                 -> Either (ConflictSet QPN, [Dep QPN]) PPreAssignment-    extendSingle a (Ext  ext )  =-      if extSupported  ext  then Right a-                            else Left (varToConflictSet var, [Ext ext])-    extendSingle a (Lang lang)  =-      if langSupported lang then Right a-                            else Left (varToConflictSet var, [Lang lang])-    extendSingle a (Pkg pn vr)  =-      if pkgPresent pn vr then Right a-                          else Left (varToConflictSet var, [Pkg pn vr])-    extendSingle a (Dep qpn ci) =-      let ci' = M.findWithDefault (Constrained []) qpn a-      in  case (\ x -> M.insert qpn x a) <$> merge ci' ci of-            Left (c, (d, d')) -> Left  (c, L.map (Dep qpn) (simplify (P qpn) d d'))-            Right x           -> Right x--    -- We're trying to remove trivial elements of the conflict. If we're just-    -- making a choice pkg == instance, and pkg => pkg == instance is a part-    -- of the conflict, then this info is clear from the context and does not-    -- have to be repeated.-    simplify v (Fixed _ var') c | v == var && var' == var = [c]-    simplify v c (Fixed _ var') | v == var && var' == var = [c]-    simplify _ c              d                           = [c, d]---- | Delivers an ordered list of fully configured packages.------ TODO: This function is (sort of) ok. However, there's an open bug--- w.r.t. unqualification. There might be several different instances--- of one package version chosen by the solver, which will lead to--- clashes.-toCPs :: Assignment -> RevDepMap -> [CP QPN]-toCPs (A pa fa sa) rdm =-  let-    -- get hold of the graph-    g   :: Graph Component-    vm  :: Vertex -> ((), QPN, [(Component, QPN)])-    cvm :: QPN -> Maybe Vertex-    -- Note that the RevDepMap contains duplicate dependencies. Therefore the nub.-    (g, vm, cvm) = graphFromEdges (L.map (\ (x, xs) -> ((), x, nub xs))-                                  (M.toList rdm))-    tg :: Graph Component-    tg = transposeG g-    -- Topsort the dependency graph, yielding a list of pkgs in the right order.-    -- The graph will still contain all the installed packages, and it might-    -- contain duplicates, because several variables might actually resolve to-    -- the same package in the presence of qualified package names.-    ps :: [PI QPN]-    ps = L.map ((\ (_, x, _) -> PI x (pa M.! x)) . vm) $-         topSort g-    -- Determine the flags per package, by walking over and regrouping the-    -- complete flag assignment by package.-    fapp :: Map QPN FlagAssignment-    fapp = M.fromListWith (++) $-           L.map (\ ((FN (PI qpn _) fn), b) -> (qpn, [(fn, b)])) $-           M.toList $-           fa-    -- Stanzas per package.-    sapp :: Map QPN [OptionalStanza]-    sapp = M.fromListWith (++) $-           L.map (\ ((SN (PI qpn _) sn), b) -> (qpn, if b then [sn] else [])) $-           M.toList $-           sa-    -- Dependencies per package.-    depp :: QPN -> [(Component, PI QPN)]-    depp qpn = let v :: Vertex-                   v   = fromJust (cvm qpn)-                   dvs :: [(Component, Vertex)]-                   dvs = tg A.! v-               in L.map (\ (comp, dv) -> case vm dv of (_, x, _) -> (comp, PI x (pa M.! x))) dvs-    -- Translated to PackageDeps-    depp' :: QPN -> ComponentDeps [PI QPN]-    depp' = CD.fromList . L.map (\(comp, d) -> (comp, [d])) . depp-  in-    L.map (\ pi@(PI qpn _) -> CP pi-                                 (M.findWithDefault [] qpn fapp)-                                 (M.findWithDefault [] qpn sapp)-                                 (depp' qpn))-          ps
− Distribution/Client/Dependency/Modular/Builder.hs
@@ -1,188 +0,0 @@-{-# LANGUAGE CPP #-}-module Distribution.Client.Dependency.Modular.Builder (buildTree) where---- Building the search tree.------ In this phase, we build a search tree that is too large, i.e, it contains--- invalid solutions. We keep track of the open goals at each point. We--- nondeterministically pick an open goal (via a goal choice node), create--- subtrees according to the index and the available solutions, and extend the--- set of open goals by superficially looking at the dependencies recorded in--- the index.------ For each goal, we keep track of all the *reasons* why it is being--- introduced. These are for debugging and error messages, mainly. A little bit--- of care has to be taken due to the way we treat flags. If a package has--- flag-guarded dependencies, we cannot introduce them immediately. Instead, we--- store the entire dependency.--import Data.List as L-import Data.Map as M-import Prelude hiding (sequence, mapM)--import Distribution.Client.Dependency.Modular.Dependency-import Distribution.Client.Dependency.Modular.Flag-import Distribution.Client.Dependency.Modular.Index-import Distribution.Client.Dependency.Modular.Package-import Distribution.Client.Dependency.Modular.PSQ (PSQ)-import qualified Distribution.Client.Dependency.Modular.PSQ as P-import Distribution.Client.Dependency.Modular.Tree--import Distribution.Client.ComponentDeps (Component)---- | The state needed during the build phase of the search tree.-data BuildState = BS {-  index :: Index,                -- ^ information about packages and their dependencies-  rdeps :: RevDepMap,            -- ^ set of all package goals, completed and open, with reverse dependencies-  open  :: PSQ (OpenGoal ()) (), -- ^ set of still open goals (flag and package goals)-  next  :: BuildType,            -- ^ kind of node to generate next-  qualifyOptions :: QualifyOptions -- ^ qualification options-}---- | Extend the set of open goals with the new goals listed.------ We also adjust the map of overall goals, and keep track of the--- reverse dependencies of each of the goals.-extendOpen :: QPN -> [OpenGoal Component] -> BuildState -> BuildState-extendOpen qpn' gs s@(BS { rdeps = gs', open = o' }) = go gs' o' gs-  where-    go :: RevDepMap -> PSQ (OpenGoal ()) () -> [OpenGoal Component] -> BuildState-    go g o []                                               = s { rdeps = g, open = o }-    go g o (ng@(OpenGoal (Flagged _ _ _ _)      _gr) : ngs) = go g (cons' ng () o) ngs-      -- Note: for 'Flagged' goals, we always insert, so later additions win.-      -- This is important, because in general, if a goal is inserted twice,-      -- the later addition will have better dependency information.-    go g o (ng@(OpenGoal (Stanza  _   _  )      _gr) : ngs) = go g (cons' ng () o) ngs-    go g o (ng@(OpenGoal (Simple (Dep qpn _) c) _gr) : ngs)-      | qpn == qpn'       = go                            g               o  ngs-          -- we ignore self-dependencies at this point; TODO: more care may be needed-      | qpn `M.member` g  = go (M.adjust ((c, qpn'):) qpn g)              o  ngs-      | otherwise         = go (M.insert qpn [(c, qpn')]  g) (cons' ng () o) ngs-          -- code above is correct; insert/adjust have different arg order-    go g o (   (OpenGoal (Simple (Ext _ext ) _) _gr) : ngs) = go g o ngs-    go g o (   (OpenGoal (Simple (Lang _lang)_) _gr) : ngs) = go g o ngs-    go g o (   (OpenGoal (Simple (Pkg _pn _vr)_) _gr) : ngs)= go g o ngs--    cons' = P.cons . forgetCompOpenGoal---- | Given the current scope, qualify all the package names in the given set of--- dependencies and then extend the set of open goals accordingly.-scopedExtendOpen :: QPN -> I -> QGoalReason -> FlaggedDeps Component PN -> FlagInfo ->-                    BuildState -> BuildState-scopedExtendOpen qpn i gr fdeps fdefs s = extendOpen qpn gs s-  where-    -- Qualify all package names-    qfdeps = qualifyDeps (qualifyOptions s) qpn fdeps-    -- Introduce all package flags-    qfdefs = L.map (\ (fn, b) -> Flagged (FN (PI qpn i) fn) b [] []) $ M.toList fdefs-    -- Combine new package and flag goals-    gs     = L.map (flip OpenGoal gr) (qfdefs ++ qfdeps)-    -- NOTE:-    ---    -- In the expression @qfdefs ++ qfdeps@ above, flags occur potentially-    -- multiple times, both via the flag declaration and via dependencies.-    -- The order is potentially important, because the occurrences via-    -- dependencies may record flag-dependency information. After a number-    -- of bugs involving computing this information incorrectly, however,-    -- we're currently not using carefully computed inter-flag dependencies-    -- anymore, but instead use 'simplifyVar' when computing conflict sets-    -- to map all flags of one package to a single flag for conflict set-    -- purposes, thereby treating them all as interdependent.-    ---    -- If we ever move to a more clever algorithm again, then the line above-    -- needs to be looked at very carefully, and probably be replaced by-    -- more systematically computed flag dependency information.---- | Datatype that encodes what to build next-data BuildType =-    Goals                                  -- ^ build a goal choice node-  | OneGoal (OpenGoal ())                  -- ^ build a node for this goal-  | Instance QPN I PInfo QGoalReason  -- ^ build a tree for a concrete instance-  deriving Show--build :: BuildState -> Tree QGoalReason-build = ana go-  where-    go :: BuildState -> TreeF QGoalReason BuildState--    -- If we have a choice between many goals, we just record the choice in-    -- the tree. We select each open goal in turn, and before we descend, remove-    -- it from the queue of open goals.-    go bs@(BS { rdeps = rds, open = gs, next = Goals })-      | P.null gs = DoneF rds-      | otherwise = GoalChoiceF (P.mapWithKey (\ g (_sc, gs') -> bs { next = OneGoal g, open = gs' })-                                              (P.splits gs))--    -- If we have already picked a goal, then the choice depends on the kind-    -- of goal.-    ---    -- For a package, we look up the instances available in the global info,-    -- and then handle each instance in turn.-    go    (BS { index = _  , next = OneGoal (OpenGoal (Simple (Ext _             ) _) _ ) }) =-      error "Distribution.Client.Dependency.Modular.Builder: build.go called with Ext goal"-    go    (BS { index = _  , next = OneGoal (OpenGoal (Simple (Lang _            ) _) _ ) }) =-      error "Distribution.Client.Dependency.Modular.Builder: build.go called with Lang goal"-    go    (BS { index = _  , next = OneGoal (OpenGoal (Simple (Pkg _ _          ) _) _ ) }) =-      error "Distribution.Client.Dependency.Modular.Builder: build.go called with Pkg goal"-    go bs@(BS { index = idx, next = OneGoal (OpenGoal (Simple (Dep qpn@(Q _ pn) _) _) gr) }) =-      -- If the package does not exist in the index, we construct an emty PChoiceF node for it-      -- After all, we have no choices here. Alternatively, we could immediately construct-      -- a Fail node here, but that would complicate the construction of conflict sets.-      -- We will probably want to give this case special treatment when generating error-      -- messages though.-      case M.lookup pn idx of-        Nothing  -> PChoiceF qpn gr (P.fromList [])-        Just pis -> PChoiceF qpn gr (P.fromList (L.map (\ (i, info) ->-                                                           (POption i Nothing, bs { next = Instance qpn i info gr }))-                                                         (M.toList pis)))-          -- TODO: data structure conversion is rather ugly here--    -- For a flag, we create only two subtrees, and we create them in the order-    -- that is indicated by the flag default.-    ---    -- TODO: Should we include the flag default in the tree?-    go bs@(BS { next = OneGoal (OpenGoal (Flagged qfn@(FN (PI qpn _) _) (FInfo b m w) t f) gr) }) =-      FChoiceF qfn gr (w || trivial) m (P.fromList (reorder b-        [(True,  (extendOpen qpn (L.map (flip OpenGoal (FDependency qfn True )) t) bs) { next = Goals }),-         (False, (extendOpen qpn (L.map (flip OpenGoal (FDependency qfn False)) f) bs) { next = Goals })]))-      where-        reorder True  = id-        reorder False = reverse-        trivial = L.null t && L.null f--    -- For a stanza, we also create only two subtrees. The order is initially-    -- False, True. This can be changed later by constraints (force enabling-    -- the stanza by replacing the False branch with failure) or preferences-    -- (try enabling the stanza if possible by moving the True branch first).--    go bs@(BS { next = OneGoal (OpenGoal (Stanza qsn@(SN (PI qpn _) _) t) gr) }) =-      SChoiceF qsn gr trivial (P.fromList-        [(False,                                                             bs  { next = Goals }),-         (True,  (extendOpen qpn (L.map (flip OpenGoal (SDependency qsn)) t) bs) { next = Goals })])-      where-        trivial = L.null t--    -- For a particular instance, we change the state: we update the scope,-    -- and furthermore we update the set of goals.-    ---    -- TODO: We could inline this above.-    go bs@(BS { next = Instance qpn i (PInfo fdeps fdefs _) _gr }) =-      go ((scopedExtendOpen qpn i (PDependency (PI qpn i)) fdeps fdefs bs)-             { next = Goals })---- | Interface to the tree builder. Just takes an index and a list of package names,--- and computes the initial state and then the tree from there.-buildTree :: Index -> Bool -> [PN] -> Tree QGoalReason-buildTree idx ind igs =-    build BS {-        index = idx-      , rdeps = M.fromList (L.map (\ qpn -> (qpn, []))              qpns)-      , open  = P.fromList (L.map (\ qpn -> (topLevelGoal qpn, ())) qpns)-      , next  = Goals-      , qualifyOptions = defaultQualifyOptions idx-      }-  where-    topLevelGoal qpn = OpenGoal (Simple (Dep qpn (Constrained [])) ()) UserGoal--    qpns | ind       = makeIndependent igs-         | otherwise = L.map (Q (PP DefaultNamespace Unqualified)) igs
− Distribution/Client/Dependency/Modular/Configured.hs
@@ -1,13 +0,0 @@-module Distribution.Client.Dependency.Modular.Configured-    ( CP(..)-    ) where--import Distribution.PackageDescription (FlagAssignment) -- from Cabal-import Distribution.Client.Types (OptionalStanza)-import Distribution.Client.ComponentDeps (ComponentDeps)--import Distribution.Client.Dependency.Modular.Package---- | A configured package is a package instance together with--- a flag assignment and complete dependencies.-data CP qpn = CP (PI qpn) FlagAssignment [OptionalStanza] (ComponentDeps [PI qpn])
− Distribution/Client/Dependency/Modular/ConfiguredConversion.hs
@@ -1,54 +0,0 @@-module Distribution.Client.Dependency.Modular.ConfiguredConversion-    ( convCP-    ) where--import Data.Maybe-import Prelude hiding (pi)--import Distribution.Package (UnitId)--import Distribution.Client.Types-import Distribution.Client.Dependency.Types (ResolverPackage(..))-import qualified Distribution.Client.PackageIndex as CI-import qualified Distribution.Simple.PackageIndex as SI--import Distribution.Client.Dependency.Modular.Configured-import Distribution.Client.Dependency.Modular.Package--import Distribution.Client.ComponentDeps (ComponentDeps)---- | Converts from the solver specific result @CP QPN@ into--- a 'ResolverPackage', which can then be converted into--- the install plan.-convCP :: SI.InstalledPackageIndex ->-          CI.PackageIndex SourcePackage ->-          CP QPN -> ResolverPackage-convCP iidx sidx (CP qpi fa es ds) =-  case convPI qpi of-    Left  pi -> PreExisting-                  (fromJust $ SI.lookupUnitId iidx pi)-    Right pi -> Configured $ ConfiguredPackage-                  srcpkg-                  fa-                  es-                  ds'-      where-        Just srcpkg = CI.lookupPackageId sidx pi-  where-    ds' :: ComponentDeps [ConfiguredId]-    ds' = fmap (map convConfId) ds--convPI :: PI QPN -> Either UnitId PackageId-convPI (PI _ (I _ (Inst pi))) = Left pi-convPI qpi                    = Right $ confSrcId $ convConfId qpi--convConfId :: PI QPN -> ConfiguredId-convConfId (PI (Q _ pn) (I v loc)) = ConfiguredId {-      confSrcId  = sourceId-    , confInstId = installedId-    }-  where-    sourceId    = PackageIdentifier pn v-    installedId = case loc of-                    Inst pi    -> pi-                    _otherwise -> fakeUnitId sourceId
− Distribution/Client/Dependency/Modular/ConflictSet.hs
@@ -1,74 +0,0 @@-{-# LANGUAGE CPP #-}--- | Conflict sets------ Intended for double import------ > import Distribution.Client.Dependency.Modular.ConflictSet (ConflictSet)--- > import qualified Distribution.Client.Dependency.Modular.ConflictSet as CS-module Distribution.Client.Dependency.Modular.ConflictSet (-    ConflictSet -- opaque-  , showCS-    -- Set-like operations-  , toList-  , union-  , unions-  , insert-  , empty-  , singleton-  , member-  , filter-  , fromList-  ) where--import Prelude hiding (filter)-import Data.List (intercalate)-import Data.Set (Set)-import qualified Data.Set as S--import Distribution.Client.Dependency.Modular.Package-import Distribution.Client.Dependency.Modular.Var---- | The set of variables involved in a solver conflict------ Since these variables should be preprocessed in some way, this type is--- kept abstract.-newtype ConflictSet qpn = CS { fromConflictSet :: Set (Var qpn) }-  deriving (Eq, Ord, Show)--showCS :: ConflictSet QPN -> String-showCS = intercalate ", " . map showVar . toList--{--------------------------------------------------------------------------------  Set-like operations--------------------------------------------------------------------------------}--toList :: ConflictSet qpn -> [Var qpn]-toList = S.toList . fromConflictSet--union :: Ord qpn => ConflictSet qpn -> ConflictSet qpn -> ConflictSet qpn-union (CS a) (CS b) = CS (a `S.union` b)--unions :: Ord qpn => [ConflictSet qpn] -> ConflictSet qpn-unions = CS . S.unions . map fromConflictSet--insert :: Ord qpn => Var qpn -> ConflictSet qpn -> ConflictSet qpn-insert var (CS set) = CS (S.insert (simplifyVar var) set)--empty :: ConflictSet qpn-empty = CS S.empty--singleton :: Var qpn -> ConflictSet qpn-singleton = CS . S.singleton . simplifyVar--member :: Ord qpn => Var qpn -> ConflictSet qpn -> Bool-member var (CS set) = S.member (simplifyVar var) set--#if MIN_VERSION_containers(0,5,0)-filter :: (Var qpn -> Bool) -> ConflictSet qpn -> ConflictSet qpn-#else-filter :: Ord qpn => (Var qpn -> Bool) -> ConflictSet qpn -> ConflictSet qpn-#endif-filter p (CS set) = CS $ S.filter p set--fromList :: Ord qpn => [Var qpn] -> ConflictSet qpn-fromList = CS . S.fromList . map simplifyVar
− Distribution/Client/Dependency/Modular/Cycles.hs
@@ -1,52 +0,0 @@-{-# LANGUAGE CPP #-}-module Distribution.Client.Dependency.Modular.Cycles (-    detectCyclesPhase-  ) where--import Prelude hiding (cycle)-import Data.Graph (SCC)-import qualified Data.Graph as Gr-import qualified Data.Map   as Map--import Distribution.Client.Dependency.Modular.Dependency-import Distribution.Client.Dependency.Modular.Package-import Distribution.Client.Dependency.Modular.Tree-import qualified Distribution.Client.Dependency.Modular.ConflictSet as CS---- | Find and reject any solutions that are cyclic-detectCyclesPhase :: Tree QGoalReason -> Tree QGoalReason-detectCyclesPhase = cata go-  where-    -- The only node of interest is DoneF-    go :: TreeF QGoalReason (Tree QGoalReason) -> Tree QGoalReason-    go (PChoiceF qpn gr     cs) = PChoice qpn gr     cs-    go (FChoiceF qfn gr w m cs) = FChoice qfn gr w m cs-    go (SChoiceF qsn gr w   cs) = SChoice qsn gr w   cs-    go (GoalChoiceF         cs) = GoalChoice         cs-    go (FailF cs reason)        = Fail cs reason--    -- We check for cycles only if we have actually found a solution-    -- This minimizes the number of cycle checks we do as cycles are rare-    go (DoneF revDeps) = do-      case findCycles revDeps of-        Nothing     -> Done revDeps-        Just relSet -> Fail relSet CyclicDependencies---- | Given the reverse dependency map from a 'Done' node in the tree, as well--- as the full conflict set containing all decisions that led to that 'Done'--- node, check if the solution is cyclic. If it is, return the conflict set--- containing all decisions that could potentially break the cycle.-findCycles :: RevDepMap -> Maybe (ConflictSet QPN)-findCycles revDeps =-    case cycles of-      []  -> Nothing-      c:_ -> Just $ CS.unions $ map (varToConflictSet . P) c-  where-    cycles :: [[QPN]]-    cycles = [vs | Gr.CyclicSCC vs <- scc]--    scc :: [SCC QPN]-    scc = Gr.stronglyConnComp . map aux . Map.toList $ revDeps--    aux :: (QPN, [(comp, QPN)]) -> (QPN, QPN, [QPN])-    aux (fr, to) = (fr, fr, map snd to)
− Distribution/Client/Dependency/Modular/Dependency.hs
@@ -1,400 +0,0 @@-{-# LANGUAGE DeriveFunctor #-}-{-# LANGUAGE RecordWildCards #-}-module Distribution.Client.Dependency.Modular.Dependency (-    -- * Variables-    Var(..)-  , simplifyVar-  , varPI-    -- * Conflict sets-  , ConflictSet-  , CS.showCS-    -- * Constrained instances-  , CI(..)-  , merge-    -- * Flagged dependencies-  , FlaggedDeps-  , FlaggedDep(..)-  , Dep(..)-  , showDep-  , flattenFlaggedDeps-  , QualifyOptions(..)-  , qualifyDeps-  , unqualifyDeps-    -- ** Setting/forgetting components-  , forgetCompOpenGoal-  , setCompFlaggedDeps-    -- * Reverse dependency map-  , RevDepMap-    -- * Goals-  , Goal(..)-  , GoalReason(..)-  , QGoalReason-  , ResetVar(..)-  , goalVarToConflictSet-  , varToConflictSet-  , goalReasonToVars-    -- * Open goals-  , OpenGoal(..)-  , close-  ) where--import Prelude hiding (pi)--import Data.Map (Map)-import qualified Data.List as L--import Language.Haskell.Extension (Extension(..), Language(..))--import Distribution.Text--import Distribution.Client.Dependency.Modular.ConflictSet (ConflictSet)-import Distribution.Client.Dependency.Modular.Flag-import Distribution.Client.Dependency.Modular.Package-import Distribution.Client.Dependency.Modular.Var-import Distribution.Client.Dependency.Modular.Version-import qualified Distribution.Client.Dependency.Modular.ConflictSet as CS--import Distribution.Client.ComponentDeps (Component(..))--{--------------------------------------------------------------------------------  Constrained instances--------------------------------------------------------------------------------}---- | Constrained instance. If the choice has already been made, this is--- a fixed instance, and we record the package name for which the choice--- is for convenience. Otherwise, it is a list of version ranges paired with--- the goals / variables that introduced them.-data CI qpn = Fixed I (Var qpn) | Constrained [VROrigin qpn]-  deriving (Eq, Show, Functor)--showCI :: CI QPN -> String-showCI (Fixed i _)      = "==" ++ showI i-showCI (Constrained vr) = showVR (collapse vr)---- | Merge constrained instances. We currently adopt a lazy strategy for--- merging, i.e., we only perform actual checking if one of the two choices--- is fixed. If the merge fails, we return a conflict set indicating the--- variables responsible for the failure, as well as the two conflicting--- fragments.------ Note that while there may be more than one conflicting pair of version--- ranges, we only return the first we find.------ TODO: Different pairs might have different conflict sets. We're--- obviously interested to return a conflict that has a "better" conflict--- set in the sense the it contains variables that allow us to backjump--- further. We might apply some heuristics here, such as to change the--- order in which we check the constraints.-merge :: Ord qpn => CI qpn -> CI qpn -> Either (ConflictSet qpn, (CI qpn, CI qpn)) (CI qpn)-merge c@(Fixed i g1)       d@(Fixed j g2)-  | i == j                                    = Right c-  | otherwise                                 = Left (CS.union (varToConflictSet g1) (varToConflictSet g2), (c, d))-merge c@(Fixed (I v _) g1)   (Constrained rs) = go rs -- I tried "reverse rs" here, but it seems to slow things down ...-  where-    go []              = Right c-    go (d@(vr, g2) : vrs)-      | checkVR vr v   = go vrs-      | otherwise      = Left (CS.union (varToConflictSet g1) (varToConflictSet g2), (c, Constrained [d]))-merge c@(Constrained _)    d@(Fixed _ _)      = merge d c-merge   (Constrained rs)     (Constrained ss) = Right (Constrained (rs ++ ss))--{--------------------------------------------------------------------------------  Flagged dependencies--------------------------------------------------------------------------------}---- | Flagged dependencies------ 'FlaggedDeps' is the modular solver's view of a packages dependencies:--- rather than having the dependencies indexed by component, each dependency--- defines what component it is in.------ However, top-level goals are also modelled as dependencies, but of course--- these don't actually belong in any component of any package. Therefore, we--- parameterize 'FlaggedDeps' and derived datatypes with a type argument that--- specifies whether or not we have a component: we only ever instantiate this--- type argument with @()@ for top-level goals, or 'Component' for everything--- else (we could express this as a kind at the type-level, but that would--- require a very recent GHC).------ Note however, crucially, that independent of the type parameters, the list--- of dependencies underneath a flag choice or stanza choices _always_ uses--- Component as the type argument. This is important: when we pick a value for--- a flag, we _must_ know what component the new dependencies belong to, or--- else we don't be able to construct fine-grained reverse dependencies.-type FlaggedDeps comp qpn = [FlaggedDep comp qpn]---- | Flagged dependencies can either be plain dependency constraints,--- or flag-dependent dependency trees.-data FlaggedDep comp qpn =-    Flagged (FN qpn) FInfo (TrueFlaggedDeps qpn) (FalseFlaggedDeps qpn)-  | Stanza  (SN qpn)       (TrueFlaggedDeps qpn)-  | Simple (Dep qpn) comp-  deriving (Eq, Show)---- | Conversatively flatten out flagged dependencies------ NOTE: We do not filter out duplicates.-flattenFlaggedDeps :: FlaggedDeps Component qpn -> [(Dep qpn, Component)]-flattenFlaggedDeps = concatMap aux-  where-    aux :: FlaggedDep Component qpn -> [(Dep qpn, Component)]-    aux (Flagged _ _ t f) = flattenFlaggedDeps t ++ flattenFlaggedDeps f-    aux (Stanza  _   t)   = flattenFlaggedDeps t-    aux (Simple d c)      = [(d, c)]--type TrueFlaggedDeps  qpn = FlaggedDeps Component qpn-type FalseFlaggedDeps qpn = FlaggedDeps Component qpn---- | A dependency (constraint) associates a package name with a--- constrained instance.------ 'Dep' intentionally has no 'Functor' instance because the type variable--- is used both to record the dependencies as well as who's doing the--- depending; having a 'Functor' instance makes bugs where we don't distinguish--- these two far too likely. (By rights 'Dep' ought to have two type variables.)-data Dep qpn = Dep  qpn (CI qpn)  -- dependency on a package-             | Ext  Extension     -- dependency on a language extension-             | Lang Language      -- dependency on a language version-             | Pkg  PN VR         -- dependency on a pkg-config package-  deriving (Eq, Show)--showDep :: Dep QPN -> String-showDep (Dep qpn (Fixed i v)            ) =-  (if P qpn /= v then showVar v ++ " => " else "") ++-  showQPN qpn ++ "==" ++ showI i-showDep (Dep qpn (Constrained [(vr, v)])) =-  showVar v ++ " => " ++ showQPN qpn ++ showVR vr-showDep (Dep qpn ci                     ) =-  showQPN qpn ++ showCI ci-showDep (Ext ext)   = "requires " ++ display ext-showDep (Lang lang) = "requires " ++ display lang-showDep (Pkg pn vr) = "requires pkg-config package "-                      ++ display pn ++ display vr-                      ++ ", not found in the pkg-config database"---- | Options for goal qualification (used in 'qualifyDeps')------ See also 'defaultQualifyOptions'-data QualifyOptions = QO {-    -- | Do we have a version of base relying on another version of base?-    qoBaseShim :: Bool--    -- Should dependencies of the setup script be treated as independent?-  , qoSetupIndependent :: Bool-  }-  deriving Show---- | Apply built-in rules for package qualifiers------ Although the behaviour of 'qualifyDeps' depends on the 'QualifyOptions',--- it is important that these 'QualifyOptions' are _static_. Qualification--- does NOT depend on flag assignment; in other words, it behaves the same no--- matter which choices the solver makes (modulo the global 'QualifyOptions');--- we rely on this in 'linkDeps' (see comment there).------ NOTE: It's the _dependencies_ of a package that may or may not be independent--- from the package itself. Package flag choices must of course be consistent.-qualifyDeps :: QualifyOptions -> QPN -> FlaggedDeps Component PN -> FlaggedDeps Component QPN-qualifyDeps QO{..} (Q pp@(PP ns q) pn) = go-  where-    go :: FlaggedDeps Component PN -> FlaggedDeps Component QPN-    go = map go1--    go1 :: FlaggedDep Component PN -> FlaggedDep Component QPN-    go1 (Flagged fn nfo t f) = Flagged (fmap (Q pp) fn) nfo (go t) (go f)-    go1 (Stanza  sn     t)   = Stanza  (fmap (Q pp) sn)     (go t)-    go1 (Simple dep comp)    = Simple (goD dep comp) comp--    -- Suppose package B has a setup dependency on package A.-    -- This will be recorded as something like-    ---    -- > Dep "A" (Constrained [(AnyVersion, Goal (P "B") reason])-    ---    -- Observe that when we qualify this dependency, we need to turn that-    -- @"A"@ into @"B-setup.A"@, but we should not apply that same qualifier-    -- to the goal or the goal reason chain.-    goD :: Dep PN -> Component -> Dep QPN-    goD (Ext  ext)    _    = Ext  ext-    goD (Lang lang)   _    = Lang lang-    goD (Pkg pkn vr)  _    = Pkg pkn vr-    goD (Dep  dep ci) comp-      | qBase  dep  = Dep (Q (PP ns (Base  pn)) dep) (fmap (Q pp) ci)-      | qSetup comp = Dep (Q (PP ns (Setup pn)) dep) (fmap (Q pp) ci)-      | otherwise   = Dep (Q (PP ns inheritedQ) dep) (fmap (Q pp) ci)--    -- If P has a setup dependency on Q, and Q has a regular dependency on R, then-    -- we say that the 'Setup' qualifier is inherited: P has an (indirect) setup-    -- dependency on R. We do not do this for the base qualifier however.-    ---    -- The inherited qualifier is only used for regular dependencies; for setup-    -- and base deppendencies we override the existing qualifier. See #3160 for-    -- a detailed discussion.-    inheritedQ :: Qualifier-    inheritedQ = case q of-                   Setup _     -> q-                   Unqualified -> q-                   Base _      -> Unqualified--    -- Should we qualify this goal with the 'Base' package path?-    qBase :: PN -> Bool-    qBase dep = qoBaseShim && unPackageName dep == "base"--    -- Should we qualify this goal with the 'Setup' packaeg path?-    qSetup :: Component -> Bool-    qSetup comp = qoSetupIndependent && comp == ComponentSetup---- | Remove qualifiers from set of dependencies------ This is used during link validation: when we link package @Q.A@ to @Q'.A@,--- then all dependencies @Q.B@ need to be linked to @Q'.B@. In order to compute--- what to link these dependencies to, we need to requalify @Q.B@ to become--- @Q'.B@; we do this by first removing all qualifiers and then calling--- 'qualifyDeps' again.-unqualifyDeps :: FlaggedDeps comp QPN -> FlaggedDeps comp PN-unqualifyDeps = go-  where-    go :: FlaggedDeps comp QPN -> FlaggedDeps comp PN-    go = map go1--    go1 :: FlaggedDep comp QPN -> FlaggedDep comp PN-    go1 (Flagged fn nfo t f) = Flagged (fmap unq fn) nfo (go t) (go f)-    go1 (Stanza  sn     t)   = Stanza  (fmap unq sn)     (go t)-    go1 (Simple dep comp)    = Simple (goD dep) comp--    goD :: Dep QPN -> Dep PN-    goD (Dep qpn ci) = Dep (unq qpn) (fmap unq ci)-    goD (Ext  ext)   = Ext ext-    goD (Lang lang)  = Lang lang-    goD (Pkg pn vr)  = Pkg pn vr--    unq :: QPN -> PN-    unq (Q _ pn) = pn--{--------------------------------------------------------------------------------  Setting/forgetting the Component--------------------------------------------------------------------------------}--forgetCompOpenGoal :: OpenGoal Component -> OpenGoal ()-forgetCompOpenGoal = mapCompOpenGoal $ const ()--setCompFlaggedDeps :: Component -> FlaggedDeps () qpn -> FlaggedDeps Component qpn-setCompFlaggedDeps = mapCompFlaggedDeps . const--{--------------------------------------------------------------------------------  Auxiliary: Mapping over the Component goal--  We don't export these, because the only type instantiations for 'a' and 'b'-  here should be () or Component. (We could express this at the type level-  if we relied on newer versions of GHC.)--------------------------------------------------------------------------------}--mapCompOpenGoal :: (a -> b) -> OpenGoal a -> OpenGoal b-mapCompOpenGoal g (OpenGoal d gr) = OpenGoal (mapCompFlaggedDep g d) gr--mapCompFlaggedDeps :: (a -> b) -> FlaggedDeps a qpn -> FlaggedDeps b qpn-mapCompFlaggedDeps = L.map . mapCompFlaggedDep--mapCompFlaggedDep :: (a -> b) -> FlaggedDep a qpn -> FlaggedDep b qpn-mapCompFlaggedDep _ (Flagged fn nfo t f) = Flagged fn nfo   t f-mapCompFlaggedDep _ (Stanza  sn     t  ) = Stanza  sn       t-mapCompFlaggedDep g (Simple  pn a      ) = Simple  pn (g a)--{--------------------------------------------------------------------------------  Reverse dependency map--------------------------------------------------------------------------------}---- | A map containing reverse dependencies between qualified--- package names.-type RevDepMap = Map QPN [(Component, QPN)]--{--------------------------------------------------------------------------------  Goals--------------------------------------------------------------------------------}---- | A goal is just a solver variable paired with a reason.--- The reason is only used for tracing.-data Goal qpn = Goal (Var qpn) (GoalReason qpn)-  deriving (Eq, Show, Functor)---- | Reason why a goal is being added to a goal set.-data GoalReason qpn =-    UserGoal-  | PDependency (PI qpn)-  | FDependency (FN qpn) Bool-  | SDependency (SN qpn)-  deriving (Eq, Show, Functor)--type QGoalReason = GoalReason QPN--class ResetVar f where-  resetVar :: Var qpn -> f qpn -> f qpn--instance ResetVar CI where-  resetVar v (Fixed i _)       = Fixed i v-  resetVar v (Constrained vrs) = Constrained (L.map (\ (x, y) -> (x, resetVar v y)) vrs)--instance ResetVar Dep where-  resetVar v (Dep qpn ci) = Dep qpn (resetVar v ci)-  resetVar _ (Ext ext)    = Ext ext-  resetVar _ (Lang lang)  = Lang lang-  resetVar _ (Pkg pn vr)  = Pkg pn vr--instance ResetVar Var where-  resetVar = const---- | Compute a singleton conflict set from a goal, containing just--- the goal variable.------ NOTE: This is just a call to 'varToConflictSet' under the hood;--- the 'GoalReason' is ignored.-goalVarToConflictSet :: Goal qpn -> ConflictSet qpn-goalVarToConflictSet (Goal g _gr) = varToConflictSet g---- | Compute a singleton conflict set from a 'Var'-varToConflictSet :: Var qpn -> ConflictSet qpn-varToConflictSet = CS.singleton---- | A goal reason is mostly just a variable paired with the--- decision we made for that variable (except for user goals,--- where we cannot really point to a solver variable). This--- function drops the decision and recovers the list of--- variables (which will be empty or contain one element).----goalReasonToVars :: GoalReason qpn -> [Var qpn]-goalReasonToVars UserGoal                 = []-goalReasonToVars (PDependency (PI qpn _)) = [P qpn]-goalReasonToVars (FDependency qfn _)      = [F qfn]-goalReasonToVars (SDependency qsn)        = [S qsn]--{--------------------------------------------------------------------------------  Open goals--------------------------------------------------------------------------------}---- | For open goals as they occur during the build phase, we need to store--- additional information about flags.-data OpenGoal comp = OpenGoal (FlaggedDep comp QPN) QGoalReason-  deriving (Eq, Show)---- | Closes a goal, i.e., removes all the extraneous information that we--- need only during the build phase.-close :: OpenGoal comp -> Goal QPN-close (OpenGoal (Simple (Dep qpn _) _) gr) = Goal (P qpn) gr-close (OpenGoal (Simple (Ext     _) _) _ ) =-  error "Distribution.Client.Dependency.Modular.Dependency.close: called on Ext goal"-close (OpenGoal (Simple (Lang    _) _) _ ) =-  error "Distribution.Client.Dependency.Modular.Dependency.close: called on Lang goal"-close (OpenGoal (Simple (Pkg   _ _) _) _ ) =-  error "Distribution.Client.Dependency.Modular.Dependency.close: called on Pkg goal"-close (OpenGoal (Flagged qfn _ _ _ )   gr) = Goal (F qfn) gr-close (OpenGoal (Stanza  qsn _)        gr) = Goal (S qsn) gr--{--------------------------------------------------------------------------------  Version ranges paired with origins--------------------------------------------------------------------------------}--type VROrigin qpn = (VR, Var qpn)---- | Helper function to collapse a list of version ranges with origins into--- a single, simplified, version range.-collapse :: [VROrigin qpn] -> VR-collapse = simplifyVR . L.foldr ((.&&.) . fst) anyVR
− Distribution/Client/Dependency/Modular/Explore.hs
@@ -1,123 +0,0 @@-module Distribution.Client.Dependency.Modular.Explore-    ( backjump-    , backjumpAndExplore-    ) where--import Data.Foldable as F-import Data.Map as M--import Distribution.Client.Dependency.Modular.Assignment-import Distribution.Client.Dependency.Modular.Dependency-import Distribution.Client.Dependency.Modular.Log-import Distribution.Client.Dependency.Modular.Message-import Distribution.Client.Dependency.Modular.Package-import qualified Distribution.Client.Dependency.Modular.PSQ as P-import qualified Distribution.Client.Dependency.Modular.ConflictSet as CS-import Distribution.Client.Dependency.Modular.Tree-import qualified Distribution.Client.Dependency.Types as T---- | This function takes the variable we're currently considering, an--- initial conflict set and a--- list of children's logs. Each log yields either a solution or a--- conflict set. The result is a combined log for the parent node that--- has explored a prefix of the children.------ We can stop traversing the children's logs if we find an individual--- conflict set that does not contain the current variable. In this--- case, we can just lift the conflict set to the current level,--- because the current level cannot possibly have contributed to this--- conflict, so no other choice at the current level would avoid the--- conflict.------ If any of the children might contain a successful solution, we can--- return it immediately. If all children contain conflict sets, we can--- take the union as the combined conflict set.------ The initial conflict set corresponds to the justification that we--- have to choose this goal at all. There is a reason why we have--- introduced the goal in the first place, and this reason is in conflict--- with the (virtual) option not to choose anything for the current--- variable. See also the comments for 'avoidSet'.----backjump :: F.Foldable t => Var QPN -> ConflictSet QPN -> t (ConflictSetLog a) -> ConflictSetLog a-backjump var initial xs = F.foldr combine logBackjump xs initial-  where-    combine :: ConflictSetLog a-            -> (ConflictSet QPN -> ConflictSetLog a)-            ->  ConflictSet QPN -> ConflictSetLog a-    combine (T.Done x)    _ _               = T.Done x-    combine (T.Fail cs)   f csAcc-      | not (var `CS.member` cs) = logBackjump cs-      | otherwise                = f (csAcc `CS.union` cs)-    combine (T.Step m ms) f cs   = T.Step m (combine ms f cs)--    logBackjump :: ConflictSet QPN -> ConflictSetLog a-    logBackjump cs = failWith (Failure cs Backjump) cs--type ConflictSetLog = T.Progress Message (ConflictSet QPN)---- | A tree traversal that simultaneously propagates conflict sets up--- the tree from the leaves and creates a log.-exploreLog :: Tree QGoalReason -> (Assignment -> ConflictSetLog (Assignment, RevDepMap))-exploreLog = cata go-  where-    go :: TreeF QGoalReason (Assignment -> ConflictSetLog (Assignment, RevDepMap))-                         -> (Assignment -> ConflictSetLog (Assignment, RevDepMap))-    go (FailF c fr)          _           = failWith (Failure c fr) c-    go (DoneF rdm)           a           = succeedWith Success (a, rdm)-    go (PChoiceF qpn gr     ts) (A pa fa sa)   =-      backjump (P qpn) (avoidSet (P qpn) gr) $    -- try children in order,-      P.mapWithKey                                -- when descending ...-        (\ i@(POption k _) r -> tryWith (TryP qpn i) $ -- log and ...-                    r (A (M.insert qpn k pa) fa sa)) -- record the pkg choice-      ts-    go (FChoiceF qfn gr _ _ ts) (A pa fa sa)   =-      backjump (F qfn) (avoidSet (F qfn) gr) $    -- try children in order,-      P.mapWithKey                                -- when descending ...-        (\ k r -> tryWith (TryF qfn k) $          -- log and ...-                    r (A pa (M.insert qfn k fa) sa)) -- record the pkg choice-      ts-    go (SChoiceF qsn gr _   ts) (A pa fa sa)   =-      backjump (S qsn) (avoidSet (S qsn) gr) $    -- try children in order,-      P.mapWithKey                                -- when descending ...-        (\ k r -> tryWith (TryS qsn k) $          -- log and ...-                    r (A pa fa (M.insert qsn k sa))) -- record the pkg choice-      ts-    go (GoalChoiceF        ts) a           =-      P.casePSQ ts-        (failWith (Failure CS.empty EmptyGoalChoice) CS.empty) -- empty goal choice is an internal error-        (\ k v _xs -> continueWith (Next (close k)) (v a))     -- commit to the first goal choice---- | Build a conflict set corresponding to the (virtual) option not to--- choose a solution for a goal at all.------ In the solver, the set of goals is not statically determined, but depends--- on the choices we make. Therefore, when dealing with conflict sets, we--- always have to consider that we could perhaps make choices that would--- avoid the existence of the goal completely.------ Whenever we actual introduce a choice in the tree, we have already established--- that the goal cannot be avoided. This is tracked in the "goal reason".--- The choice to avoid the goal therefore is a conflict between the goal itself--- and its goal reason. We build this set here, and pass it to the 'backjump'--- function as the initial conflict set.------ This has two effects:------ - In a situation where there are no choices available at all (this happens--- if an unknown package is requested), the initial conflict set becomes the--- actual conflict set.------ - In a situation where we backjump past the current node, the goal reason--- of the current node will be added to the conflict set.----avoidSet :: Var QPN -> QGoalReason -> ConflictSet QPN-avoidSet var gr =-  CS.fromList (var : goalReasonToVars gr)---- | Interface.-backjumpAndExplore :: Tree QGoalReason -> Log Message (Assignment, RevDepMap)-backjumpAndExplore t = toLog $ exploreLog t (A M.empty M.empty M.empty)-  where-    toLog :: T.Progress step fail done -> Log step done-    toLog = T.foldProgress T.Step (const (T.Fail ())) T.Done
− Distribution/Client/Dependency/Modular/Flag.hs
@@ -1,80 +0,0 @@-{-# LANGUAGE DeriveFunctor #-}-module Distribution.Client.Dependency.Modular.Flag-    ( FInfo(..)-    , Flag-    , FlagInfo-    , FN(..)-    , QFN-    , QSN-    , SN(..)-    , mkFlag-    , showFBool-    , showQFN-    , showQFNBool-    , showQSN-    , showQSNBool-    ) where--import Data.Map as M-import Prelude hiding (pi)--import Distribution.PackageDescription hiding (Flag) -- from Cabal--import Distribution.Client.Dependency.Modular.Package-import Distribution.Client.Types (OptionalStanza(..))---- | Flag name. Consists of a package instance and the flag identifier itself.-data FN qpn = FN (PI qpn) Flag-  deriving (Eq, Ord, Show, Functor)---- | Flag identifier. Just a string.-type Flag = FlagName--unFlag :: Flag -> String-unFlag (FlagName fn) = fn--mkFlag :: String -> Flag-mkFlag fn = FlagName fn---- | Flag info. Default value, whether the flag is manual, and--- whether the flag is weak. Manual flags can only be set explicitly.--- Weak flags are typically deferred by the solver.-data FInfo = FInfo { fdefault :: Bool, fmanual :: Bool, fweak :: Bool }-  deriving (Eq, Ord, Show)---- | Flag defaults.-type FlagInfo = Map Flag FInfo---- | Qualified flag name.-type QFN = FN QPN---- | Stanza name. Paired with a package name, much like a flag.-data SN qpn = SN (PI qpn) OptionalStanza-  deriving (Eq, Ord, Show, Functor)---- | Qualified stanza name.-type QSN = SN QPN--unStanza :: OptionalStanza -> String-unStanza TestStanzas  = "test"-unStanza BenchStanzas = "bench"--showQFNBool :: QFN -> Bool -> String-showQFNBool qfn@(FN pi _f) b = showPI pi ++ ":" ++ showFBool qfn b--showQSNBool :: QSN -> Bool -> String-showQSNBool qsn@(SN pi _f) b = showPI pi ++ ":" ++ showSBool qsn b--showFBool :: FN qpn -> Bool -> String-showFBool (FN _ f) True  = "+" ++ unFlag f-showFBool (FN _ f) False = "-" ++ unFlag f--showSBool :: SN qpn -> Bool -> String-showSBool (SN _ s) True  = "*" ++ unStanza s-showSBool (SN _ s) False = "!" ++ unStanza s--showQFN :: QFN -> String-showQFN (FN pi f) = showPI pi ++ ":" ++ unFlag f--showQSN :: QSN -> String-showQSN (SN pi f) = showPI pi ++ ":" ++ unStanza f
− Distribution/Client/Dependency/Modular/Index.hs
@@ -1,52 +0,0 @@-module Distribution.Client.Dependency.Modular.Index-    ( Index-    , PInfo(..)-    , defaultQualifyOptions-    , mkIndex-    ) where--import Data.List as L-import Data.Map as M-import Prelude hiding (pi)--import Distribution.Client.Dependency.Modular.Dependency-import Distribution.Client.Dependency.Modular.Flag-import Distribution.Client.Dependency.Modular.Package-import Distribution.Client.Dependency.Modular.Tree--import Distribution.Client.ComponentDeps (Component)---- | An index contains information about package instances. This is a nested--- dictionary. Package names are mapped to instances, which in turn is mapped--- to info.-type Index = Map PN (Map I PInfo)---- | Info associated with a package instance.--- Currently, dependencies, flags and failure reasons.--- Packages that have a failure reason recorded for them are disabled--- globally, for reasons external to the solver. We currently use this--- for shadowing which essentially is a GHC limitation, and for--- installed packages that are broken.-data PInfo = PInfo (FlaggedDeps Component PN) FlagInfo (Maybe FailReason)-  deriving (Show)--mkIndex :: [(PN, I, PInfo)] -> Index-mkIndex xs = M.map M.fromList (groupMap (L.map (\ (pn, i, pi) -> (pn, (i, pi))) xs))--groupMap :: Ord a => [(a, b)] -> Map a [b]-groupMap xs = M.fromListWith (flip (++)) (L.map (\ (x, y) -> (x, [y])) xs)--defaultQualifyOptions :: Index -> QualifyOptions-defaultQualifyOptions idx = QO {-      qoBaseShim         = or [ dep == base-                              | -- Find all versions of base ..-                                Just is <- [M.lookup base idx]-                                -- .. which are installed ..-                              , (I _ver (Inst _), PInfo deps _flagNfo _fr) <- M.toList is-                                -- .. and flatten all their dependencies ..-                              , (Dep dep _ci, _comp) <- flattenFlaggedDeps deps-                              ]-    , qoSetupIndependent = True-    }-  where-    base = PackageName "base"
− Distribution/Client/Dependency/Modular/IndexConversion.hs
@@ -1,219 +0,0 @@-module Distribution.Client.Dependency.Modular.IndexConversion-    ( convPIs-    ) where--import Data.List as L-import Data.Map as M-import Data.Maybe-import Data.Monoid as Mon-import Prelude hiding (pi)--import qualified Distribution.Client.PackageIndex as CI-import Distribution.Client.Types-import Distribution.Client.ComponentDeps (Component(..))-import Distribution.Compiler-import Distribution.InstalledPackageInfo as IPI-import Distribution.Package                          -- from Cabal-import Distribution.PackageDescription as PD         -- from Cabal-import Distribution.PackageDescription.Configuration as PDC-import qualified Distribution.Simple.PackageIndex as SI-import Distribution.System--import Distribution.Client.Dependency.Modular.Dependency as D-import Distribution.Client.Dependency.Modular.Flag as F-import Distribution.Client.Dependency.Modular.Index-import Distribution.Client.Dependency.Modular.Package-import Distribution.Client.Dependency.Modular.Tree-import Distribution.Client.Dependency.Modular.Version---- | Convert both the installed package index and the source package--- index into one uniform solver index.------ We use 'allPackagesBySourcePackageId' for the installed package index--- because that returns us several instances of the same package and version--- in order of preference. This allows us in principle to \"shadow\"--- packages if there are several installed packages of the same version.--- There are currently some shortcomings in both GHC and Cabal in--- resolving these situations. However, the right thing to do is to--- fix the problem there, so for now, shadowing is only activated if--- explicitly requested.-convPIs :: OS -> Arch -> CompilerInfo -> Bool -> Bool ->-           SI.InstalledPackageIndex -> CI.PackageIndex SourcePackage -> Index-convPIs os arch comp sip strfl iidx sidx =-  mkIndex (convIPI' sip iidx ++ convSPI' os arch comp strfl sidx)---- | Convert a Cabal installed package index to the simpler,--- more uniform index format of the solver.-convIPI' :: Bool -> SI.InstalledPackageIndex -> [(PN, I, PInfo)]-convIPI' sip idx =-    -- apply shadowing whenever there are multiple installed packages with-    -- the same version-    [ maybeShadow (convIP idx pkg)-    | (_pkgid, pkgs) <- SI.allPackagesBySourcePackageId idx-    , (maybeShadow, pkg) <- zip (id : repeat shadow) pkgs ]-  where--    -- shadowing is recorded in the package info-    shadow (pn, i, PInfo fdeps fds _) | sip = (pn, i, PInfo fdeps fds (Just Shadowed))-    shadow x                                = x---- | Convert a single installed package into the solver-specific format.-convIP :: SI.InstalledPackageIndex -> InstalledPackageInfo -> (PN, I, PInfo)-convIP idx ipi =-  let ipid = IPI.installedUnitId ipi-      i = I (pkgVersion (sourcePackageId ipi)) (Inst ipid)-      pn = pkgName (sourcePackageId ipi)-  in  case mapM (convIPId pn idx) (IPI.depends ipi) of-        Nothing  -> (pn, i, PInfo []            M.empty (Just Broken))-        Just fds -> (pn, i, PInfo (setComp fds) M.empty Nothing)- where-  -- We assume that all dependencies of installed packages are _library_ deps-  setComp = setCompFlaggedDeps ComponentLib--- TODO: Installed packages should also store their encapsulations!---- | Convert dependencies specified by an installed package id into--- flagged dependencies of the solver.------ May return Nothing if the package can't be found in the index. That--- indicates that the original package having this dependency is broken--- and should be ignored.-convIPId :: PN -> SI.InstalledPackageIndex -> UnitId -> Maybe (FlaggedDep () PN)-convIPId pn' idx ipid =-  case SI.lookupUnitId idx ipid of-    Nothing  -> Nothing-    Just ipi -> let i = I (pkgVersion (sourcePackageId ipi)) (Inst ipid)-                    pn = pkgName (sourcePackageId ipi)-                in  Just (D.Simple (Dep pn (Fixed i (P pn'))) ())---- | Convert a cabal-install source package index to the simpler,--- more uniform index format of the solver.-convSPI' :: OS -> Arch -> CompilerInfo -> Bool ->-            CI.PackageIndex SourcePackage -> [(PN, I, PInfo)]-convSPI' os arch cinfo strfl = L.map (convSP os arch cinfo strfl) . CI.allPackages---- | Convert a single source package into the solver-specific format.-convSP :: OS -> Arch -> CompilerInfo -> Bool -> SourcePackage -> (PN, I, PInfo)-convSP os arch cinfo strfl (SourcePackage (PackageIdentifier pn pv) gpd _ _pl) =-  let i = I pv InRepo-  in  (pn, i, convGPD os arch cinfo strfl (PI pn i) gpd)---- We do not use 'flattenPackageDescription' or 'finalizePackageDescription'--- from 'Distribution.PackageDescription.Configuration' here, because we--- want to keep the condition tree, but simplify much of the test.---- | Convert a generic package description to a solver-specific 'PInfo'.-convGPD :: OS -> Arch -> CompilerInfo -> Bool ->-           PI PN -> GenericPackageDescription -> PInfo-convGPD os arch cinfo strfl pi-        (GenericPackageDescription pkg flags libs exes tests benchs) =-  let-    fds  = flagInfo strfl flags--    conv :: Mon.Monoid a => Component -> (a -> BuildInfo) ->-            CondTree ConfVar [Dependency] a -> FlaggedDeps Component PN-    conv comp getInfo = convCondTree os arch cinfo pi fds comp getInfo .-                        PDC.addBuildableCondition getInfo-  in-    PInfo-      (maybe []    (conv ComponentLib                     libBuildInfo         ) libs    ++-       maybe []    (convSetupBuildInfo pi)    (setupBuildInfo pkg)    ++-       concatMap   (\(nm, ds) -> conv (ComponentExe nm)   buildInfo          ds) exes    ++-      prefix (Stanza (SN pi TestStanzas))-        (L.map     (\(nm, ds) -> conv (ComponentTest nm)  testBuildInfo      ds) tests)  ++-      prefix (Stanza (SN pi BenchStanzas))-        (L.map     (\(nm, ds) -> conv (ComponentBench nm) benchmarkBuildInfo ds) benchs))-      fds-      Nothing--prefix :: (FlaggedDeps comp qpn -> FlaggedDep comp' qpn) -> [FlaggedDeps comp qpn] -> FlaggedDeps comp' qpn-prefix _ []  = []-prefix f fds = [f (concat fds)]---- | Convert flag information. Automatic flags are now considered weak--- unless strong flags have been selected explicitly.-flagInfo :: Bool -> [PD.Flag] -> FlagInfo-flagInfo strfl = M.fromList . L.map (\ (MkFlag fn _ b m) -> (fn, FInfo b m (not (strfl || m))))---- | Convert condition trees to flagged dependencies.-convCondTree :: OS -> Arch -> CompilerInfo -> PI PN -> FlagInfo ->-                Component ->-                (a -> BuildInfo) ->-                CondTree ConfVar [Dependency] a -> FlaggedDeps Component PN-convCondTree os arch cinfo pi@(PI pn _) fds comp getInfo (CondNode info ds branches) =-                 L.map (\d -> D.Simple (convDep pn d) comp) ds  -- unconditional package dependencies-              ++ L.map (\e -> D.Simple (Ext  e) comp) (PD.allExtensions bi) -- unconditional extension dependencies-              ++ L.map (\l -> D.Simple (Lang l) comp) (PD.allLanguages  bi) -- unconditional language dependencies-              ++ L.map (\(Dependency pkn vr) -> D.Simple (Pkg pkn vr) comp) (PD.pkgconfigDepends bi) -- unconditional pkg-config dependencies-              ++ concatMap (convBranch os arch cinfo pi fds comp getInfo) branches-  where-    bi = getInfo info---- | Branch interpreter.------ Here, we try to simplify one of Cabal's condition tree branches into the--- solver's flagged dependency format, which is weaker. Condition trees can--- contain complex logical expression composed from flag choices and special--- flags (such as architecture, or compiler flavour). We try to evaluate the--- special flags and subsequently simplify to a tree that only depends on--- simple flag choices.-convBranch :: OS -> Arch -> CompilerInfo ->-              PI PN -> FlagInfo ->-              Component ->-              (a -> BuildInfo) ->-              (Condition ConfVar,-               CondTree ConfVar [Dependency] a,-               Maybe (CondTree ConfVar [Dependency] a)) -> FlaggedDeps Component PN-convBranch os arch cinfo pi@(PI pn _) fds comp getInfo (c', t', mf') =-  go c' (          convCondTree os arch cinfo pi fds comp getInfo   t')-        (maybe [] (convCondTree os arch cinfo pi fds comp getInfo) mf')-  where-    go :: Condition ConfVar ->-          FlaggedDeps Component PN -> FlaggedDeps Component PN -> FlaggedDeps Component PN-    go (Lit True)  t _ = t-    go (Lit False) _ f = f-    go (CNot c)    t f = go c f t-    go (CAnd c d)  t f = go c (go d t f) f-    go (COr  c d)  t f = go c t (go d t f)-    go (Var (Flag fn)) t f = extractCommon t f ++ [Flagged (FN pi fn) (fds ! fn) t f]-    go (Var (OS os')) t f-      | os == os'      = t-      | otherwise      = f-    go (Var (Arch arch')) t f-      | arch == arch'  = t-      | otherwise      = f-    go (Var (Impl cf cvr)) t f-      | matchImpl (compilerInfoId cinfo) ||-            -- fixme: Nothing should be treated as unknown, rather than empty-            --        list. This code should eventually be changed to either-            --        support partial resolution of compiler flags or to-            --        complain about incompletely configured compilers.-        any matchImpl (fromMaybe [] $ compilerInfoCompat cinfo) = t-      | otherwise      = f-      where-        matchImpl (CompilerId cf' cv) = cf == cf' && checkVR cvr cv--    -- If both branches contain the same package as a simple dep, we lift it to-    -- the next higher-level, but without constraints. This heuristic together-    -- with deferring flag choices will then usually first resolve this package,-    -- and try an already installed version before imposing a default flag choice-    -- that might not be what we want.-    ---    -- Note that we make assumptions here on the form of the dependencies that-    -- can occur at this point. In particular, no occurrences of Fixed, and no-    -- occurrences of multiple version ranges, as all dependencies below this-    -- point have been generated using 'convDep'.-    extractCommon :: FlaggedDeps Component PN -> FlaggedDeps Component PN -> FlaggedDeps Component PN-    extractCommon ps ps' = [ D.Simple (Dep pn1 (Constrained [(vr1 .||. vr2, P pn)])) comp-                           | D.Simple (Dep pn1 (Constrained [(vr1, _)])) _ <- ps-                           , D.Simple (Dep pn2 (Constrained [(vr2, _)])) _ <- ps'-                           , pn1 == pn2-                           ]---- | Convert a Cabal dependency to a solver-specific dependency.-convDep :: PN -> Dependency -> Dep PN-convDep pn' (Dependency pn vr) = Dep pn (Constrained [(vr, P pn')])---- | Convert setup dependencies-convSetupBuildInfo :: PI PN -> SetupBuildInfo -> FlaggedDeps Component PN-convSetupBuildInfo (PI pn _i) nfo =-    L.map (\d -> D.Simple (convDep pn d) ComponentSetup) (PD.setupDepends nfo)
− Distribution/Client/Dependency/Modular/Linking.hs
@@ -1,574 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE MultiParamTypeClasses #-}-module Distribution.Client.Dependency.Modular.Linking (-    addLinking-  , validateLinking-  ) where--import Prelude hiding (pi)-import Control.Exception (assert)-import Control.Monad.Reader-import Control.Monad.State-import Data.Maybe (catMaybes)-import Data.Map (Map, (!))-import Data.List (intercalate)-import Data.Set (Set)-import qualified Data.Map         as M-import qualified Data.Set         as S-import qualified Data.Traversable as T--#if !MIN_VERSION_base(4,8,0)-import Control.Applicative-#endif--import Distribution.Client.Dependency.Modular.Assignment-import Distribution.Client.Dependency.Modular.Dependency-import Distribution.Client.Dependency.Modular.Flag-import Distribution.Client.Dependency.Modular.Index-import Distribution.Client.Dependency.Modular.Package-import Distribution.Client.Dependency.Modular.Tree-import qualified Distribution.Client.Dependency.Modular.PSQ as P-import qualified Distribution.Client.Dependency.Modular.ConflictSet as CS--import Distribution.Client.Types (OptionalStanza(..))-import Distribution.Client.ComponentDeps (Component)--{--------------------------------------------------------------------------------  Add linking--------------------------------------------------------------------------------}--type RelatedGoals = Map (PN, I) [PP]-type Linker       = Reader RelatedGoals---- | Introduce link nodes into tree tree------ Linking is a traversal of the solver tree that adapts package choice nodes--- and adds the option to link wherever appropriate: Package goals are called--- "related" if they are for the same version of the same package (but have--- different prefixes). A link option is available in a package choice node--- whenever we can choose an instance that has already been chosen for a related--- goal at a higher position in the tree.------ The code here proceeds by maintaining a finite map recording choices that--- have been made at higher positions in the tree. For each pair of package name--- and instance, it stores the prefixes at which we have made a choice for this--- package instance. Whenever we make a choice, we extend the map. Whenever we--- find a choice, we look into the map in order to find out what link options we--- have to add.-addLinking :: Tree QGoalReason -> Tree QGoalReason-addLinking = (`runReader` M.empty) .  cata go-  where-    go :: TreeF QGoalReason (Linker (Tree QGoalReason)) -> Linker (Tree QGoalReason)--    -- The only nodes of interest are package nodes-    go (PChoiceF qpn gr cs) = do-      env <- ask-      cs' <- T.sequence $ P.mapWithKey (goP qpn) cs-      let newCs = concatMap (linkChoices env qpn) (P.toList cs')-      return $ PChoice qpn gr (cs' `P.union` P.fromList newCs)-    go _otherwise =-      innM _otherwise--    -- Recurse underneath package choices. Here we just need to make sure-    -- that we record the package choice so that it is available below-    goP :: QPN -> POption -> Linker (Tree QGoalReason) -> Linker (Tree QGoalReason)-    goP (Q pp pn) (POption i Nothing) = local (M.insertWith (++) (pn, i) [pp])-    goP _ _ = alreadyLinked--linkChoices :: RelatedGoals -> QPN -> (POption, Tree QGoalReason) -> [(POption, Tree QGoalReason)]-linkChoices related (Q _pp pn) (POption i Nothing, subtree) =-    map aux (M.findWithDefault [] (pn, i) related)-  where-    aux :: PP -> (POption, Tree QGoalReason)-    aux pp = (POption i (Just pp), subtree)-linkChoices _ _ (POption _ (Just _), _) =-    alreadyLinked--alreadyLinked :: a-alreadyLinked = error "addLinking called on tree that already contains linked nodes"--{--------------------------------------------------------------------------------  Validation--  Validation of links is a separate pass that's performed after normal-  validation. Validation of links checks that if the tree indicates that a-  package is linked, then everything underneath that choice really matches the-  package we have linked to.--  This is interesting because it isn't unidirectional. Consider that we've-  chosen a.foo to be version 1 and later decide that b.foo should link to a.foo.-  Now foo depends on bar. Because a.foo and b.foo are linked, it's required that-  a.bar and b.bar are also linked. However, it's not required that we actually-  choose a.bar before b.bar. Goal choice order is relatively free. It's possible-  that we choose a.bar first, but also possible that we choose b.bar first. In-  both cases, we have to recognize that we have freedom of choice for the first-  of the two, but no freedom of choice for the second.--  This is what LinkGroups are all about. Using LinkGroup, we can record (in the-  situation above) that a.bar and b.bar need to be linked even if we haven't-  chosen either of them yet.--------------------------------------------------------------------------------}--data ValidateState = VS {-      vsIndex    :: Index-    , vsLinks    :: Map QPN LinkGroup-    , vsFlags    :: FAssignment-    , vsStanzas  :: SAssignment-    , vsQualifyOptions :: QualifyOptions-    }-    deriving Show--type Validate = Reader ValidateState---- | Validate linked packages------ Verify that linked packages have------ * Linked dependencies,--- * Equal flag assignments--- * Equal stanza assignments-validateLinking :: Index -> Tree QGoalReason -> Tree QGoalReason-validateLinking index = (`runReader` initVS) . cata go-  where-    go :: TreeF QGoalReason (Validate (Tree QGoalReason)) -> Validate (Tree QGoalReason)--    go (PChoiceF qpn gr cs) =-      PChoice qpn gr     <$> T.sequence (P.mapWithKey (goP qpn) cs)-    go (FChoiceF qfn gr t m cs) =-      FChoice qfn gr t m <$> T.sequence (P.mapWithKey (goF qfn) cs)-    go (SChoiceF qsn gr t cs) =-      SChoice qsn gr t   <$> T.sequence (P.mapWithKey (goS qsn) cs)--    -- For the other nodes we just recurse-    go (GoalChoiceF         cs)       = GoalChoice          <$> T.sequence cs-    go (DoneF revDepMap)              = return $ Done revDepMap-    go (FailF conflictSet failReason) = return $ Fail conflictSet failReason--    -- Package choices-    goP :: QPN -> POption -> Validate (Tree QGoalReason) -> Validate (Tree QGoalReason)-    goP qpn@(Q _pp pn) opt@(POption i _) r = do-      vs <- ask-      let PInfo deps _ _ = vsIndex vs ! pn ! i-          qdeps          = qualifyDeps (vsQualifyOptions vs) qpn deps-      case execUpdateState (pickPOption qpn opt qdeps) vs of-        Left  (cs, err) -> return $ Fail cs (DependenciesNotLinked err)-        Right vs'       -> local (const vs') r--    -- Flag choices-    goF :: QFN -> Bool -> Validate (Tree QGoalReason) -> Validate (Tree QGoalReason)-    goF qfn b r = do-      vs <- ask-      case execUpdateState (pickFlag qfn b) vs of-        Left  (cs, err) -> return $ Fail cs (DependenciesNotLinked err)-        Right vs'       -> local (const vs') r--    -- Stanza choices (much the same as flag choices)-    goS :: QSN -> Bool -> Validate (Tree QGoalReason) -> Validate (Tree QGoalReason)-    goS qsn b r = do-      vs <- ask-      case execUpdateState (pickStanza qsn b) vs of-        Left  (cs, err) -> return $ Fail cs (DependenciesNotLinked err)-        Right vs'       -> local (const vs') r--    initVS :: ValidateState-    initVS = VS {-        vsIndex   = index-      , vsLinks   = M.empty-      , vsFlags   = M.empty-      , vsStanzas = M.empty-      , vsQualifyOptions = defaultQualifyOptions index-      }--{--------------------------------------------------------------------------------  Updating the validation state--------------------------------------------------------------------------------}--type Conflict = (ConflictSet QPN, String)--newtype UpdateState a = UpdateState {-    unUpdateState :: StateT ValidateState (Either Conflict) a-  }-  deriving (Functor, Applicative, Monad)--instance MonadState ValidateState UpdateState where-  get    = UpdateState $ get-  put st = UpdateState $ do-             assert (lgInvariant $ vsLinks st) $ return ()-             put st--lift' :: Either Conflict a -> UpdateState a-lift' = UpdateState . lift--conflict :: Conflict -> UpdateState a-conflict = lift' . Left--execUpdateState :: UpdateState () -> ValidateState -> Either Conflict ValidateState-execUpdateState = execStateT . unUpdateState--pickPOption :: QPN -> POption -> FlaggedDeps Component QPN -> UpdateState ()-pickPOption qpn (POption i Nothing)    _deps = pickConcrete qpn i-pickPOption qpn (POption i (Just pp'))  deps = pickLink     qpn i pp' deps--pickConcrete :: QPN -> I -> UpdateState ()-pickConcrete qpn@(Q pp _) i = do-    vs <- get-    case M.lookup qpn (vsLinks vs) of-      -- Package is not yet in a LinkGroup. Create a new singleton link group.-      Nothing -> do-        let lg = lgSingleton qpn (Just $ PI pp i)-        updateLinkGroup lg--      -- Package is already in a link group. Since we are picking a concrete-      -- instance here, it must by definition be the canonical package.-      Just lg ->-        makeCanonical lg qpn i--pickLink :: QPN -> I -> PP -> FlaggedDeps Component QPN -> UpdateState ()-pickLink qpn@(Q _pp pn) i pp' deps = do-    vs <- get--    -- The package might already be in a link group-    -- (because one of its reverse dependencies is)-    let lgSource = case M.lookup qpn (vsLinks vs) of-                     Nothing -> lgSingleton qpn Nothing-                     Just lg -> lg--    -- Find the link group for the package we are linking to-    ---    -- Since the builder never links to a package without having first picked a-    -- concrete instance for that package, and since we create singleton link-    -- groups for concrete instances, this link group must exist (and must-    -- in fact already have a canonical member).-    let target   = Q pp' pn-        lgTarget = vsLinks vs ! target--    -- Verify here that the member we add is in fact for the same package and-    -- matches the version of the canonical instance. However, violations of-    -- these checks would indicate a bug in the linker, not a true conflict.-    let sanityCheck :: Maybe (PI PP) -> Bool-        sanityCheck Nothing              = False-        sanityCheck (Just (PI _ canonI)) = pn == lgPackage lgTarget && i == canonI-    assert (sanityCheck (lgCanon lgTarget)) $ return ()--    -- Merge the two link groups (updateLinkGroup will propagate the change)-    lgTarget' <- lift' $ lgMerge [] lgSource lgTarget-    updateLinkGroup lgTarget'--    -- Make sure all dependencies are linked as well-    linkDeps target [P qpn] deps--makeCanonical :: LinkGroup -> QPN -> I -> UpdateState ()-makeCanonical lg qpn@(Q pp _) i =-    case lgCanon lg of-      -- There is already a canonical member. Fail.-      Just _ ->-        conflict ( CS.insert (P qpn) (lgConflictSet lg)-                 ,    "cannot make " ++ showQPN qpn-                   ++ " canonical member of " ++ showLinkGroup lg-                 )-      Nothing -> do-        let lg' = lg { lgCanon = Just (PI pp i) }-        updateLinkGroup lg'---- | Link the dependencies of linked parents.------ When we decide to link one package against another we walk through the--- package's direct depedencies and make sure that they're all linked to each--- other by merging their link groups (or creating new singleton link groups if--- they don't have link groups yet). We do not need to do this recursively,--- because having the direct dependencies in a link group means that we must--- have already made or will make sooner or later a link choice for one of these--- as well, and cover their dependencies at that point.-linkDeps :: QPN -> [Var QPN] -> FlaggedDeps Component QPN -> UpdateState ()-linkDeps target = \blame deps -> do-    -- linkDeps is called in two places: when we first link one package to-    -- another, and when we discover more dependencies of an already linked-    -- package after doing some flag assignment. It is therefore important that-    -- flag assignments cannot influence _how_ dependencies are qualified;-    -- fortunately this is a documented property of 'qualifyDeps'.-    rdeps <- requalify deps-    go blame deps rdeps-  where-    go :: [Var QPN] -> FlaggedDeps Component QPN -> FlaggedDeps Component QPN -> UpdateState ()-    go = zipWithM_ . go1--    go1 :: [Var QPN] -> FlaggedDep Component QPN -> FlaggedDep Component QPN -> UpdateState ()-    go1 blame dep rdep = case (dep, rdep) of-      (Simple (Dep qpn _) _, ~(Simple (Dep qpn' _) _)) -> do-        vs <- get-        let lg   = M.findWithDefault (lgSingleton qpn  Nothing) qpn  $ vsLinks vs-            lg'  = M.findWithDefault (lgSingleton qpn' Nothing) qpn' $ vsLinks vs-        lg'' <- lift' $ lgMerge blame lg lg'-        updateLinkGroup lg''-      (Flagged fn _ t f, ~(Flagged _ _ t' f')) -> do-        vs <- get-        case M.lookup fn (vsFlags vs) of-          Nothing    -> return () -- flag assignment not yet known-          Just True  -> go (F fn:blame) t t'-          Just False -> go (F fn:blame) f f'-      (Stanza sn t, ~(Stanza _ t')) -> do-        vs <- get-        case M.lookup sn (vsStanzas vs) of-          Nothing    -> return () -- stanza assignment not yet known-          Just True  -> go (S sn:blame) t t'-          Just False -> return () -- stanza not enabled; no new deps-    -- For extensions and language dependencies, there is nothing to do.-    -- No choice is involved, just checking, so there is nothing to link.-    -- The same goes for for pkg-config constraints.-      (Simple (Ext  _)   _, _) -> return ()-      (Simple (Lang _)   _, _) -> return ()-      (Simple (Pkg  _ _) _, _) -> return ()--    requalify :: FlaggedDeps Component QPN -> UpdateState (FlaggedDeps Component QPN)-    requalify deps = do-      vs <- get-      return $ qualifyDeps (vsQualifyOptions vs) target (unqualifyDeps deps)--pickFlag :: QFN -> Bool -> UpdateState ()-pickFlag qfn b = do-    modify $ \vs -> vs { vsFlags = M.insert qfn b (vsFlags vs) }-    verifyFlag qfn-    linkNewDeps (F qfn) b--pickStanza :: QSN -> Bool -> UpdateState ()-pickStanza qsn b = do-    modify $ \vs -> vs { vsStanzas = M.insert qsn b (vsStanzas vs) }-    verifyStanza qsn-    linkNewDeps (S qsn) b---- | Link dependencies that we discover after making a flag choice.------ When we make a flag choice for a package, then new dependencies for that--- package might become available. If the package under consideration is in a--- non-trivial link group, then these new dependencies have to be linked as--- well. In linkNewDeps, we compute such new dependencies and make sure they are--- linked.-linkNewDeps :: Var QPN -> Bool -> UpdateState ()-linkNewDeps var b = do-    vs <- get-    let (qpn@(Q pp pn), Just i) = varPI var-        PInfo deps _ _          = vsIndex vs ! pn ! i-        qdeps                   = qualifyDeps (vsQualifyOptions vs) qpn deps-        lg                      = vsLinks vs ! qpn-        (parents, newDeps)      = findNewDeps vs qdeps-        linkedTo                = S.delete pp (lgMembers lg)-    forM_ (S.toList linkedTo) $ \pp' -> linkDeps (Q pp' pn) (P qpn : parents) newDeps-  where-    findNewDeps :: ValidateState -> FlaggedDeps comp QPN -> ([Var QPN], FlaggedDeps Component QPN)-    findNewDeps vs = concatMapUnzip (findNewDeps' vs)--    findNewDeps' :: ValidateState -> FlaggedDep comp QPN -> ([Var QPN], FlaggedDeps Component QPN)-    findNewDeps' _  (Simple _ _)        = ([], [])-    findNewDeps' vs (Flagged qfn _ t f) =-      case (F qfn == var, M.lookup qfn (vsFlags vs)) of-        (True, _)    -> ([F qfn], if b then t else f)-        (_, Nothing) -> ([], []) -- not yet known-        (_, Just b') -> let (parents, deps) = findNewDeps vs (if b' then t else f)-                        in (F qfn:parents, deps)-    findNewDeps' vs (Stanza qsn t) =-      case (S qsn == var, M.lookup qsn (vsStanzas vs)) of-        (True, _)    -> ([S qsn], if b then t else [])-        (_, Nothing) -> ([], []) -- not yet known-        (_, Just b') -> let (parents, deps) = findNewDeps vs (if b' then t else [])-                        in (S qsn:parents, deps)--updateLinkGroup :: LinkGroup -> UpdateState ()-updateLinkGroup lg = do-    verifyLinkGroup lg-    modify $ \vs -> vs {-        vsLinks =           M.fromList (map aux (S.toList (lgMembers lg)))-                  `M.union` vsLinks vs-      }-  where-    aux pp = (Q pp (lgPackage lg), lg)--{--------------------------------------------------------------------------------  Verification--------------------------------------------------------------------------------}--verifyLinkGroup :: LinkGroup -> UpdateState ()-verifyLinkGroup lg =-    case lgInstance lg of-      -- No instance picked yet. Nothing to verify-      Nothing ->-        return ()--      -- We picked an instance. Verify flags and stanzas-      -- TODO: The enumeration of OptionalStanza names is very brittle;-      -- if a constructor is added to the datatype we won't notice it here-      Just i -> do-        vs <- get-        let PInfo _deps finfo _ = vsIndex vs ! lgPackage lg ! i-            flags   = M.keys finfo-            stanzas = [TestStanzas, BenchStanzas]-        forM_ flags $ \fn -> do-          let flag = FN (PI (lgPackage lg) i) fn-          verifyFlag' flag lg-        forM_ stanzas $ \sn -> do-          let stanza = SN (PI (lgPackage lg) i) sn-          verifyStanza' stanza lg--verifyFlag :: QFN -> UpdateState ()-verifyFlag (FN (PI qpn@(Q _pp pn) i) fn) = do-    vs <- get-    -- We can only pick a flag after picking an instance; link group must exist-    verifyFlag' (FN (PI pn i) fn) (vsLinks vs ! qpn)--verifyStanza :: QSN -> UpdateState ()-verifyStanza (SN (PI qpn@(Q _pp pn) i) sn) = do-    vs <- get-    -- We can only pick a stanza after picking an instance; link group must exist-    verifyStanza' (SN (PI pn i) sn) (vsLinks vs ! qpn)---- | Verify that all packages in the link group agree on flag assignments------ For the given flag and the link group, obtain all assignments for the flag--- that have already been made for link group members, and check that they are--- equal.-verifyFlag' :: FN PN -> LinkGroup -> UpdateState ()-verifyFlag' (FN (PI pn i) fn) lg = do-    vs <- get-    let flags = map (\pp' -> FN (PI (Q pp' pn) i) fn) (S.toList (lgMembers lg))-        vals  = map (`M.lookup` vsFlags vs) flags-    if allEqual (catMaybes vals) -- We ignore not-yet assigned flags-      then return ()-      else conflict ( CS.fromList (map F flags) `CS.union` lgConflictSet lg-                    , "flag " ++ show fn ++ " incompatible"-                    )---- | Verify that all packages in the link group agree on stanza assignments------ For the given stanza and the link group, obtain all assignments for the--- stanza that have already been made for link group members, and check that--- they are equal.------ This function closely mirrors 'verifyFlag''.-verifyStanza' :: SN PN -> LinkGroup -> UpdateState ()-verifyStanza' (SN (PI pn i) sn) lg = do-    vs <- get-    let stanzas = map (\pp' -> SN (PI (Q pp' pn) i) sn) (S.toList (lgMembers lg))-        vals    = map (`M.lookup` vsStanzas vs) stanzas-    if allEqual (catMaybes vals) -- We ignore not-yet assigned stanzas-      then return ()-      else conflict ( CS.fromList (map S stanzas) `CS.union` lgConflictSet lg-                    , "stanza " ++ show sn ++ " incompatible"-                    )--{--------------------------------------------------------------------------------  Link groups--------------------------------------------------------------------------------}---- | Set of packages that must be linked together------ A LinkGroup is between several qualified package names. In the validation--- state, we maintain a map vsLinks from qualified package names to link groups.--- There is an invariant that for all members of a link group, vsLinks must map--- to the same link group. The function updateLinkGroup can be used to--- re-establish this invariant after creating or expanding a LinkGroup.-data LinkGroup = LinkGroup {-      -- | The name of the package of this link group-      lgPackage :: PN--      -- | The canonical member of this link group (the one where we picked-      -- a concrete instance). Once we have picked a canonical member, all-      -- other packages must link to this one.-      ---      -- We may not know this yet (if we are constructing link groups-      -- for dependencies)-    , lgCanon :: Maybe (PI PP)--      -- | The members of the link group-    , lgMembers :: Set PP--      -- | The set of variables that should be added to the conflict set if-      -- something goes wrong with this link set (in addition to the members-      -- of the link group itself)-    , lgBlame :: ConflictSet QPN-    }-    deriving (Show, Eq)---- | Invariant for the set of link groups: every element in the link group--- must be pointing to the /same/ link group-lgInvariant :: Map QPN LinkGroup -> Bool-lgInvariant links = all invGroup (M.elems links)-  where-    invGroup :: LinkGroup -> Bool-    invGroup lg = allEqual $ map (`M.lookup` links) members-      where-        members :: [QPN]-        members = map (`Q` lgPackage lg) $ S.toList (lgMembers lg)---- | Package version of this group------ This is only known once we have picked a canonical element.-lgInstance :: LinkGroup -> Maybe I-lgInstance = fmap (\(PI _ i) -> i) . lgCanon--showLinkGroup :: LinkGroup -> String-showLinkGroup lg =-    "{" ++ intercalate "," (map showMember (S.toList (lgMembers lg))) ++ "}"-  where-    showMember :: PP -> String-    showMember pp = case lgCanon lg of-                      Just (PI pp' _i) | pp == pp' -> "*"-                      _otherwise                   -> ""-                 ++ case lgInstance lg of-                      Nothing -> showQPN (qpn pp)-                      Just i  -> showPI (PI (qpn pp) i)--    qpn :: PP -> QPN-    qpn pp = Q pp (lgPackage lg)---- | Creates a link group that contains a single member.-lgSingleton :: QPN -> Maybe (PI PP) -> LinkGroup-lgSingleton (Q pp pn) canon = LinkGroup {-      lgPackage = pn-    , lgCanon   = canon-    , lgMembers = S.singleton pp-    , lgBlame   = CS.empty-    }--lgMerge :: [Var QPN] -> LinkGroup -> LinkGroup -> Either Conflict LinkGroup-lgMerge blame lg lg' = do-    canon <- pick (lgCanon lg) (lgCanon lg')-    return LinkGroup {-        lgPackage = lgPackage lg-      , lgCanon   = canon-      , lgMembers = lgMembers lg `S.union` lgMembers lg'-      , lgBlame   = CS.unions [CS.fromList blame, lgBlame lg, lgBlame lg']-      }-  where-    pick :: Eq a => Maybe a -> Maybe a -> Either Conflict (Maybe a)-    pick Nothing  Nothing  = Right Nothing-    pick (Just x) Nothing  = Right $ Just x-    pick Nothing  (Just y) = Right $ Just y-    pick (Just x) (Just y) =-      if x == y then Right $ Just x-                else Left ( CS.unions [-                               CS.fromList blame-                             , lgConflictSet lg-                             , lgConflictSet lg'-                             ]-                          ,    "cannot merge " ++ showLinkGroup lg-                            ++ " and " ++ showLinkGroup lg'-                          )--lgConflictSet :: LinkGroup -> ConflictSet QPN-lgConflictSet lg =-               CS.fromList (map aux (S.toList (lgMembers lg)))-    `CS.union` lgBlame lg-  where-    aux pp = P (Q pp (lgPackage lg))--{--------------------------------------------------------------------------------  Auxiliary--------------------------------------------------------------------------------}--allEqual :: Eq a => [a] -> Bool-allEqual []       = True-allEqual [_]      = True-allEqual (x:y:ys) = x == y && allEqual (y:ys)--concatMapUnzip :: (a -> ([b], [c])) -> [a] -> ([b], [c])-concatMapUnzip f = (\(xs, ys) -> (concat xs, concat ys)) . unzip . map f
− Distribution/Client/Dependency/Modular/Log.hs
@@ -1,106 +0,0 @@-module Distribution.Client.Dependency.Modular.Log-    ( Log-    , continueWith-    , failWith-    , logToProgress-    , succeedWith-    , tryWith-    ) where--import Control.Applicative-import Data.List as L-import Data.Maybe (isNothing)--import Distribution.Client.Dependency.Types -- from Cabal--import Distribution.Client.Dependency.Modular.Dependency-import Distribution.Client.Dependency.Modular.Message-import Distribution.Client.Dependency.Modular.Package-import Distribution.Client.Dependency.Modular.Tree (FailReason(..))-import qualified Distribution.Client.Dependency.Modular.ConflictSet as CS---- | The 'Log' datatype.------ Represents the progress of a computation lazily.------ Parameterized over the type of actual messages and the final result.-type Log m a = Progress m () a--messages :: Progress step fail done -> [step]-messages = foldProgress (:) (const []) (const [])---- | Postprocesses a log file. Takes as an argument a limit on allowed backjumps.--- If the limit is 'Nothing', then infinitely many backjumps are allowed. If the--- limit is 'Just 0', backtracking is completely disabled.-logToProgress :: Maybe Int -> Log Message a -> Progress String String a-logToProgress mbj l = let-                        es = proc (Just 0) l -- catch first error (always)-                        ms = useFirstError (proc mbj l)-                      in go es es -- trace for first error-                            (showMessages (const True) True ms) -- run with backjump limit applied-  where-    -- Proc takes the allowed number of backjumps and a 'Progress' and explores the-    -- messages until the maximum number of backjumps has been reached. It filters out-    -- and ignores repeated backjumps. If proc reaches the backjump limit, it truncates-    -- the 'Progress' and ends it with the last conflict set. Otherwise, it leaves the-    -- original success result or replaces the original failure with 'Nothing'.-    proc :: Maybe Int -> Progress Message a b -> Progress Message (Maybe (ConflictSet QPN)) b-    proc _        (Done x)                          = Done x-    proc _        (Fail _)                          = Fail Nothing-    proc mbj'     (Step   (Failure cs Backjump) xs@(Step Leave (Step (Failure cs' Backjump) _)))-      | cs == cs'                                   = proc mbj' xs -- repeated backjumps count as one-    proc (Just 0) (Step   (Failure cs Backjump)  _) = Fail (Just cs)-    proc (Just n) (Step x@(Failure _  Backjump) xs) = Step x (proc (Just (n - 1)) xs)-    proc mbj'     (Step x                       xs) = Step x (proc mbj'           xs)--    -- Sets the conflict set from the first backjump as the final error, and records-    -- whether the search was exhaustive.-    useFirstError :: Progress Message (Maybe (ConflictSet QPN)) b-                  -> Progress Message (Bool, Maybe (ConflictSet QPN)) b-    useFirstError = replace Nothing-      where-        replace _       (Done x)                          = Done x-        replace cs'     (Fail cs)                         = -- 'Nothing' means backjump limit not reached.-                                                            -- Prefer first error over later error.-                                                            Fail (isNothing cs, cs' <|> cs)-        replace Nothing (Step x@(Failure cs Backjump) xs) = Step x $ replace (Just cs) xs-        replace cs'     (Step x                       xs) = Step x $ replace cs' xs--    -- The first two arguments are both supposed to be the log up to the first error.-    -- That's the error that will always be printed in case we do not find a solution.-    -- We pass this log twice, because we evaluate it in parallel with the full log,-    -- but we also want to retain the reference to its beginning for when we print it.-    -- This trick prevents a space leak!-    ---    -- The third argument is the full log, ending with either the solution or the-    -- exhaustiveness and first conflict set.-    go :: Progress Message a b-       -> Progress Message a b-       -> Progress String (Bool, Maybe (ConflictSet QPN)) b-       -> Progress String String b-    go ms (Step _ ns) (Step x xs)           = Step x (go ms ns xs)-    go ms r           (Step x xs)           = Step x (go ms r  xs)-    go ms _           (Fail (exh, Just cs)) = Fail $-                                              "Could not resolve dependencies:\n" ++-                                              unlines (messages $ showMessages (L.foldr (\ v _ -> v `CS.member` cs) True) False ms) ++-                                              (if exh then "Dependency tree exhaustively searched.\n"-                                                      else "Backjump limit reached (" ++ currlimit mbj ++-                                                               "change with --max-backjumps or try to run with --reorder-goals).\n")-                                                  where currlimit (Just n) = "currently " ++ show n ++ ", "-                                                        currlimit Nothing  = ""-    go _  _           (Done s)              = Done s-    go _  _           (Fail (_, Nothing))   = Fail ("Could not resolve dependencies; something strange happened.") -- should not happen--failWith :: step -> fail -> Progress step fail done-failWith s f = Step s (Fail f)--succeedWith :: step -> done -> Progress step fail done-succeedWith s d = Step s (Done d)--continueWith :: step -> Progress step fail done -> Progress step fail done-continueWith = Step--tryWith :: Message-        -> Progress Message fail done-        -> Progress Message fail done-tryWith m = Step m . Step Enter . foldProgress Step (failWith Leave) Done
− Distribution/Client/Dependency/Modular/Message.hs
@@ -1,154 +0,0 @@-{-# LANGUAGE BangPatterns #-}--module Distribution.Client.Dependency.Modular.Message (-    Message(..),-    showMessages-  ) where--import qualified Data.List as L-import Prelude hiding (pi)--import Distribution.Text -- from Cabal--import Distribution.Client.Dependency.Modular.Dependency-import Distribution.Client.Dependency.Modular.Flag-import Distribution.Client.Dependency.Modular.Package-import Distribution.Client.Dependency.Modular.Tree-         ( FailReason(..), POption(..) )-import Distribution.Client.Dependency.Types-         ( ConstraintSource(..), showConstraintSource, Progress(..) )--data Message =-    Enter           -- ^ increase indentation level-  | Leave           -- ^ decrease indentation level-  | TryP QPN POption-  | TryF QFN Bool-  | TryS QSN Bool-  | Next (Goal QPN)-  | Success-  | Failure (ConflictSet QPN) FailReason---- | Transforms the structured message type to actual messages (strings).------ Takes an additional relevance predicate. The predicate gets a stack of goal--- variables and can decide whether messages regarding these goals are relevant.--- You can plug in 'const True' if you're interested in a full trace. If you--- want a slice of the trace concerning a particular conflict set, then plug in--- a predicate returning 'True' on the empty stack and if the head is in the--- conflict set.------ The second argument indicates if the level numbers should be shown. This is--- recommended for any trace that involves backtracking, because only the level--- numbers will allow to keep track of backjumps.-showMessages :: ([Var QPN] -> Bool) -> Bool -> Progress Message a b -> Progress String a b-showMessages p sl = go [] 0-  where-    -- The stack 'v' represents variables that are currently assigned by the-    -- solver.  'go' pushes a variable for a recursive call when it encounters-    -- 'TryP', 'TryF', or 'TryS' and pops a variable when it encounters 'Leave'.-    -- When 'go' processes a package goal, or a package goal followed by a-    -- 'Failure', it calls 'atLevel' with the goal variable at the head of the-    -- stack so that the predicate can also select messages relating to package-    -- goal choices.-    go :: [Var QPN] -> Int -> Progress Message a b -> Progress String a b-    go !_ !_ (Done x)                           = Done x-    go !_ !_ (Fail x)                           = Fail x-    -- complex patterns-    go !v !l (Step (TryP qpn i) (Step Enter (Step (Failure c fr) (Step Leave ms)))) =-        goPReject v l qpn [i] c fr ms-    go !v !l (Step (TryF qfn b) (Step Enter (Step (Failure c fr) (Step Leave ms)))) =-        (atLevel (add (F qfn) v) l $ "rejecting: " ++ showQFNBool qfn b ++ showFR c fr) (go v l ms)-    go !v !l (Step (TryS qsn b) (Step Enter (Step (Failure c fr) (Step Leave ms)))) =-        (atLevel (add (S qsn) v) l $ "rejecting: " ++ showQSNBool qsn b ++ showFR c fr) (go v l ms)-    go !v !l (Step (Next (Goal (P qpn) gr)) (Step (TryP qpn' i) ms@(Step Enter (Step (Next _) _)))) =-        (atLevel (add (P qpn) v) l $ "trying: " ++ showQPNPOpt qpn' i ++ showGR gr) (go (add (P qpn) v) l ms)-    go !v !l (Step (Next (Goal (P qpn) gr)) ms@(Fail _)) =-        (atLevel (add (P qpn) v) l $ "unknown package: " ++ showQPN qpn ++ showGR gr) $ go v l ms-        -- the previous case potentially arises in the error output, because we remove the backjump itself-        -- if we cut the log after the first error-    go !v !l (Step (Next (Goal (P qpn) gr)) ms@(Step (Failure _c Backjump) _)) =-        (atLevel (add (P qpn) v) l $ "unknown package: " ++ showQPN qpn ++ showGR gr) $ go v l ms-    go !v !l (Step (Next (Goal (P qpn) gr)) (Step (Failure c fr) ms)) =-        let v' = add (P qpn) v-        in (atLevel v' l $ showPackageGoal qpn gr) $ (atLevel v' l $ showFailure c fr) (go v l ms)-    go !v !l (Step (Failure c Backjump) ms@(Step Leave (Step (Failure c' Backjump) _)))-        | c == c' = go v l ms-    -- standard display-    go !v !l (Step Enter                    ms) = go v          (l+1) ms-    go !v !l (Step Leave                    ms) = go (drop 1 v) (l-1) ms-    go !v !l (Step (TryP qpn i)             ms) = (atLevel (add (P qpn) v) l $ "trying: " ++ showQPNPOpt qpn i) (go (add (P qpn) v) l ms)-    go !v !l (Step (TryF qfn b)             ms) = (atLevel (add (F qfn) v) l $ "trying: " ++ showQFNBool qfn b) (go (add (F qfn) v) l ms)-    go !v !l (Step (TryS qsn b)             ms) = (atLevel (add (S qsn) v) l $ "trying: " ++ showQSNBool qsn b) (go (add (S qsn) v) l ms)-    go !v !l (Step (Next (Goal (P qpn) gr)) ms) = (atLevel (add (P qpn) v) l $ showPackageGoal qpn gr) (go v l ms)-    go !v !l (Step (Next _)                 ms) = go v l ms -- ignore flag goals in the log-    go !v !l (Step (Success)                ms) = (atLevel v l $ "done") (go v l ms)-    go !v !l (Step (Failure c fr)           ms) = (atLevel v l $ showFailure c fr) (go v l ms)--    showPackageGoal :: QPN -> QGoalReason -> String-    showPackageGoal qpn gr = "next goal: " ++ showQPN qpn ++ showGR gr--    showFailure :: ConflictSet QPN -> FailReason -> String-    showFailure c fr = "fail" ++ showFR c fr--    add :: Var QPN -> [Var QPN] -> [Var QPN]-    add v vs = simplifyVar v : vs--    -- special handler for many subsequent package rejections-    goPReject :: [Var QPN]-              -> Int-              -> QPN-              -> [POption]-              -> ConflictSet QPN-              -> FailReason-              -> Progress Message a b-              -> Progress String a b-    goPReject v l qpn is c fr (Step (TryP qpn' i) (Step Enter (Step (Failure _ fr') (Step Leave ms))))-      | qpn == qpn' && fr == fr' = goPReject v l qpn (i : is) c fr ms-    goPReject v l qpn is c fr ms =-        (atLevel (P qpn : v) l $ "rejecting: " ++ L.intercalate ", " (map (showQPNPOpt qpn) (reverse is)) ++ showFR c fr) (go v l ms)--    -- write a message, but only if it's relevant; we can also enable or disable the display of the current level-    atLevel :: [Var QPN] -> Int -> String -> Progress String a b -> Progress String a b-    atLevel v l x xs-      | sl && p v = let s = show l-                    in  Step ("[" ++ replicate (3 - length s) '_' ++ s ++ "] " ++ x) xs-      | p v       = Step x xs-      | otherwise = xs--showQPNPOpt :: QPN -> POption -> String-showQPNPOpt qpn@(Q _pp pn) (POption i linkedTo) =-  case linkedTo of-    Nothing  -> showPI (PI qpn i) -- Consistent with prior to POption-    Just pp' -> showQPN qpn ++ "~>" ++ showPI (PI (Q pp' pn) i)--showGR :: QGoalReason -> String-showGR UserGoal            = " (user goal)"-showGR (PDependency pi)    = " (dependency of " ++ showPI pi            ++ ")"-showGR (FDependency qfn b) = " (dependency of " ++ showQFNBool qfn b    ++ ")"-showGR (SDependency qsn)   = " (dependency of " ++ showQSNBool qsn True ++ ")"--showFR :: ConflictSet QPN -> FailReason -> String-showFR _ InconsistentInitialConstraints   = " (inconsistent initial constraints)"-showFR _ (Conflicting ds)                 = " (conflict: " ++ L.intercalate ", " (map showDep ds) ++ ")"-showFR _ CannotInstall                    = " (only already installed instances can be used)"-showFR _ CannotReinstall                  = " (avoiding to reinstall a package with same version but new dependencies)"-showFR _ Shadowed                         = " (shadowed by another installed package with same version)"-showFR _ Broken                           = " (package is broken)"-showFR _ (GlobalConstraintVersion vr src) = " (" ++ constraintSource src ++ " requires " ++ display vr ++ ")"-showFR _ (GlobalConstraintInstalled src)  = " (" ++ constraintSource src ++ " requires installed instance)"-showFR _ (GlobalConstraintSource src)     = " (" ++ constraintSource src ++ " requires source instance)"-showFR _ (GlobalConstraintFlag src)       = " (" ++ constraintSource src ++ " requires opposite flag selection)"-showFR _ ManualFlag                       = " (manual flag can only be changed explicitly)"-showFR c Backjump                         = " (backjumping, conflict set: " ++ showCS c ++ ")"-showFR _ MultipleInstances                = " (multiple instances)"-showFR c (DependenciesNotLinked msg)      = " (dependencies not linked: " ++ msg ++ "; conflict set: " ++ showCS c ++ ")"-showFR c CyclicDependencies               = " (cyclic dependencies; conflict set: " ++ showCS c ++ ")"--- The following are internal failures. They should not occur. In the--- interest of not crashing unnecessarily, we still just print an error--- message though.-showFR _ (MalformedFlagChoice qfn)        = " (INTERNAL ERROR: MALFORMED FLAG CHOICE: " ++ showQFN qfn ++ ")"-showFR _ (MalformedStanzaChoice qsn)      = " (INTERNAL ERROR: MALFORMED STANZA CHOICE: " ++ showQSN qsn ++ ")"-showFR _ EmptyGoalChoice                  = " (INTERNAL ERROR: EMPTY GOAL CHOICE)"--constraintSource :: ConstraintSource -> String-constraintSource src = "constraint from " ++ showConstraintSource src
− Distribution/Client/Dependency/Modular/PSQ.hs
@@ -1,213 +0,0 @@-{-# LANGUAGE DeriveFunctor, DeriveFoldable, DeriveTraversable #-}-module Distribution.Client.Dependency.Modular.PSQ-    ( PSQ(..)  -- Unit test needs constructor access-    , Degree(..)-    , casePSQ-    , cons-    , degree-    , delete-    , dminimumBy-    , length-    , lookup-    , filter-    , filterKeys-    , firstOnly-    , fromList-    , isZeroOrOne-    , keys-    , map-    , mapKeys-    , mapWithKey-    , mapWithKeyState-    , minimumBy-    , null-    , prefer-    , preferByKeys-    , preferOrElse-    , snoc-    , sortBy-    , sortByKeys-    , splits-    , toList-    , union-    ) where---- Priority search queues.------ I am not yet sure what exactly is needed. But we need a data structure with--- key-based lookup that can be sorted. We're using a sequence right now with--- (inefficiently implemented) lookup, because I think that queue-based--- operations and sorting turn out to be more efficiency-critical in practice.--import Control.Arrow (first, second)--import qualified Data.Foldable as F-import Data.Function-import qualified Data.List as S-import Data.Ord (comparing)-import Data.Traversable-import Prelude hiding (foldr, length, lookup, filter, null, map)--newtype PSQ k v = PSQ [(k, v)]-  deriving (Eq, Show, Functor, F.Foldable, Traversable) -- Qualified Foldable to avoid issues with FTP--keys :: PSQ k v -> [k]-keys (PSQ xs) = fmap fst xs--lookup :: Eq k => k -> PSQ k v -> Maybe v-lookup k (PSQ xs) = S.lookup k xs--map :: (v1 -> v2) -> PSQ k v1 -> PSQ k v2-map f (PSQ xs) = PSQ (fmap (second f) xs)--mapKeys :: (k1 -> k2) -> PSQ k1 v -> PSQ k2 v-mapKeys f (PSQ xs) = PSQ (fmap (first f) xs)--mapWithKey :: (k -> a -> b) -> PSQ k a -> PSQ k b-mapWithKey f (PSQ xs) = PSQ (fmap (\ (k, v) -> (k, f k v)) xs)--mapWithKeyState :: (s -> k -> a -> (b, s)) -> PSQ k a -> s -> PSQ k b-mapWithKeyState p (PSQ xs) s0 =-  PSQ (F.foldr (\ (k, v) r s -> case p s k v of-                                  (w, n) -> (k, w) : (r n))-               (const []) xs s0)--delete :: Eq k => k -> PSQ k a -> PSQ k a-delete k (PSQ xs) = PSQ (snd (S.partition ((== k) . fst) xs))--fromList :: [(k, a)] -> PSQ k a-fromList = PSQ--cons :: k -> a -> PSQ k a -> PSQ k a-cons k x (PSQ xs) = PSQ ((k, x) : xs)--snoc :: PSQ k a -> k -> a -> PSQ k a-snoc (PSQ xs) k x = PSQ (xs ++ [(k, x)])--casePSQ :: PSQ k a -> r -> (k -> a -> PSQ k a -> r) -> r-casePSQ (PSQ xs) n c =-  case xs of-    []          -> n-    (k, v) : ys -> c k v (PSQ ys)--splits :: PSQ k a -> PSQ k (a, PSQ k a)-splits = go id-  where-    go f xs = casePSQ xs-        (PSQ [])-        (\ k v ys -> cons k (v, f ys) (go (f . cons k v) ys))--sortBy :: (a -> a -> Ordering) -> PSQ k a -> PSQ k a-sortBy cmp (PSQ xs) = PSQ (S.sortBy (cmp `on` snd) xs)--sortByKeys :: (k -> k -> Ordering) -> PSQ k a -> PSQ k a-sortByKeys cmp (PSQ xs) = PSQ (S.sortBy (cmp `on` fst) xs)---- | Given a measure in form of a pseudo-peano-natural number,--- determine the approximate minimum. This is designed to stop--- even traversing the list as soon as we find any element with--- measure 'ZeroOrOne'.------ Always returns a one-element queue (except if the queue is--- empty, then we return an empty queue again).----dminimumBy :: (a -> Degree) -> PSQ k a -> PSQ k a-dminimumBy _   (PSQ [])       = PSQ []-dminimumBy sel (PSQ (x : xs)) = go (sel (snd x)) x xs-  where-    go ZeroOrOne v _ = PSQ [v]-    go _ v []        = PSQ [v]-    go c v (y : ys)  = case compare c d of-      LT -> go c v ys-      EQ -> go c v ys-      GT -> go d y ys-      where-        d = sel (snd y)--minimumBy :: (a -> Int) -> PSQ k a -> PSQ k a-minimumBy sel (PSQ xs) =-  PSQ [snd (S.minimumBy (comparing fst) (S.map (\ x -> (sel (snd x), x)) xs))]---- | Will partition the list according to the predicate. If--- there is any element that satisfies the precidate, then only--- the elements satisfying the predicate are returned.--- Otherwise, the rest is returned.----prefer :: (a -> Bool) -> PSQ k a -> PSQ k a-prefer p (PSQ xs) =-  let-    (pro, con) = S.partition (p . snd) xs-  in-    if S.null pro then PSQ con else PSQ pro---- | Variant of 'prefer' that takes a continuation for the case--- that there are none of the desired elements.-preferOrElse :: (a -> Bool) -> (PSQ k a -> PSQ k a) -> PSQ k a -> PSQ k a-preferOrElse p k (PSQ xs) =-  let-    (pro, con) = S.partition (p . snd) xs-  in-    if S.null pro then k (PSQ con) else PSQ pro---- | Variant of 'prefer' that takes a predicate on the keys--- rather than on the values.----preferByKeys :: (k -> Bool) -> PSQ k a -> PSQ k a-preferByKeys p (PSQ xs) =-  let-    (pro, con) = S.partition (p . fst) xs-  in-    if S.null pro then PSQ con else PSQ pro--filterKeys :: (k -> Bool) -> PSQ k a -> PSQ k a-filterKeys p (PSQ xs) = PSQ (S.filter (p . fst) xs)--filter :: (a -> Bool) -> PSQ k a -> PSQ k a-filter p (PSQ xs) = PSQ (S.filter (p . snd) xs)--length :: PSQ k a -> Int-length (PSQ xs) = S.length xs---- | Approximation of the branching degree.------ This is designed for computing the branching degree of a goal choice--- node. If the degree is 0 or 1, it is always good to take that goal,--- because we can either abort immediately, or have no other choice anyway.------ So we do not actually want to compute the full degree (which is--- somewhat costly) in cases where we have such an easy choice.----data Degree = ZeroOrOne | Two | Other-  deriving (Show, Eq)--instance Ord Degree where-  compare ZeroOrOne _         = LT -- lazy approximation-  compare _         ZeroOrOne = GT -- approximation-  compare Two       Two       = EQ-  compare Two       Other     = LT-  compare Other     Two       = GT-  compare Other     Other     = EQ--degree :: PSQ k a -> Degree-degree (PSQ [])     = ZeroOrOne-degree (PSQ [_])    = ZeroOrOne-degree (PSQ [_, _]) = Two-degree (PSQ _)      = Other--null :: PSQ k a -> Bool-null (PSQ xs) = S.null xs--isZeroOrOne :: PSQ k a -> Bool-isZeroOrOne (PSQ [])  = True-isZeroOrOne (PSQ [_]) = True-isZeroOrOne _         = False--firstOnly :: PSQ k a -> PSQ k a-firstOnly (PSQ [])      = PSQ []-firstOnly (PSQ (x : _)) = PSQ [x]--toList :: PSQ k a -> [(k, a)]-toList (PSQ xs) = xs--union :: PSQ k a -> PSQ k a -> PSQ k a-union (PSQ xs) (PSQ ys) = PSQ (xs ++ ys)
− Distribution/Client/Dependency/Modular/Package.hs
@@ -1,175 +0,0 @@-{-# LANGUAGE DeriveFunctor #-}-module Distribution.Client.Dependency.Modular.Package-  ( I(..)-  , Loc(..)-  , PackageId-  , PackageIdentifier(..)-  , PackageName(..)-  , PI(..)-  , PN-  , PP(..)-  , Namespace(..)-  , Qualifier(..)-  , QPN-  , QPV-  , Q(..)-  , instI-  , makeIndependent-  , primaryPP-  , showI-  , showPI-  , showQPN-  , unPN-  ) where--import Data.List as L--import Distribution.Package -- from Cabal-import Distribution.Text    -- from Cabal--import Distribution.Client.Dependency.Modular.Version---- | A package name.-type PN = PackageName---- | Unpacking a package name.-unPN :: PN -> String-unPN (PackageName pn) = pn---- | Package version. A package name plus a version number.-type PV = PackageId---- | Qualified package version.-type QPV = Q PV---- | Package id. Currently just a black-box string.-type PId = UnitId---- | Location. Info about whether a package is installed or not, and where--- exactly it is located. For installed packages, uniquely identifies the--- package instance via its 'PId'.------ TODO: More information is needed about the repo.-data Loc = Inst PId | InRepo-  deriving (Eq, Ord, Show)---- | Instance. A version number and a location.-data I = I Ver Loc-  deriving (Eq, Ord, Show)---- | String representation of an instance.-showI :: I -> String-showI (I v InRepo)   = showVer v-showI (I v (Inst uid)) = showVer v ++ "/installed" ++ shortId uid-  where-    -- A hack to extract the beginning of the package ABI hash-    shortId (SimpleUnitId (ComponentId i))-            = snip (splitAt 4) (++ "...")-            . snip ((\ (x, y) -> (reverse x, y)) . break (=='-') . reverse) ('-':)-            $ i-    snip p f xs = case p xs of-                    (ys, zs) -> (if L.null zs then id else f) ys---- | Package instance. A package name and an instance.-data PI qpn = PI qpn I-  deriving (Eq, Ord, Show, Functor)---- | String representation of a package instance.-showPI :: PI QPN -> String-showPI (PI qpn i) = showQPN qpn ++ "-" ++ showI i--instI :: I -> Bool-instI (I _ (Inst _)) = True-instI _              = False---- | A package path consists of a namespace and a package path inside that--- namespace.-data PP = PP Namespace Qualifier-  deriving (Eq, Ord, Show)---- | Top-level namespace------ Package choices in different namespaces are considered completely independent--- by the solver.-data Namespace =-    -- | The default namespace-    DefaultNamespace--    -- | Independent namespace-    ---    -- For now we just number these (rather than giving them more structure).-  | Independent Int-  deriving (Eq, Ord, Show)---- | Qualifier of a package within a namespace (see 'PP')-data Qualifier =-    -- | Top-level dependency in this namespace-    Unqualified--    -- | Any dependency on base is considered independent-    ---    -- This makes it possible to have base shims.-  | Base PN--    -- | Setup dependency-    ---    -- By rights setup dependencies ought to be nestable; after all, the setup-    -- dependencies of a package might themselves have setup dependencies, which-    -- are independent from everything else. However, this very quickly leads to-    -- infinite search trees in the solver. Therefore we limit ourselves to-    -- a single qualifier (within a given namespace).-  | Setup PN-  deriving (Eq, Ord, Show)---- | Is the package in the primary group of packages. In particular this--- does not include packages pulled in as setup deps.----primaryPP :: PP -> Bool-primaryPP (PP _ns q) = go q-  where-    go Unqualified = True-    go (Base  _)   = True-    go (Setup _)   = False---- | String representation of a package path.------ NOTE: The result of 'showPP' is either empty or results in a period, so that--- it can be prepended to a package name.-showPP :: PP -> String-showPP (PP ns q) =-    case ns of-      DefaultNamespace -> go q-      Independent i    -> show i ++ "." ++ go q-  where-    -- Print the qualifier-    ---    -- NOTE: the base qualifier is for a dependency _on_ base; the qualifier is-    -- there to make sure different dependencies on base are all independent.-    -- So we want to print something like @"A.base"@, where the @"A."@ part-    -- is the qualifier and @"base"@ is the actual dependency (which, for the-    -- 'Base' qualifier, will always be @base@).-    go Unqualified = ""-    go (Setup pn)  = display pn ++ "-setup."-    go (Base  pn)  = display pn ++ "."---- | A qualified entity. Pairs a package path with the entity.-data Q a = Q PP a-  deriving (Eq, Ord, Show)---- | Standard string representation of a qualified entity.-showQ :: (a -> String) -> (Q a -> String)-showQ showa (Q pp x) = showPP pp ++ showa x---- | Qualified package name.-type QPN = Q PN---- | String representation of a qualified package path.-showQPN :: QPN -> String-showQPN = showQ display---- | Create artificial parents for each of the package names, making--- them all independent.-makeIndependent :: [PN] -> [QPN]-makeIndependent ps = [ Q pp pn | (pn, i) <- zip ps [0::Int ..]-                               , let pp = PP (Independent i) Unqualified-                     ]
− Distribution/Client/Dependency/Modular/Preference.hs
@@ -1,397 +0,0 @@-{-# LANGUAGE CPP #-}-module Distribution.Client.Dependency.Modular.Preference-    ( avoidReinstalls-    , deferSetupChoices-    , deferWeakFlagChoices-    , enforceManualFlags-    , enforcePackageConstraints-    , enforceSingleInstanceRestriction-    , firstGoal-    , preferBaseGoalChoice-    , preferEasyGoalChoices-    , preferLinked-    , preferPackagePreferences-    , preferReallyEasyGoalChoices-    , requireInstalled-    ) where---- Reordering or pruning the tree in order to prefer or make certain choices.--import qualified Data.List as L-import qualified Data.Map as M-#if !MIN_VERSION_base(4,8,0)-import Data.Monoid-import Control.Applicative-#endif-import Prelude hiding (sequence)-import Control.Monad.Reader hiding (sequence)-import Data.Map (Map)-import Data.Traversable (sequence)--import Distribution.Client.Dependency.Types-  ( PackageConstraint(..), LabeledPackageConstraint(..), ConstraintSource(..)-  , PackagePreferences(..), InstalledPreference(..) )-import Distribution.Client.Types-  ( OptionalStanza(..) )--import Distribution.Client.Dependency.Modular.Dependency-import Distribution.Client.Dependency.Modular.Flag-import Distribution.Client.Dependency.Modular.Package-import qualified Distribution.Client.Dependency.Modular.PSQ as P-import Distribution.Client.Dependency.Modular.Tree-import Distribution.Client.Dependency.Modular.Version-import qualified Distribution.Client.Dependency.Modular.ConflictSet as CS---- | Generic abstraction for strategies that just rearrange the package order.--- Only packages that match the given predicate are reordered.-packageOrderFor :: (PN -> Bool) -> (PN -> I -> I -> Ordering) -> Tree a -> Tree a-packageOrderFor p cmp' = trav go-  where-    go (PChoiceF v@(Q _ pn) r cs)-      | p pn                        = PChoiceF v r (P.sortByKeys (flip (cmp pn)) cs)-      | otherwise                   = PChoiceF v r                               cs-    go x                            = x--    cmp :: PN -> POption -> POption -> Ordering-    cmp pn (POption i _) (POption i' _) = cmp' pn i i'---- | Prefer to link packages whenever possible-preferLinked :: Tree a -> Tree a-preferLinked = trav go-  where-    go (PChoiceF qn a  cs) = PChoiceF qn a (P.sortByKeys cmp cs)-    go x                   = x--    cmp (POption _ linkedTo) (POption _ linkedTo') = cmpL linkedTo linkedTo'--    cmpL Nothing  Nothing  = EQ-    cmpL Nothing  (Just _) = GT-    cmpL (Just _) Nothing  = LT-    cmpL (Just _) (Just _) = EQ---- | Ordering that treats versions satisfying more preferred ranges as greater---   than versions satisfying less preferred ranges.-preferredVersionsOrdering :: [VR] -> Ver -> Ver -> Ordering-preferredVersionsOrdering vrs v1 v2 = compare (check v1) (check v2)-  where-     check v = Prelude.length . Prelude.filter (==True) .-               Prelude.map (flip checkVR v) $ vrs---- | Traversal that tries to establish package preferences (not constraints).--- Works by reordering choice nodes. Also applies stanza preferences.-preferPackagePreferences :: (PN -> PackagePreferences) -> Tree a -> Tree a-preferPackagePreferences pcs = preferPackageStanzaPreferences pcs-                             . packageOrderFor (const True) preference-  where-    preference pn i1@(I v1 _) i2@(I v2 _) =-      let PackagePreferences vrs ipref _ = pcs pn-      in  preferredVersionsOrdering vrs v1 v2 `mappend` -- combines lexically-          locationsOrdering ipref i1 i2--    -- Note that we always rank installed before uninstalled, and later-    -- versions before earlier, but we can change the priority of the-    -- two orderings.-    locationsOrdering PreferInstalled v1 v2 =-      preferInstalledOrdering v1 v2 `mappend` preferLatestOrdering v1 v2-    locationsOrdering PreferLatest v1 v2 =-      preferLatestOrdering v1 v2 `mappend` preferInstalledOrdering v1 v2---- | Ordering that treats installed instances as greater than uninstalled ones.-preferInstalledOrdering :: I -> I -> Ordering-preferInstalledOrdering (I _ (Inst _)) (I _ (Inst _)) = EQ-preferInstalledOrdering (I _ (Inst _)) _              = GT-preferInstalledOrdering _              (I _ (Inst _)) = LT-preferInstalledOrdering _              _              = EQ---- | Compare instances by their version numbers.-preferLatestOrdering :: I -> I -> Ordering-preferLatestOrdering (I v1 _) (I v2 _) = compare v1 v2---- | Traversal that tries to establish package stanza enable\/disable--- preferences. Works by reordering the branches of stanza choices.-preferPackageStanzaPreferences :: (PN -> PackagePreferences) -> Tree a -> Tree a-preferPackageStanzaPreferences pcs = trav go-  where-    go (SChoiceF qsn@(SN (PI (Q pp pn) _) s) gr _tr ts) | primaryPP pp =-        let PackagePreferences _ _ spref = pcs pn-            enableStanzaPref = s `elem` spref-                  -- move True case first to try enabling the stanza-            ts' | enableStanzaPref = P.sortByKeys (flip compare) ts-                | otherwise        = ts-         in SChoiceF qsn gr True ts'   -- True: now weak choice-    go x = x---- | Helper function that tries to enforce a single package constraint on a--- given instance for a P-node. Translates the constraint into a--- tree-transformer that either leaves the subtree untouched, or replaces it--- with an appropriate failure node.-processPackageConstraintP :: PP-                          -> ConflictSet QPN-                          -> I-                          -> LabeledPackageConstraint-                          -> Tree a-                          -> Tree a-processPackageConstraintP pp _ _ (LabeledPackageConstraint _ src) r-  | src == ConstraintSourceUserTarget && not (primaryPP pp)         = r-    -- the constraints arising from targets, like "foo-1.0" only apply to-    -- the main packages in the solution, they don't constrain setup deps--processPackageConstraintP _ c i (LabeledPackageConstraint pc src) r = go i pc-  where-    go (I v _) (PackageConstraintVersion _ vr)-        | checkVR vr v  = r-        | otherwise     = Fail c (GlobalConstraintVersion vr src)-    go _       (PackageConstraintInstalled _)-        | instI i       = r-        | otherwise     = Fail c (GlobalConstraintInstalled src)-    go _       (PackageConstraintSource    _)-        | not (instI i) = r-        | otherwise     = Fail c (GlobalConstraintSource src)-    go _       _ = r---- | Helper function that tries to enforce a single package constraint on a--- given flag setting for an F-node. Translates the constraint into a--- tree-transformer that either leaves the subtree untouched, or replaces it--- with an appropriate failure node.-processPackageConstraintF :: Flag-                          -> ConflictSet QPN-                          -> Bool-                          -> LabeledPackageConstraint-                          -> Tree a-                          -> Tree a-processPackageConstraintF f c b' (LabeledPackageConstraint pc src) r = go pc-  where-    go (PackageConstraintFlags _ fa) =-        case L.lookup f fa of-          Nothing            -> r-          Just b | b == b'   -> r-                 | otherwise -> Fail c (GlobalConstraintFlag src)-    go _                             = r---- | Helper function that tries to enforce a single package constraint on a--- given flag setting for an F-node. Translates the constraint into a--- tree-transformer that either leaves the subtree untouched, or replaces it--- with an appropriate failure node.-processPackageConstraintS :: OptionalStanza-                          -> ConflictSet QPN-                          -> Bool-                          -> LabeledPackageConstraint-                          -> Tree a-                          -> Tree a-processPackageConstraintS s c b' (LabeledPackageConstraint pc src) r = go pc-  where-    go (PackageConstraintStanzas _ ss) =-        if not b' && s `elem` ss then Fail c (GlobalConstraintFlag src)-                                 else r-    go _                               = r---- | Traversal that tries to establish various kinds of user constraints. Works--- by selectively disabling choices that have been ruled out by global user--- constraints.-enforcePackageConstraints :: M.Map PN [LabeledPackageConstraint]-                          -> Tree QGoalReason-                          -> Tree QGoalReason-enforcePackageConstraints pcs = trav go-  where-    go (PChoiceF qpn@(Q pp pn)              gr      ts) =-      let c = varToConflictSet (P qpn)-          -- compose the transformation functions for each of the relevant constraint-          g = \ (POption i _) -> foldl (\ h pc -> h . processPackageConstraintP pp c i pc) id-                           (M.findWithDefault [] pn pcs)-      in PChoiceF qpn gr      (P.mapWithKey g ts)-    go (FChoiceF qfn@(FN (PI (Q _ pn) _) f) gr tr m ts) =-      let c = varToConflictSet (F qfn)-          -- compose the transformation functions for each of the relevant constraint-          g = \ b -> foldl (\ h pc -> h . processPackageConstraintF f c b pc) id-                           (M.findWithDefault [] pn pcs)-      in FChoiceF qfn gr tr m (P.mapWithKey g ts)-    go (SChoiceF qsn@(SN (PI (Q _ pn) _) f) gr tr   ts) =-      let c = varToConflictSet (S qsn)-          -- compose the transformation functions for each of the relevant constraint-          g = \ b -> foldl (\ h pc -> h . processPackageConstraintS f c b pc) id-                           (M.findWithDefault [] pn pcs)-      in SChoiceF qsn gr tr   (P.mapWithKey g ts)-    go x = x---- | Transformation that tries to enforce manual flags. Manual flags--- can only be re-set explicitly by the user. This transformation should--- be run after user preferences have been enforced. For manual flags,--- it checks if a user choice has been made. If not, it disables all but--- the first choice.-enforceManualFlags :: Tree QGoalReason -> Tree QGoalReason-enforceManualFlags = trav go-  where-    go (FChoiceF qfn gr tr True ts) = FChoiceF qfn gr tr True $-      let c = varToConflictSet (F qfn)-      in  case span isDisabled (P.toList ts) of-            ([], y : ys) -> P.fromList (y : L.map (\ (b, _) -> (b, Fail c ManualFlag)) ys)-            _            -> ts -- something has been manually selected, leave things alone-      where-        isDisabled (_, Fail _ (GlobalConstraintFlag _)) = True-        isDisabled _                                    = False-    go x                                                   = x---- | Require installed packages.-requireInstalled :: (PN -> Bool) -> Tree QGoalReason -> Tree QGoalReason-requireInstalled p = trav go-  where-    go (PChoiceF v@(Q _ pn) gr cs)-      | p pn      = PChoiceF v gr (P.mapWithKey installed cs)-      | otherwise = PChoiceF v gr                         cs-      where-        installed (POption (I _ (Inst _)) _) x = x-        installed _ _ = Fail (varToConflictSet (P v)) CannotInstall-    go x          = x---- | Avoid reinstalls.------ This is a tricky strategy. If a package version is installed already and the--- same version is available from a repo, the repo version will never be chosen.--- This would result in a reinstall (either destructively, or potentially,--- shadowing). The old instance won't be visible or even present anymore, but--- other packages might have depended on it.------ TODO: It would be better to actually check the reverse dependencies of installed--- packages. If they're not depended on, then reinstalling should be fine. Even if--- they are, perhaps this should just result in trying to reinstall those other--- packages as well. However, doing this all neatly in one pass would require to--- change the builder, or at least to change the goal set after building.-avoidReinstalls :: (PN -> Bool) -> Tree QGoalReason -> Tree QGoalReason-avoidReinstalls p = trav go-  where-    go (PChoiceF qpn@(Q _ pn) gr cs)-      | p pn      = PChoiceF qpn gr disableReinstalls-      | otherwise = PChoiceF qpn gr cs-      where-        disableReinstalls =-          let installed = [ v | (POption (I v (Inst _)) _, _) <- P.toList cs ]-          in  P.mapWithKey (notReinstall installed) cs--        notReinstall vs (POption (I v InRepo) _) _ | v `elem` vs =-          Fail (varToConflictSet (P qpn)) CannotReinstall-        notReinstall _ _ x =-          x-    go x          = x---- | Always choose the first goal in the list next, abandoning all--- other choices.------ This is unnecessary for the default search strategy, because--- it descends only into the first goal choice anyway,--- but may still make sense to just reduce the tree size a bit.-firstGoal :: Tree a -> Tree a-firstGoal = trav go-  where-    go (GoalChoiceF xs) = GoalChoiceF (P.firstOnly xs)-    go x                = x-    -- Note that we keep empty choice nodes, because they mean success.---- | Transformation that tries to make a decision on base as early as--- possible. In nearly all cases, there's a single choice for the base--- package. Also, fixing base early should lead to better error messages.-preferBaseGoalChoice :: Tree a -> Tree a-preferBaseGoalChoice = trav go-  where-    go (GoalChoiceF xs) = GoalChoiceF (P.preferByKeys isBase xs)-    go x                = x--    isBase :: OpenGoal comp -> Bool-    isBase (OpenGoal (Simple (Dep (Q _pp pn) _) _) _) | unPN pn == "base" = True-    isBase _                                                              = False---- | Deal with setup dependencies after regular dependencies, so that we can--- will link setup depencencies against package dependencies when possible-deferSetupChoices :: Tree a -> Tree a-deferSetupChoices = trav go-  where-    go (GoalChoiceF xs) = GoalChoiceF (P.preferByKeys noSetup xs)-    go x                = x--    noSetup :: OpenGoal comp -> Bool-    noSetup (OpenGoal (Simple (Dep (Q (PP _ns (Setup _)) _) _) _) _) = False-    noSetup _                                                        = True---- | Transformation that tries to avoid making weak flag choices early.--- Weak flags are trivial flags (not influencing dependencies) or such--- flags that are explicitly declared to be weak in the index.-deferWeakFlagChoices :: Tree a -> Tree a-deferWeakFlagChoices = trav go-  where-    go (GoalChoiceF xs) = GoalChoiceF (P.prefer noWeakStanza (P.prefer noWeakFlag xs))-    go x                = x--    noWeakStanza :: Tree a -> Bool-    noWeakStanza (SChoice _ _ True _) = False-    noWeakStanza _                    = True--    noWeakFlag :: Tree a -> Bool-    noWeakFlag (FChoice _ _ True _ _) = False-    noWeakFlag _                      = True---- | Transformation that sorts choice nodes so that--- child nodes with a small branching degree are preferred.------ Only approximates the number of choices in the branches.--- In particular, we try to take any goal immediately if it has--- a branching degree of 0 (guaranteed failure) or 1 (no other--- choice possible).------ Returns at most one choice.----preferEasyGoalChoices :: Tree a -> Tree a-preferEasyGoalChoices = trav go-  where-    go (GoalChoiceF xs) = GoalChoiceF (P.dminimumBy dchoices xs)-      -- (a different implementation that seems slower):-      -- GoalChoiceF (P.firstOnly (P.preferOrElse zeroOrOneChoices (P.minimumBy choices) xs))-    go x                = x---- | A variant of 'preferEasyGoalChoices' that just keeps the--- ones with a branching degree of 0 or 1. Note that unlike--- 'preferEasyGoalChoices', this may return more than one--- choice.----preferReallyEasyGoalChoices :: Tree a -> Tree a-preferReallyEasyGoalChoices = trav go-  where-    go (GoalChoiceF xs) = GoalChoiceF (P.prefer zeroOrOneChoices xs)-    go x                = x---- | Monad used internally in enforceSingleInstanceRestriction------ For each package instance we record the goal for which we picked a concrete--- instance. The SIR means that for any package instance there can only be one.-type EnforceSIR = Reader (Map (PI PN) QPN)---- | Enforce ghc's single instance restriction------ From the solver's perspective, this means that for any package instance--- (that is, package name + package version) there can be at most one qualified--- goal resolving to that instance (there may be other goals _linking_ to that--- instance however).-enforceSingleInstanceRestriction :: Tree QGoalReason -> Tree QGoalReason-enforceSingleInstanceRestriction = (`runReader` M.empty) . cata go-  where-    go :: TreeF QGoalReason (EnforceSIR (Tree QGoalReason)) -> EnforceSIR (Tree QGoalReason)--    -- We just verify package choices.-    go (PChoiceF qpn gr cs) =-      PChoice qpn gr <$> sequence (P.mapWithKey (goP qpn) cs)-    go _otherwise =-      innM _otherwise--    -- The check proper-    goP :: QPN -> POption -> EnforceSIR (Tree QGoalReason) -> EnforceSIR (Tree QGoalReason)-    goP qpn@(Q _ pn) (POption i linkedTo) r = do-      let inst = PI pn i-      env <- ask-      case (linkedTo, M.lookup inst env) of-        (Just _, _) ->-          -- For linked nodes we don't check anything-          r-        (Nothing, Nothing) ->-          -- Not linked, not already used-          local (M.insert inst qpn) r-        (Nothing, Just qpn') -> do-          -- Not linked, already used. This is an error-          return $ Fail (CS.union (varToConflictSet (P qpn)) (varToConflictSet (P qpn'))) MultipleInstances
− Distribution/Client/Dependency/Modular/Solver.hs
@@ -1,100 +0,0 @@-module Distribution.Client.Dependency.Modular.Solver-    ( SolverConfig(..)-    , solve-    ) where--import Data.Map as M--import Distribution.Compiler (CompilerInfo)--import Distribution.Client.PkgConfigDb (PkgConfigDb)--import Distribution.Client.Dependency.Types--import Distribution.Client.Dependency.Modular.Assignment-import Distribution.Client.Dependency.Modular.Builder-import Distribution.Client.Dependency.Modular.Cycles-import Distribution.Client.Dependency.Modular.Dependency-import Distribution.Client.Dependency.Modular.Explore-import Distribution.Client.Dependency.Modular.Index-import Distribution.Client.Dependency.Modular.Log-import Distribution.Client.Dependency.Modular.Message-import Distribution.Client.Dependency.Modular.Package-import qualified Distribution.Client.Dependency.Modular.Preference as P-import Distribution.Client.Dependency.Modular.Validate-import Distribution.Client.Dependency.Modular.Linking---- | Various options for the modular solver.-data SolverConfig = SolverConfig {-  preferEasyGoalChoices :: Bool,-  independentGoals      :: Bool,-  avoidReinstalls       :: Bool,-  shadowPkgs            :: Bool,-  strongFlags           :: Bool,-  maxBackjumps          :: Maybe Int-}---- | Run all solver phases.------ In principle, we have a valid tree after 'validationPhase', which--- means that every 'Done' node should correspond to valid solution.------ There is one exception, though, and that is cycle detection, which--- has been added relatively recently. Cycles are only removed directly--- before exploration.------ Semantically, there is no difference. Cycle detection, as implemented--- now, only occurs for 'Done' nodes we encounter during exploration,--- and cycle detection itself does not change the shape of the tree,--- it only marks some 'Done' nodes as 'Fail', if they contain cyclic--- solutions.------ There is a tiny performance impact, however, in doing cycle detection--- directly after validation. Probably because cycle detection maintains--- some information, and the various reorderings implemented by--- 'preferencesPhase' and 'heuristicsPhase' are ever so slightly more--- costly if that information is already around during the reorderings.------ With the current positioning directly before the 'explorePhase', there--- seems to be no statistically significant performance impact of cycle--- detection in the common case where there are no cycles.----solve :: SolverConfig ->                      -- ^ solver parameters-         CompilerInfo ->-         Index ->                             -- ^ all available packages as an index-         PkgConfigDb ->                       -- ^ available pkg-config pkgs-         (PN -> PackagePreferences) ->        -- ^ preferences-         Map PN [LabeledPackageConstraint] -> -- ^ global constraints-         [PN] ->                              -- ^ global goals-         Log Message (Assignment, RevDepMap)-solve sc cinfo idx pkgConfigDB userPrefs userConstraints userGoals =-  explorePhase     $-  detectCyclesPhase$-  heuristicsPhase  $-  preferencesPhase $-  validationPhase  $-  prunePhase       $-  buildPhase-  where-    explorePhase     = backjumpAndExplore-    heuristicsPhase  = (if preferEasyGoalChoices sc-                         then P.preferEasyGoalChoices -- also leaves just one choice-                         else P.firstGoal) . -- after doing goal-choice heuristics, commit to the first choice (saves space)-                       P.deferWeakFlagChoices .-                       P.deferSetupChoices .-                       P.preferBaseGoalChoice .-                       P.preferLinked-    preferencesPhase = P.preferPackagePreferences userPrefs-    validationPhase  = P.enforceManualFlags . -- can only be done after user constraints-                       P.enforcePackageConstraints userConstraints .-                       P.enforceSingleInstanceRestriction .-                       validateLinking idx .-                       validateTree cinfo idx pkgConfigDB-    prunePhase       = (if avoidReinstalls sc then P.avoidReinstalls (const True) else id) .-                       -- packages that can never be "upgraded":-                       P.requireInstalled (`elem` [ PackageName "base"-                                                  , PackageName "ghc-prim"-                                                  , PackageName "integer-gmp"-                                                  , PackageName "integer-simple"-                                                  ])-    buildPhase       = addLinking $ buildTree idx (independentGoals sc) userGoals
− Distribution/Client/Dependency/Modular/Tree.hs
@@ -1,168 +0,0 @@-{-# LANGUAGE DeriveFunctor, DeriveFoldable, DeriveTraversable #-}-module Distribution.Client.Dependency.Modular.Tree-    ( FailReason(..)-    , POption(..)-    , Tree(..)-    , TreeF(..)-    , ana-    , cata-    , choices-    , dchoices-    , inn-    , innM-    , para-    , trav-    , zeroOrOneChoices-    ) where--import Control.Monad hiding (mapM, sequence)-import Data.Foldable-import Data.Traversable-import Prelude hiding (foldr, mapM, sequence)--import Distribution.Client.Dependency.Modular.Dependency-import Distribution.Client.Dependency.Modular.Flag-import Distribution.Client.Dependency.Modular.Package-import Distribution.Client.Dependency.Modular.PSQ (PSQ)-import qualified Distribution.Client.Dependency.Modular.PSQ as P-import Distribution.Client.Dependency.Modular.Version-import Distribution.Client.Dependency.Types ( ConstraintSource(..) )---- | Type of the search tree. Inlining the choice nodes for now.-data Tree a =-    PChoice     QPN a           (PSQ POption       (Tree a))-  | FChoice     QFN a Bool Bool (PSQ Bool          (Tree a)) -- Bool indicates whether it's weak/trivial, second Bool whether it's manual-  | SChoice     QSN a Bool      (PSQ Bool          (Tree a)) -- Bool indicates whether it's trivial-  | GoalChoice                  (PSQ (OpenGoal ()) (Tree a)) -- PSQ should never be empty-  | Done        RevDepMap-  | Fail        (ConflictSet QPN) FailReason-  deriving (Eq, Show, Functor)-  -- Above, a choice is called trivial if it clearly does not matter. The-  -- special case of triviality we actually consider is if there are no new-  -- dependencies introduced by this node.-  ---  -- A (flag) choice is called weak if we do want to defer it. This is the-  -- case for flags that should be implied by what's currently installed on-  -- the system, as opposed to flags that are used to explicitly enable or-  -- disable some functionality.---- | A package option is a package instance with an optional linking annotation------ The modular solver has a number of package goals to solve for, and can only--- pick a single package version for a single goal. In order to allow to--- install multiple versions of the same package as part of a single solution--- the solver uses qualified goals. For example, @0.P@ and @1.P@ might both--- be qualified goals for @P@, allowing to pick a difference version of package--- @P@ for @0.P@ and @1.P@.------ Linking is an essential part of this story. In addition to picking a specific--- version for @1.P@, the solver can also decide to link @1.P@ to @0.P@ (or--- vice versa). Teans that @1.P@ and @0.P@ really must be the very same package--- (and hence must have the same build time configuration, and their--- dependencies must also be the exact same).------ See <http://www.well-typed.com/blog/2015/03/qualified-goals/> for details.-data POption = POption I (Maybe PP)-  deriving (Eq, Show)--data FailReason = InconsistentInitialConstraints-                | Conflicting [Dep QPN]-                | CannotInstall-                | CannotReinstall-                | Shadowed-                | Broken-                | GlobalConstraintVersion VR ConstraintSource-                | GlobalConstraintInstalled ConstraintSource-                | GlobalConstraintSource ConstraintSource-                | GlobalConstraintFlag ConstraintSource-                | ManualFlag-                | MalformedFlagChoice QFN-                | MalformedStanzaChoice QSN-                | EmptyGoalChoice-                | Backjump-                | MultipleInstances-                | DependenciesNotLinked String-                | CyclicDependencies-  deriving (Eq, Show)---- | Functor for the tree type.-data TreeF a b =-    PChoiceF    QPN a           (PSQ POption       b)-  | FChoiceF    QFN a Bool Bool (PSQ Bool          b)-  | SChoiceF    QSN a Bool      (PSQ Bool          b)-  | GoalChoiceF                 (PSQ (OpenGoal ()) b)-  | DoneF       RevDepMap-  | FailF       (ConflictSet QPN) FailReason-  deriving (Functor, Foldable, Traversable)--out :: Tree a -> TreeF a (Tree a)-out (PChoice    p i     ts) = PChoiceF    p i     ts-out (FChoice    p i b m ts) = FChoiceF    p i b m ts-out (SChoice    p i b   ts) = SChoiceF    p i b   ts-out (GoalChoice         ts) = GoalChoiceF         ts-out (Done       x         ) = DoneF       x-out (Fail       c x       ) = FailF       c x--inn :: TreeF a (Tree a) -> Tree a-inn (PChoiceF    p i     ts) = PChoice    p i     ts-inn (FChoiceF    p i b m ts) = FChoice    p i b m ts-inn (SChoiceF    p i b   ts) = SChoice    p i b   ts-inn (GoalChoiceF         ts) = GoalChoice         ts-inn (DoneF       x         ) = Done       x-inn (FailF       c x       ) = Fail       c x--innM :: Monad m => TreeF a (m (Tree a)) -> m (Tree a)-innM (PChoiceF    p i     ts) = liftM (PChoice    p i    ) (sequence ts)-innM (FChoiceF    p i b m ts) = liftM (FChoice    p i b m) (sequence ts)-innM (SChoiceF    p i b   ts) = liftM (SChoice    p i b  ) (sequence ts)-innM (GoalChoiceF         ts) = liftM (GoalChoice        ) (sequence ts)-innM (DoneF       x         ) = return $ Done     x-innM (FailF       c x       ) = return $ Fail     c x---- | Determines whether a tree is active, i.e., isn't a failure node.-active :: Tree a -> Bool-active (Fail _ _) = False-active _          = True---- | Determines how many active choices are available in a node. Note that we--- count goal choices as having one choice, always.-choices :: Tree a -> Int-choices (PChoice    _ _     ts) = P.length (P.filter active ts)-choices (FChoice    _ _ _ _ ts) = P.length (P.filter active ts)-choices (SChoice    _ _ _   ts) = P.length (P.filter active ts)-choices (GoalChoice         _ ) = 1-choices (Done       _         ) = 1-choices (Fail       _ _       ) = 0---- | Variant of 'choices' that only approximates the number of choices.-dchoices :: Tree a -> P.Degree-dchoices (PChoice    _ _     ts) = P.degree (P.filter active ts)-dchoices (FChoice    _ _ _ _ ts) = P.degree (P.filter active ts)-dchoices (SChoice    _ _ _   ts) = P.degree (P.filter active ts)-dchoices (GoalChoice         _ ) = P.ZeroOrOne-dchoices (Done       _         ) = P.ZeroOrOne-dchoices (Fail       _ _       ) = P.ZeroOrOne---- | Variant of 'choices' that only approximates the number of choices.-zeroOrOneChoices :: Tree a -> Bool-zeroOrOneChoices (PChoice    _ _     ts) = P.isZeroOrOne (P.filter active ts)-zeroOrOneChoices (FChoice    _ _ _ _ ts) = P.isZeroOrOne (P.filter active ts)-zeroOrOneChoices (SChoice    _ _ _   ts) = P.isZeroOrOne (P.filter active ts)-zeroOrOneChoices (GoalChoice         _ ) = True-zeroOrOneChoices (Done       _         ) = True-zeroOrOneChoices (Fail       _ _       ) = True---- | Catamorphism on trees.-cata :: (TreeF a b -> b) -> Tree a -> b-cata phi x = (phi . fmap (cata phi) . out) x--trav :: (TreeF a (Tree b) -> TreeF b (Tree b)) -> Tree a -> Tree b-trav psi x = cata (inn . psi) x---- | Paramorphism on trees.-para :: (TreeF a (b, Tree a) -> b) -> Tree a -> b-para phi = phi . fmap (\ x -> (para phi x, x)) . out---- | Anamorphism on trees.-ana :: (b -> TreeF a b) -> b -> Tree a-ana psi = inn . fmap (ana psi) . psi
− Distribution/Client/Dependency/Modular/Validate.hs
@@ -1,269 +0,0 @@-module Distribution.Client.Dependency.Modular.Validate (validateTree) where---- Validation of the tree.------ The task here is to make sure all constraints hold. After validation, any--- assignment returned by exploration of the tree should be a complete valid--- assignment, i.e., actually constitute a solution.--import Control.Applicative-import Control.Monad.Reader hiding (sequence)-import Data.List as L-import Data.Map as M-import Data.Set as S-import Data.Traversable-import Prelude hiding (sequence)--import Language.Haskell.Extension (Extension, Language)--import Distribution.Compiler (CompilerInfo(..))--import Distribution.Client.Dependency.Modular.Assignment-import Distribution.Client.Dependency.Modular.Dependency-import Distribution.Client.Dependency.Modular.Flag-import Distribution.Client.Dependency.Modular.Index-import Distribution.Client.Dependency.Modular.Package-import qualified Distribution.Client.Dependency.Modular.PSQ as P-import Distribution.Client.Dependency.Modular.Tree-import Distribution.Client.Dependency.Modular.Version (VR)--import Distribution.Client.ComponentDeps (Component)-import Distribution.Client.PkgConfigDb (PkgConfigDb, pkgConfigPkgIsPresent)---- In practice, most constraints are implication constraints (IF we have made--- a number of choices, THEN we also have to ensure that). We call constraints--- that for which the preconditions are fulfilled ACTIVE. We maintain a set--- of currently active constraints that we pass down the node.------ We aim at detecting inconsistent states as early as possible.------ Whenever we make a choice, there are two things that need to happen:------   (1) We must check that the choice is consistent with the currently---       active constraints.------   (2) The choice increases the set of active constraints. For the new---       active constraints, we must check that they are consistent with---       the current state.------ We can actually merge (1) and (2) by saying the the current choice is--- a new active constraint, fixing the choice.------ If a test fails, we have detected an inconsistent state. We can--- disable the current subtree and do not have to traverse it any further.------ We need a good way to represent the current state, i.e., the current--- set of active constraints. Since the main situation where we have to--- search in it is (1), it seems best to store the state by package: for--- every package, we store which versions are still allowed. If for any--- package, we have inconsistent active constraints, we can also stop.--- This is a particular way to read task (2):------   (2, weak) We only check if the new constraints are consistent with---       the choices we've already made, and add them to the active set.------   (2, strong) We check if the new constraints are consistent with the---       choices we've already made, and the constraints we already have.------ It currently seems as if we're implementing the weak variant. However,--- when used together with 'preferEasyGoalChoices', we will find an--- inconsistent state in the very next step.------ What do we do about flags?------ Like for packages, we store the flag choices we have already made.--- Now, regarding (1), we only have to test whether we've decided the--- current flag before. Regarding (2), the interesting bit is in discovering--- the new active constraints. To this end, we look up the constraints for--- the package the flag belongs to, and traverse its flagged dependencies.--- Wherever we find the flag in question, we start recording dependencies--- underneath as new active dependencies. If we encounter other flags, we--- check if we've chosen them already and either proceed or stop.---- | The state needed during validation.-data ValidateState = VS {-  supportedExt  :: Extension -> Bool,-  supportedLang :: Language  -> Bool,-  presentPkgs   :: PN -> VR  -> Bool,-  index :: Index,-  saved :: Map QPN (FlaggedDeps Component QPN), -- saved, scoped, dependencies-  pa    :: PreAssignment,-  qualifyOptions :: QualifyOptions-}--type Validate = Reader ValidateState--validate :: Tree QGoalReason -> Validate (Tree QGoalReason)-validate = cata go-  where-    go :: TreeF QGoalReason (Validate (Tree QGoalReason)) -> Validate (Tree QGoalReason)--    go (PChoiceF qpn gr     ts) = PChoice qpn gr <$> sequence (P.mapWithKey (goP qpn) ts)-    go (FChoiceF qfn gr b m ts) =-      do-        -- Flag choices may occur repeatedly (because they can introduce new constraints-        -- in various places). However, subsequent choices must be consistent. We thereby-        -- collapse repeated flag choice nodes.-        PA _ pfa _ <- asks pa -- obtain current flag-preassignment-        case M.lookup qfn pfa of-          Just rb -> -- flag has already been assigned; collapse choice to the correct branch-                     case P.lookup rb ts of-                       Just t  -> goF qfn rb t-                       Nothing -> return $ Fail (varToConflictSet (F qfn)) (MalformedFlagChoice qfn)-          Nothing -> -- flag choice is new, follow both branches-                     FChoice qfn gr b m <$> sequence (P.mapWithKey (goF qfn) ts)-    go (SChoiceF qsn gr b   ts) =-      do-        -- Optional stanza choices are very similar to flag choices.-        PA _ _ psa <- asks pa -- obtain current stanza-preassignment-        case M.lookup qsn psa of-          Just rb -> -- stanza choice has already been made; collapse choice to the correct branch-                     case P.lookup rb ts of-                       Just t  -> goS qsn rb t-                       Nothing -> return $ Fail (varToConflictSet (S qsn)) (MalformedStanzaChoice qsn)-          Nothing -> -- stanza choice is new, follow both branches-                     SChoice qsn gr b <$> sequence (P.mapWithKey (goS qsn) ts)--    -- We don't need to do anything for goal choices or failure nodes.-    go (GoalChoiceF              ts) = GoalChoice <$> sequence ts-    go (DoneF    rdm               ) = pure (Done rdm)-    go (FailF    c fr              ) = pure (Fail c fr)--    -- What to do for package nodes ...-    goP :: QPN -> POption -> Validate (Tree QGoalReason) -> Validate (Tree QGoalReason)-    goP qpn@(Q _pp pn) (POption i _) r = do-      PA ppa pfa psa <- asks pa    -- obtain current preassignment-      extSupported   <- asks supportedExt  -- obtain the supported extensions-      langSupported  <- asks supportedLang -- obtain the supported languages-      pkgPresent     <- asks presentPkgs -- obtain the present pkg-config pkgs-      idx            <- asks index -- obtain the index-      svd            <- asks saved -- obtain saved dependencies-      qo             <- asks qualifyOptions-      -- obtain dependencies and index-dictated exclusions introduced by the choice-      let (PInfo deps _ mfr) = idx ! pn ! i-      -- qualify the deps in the current scope-      let qdeps = qualifyDeps qo qpn deps-      -- the new active constraints are given by the instance we have chosen,-      -- plus the dependency information we have for that instance-      let newactives = Dep qpn (Fixed i (P qpn)) : L.map (resetVar (P qpn)) (extractDeps pfa psa qdeps)-      -- We now try to extend the partial assignment with the new active constraints.-      let mnppa = extend extSupported langSupported pkgPresent (P qpn) ppa newactives-      -- In case we continue, we save the scoped dependencies-      let nsvd = M.insert qpn qdeps svd-      case mfr of-        Just fr -> -- The index marks this as an invalid choice. We can stop.-                   return (Fail (varToConflictSet (P qpn)) fr)-        _       -> case mnppa of-                     Left (c, d) -> -- We have an inconsistency. We can stop.-                                    return (Fail c (Conflicting d))-                     Right nppa  -> -- We have an updated partial assignment for the recursive validation.-                                    local (\ s -> s { pa = PA nppa pfa psa, saved = nsvd }) r--    -- What to do for flag nodes ...-    goF :: QFN -> Bool -> Validate (Tree QGoalReason) -> Validate (Tree QGoalReason)-    goF qfn@(FN (PI qpn _i) _f) b r = do-      PA ppa pfa psa <- asks pa -- obtain current preassignment-      extSupported   <- asks supportedExt  -- obtain the supported extensions-      langSupported  <- asks supportedLang -- obtain the supported languages-      pkgPresent     <- asks presentPkgs   -- obtain the present pkg-config pkgs-      svd <- asks saved         -- obtain saved dependencies-      -- Note that there should be saved dependencies for the package in question,-      -- because while building, we do not choose flags before we see the packages-      -- that define them.-      let qdeps = svd ! qpn-      -- We take the *saved* dependencies, because these have been qualified in the-      -- correct scope.-      ---      -- Extend the flag assignment-      let npfa = M.insert qfn b pfa-      -- We now try to get the new active dependencies we might learn about because-      -- we have chosen a new flag.-      let newactives = extractNewDeps (F qfn) b npfa psa qdeps-      -- As in the package case, we try to extend the partial assignment.-      case extend extSupported langSupported pkgPresent (F qfn) ppa newactives of-        Left (c, d) -> return (Fail c (Conflicting d)) -- inconsistency found-        Right nppa  -> local (\ s -> s { pa = PA nppa npfa psa }) r--    -- What to do for stanza nodes (similar to flag nodes) ...-    goS :: QSN -> Bool -> Validate (Tree QGoalReason) -> Validate (Tree QGoalReason)-    goS qsn@(SN (PI qpn _i) _f) b r = do-      PA ppa pfa psa <- asks pa -- obtain current preassignment-      extSupported   <- asks supportedExt  -- obtain the supported extensions-      langSupported  <- asks supportedLang -- obtain the supported languages-      pkgPresent     <- asks presentPkgs -- obtain the present pkg-config pkgs-      svd <- asks saved         -- obtain saved dependencies-      -- Note that there should be saved dependencies for the package in question,-      -- because while building, we do not choose flags before we see the packages-      -- that define them.-      let qdeps = svd ! qpn-      -- We take the *saved* dependencies, because these have been qualified in the-      -- correct scope.-      ---      -- Extend the flag assignment-      let npsa = M.insert qsn b psa-      -- We now try to get the new active dependencies we might learn about because-      -- we have chosen a new flag.-      let newactives = extractNewDeps (S qsn) b pfa npsa qdeps-      -- As in the package case, we try to extend the partial assignment.-      case extend extSupported langSupported pkgPresent (S qsn) ppa newactives of-        Left (c, d) -> return (Fail c (Conflicting d)) -- inconsistency found-        Right nppa  -> local (\ s -> s { pa = PA nppa pfa npsa }) r---- | We try to extract as many concrete dependencies from the given flagged--- dependencies as possible. We make use of all the flag knowledge we have--- already acquired.-extractDeps :: FAssignment -> SAssignment -> FlaggedDeps comp QPN -> [Dep QPN]-extractDeps fa sa deps = do-  d <- deps-  case d of-    Simple sd _         -> return sd-    Flagged qfn _ td fd -> case M.lookup qfn fa of-                             Nothing    -> mzero-                             Just True  -> extractDeps fa sa td-                             Just False -> extractDeps fa sa fd-    Stanza qsn td       -> case M.lookup qsn sa of-                             Nothing    -> mzero-                             Just True  -> extractDeps fa sa td-                             Just False -> []---- | We try to find new dependencies that become available due to the given--- flag or stanza choice. We therefore look for the choice in question, and then call--- 'extractDeps' for everything underneath.-extractNewDeps :: Var QPN -> Bool -> FAssignment -> SAssignment -> FlaggedDeps comp QPN -> [Dep QPN]-extractNewDeps v b fa sa = go-  where-    go :: FlaggedDeps comp QPN -> [Dep QPN] -- Type annotation necessary (polymorphic recursion)-    go deps = do-      d <- deps-      case d of-        Simple _ _           -> mzero-        Flagged qfn' _ td fd-          | v == F qfn'      -> L.map (resetVar v) $-                                if b then extractDeps fa sa td else extractDeps fa sa fd-          | otherwise        -> case M.lookup qfn' fa of-                                  Nothing    -> mzero-                                  Just True  -> go td-                                  Just False -> go fd-        Stanza qsn' td-          | v == S qsn'      -> L.map (resetVar v) $-                                if b then extractDeps fa sa td else []-          | otherwise        -> case M.lookup qsn' sa of-                                  Nothing    -> mzero-                                  Just True  -> go td-                                  Just False -> []---- | Interface.-validateTree :: CompilerInfo -> Index -> PkgConfigDb -> Tree QGoalReason -> Tree QGoalReason-validateTree cinfo idx pkgConfigDb t = runReader (validate t) VS {-    supportedExt   = maybe (const True) -- if compiler has no list of extensions, we assume everything is supported-                           (\ es -> let s = S.fromList es in \ x -> S.member x s)-                           (compilerInfoExtensions cinfo)-  , supportedLang  = maybe (const True)-                           (flip L.elem) -- use list lookup because language list is small and no Ord instance-                           (compilerInfoLanguages  cinfo)-  , presentPkgs    = pkgConfigPkgIsPresent pkgConfigDb-  , index          = idx-  , saved          = M.empty-  , pa             = PA M.empty M.empty M.empty-  , qualifyOptions = defaultQualifyOptions idx-  }
− Distribution/Client/Dependency/Modular/Var.hs
@@ -1,45 +0,0 @@-{-# LANGUAGE DeriveFunctor #-}-module Distribution.Client.Dependency.Modular.Var (-    Var(..)-  , simplifyVar-  , showVar-  , varPI-  ) where--import Prelude hiding (pi)--import Distribution.Client.Dependency.Modular.Flag-import Distribution.Client.Dependency.Modular.Package--{--------------------------------------------------------------------------------  Variables--------------------------------------------------------------------------------}---- | The type of variables that play a role in the solver.--- Note that the tree currently does not use this type directly,--- and rather has separate tree nodes for the different types of--- variables. This fits better with the fact that in most cases,--- these have to be treated differently.-data Var qpn = P qpn | F (FN qpn) | S (SN qpn)-  deriving (Eq, Ord, Show, Functor)---- | For computing conflict sets, we map flag choice vars to a--- single flag choice. This means that all flag choices are treated--- as interdependent. So if one flag of a package ends up in a--- conflict set, then all flags are being treated as being part of--- the conflict set.-simplifyVar :: Var qpn -> Var qpn-simplifyVar (P qpn)       = P qpn-simplifyVar (F (FN pi _)) = F (FN pi (mkFlag "flag"))-simplifyVar (S qsn)       = S qsn--showVar :: Var QPN -> String-showVar (P qpn) = showQPN qpn-showVar (F qfn) = showQFN qfn-showVar (S qsn) = showQSN qsn---- | Extract the package instance from a Var-varPI :: Var QPN -> (QPN, Maybe I)-varPI (P qpn)               = (qpn, Nothing)-varPI (F (FN (PI qpn i) _)) = (qpn, Just i)-varPI (S (SN (PI qpn i) _)) = (qpn, Just i)
− Distribution/Client/Dependency/Modular/Version.hs
@@ -1,53 +0,0 @@-module Distribution.Client.Dependency.Modular.Version-    ( Ver-    , VR-    , anyVR-    , checkVR-    , eqVR-    , showVer-    , showVR-    , simplifyVR-    , (.&&.)-    , (.||.)-    ) where--import qualified Distribution.Version as CV -- from Cabal-import Distribution.Text -- from Cabal---- | Preliminary type for versions.-type Ver = CV.Version---- | String representation of a version.-showVer :: Ver -> String-showVer = display---- | Version range. Consists of a lower and upper bound.-type VR = CV.VersionRange---- | String representation of a version range.-showVR :: VR -> String-showVR = display---- | Unconstrained version range.-anyVR :: VR-anyVR = CV.anyVersion---- | Version range fixing a single version.-eqVR :: Ver -> VR-eqVR = CV.thisVersion---- | Intersect two version ranges.-(.&&.) :: VR -> VR -> VR-(.&&.) = CV.intersectVersionRanges---- | Union of two version ranges.-(.||.) :: VR -> VR -> VR-(.||.) = CV.unionVersionRanges---- | Simplify a version range.-simplifyVR :: VR -> VR-simplifyVR = CV.simplifyVersionRange---- | Checking a version against a version range.-checkVR :: VR -> Ver -> Bool-checkVR = flip CV.withinRange
− Distribution/Client/Dependency/TopDown.hs
@@ -1,1079 +0,0 @@-{-# LANGUAGE CPP #-}--------------------------------------------------------------------------------- |--- Module      :  Distribution.Client.Dependency.Types--- Copyright   :  (c) Duncan Coutts 2008--- License     :  BSD-like------ Maintainer  :  cabal-devel@haskell.org--- Stability   :  provisional--- Portability :  portable------ Common types for dependency resolution.-------------------------------------------------------------------------------module Distribution.Client.Dependency.TopDown (-    topDownResolver-  ) where--import Distribution.Client.Dependency.TopDown.Types-import qualified Distribution.Client.Dependency.TopDown.Constraints as Constraints-import Distribution.Client.Dependency.TopDown.Constraints-         ( Satisfiable(..) )-import Distribution.Client.Types-         ( SourcePackage(..), ConfiguredPackage(..)-         , enableStanzas, ConfiguredId(..), fakeUnitId )-import Distribution.Client.Dependency.Types-         ( DependencyResolver, ResolverPackage(..)-         , PackageConstraint(..), unlabelPackageConstraint-         , PackagePreferences(..), InstalledPreference(..)-         , Progress(..), foldProgress )--import qualified Distribution.Client.PackageIndex as PackageIndex-import qualified Distribution.Simple.PackageIndex  as InstalledPackageIndex-import Distribution.Simple.PackageIndex (InstalledPackageIndex)-import qualified Distribution.InstalledPackageInfo as InstalledPackageInfo-import Distribution.Client.ComponentDeps-         ( ComponentDeps )-import qualified Distribution.Client.ComponentDeps as CD-import Distribution.Client.PackageIndex-         ( PackageIndex )-import Distribution.Package-         ( PackageName(..), PackageId, PackageIdentifier(..)-         , UnitId(..), ComponentId(..)-         , Package(..), packageVersion, packageName-         , Dependency(Dependency), thisPackageVersion, simplifyDependency )-import Distribution.PackageDescription-         ( PackageDescription(buildDepends) )-import Distribution.Client.PackageUtils-         ( externalBuildDepends )-import Distribution.PackageDescription.Configuration-         ( finalizePackageDescription, flattenPackageDescription )-import Distribution.Version-         ( Version(..), VersionRange, withinRange, simplifyVersionRange-         , UpperBound(..), asVersionIntervals )-import Distribution.Compiler-         ( CompilerInfo )-import Distribution.System-         ( Platform )-import Distribution.Simple.Utils-         ( equating, comparing )-import Distribution.Text-         ( display )--import Data.List-         ( foldl', maximumBy, minimumBy, nub, sort, sortBy, groupBy )-import Data.Maybe-         ( fromJust, fromMaybe, catMaybes )-#if !MIN_VERSION_base(4,8,0)-import Data.Monoid-         ( Monoid(mempty) )-#endif-import Control.Monad-         ( guard )-import qualified Data.Set as Set-import Data.Set (Set)-import qualified Data.Map as Map-import qualified Data.Graph as Graph-import qualified Data.Array as Array-import Control.Exception-         ( assert )---- --------------------------------------------------------------- * Search state types--- --------------------------------------------------------------type Constraints  = Constraints.Constraints-                      InstalledPackageEx UnconfiguredPackage ExclusionReason-type SelectedPackages = PackageIndex SelectedPackage---- --------------------------------------------------------------- * The search tree type--- --------------------------------------------------------------data SearchSpace inherited pkg-   = ChoiceNode inherited [[(pkg, SearchSpace inherited pkg)]]-   | Failure Failure---- --------------------------------------------------------------- * Traverse a search tree--- --------------------------------------------------------------explore :: (PackageName -> PackagePreferences)-        -> SearchSpace (SelectedPackages, Constraints, SelectionChanges)-                       SelectablePackage-        -> Progress Log Failure (SelectedPackages, Constraints)--explore _    (Failure failure)       = Fail failure-explore _    (ChoiceNode (s,c,_) []) = Done (s,c)-explore pref (ChoiceNode _ choices)  =-  case [ choice | [choice] <- choices ] of-    ((_, node'):_) -> Step (logInfo node') (explore pref node')-    []             -> Step (logInfo node') (explore pref node')-      where-        choice     = minimumBy (comparing topSortNumber) choices-        pkgname    = packageName . fst . head $ choice-        (_, node') = maximumBy (bestByPref pkgname) choice-  where-    topSortNumber choice = case fst (head choice) of-      InstalledOnly        (InstalledPackageEx  _ i _) -> i-      SourceOnly           (UnconfiguredPackage _ i _ _) -> i-      InstalledAndSource _ (UnconfiguredPackage _ i _ _) -> i--    bestByPref pkgname = case packageInstalledPreference of-        PreferLatest    ->-          comparing (\(p,_) -> (               isPreferred p, packageId p))-        PreferInstalled ->-          comparing (\(p,_) -> (isInstalled p, isPreferred p, packageId p))-      where-        isInstalled (SourceOnly _) = False-        isInstalled _              = True-        isPreferred p = length . filter (packageVersion p `withinRange`) $-                        preferredVersions--        (PackagePreferences preferredVersions packageInstalledPreference _)-          = pref pkgname--    logInfo node = Select selected discarded-      where (selected, discarded) = case node of-              Failure    _               -> ([], [])-              ChoiceNode (_,_,changes) _ -> changes---- --------------------------------------------------------------- * Generate a search tree--- --------------------------------------------------------------type ConfigurePackage = PackageIndex SelectablePackage-                     -> SelectablePackage-                     -> Either [Dependency] SelectedPackage---- | (packages selected, packages discarded)-type SelectionChanges = ([SelectedPackage], [PackageId])--searchSpace :: ConfigurePackage-            -> Constraints-            -> SelectedPackages-            -> SelectionChanges-            -> Set PackageName-            -> SearchSpace (SelectedPackages, Constraints, SelectionChanges)-                           SelectablePackage-searchSpace configure constraints selected changes next =-  assert (Set.null (selectedSet `Set.intersection` next)) $-  assert (selectedSet `Set.isSubsetOf` Constraints.packages constraints) $-  assert (next `Set.isSubsetOf` Constraints.packages constraints) $--  ChoiceNode (selected, constraints, changes)-    [ [ (pkg, select name pkg)-      | pkg <- PackageIndex.lookupPackageName available name ]-    | name <- Set.elems next ]-  where-    available = Constraints.choices constraints--    selectedSet = Set.fromList (map packageName (PackageIndex.allPackages selected))--    select name pkg = case configure available pkg of-      Left missing -> Failure $ ConfigureFailed pkg-                        [ (dep, Constraints.conflicting constraints dep)-                        | dep <- missing ]-      Right pkg' ->-        case constrainDeps pkg' newDeps (addDeps constraints newPkgs) [] of-          Left failure       -> Failure failure-          Right (constraints', newDiscarded) ->-            searchSpace configure-              constraints' selected' (newSelected, newDiscarded) next'-        where-          selected' = foldl' (flip PackageIndex.insert) selected newSelected-          newSelected =-            case Constraints.isPaired constraints (packageId pkg) of-              Nothing     -> [pkg']-              Just pkgid' -> [pkg', pkg'']-                where-                  Just pkg'' = fmap (\(InstalledOnly p) -> InstalledOnly p)-                    (PackageIndex.lookupPackageId available pkgid')--          newPkgs   = [ name'-                      | (Dependency name' _, _) <- newDeps-                      , null (PackageIndex.lookupPackageName selected' name') ]-          newDeps   = concatMap packageConstraints newSelected-          next'     = Set.delete name-                    $ foldl' (flip Set.insert) next newPkgs--packageConstraints :: SelectedPackage -> [(Dependency, Bool)]-packageConstraints = either installedConstraints availableConstraints-                   . preferSource-  where-    preferSource (InstalledOnly        pkg) = Left pkg-    preferSource (SourceOnly           pkg) = Right pkg-    preferSource (InstalledAndSource _ pkg) = Right pkg-    installedConstraints (InstalledPackageEx    _ _ deps) =-      [ (thisPackageVersion dep, True)-      | dep <- deps ]-    availableConstraints (SemiConfiguredPackage _ _ _ deps) =-      [ (dep, False) | dep <- deps ]--addDeps :: Constraints -> [PackageName] -> Constraints-addDeps =-  foldr $ \pkgname cs ->-            case Constraints.addTarget pkgname cs of-              Satisfiable cs' () -> cs'-              _                  -> impossible "addDeps unsatisfiable"--constrainDeps :: SelectedPackage -> [(Dependency, Bool)] -> Constraints-              -> [PackageId]-              -> Either Failure (Constraints, [PackageId])-constrainDeps pkg []         cs discard =-  case addPackageSelectConstraint (packageId pkg) cs of-    Satisfiable cs' discard' -> Right (cs', discard' ++ discard)-    _                        -> impossible "constrainDeps unsatisfiable(1)"-constrainDeps pkg ((dep, installedConstraint):deps) cs discard =-  case addPackageDependencyConstraint (packageId pkg) dep installedConstraint cs of-    Satisfiable cs' discard' -> constrainDeps pkg deps cs' (discard' ++ discard)-    Unsatisfiable            -> impossible "constrainDeps unsatisfiable(2)"-    ConflictsWith conflicts  ->-      Left (DependencyConflict pkg dep installedConstraint conflicts)---- --------------------------------------------------------------- * The main algorithm--- --------------------------------------------------------------search :: ConfigurePackage-       -> (PackageName -> PackagePreferences)-       -> Constraints-       -> Set PackageName-       -> Progress Log Failure (SelectedPackages, Constraints)-search configure pref constraints =-  explore pref . searchSpace configure constraints mempty ([], [])---- --------------------------------------------------------------- * The top level resolver--- ---------------------------------------------------------------- | The main exported resolver, with string logging and failure types to fit--- the standard 'DependencyResolver' interface.----topDownResolver :: DependencyResolver-topDownResolver platform cinfo installedPkgIndex sourcePkgIndex _pkgConfigDB-                preferences constraints targets =-    mapMessages $ topDownResolver'-                    platform cinfo-                    (convertInstalledPackageIndex installedPkgIndex)-                    sourcePkgIndex-                    preferences-                    (map unlabelPackageConstraint constraints)-                    targets-  where-    mapMessages :: Progress Log Failure a -> Progress String String a-    mapMessages = foldProgress (Step . showLog) (Fail . showFailure) Done---- | The native resolver with detailed structured logging and failure types.----topDownResolver' :: Platform -> CompilerInfo-                 -> PackageIndex InstalledPackage-                 -> PackageIndex SourcePackage-                 -> (PackageName -> PackagePreferences)-                 -> [PackageConstraint]-                 -> [PackageName]-                 -> Progress Log Failure [ResolverPackage]-topDownResolver' platform cinfo installedPkgIndex sourcePkgIndex-                 preferences constraints targets =-      fmap (uncurry finalise)-    . (\cs -> search configure preferences cs initialPkgNames)-  =<< pruneBottomUp platform cinfo-  =<< addTopLevelConstraints constraints-  =<< addTopLevelTargets targets emptyConstraintSet--  where-    configure   = configurePackage platform cinfo-    emptyConstraintSet :: Constraints-    emptyConstraintSet = Constraints.empty-      (annotateInstalledPackages          topSortNumber installedPkgIndex')-      (annotateSourcePackages constraints topSortNumber sourcePkgIndex')-    (installedPkgIndex', sourcePkgIndex') =-      selectNeededSubset installedPkgIndex sourcePkgIndex initialPkgNames-    topSortNumber = topologicalSortNumbering installedPkgIndex' sourcePkgIndex'--    initialPkgNames = Set.fromList targets--    finalise selected' constraints' =-        map toResolverPackage-      . PackageIndex.allPackages-      . fst . improvePlan installedPkgIndex' constraints'-      . PackageIndex.fromList-      $ finaliseSelectedPackages preferences selected' constraints'--    toResolverPackage :: FinalSelectedPackage -> ResolverPackage-    toResolverPackage (SelectedInstalled (InstalledPackage pkg _))-                                              = PreExisting pkg-    toResolverPackage (SelectedSource    pkg) = Configured  pkg--addTopLevelTargets :: [PackageName]-                   -> Constraints-                   -> Progress a Failure Constraints-addTopLevelTargets []         cs = Done cs-addTopLevelTargets (pkg:pkgs) cs =-  case Constraints.addTarget pkg cs of-    Satisfiable cs' ()       -> addTopLevelTargets pkgs cs'-    Unsatisfiable            -> Fail (NoSuchPackage pkg)-    ConflictsWith _conflicts -> impossible "addTopLevelTargets conflicts"---addTopLevelConstraints :: [PackageConstraint] -> Constraints-                       -> Progress Log Failure Constraints-addTopLevelConstraints []                                      cs = Done cs-addTopLevelConstraints (PackageConstraintFlags   _   _  :deps) cs =-  addTopLevelConstraints deps cs--addTopLevelConstraints (PackageConstraintVersion pkg ver:deps) cs =-  case addTopLevelVersionConstraint pkg ver cs of-    Satisfiable cs' pkgids  ->-      Step (AppliedVersionConstraint pkg ver pkgids)-           (addTopLevelConstraints deps cs')--    Unsatisfiable           ->-      Fail (TopLevelVersionConstraintUnsatisfiable pkg ver)--    ConflictsWith conflicts ->-      Fail (TopLevelVersionConstraintConflict pkg ver conflicts)--addTopLevelConstraints (PackageConstraintInstalled pkg:deps) cs =-  case addTopLevelInstalledConstraint pkg cs of-    Satisfiable cs' pkgids  ->-      Step (AppliedInstalledConstraint pkg InstalledConstraint pkgids)-           (addTopLevelConstraints deps cs')--    Unsatisfiable           ->-      Fail (TopLevelInstallConstraintUnsatisfiable pkg InstalledConstraint)--    ConflictsWith conflicts ->-      Fail (TopLevelInstallConstraintConflict pkg InstalledConstraint conflicts)--addTopLevelConstraints (PackageConstraintSource pkg:deps) cs =-  case addTopLevelSourceConstraint pkg cs of-    Satisfiable cs' pkgids  ->-      Step (AppliedInstalledConstraint pkg SourceConstraint pkgids)-            (addTopLevelConstraints deps cs')--    Unsatisfiable           ->-      Fail (TopLevelInstallConstraintUnsatisfiable pkg SourceConstraint)--    ConflictsWith conflicts ->-      Fail (TopLevelInstallConstraintConflict pkg SourceConstraint conflicts)--addTopLevelConstraints (PackageConstraintStanzas _ _ : deps) cs =-    addTopLevelConstraints deps cs---- | Add exclusion on available packages that cannot be configured.----pruneBottomUp :: Platform -> CompilerInfo-              -> Constraints -> Progress Log Failure Constraints-pruneBottomUp platform comp constraints =-    foldr prune Done (initialPackages constraints) constraints--  where-    prune pkgs rest cs = foldr addExcludeConstraint rest unconfigurable cs-      where-        unconfigurable =-          [ (pkg, missing) -- if necessary we could look up missing reasons-          | (Just pkg', pkg) <- zip (map getSourcePkg pkgs) pkgs-          , Left missing <- [configure cs pkg'] ]--    addExcludeConstraint (pkg, missing) rest cs =-      let reason = ExcludedByConfigureFail missing in-      case addPackageExcludeConstraint (packageId pkg) reason cs of-        Satisfiable cs' [pkgid]| packageId pkg == pkgid-                         -> Step (ExcludeUnconfigurable pkgid) (rest cs')-        Satisfiable _ _  -> impossible "pruneBottomUp satisfiable"-        _                -> Fail $ ConfigureFailed pkg-                              [ (dep, Constraints.conflicting cs dep)-                              | dep <- missing ]--    configure cs (UnconfiguredPackage (SourcePackage _ pkg _ _) _ flags stanzas) =-      finalizePackageDescription flags (dependencySatisfiable cs)-                                 platform comp [] (enableStanzas stanzas pkg)-    dependencySatisfiable cs =-      not . null . PackageIndex.lookupDependency (Constraints.choices cs)--    -- collect each group of packages (by name) in reverse topsort order-    initialPackages =-        reverse-      . sortBy (comparing (topSortNumber . head))-      . PackageIndex.allPackagesByName-      . Constraints.choices--    topSortNumber (InstalledOnly        (InstalledPackageEx  _ i _)) = i-    topSortNumber (SourceOnly           (UnconfiguredPackage _ i _ _)) = i-    topSortNumber (InstalledAndSource _ (UnconfiguredPackage _ i _ _)) = i--    getSourcePkg (InstalledOnly      _     ) = Nothing-    getSourcePkg (SourceOnly           spkg) = Just spkg-    getSourcePkg (InstalledAndSource _ spkg) = Just spkg---configurePackage :: Platform -> CompilerInfo -> ConfigurePackage-configurePackage platform cinfo available spkg = case spkg of-  InstalledOnly      ipkg      -> Right (InstalledOnly ipkg)-  SourceOnly              apkg -> fmap SourceOnly (configure apkg)-  InstalledAndSource ipkg apkg -> fmap (InstalledAndSource ipkg)-                                       (configure apkg)-  where-  configure (UnconfiguredPackage apkg@(SourcePackage _ p _ _) _ flags stanzas) =-    case finalizePackageDescription flags dependencySatisfiable-                                    platform cinfo []-                                    (enableStanzas stanzas p) of-      Left missing        -> Left missing-      Right (pkg, flags') -> Right $-        SemiConfiguredPackage apkg flags' stanzas (externalBuildDepends pkg)--  dependencySatisfiable = not . null . PackageIndex.lookupDependency available---- | Annotate each installed packages with its set of transitive dependencies--- and its topological sort number.----annotateInstalledPackages :: (PackageName -> TopologicalSortNumber)-                          -> PackageIndex InstalledPackage-                          -> PackageIndex InstalledPackageEx-annotateInstalledPackages dfsNumber installed = PackageIndex.fromList-  [ InstalledPackageEx pkg (dfsNumber (packageName pkg)) (transitiveDepends pkg)-  | pkg <- PackageIndex.allPackages installed ]-  where-    transitiveDepends :: InstalledPackage -> [PackageId]-    transitiveDepends = map (packageId . toPkg) . tail . Graph.reachable graph-                      . fromJust . toVertex . packageId-    (graph, toPkg, toVertex) = dependencyGraph installed----- | Annotate each available packages with its topological sort number and any--- user-supplied partial flag assignment.----annotateSourcePackages :: [PackageConstraint]-                       -> (PackageName -> TopologicalSortNumber)-                       -> PackageIndex SourcePackage-                       -> PackageIndex UnconfiguredPackage-annotateSourcePackages constraints dfsNumber sourcePkgIndex =-    PackageIndex.fromList-      [ UnconfiguredPackage pkg (dfsNumber name) (flagsFor name) (stanzasFor name)-      | pkg <- PackageIndex.allPackages sourcePkgIndex-      , let name = packageName pkg ]-  where-    flagsFor = fromMaybe [] . flip Map.lookup flagsMap-    flagsMap = Map.fromList-      [ (name, flags)-      | PackageConstraintFlags name flags <- constraints ]-    stanzasFor = fromMaybe [] . flip Map.lookup stanzasMap-    stanzasMap = Map.fromListWith (++)-        [ (name, stanzas)-        | PackageConstraintStanzas name stanzas <- constraints ]---- | One of the heuristics we use when guessing which path to take in the--- search space is an ordering on the choices we make. It's generally better--- to make decisions about packages higer in the dep graph first since they--- place constraints on packages lower in the dep graph.------ To pick them in that order we annotate each package with its topological--- sort number. So if package A depends on package B then package A will have--- a lower topological sort number than B and we'll make a choice about which--- version of A to pick before we make a choice about B (unless there is only--- one possible choice for B in which case we pick that immediately).------ To construct these topological sort numbers we combine and flatten the--- installed and source package sets. We consider only dependencies between--- named packages, not including versions and for not-yet-configured packages--- we look at all the possible dependencies, not just those under any single--- flag assignment. This means we can actually get impossible combinations of--- edges and even cycles, but that doesn't really matter here, it's only a--- heuristic.----topologicalSortNumbering :: PackageIndex InstalledPackage-                         -> PackageIndex SourcePackage-                         -> (PackageName -> TopologicalSortNumber)-topologicalSortNumbering installedPkgIndex sourcePkgIndex =-    \pkgname -> let Just vertex = toVertex pkgname-                 in topologicalSortNumbers Array.! vertex-  where-    topologicalSortNumbers = Array.array (Array.bounds graph)-                                         (zip (Graph.topSort graph) [0..])-    (graph, _, toVertex)   = Graph.graphFromEdges $-         [ ((), packageName pkg, nub deps)-         | pkgs@(pkg:_) <- PackageIndex.allPackagesByName installedPkgIndex-         , let deps = [ packageName dep-                      | pkg' <- pkgs-                      , dep  <- sourceDeps pkg' ] ]-      ++ [ ((), packageName pkg, nub deps)-         | pkgs@(pkg:_) <- PackageIndex.allPackagesByName sourcePkgIndex-         , let deps = [ depName-                      | SourcePackage _ pkg' _ _ <- pkgs-                      , Dependency depName _ <--                          buildDepends (flattenPackageDescription pkg') ] ]---- | We don't need the entire index (which is rather large and costly if we--- force it by examining the whole thing). So trace out the maximul subset of--- each index that we could possibly ever need. Do this by flattening packages--- and looking at the names of all possible dependencies.----selectNeededSubset :: PackageIndex InstalledPackage-                   -> PackageIndex SourcePackage-                   -> Set PackageName-                   -> (PackageIndex InstalledPackage-                      ,PackageIndex SourcePackage)-selectNeededSubset installedPkgIndex sourcePkgIndex = select mempty mempty-  where-    select :: PackageIndex InstalledPackage-           -> PackageIndex SourcePackage-           -> Set PackageName-           -> (PackageIndex InstalledPackage-              ,PackageIndex SourcePackage)-    select installedPkgIndex' sourcePkgIndex' remaining-      | Set.null remaining = (installedPkgIndex', sourcePkgIndex')-      | otherwise = select installedPkgIndex'' sourcePkgIndex'' remaining''-      where-        (next, remaining') = Set.deleteFindMin remaining-        moreInstalled = PackageIndex.lookupPackageName installedPkgIndex next-        moreSource    = PackageIndex.lookupPackageName sourcePkgIndex next-        moreRemaining = -- we filter out packages already included in the indexes-                        -- this avoids an infinite loop if a package depends on itself-                        -- like base-3.0.3.0 with base-4.0.0.0-                        filter notAlreadyIncluded-                      $ [ packageName dep-                        | pkg <- moreInstalled-                        , dep <- sourceDeps pkg ]-                     ++ [ name-                        | SourcePackage _ pkg _ _ <- moreSource-                        , Dependency name _ <--                            buildDepends (flattenPackageDescription pkg) ]-        installedPkgIndex'' = foldl' (flip PackageIndex.insert)-                                     installedPkgIndex' moreInstalled-        sourcePkgIndex''    = foldl' (flip PackageIndex.insert)-                                     sourcePkgIndex' moreSource-        remaining''         = foldl' (flip          Set.insert)-                                     remaining' moreRemaining-        notAlreadyIncluded name =-            null (PackageIndex.lookupPackageName installedPkgIndex' name)-         && null (PackageIndex.lookupPackageName sourcePkgIndex' name)----- | The old top down solver assumes that installed packages are indexed by--- their source package id. But these days they're actually indexed by an--- installed package id and there can be many installed packages with the same--- source package id. This function tries to do a convertion, but it can only--- be partial.----convertInstalledPackageIndex :: InstalledPackageIndex-                             -> PackageIndex InstalledPackage-convertInstalledPackageIndex index' = PackageIndex.fromList-    -- There can be multiple installed instances of each package version,-    -- like when the same package is installed in the global & user DBs.-    -- InstalledPackageIndex.allPackagesBySourcePackageId gives us the-    -- installed packages with the most preferred instances first, so by-    -- picking the first we should get the user one. This is almost but not-    -- quite the same as what ghc does.-    [ InstalledPackage ipkg (sourceDepsOf index' ipkg)-    | (_,ipkg:_) <- InstalledPackageIndex.allPackagesBySourcePackageId index' ]-  where-    -- The InstalledPackageInfo only lists dependencies by the-    -- UnitId, which means we do not directly know the corresponding-    -- source dependency. The only way to find out is to lookup the-    -- UnitId to get the InstalledPackageInfo and look at its-    -- source PackageId. But if the package is broken because it depends on-    -- other packages that do not exist then we have a problem we cannot find-    -- the original source package id. Instead we make up a bogus package id.-    -- This should have the same effect since it should be a dependency on a-    -- nonexistent package.-    sourceDepsOf index ipkg =-      [ maybe (brokenPackageId depid) packageId mdep-      | let depids = InstalledPackageInfo.depends ipkg-            getpkg = InstalledPackageIndex.lookupUnitId index-      , (depid, mdep) <- zip depids (map getpkg depids) ]--    brokenPackageId (SimpleUnitId (ComponentId str)) =-      PackageIdentifier (PackageName (str ++ "-broken")) (Version [] [])---- --------------------------------------------------------------- * Post processing the solution--- --------------------------------------------------------------finaliseSelectedPackages :: (PackageName -> PackagePreferences)-                         -> SelectedPackages-                         -> Constraints-                         -> [FinalSelectedPackage]-finaliseSelectedPackages pref selected constraints =-  map finaliseSelected (PackageIndex.allPackages selected)-  where-    remainingChoices = Constraints.choices constraints-    finaliseSelected (InstalledOnly      ipkg     ) = finaliseInstalled ipkg-    finaliseSelected (SourceOnly              apkg) = finaliseSource Nothing apkg-    finaliseSelected (InstalledAndSource ipkg apkg) =-      case PackageIndex.lookupPackageId remainingChoices (packageId ipkg) of-                                        --picked package not in constraints-        Nothing                       -> impossible "finaliseSelected no pkg"-                                        -- to constrain to avail only:-        Just (SourceOnly _)           -> impossible "finaliseSelected src only"-        Just (InstalledOnly _)        -> finaliseInstalled ipkg-        Just (InstalledAndSource _ _) -> finaliseSource (Just ipkg) apkg--    finaliseInstalled (InstalledPackageEx pkg _ _) = SelectedInstalled pkg-    finaliseSource mipkg (SemiConfiguredPackage pkg flags stanzas deps) =-        SelectedSource (ConfiguredPackage pkg flags stanzas deps')-      where-        -- We cheat in the cabal solver, and classify all dependencies as-        -- library dependencies.-        deps' :: ComponentDeps [ConfiguredId]-        deps' = CD.fromLibraryDeps $ map (confId . pickRemaining mipkg) deps--    -- InstalledOrSource indicates that we either have a source package-    -- available, or an installed one, or both. In the case that we have both-    -- available, we don't yet know if we can pick the installed one (the-    -- dependencies may not match up, for instance); this is verified in-    -- `improvePlan`.-    ---    -- This means that at this point we cannot construct a valid installed-    -- package ID yet for the dependencies. We therefore have two options:-    ---    -- * We could leave the installed package ID undefined here, and have a-    --   separate pass over the output of the top-down solver, fixing all-    --   dependencies so that if we depend on an already installed package we-    --   use the proper installed package ID.-    ---    -- * We can _always_ use fake installed IDs, irrespective of whether we the-    --   dependency is on an already installed package or not. This is okay-    --   because (i) the top-down solver does not (and never will) support-    --   multiple package instances, and (ii) we initialize the FakeMap with-    --   fake IDs for already installed packages.-    ---    -- For now we use the second option; if however we change the implementation-    -- of these fake IDs so that we do away with the FakeMap and update a-    -- package reverse dependencies as we execute the install plan and discover-    -- real package IDs, then this is no longer possible and we have to-    -- implement the first option (see also Note [FakeMap] in Cabal).-    confId :: InstalledOrSource InstalledPackageEx UnconfiguredPackage -> ConfiguredId-    confId pkg = ConfiguredId {-        confSrcId  = packageId pkg-      , confInstId = fakeUnitId (packageId pkg)-      }--    pickRemaining mipkg dep@(Dependency _name versionRange) =-          case PackageIndex.lookupDependency remainingChoices dep of-            []        -> impossible "pickRemaining no pkg"-            [pkg']    -> pkg'-            remaining -> assert (checkIsPaired remaining)-                       $ maximumBy bestByPref remaining-      where-        -- We order candidate packages to pick for a dependency by these-        -- three factors. The last factor is just highest version wins.-        bestByPref =-          comparing (\p -> (isCurrent p, isPreferred p, packageVersion p))-        -- Is the package already used by the installed version of this-        -- package? If so we should pick that first. This stops us from doing-        -- silly things like deciding to rebuild haskell98 against base 3.-        isCurrent = case mipkg :: Maybe InstalledPackageEx of-          Nothing   -> \_ -> False-          Just ipkg -> \p -> packageId p `elem` sourceDeps ipkg-        -- If there is no upper bound on the version range then we apply a-        -- preferred version according to the hackage or user's suggested-        -- version constraints. TODO: distinguish hacks from prefs-        bounded = boundedAbove versionRange-        isPreferred p-          | bounded   = boundedRank -- this is a dummy constant-          | otherwise = length . filter (packageVersion p `withinRange`) $-                        preferredVersions-          where (PackagePreferences preferredVersions _ _) = pref (packageName p)-        boundedRank = 0 -- any value will do--        boundedAbove :: VersionRange -> Bool-        boundedAbove vr = case asVersionIntervals vr of-          []        -> True -- this is the inconsistent version range.-          intervals -> case last intervals of-            (_,   UpperBound _ _) -> True-            (_, NoUpperBound    ) -> False--        -- We really only expect to find more than one choice remaining when-        -- we're finalising a dependency on a paired package.-        checkIsPaired [p1, p2] =-          case Constraints.isPaired constraints (packageId p1) of-            Just p2'   -> packageId p2' == packageId p2-            Nothing    -> False-        checkIsPaired _ = False---- | Improve an existing installation plan by, where possible, swapping--- packages we plan to install with ones that are already installed.--- This may add additional constraints due to the dependencies of installed--- packages on other installed packages.----improvePlan :: PackageIndex InstalledPackage-            -> Constraints-            -> PackageIndex FinalSelectedPackage-            -> (PackageIndex FinalSelectedPackage, Constraints)-improvePlan installed constraints0 selected0 =-  foldl' improve (selected0, constraints0) (reverseTopologicalOrder selected0)-  where-    improve (selected, constraints) = fromMaybe (selected, constraints)-                                    . improvePkg selected constraints--    -- The idea is to improve the plan by swapping a configured package for-    -- an equivalent installed one. For a particular package the condition is-    -- that the package be in a configured state, that a the same version be-    -- already installed with the exact same dependencies and all the packages-    -- in the plan that it depends on are in the installed state-    improvePkg selected constraints pkgid = do-      SelectedSource pkg  <- PackageIndex.lookupPackageId selected  pkgid-      ipkg                <- PackageIndex.lookupPackageId installed pkgid-      guard $ all (isInstalled selected) (sourceDeps pkg)-      tryInstalled selected constraints [ipkg]--    isInstalled selected pkgid =-      case PackageIndex.lookupPackageId selected pkgid of-        Just (SelectedInstalled _) -> True-        _                          -> False--    tryInstalled :: PackageIndex FinalSelectedPackage -> Constraints-                 -> [InstalledPackage]-                 -> Maybe (PackageIndex FinalSelectedPackage, Constraints)-    tryInstalled selected constraints [] = Just (selected, constraints)-    tryInstalled selected constraints (pkg:pkgs) =-      case constraintsOk (packageId pkg) (sourceDeps pkg) constraints of-        Nothing           -> Nothing-        Just constraints' -> tryInstalled selected' constraints' pkgs'-          where-            selected' = PackageIndex.insert (SelectedInstalled pkg) selected-            pkgs'      = catMaybes (map notSelected (sourceDeps pkg)) ++ pkgs-            notSelected pkgid =-              case (PackageIndex.lookupPackageId installed pkgid-                   ,PackageIndex.lookupPackageId selected  pkgid) of-                (Just pkg', Nothing) -> Just pkg'-                _                    -> Nothing--    constraintsOk _     []              constraints = Just constraints-    constraintsOk pkgid (pkgid':pkgids) constraints =-      case addPackageDependencyConstraint pkgid dep True constraints of-        Satisfiable constraints' _ -> constraintsOk pkgid pkgids constraints'-        _                          -> Nothing-      where-        dep = thisPackageVersion pkgid'--    reverseTopologicalOrder :: PackageIndex FinalSelectedPackage -> [PackageId]-    reverseTopologicalOrder index = map (packageId . toPkg)-                                  . Graph.topSort-                                  . Graph.transposeG-                                  $ graph-      where (graph, toPkg, _) = dependencyGraph index---- --------------------------------------------------------------- * Adding and recording constraints--- --------------------------------------------------------------addPackageSelectConstraint :: PackageId -> Constraints-                           -> Satisfiable Constraints-                                [PackageId] ExclusionReason-addPackageSelectConstraint pkgid =-    Constraints.constrain pkgname constraint reason-  where-    pkgname          = packageName pkgid-    constraint ver _ = ver == packageVersion pkgid-    reason           = SelectedOther pkgid--addPackageExcludeConstraint :: PackageId -> ExclusionReason-                            -> Constraints-                            -> Satisfiable Constraints-                                           [PackageId] ExclusionReason-addPackageExcludeConstraint pkgid reason =-    Constraints.constrain pkgname constraint reason-  where-    pkgname = packageName pkgid-    constraint ver installed-      | ver == packageVersion pkgid = installed-      | otherwise                   = True--addPackageDependencyConstraint :: PackageId -> Dependency -> Bool-                               -> Constraints-                               -> Satisfiable Constraints-                                    [PackageId] ExclusionReason-addPackageDependencyConstraint pkgid dep@(Dependency pkgname verrange)-                                     installedConstraint =-    Constraints.constrain pkgname constraint reason-  where-    constraint ver installed = ver `withinRange` verrange-                            && if installedConstraint then installed else True-    reason = ExcludedByPackageDependency pkgid dep installedConstraint--addTopLevelVersionConstraint :: PackageName -> VersionRange-                             -> Constraints-                             -> Satisfiable Constraints-                                  [PackageId] ExclusionReason-addTopLevelVersionConstraint pkgname verrange =-    Constraints.constrain pkgname constraint reason-  where-    constraint ver _installed = ver `withinRange` verrange-    reason = ExcludedByTopLevelConstraintVersion pkgname verrange--addTopLevelInstalledConstraint,-  addTopLevelSourceConstraint :: PackageName-                              -> Constraints-                              -> Satisfiable Constraints-                                   [PackageId] ExclusionReason-addTopLevelInstalledConstraint pkgname =-    Constraints.constrain pkgname constraint reason-  where-    constraint _ver installed = installed-    reason = ExcludedByTopLevelConstraintInstalled pkgname--addTopLevelSourceConstraint pkgname =-    Constraints.constrain pkgname constraint reason-  where-    constraint _ver installed = not installed-    reason = ExcludedByTopLevelConstraintSource pkgname----- --------------------------------------------------------------- * Reasons for constraints--- ---------------------------------------------------------------- | For every constraint we record we also record the reason that constraint--- is needed. So if we end up failing due to conflicting constraints then we--- can give an explnanation as to what was conflicting and why.----data ExclusionReason =--     -- | We selected this other version of the package. That means we exclude-     -- all the other versions.-     SelectedOther PackageId--     -- | We excluded this version of the package because it failed to-     -- configure probably because of unsatisfiable deps.-   | ExcludedByConfigureFail [Dependency]--     -- | We excluded this version of the package because another package that-     -- we selected imposed a dependency which this package did not satisfy.-   | ExcludedByPackageDependency PackageId Dependency Bool--     -- | We excluded this version of the package because it did not satisfy-     -- a dependency given as an original top level input.-     ---   | ExcludedByTopLevelConstraintVersion   PackageName VersionRange-   | ExcludedByTopLevelConstraintInstalled PackageName-   | ExcludedByTopLevelConstraintSource    PackageName--  deriving Eq---- | Given an excluded package and the reason it was excluded, produce a human--- readable explanation.----showExclusionReason :: PackageId -> ExclusionReason -> String-showExclusionReason pkgid (SelectedOther pkgid') =-  display pkgid ++ " was excluded because " ++-  display pkgid' ++ " was selected instead"-showExclusionReason pkgid (ExcludedByConfigureFail missingDeps) =-  display pkgid ++ " was excluded because it could not be configured. "-  ++ "It requires " ++ listOf displayDep missingDeps-showExclusionReason pkgid (ExcludedByPackageDependency pkgid' dep installedConstraint)-  = display pkgid ++ " was excluded because " ++ display pkgid' ++ " requires "- ++ (if installedConstraint then "an installed instance of " else "")- ++ displayDep dep-showExclusionReason pkgid (ExcludedByTopLevelConstraintVersion pkgname verRange) =-  display pkgid ++ " was excluded because of the top level constraint " ++-  displayDep (Dependency pkgname verRange)-showExclusionReason pkgid (ExcludedByTopLevelConstraintInstalled pkgname)-  = display pkgid ++ " was excluded because of the top level constraint '"- ++ display pkgname ++ " installed' which means that only installed instances "- ++ "of the package may be selected."-showExclusionReason pkgid (ExcludedByTopLevelConstraintSource pkgname)-  = display pkgid ++ " was excluded because of the top level constraint '"- ++ display pkgname ++ " source' which means that only source versions "- ++ "of the package may be selected."----- --------------------------------------------------------------- * Logging progress and failures--- --------------------------------------------------------------data Log = Select [SelectedPackage] [PackageId]-         | AppliedVersionConstraint   PackageName VersionRange [PackageId]-         | AppliedInstalledConstraint PackageName InstalledConstraint [PackageId]-         | ExcludeUnconfigurable PackageId--data Failure-   = NoSuchPackage-       PackageName-   | ConfigureFailed-       SelectablePackage-       [(Dependency, [(PackageId, [ExclusionReason])])]-   | DependencyConflict-       SelectedPackage Dependency Bool-       [(PackageId, [ExclusionReason])]-   | TopLevelVersionConstraintConflict-       PackageName VersionRange-       [(PackageId, [ExclusionReason])]-   | TopLevelVersionConstraintUnsatisfiable-       PackageName VersionRange-   | TopLevelInstallConstraintConflict-       PackageName InstalledConstraint-       [(PackageId, [ExclusionReason])]-   | TopLevelInstallConstraintUnsatisfiable-       PackageName InstalledConstraint--showLog :: Log -> String-showLog (Select selected discarded) = case (selectedMsg, discardedMsg) of-  ("", y) -> y-  (x, "") -> x-  (x,  y) -> x ++ " and " ++ y--  where-    selectedMsg  = "selecting " ++ case selected of-      []     -> ""-      [s]    -> display (packageId s) ++ " " ++ kind s-      (s:ss) -> listOf id-              $ (display (packageId s) ++ " " ++ kind s)-              : [ display (packageVersion s') ++ " " ++ kind s'-                | s' <- ss ]--    kind (InstalledOnly _)        = "(installed)"-    kind (SourceOnly _)           = "(source)"-    kind (InstalledAndSource _ _) = "(installed or source)"--    discardedMsg = case discarded of-      []  -> ""-      _   -> "discarding " ++ listOf id-        [ element-        | (pkgid:pkgids) <- groupBy (equating packageName) (sort discarded)-        , element <- display pkgid : map (display . packageVersion) pkgids ]-showLog (AppliedVersionConstraint pkgname ver pkgids) =-     "applying constraint " ++ display (Dependency pkgname ver)-  ++ if null pkgids-       then ""-       else " which excludes " ++ listOf display pkgids-showLog (AppliedInstalledConstraint pkgname inst pkgids) =-     "applying constraint " ++ display pkgname ++ " '"-  ++ (case inst of InstalledConstraint -> "installed"; _ -> "source") ++ "' "-  ++ if null pkgids-       then ""-       else "which excludes " ++ listOf display pkgids-showLog (ExcludeUnconfigurable pkgid) =-     "excluding " ++ display pkgid ++ " (it cannot be configured)"--showFailure :: Failure -> String-showFailure (NoSuchPackage pkgname) =-     "The package " ++ display pkgname ++ " is unknown."-showFailure (ConfigureFailed pkg missingDeps) =-     "cannot configure " ++ displayPkg pkg ++ ". It requires "-  ++ listOf (displayDep . fst) missingDeps-  ++ '\n' : unlines (map (uncurry whyNot) missingDeps)--  where-    whyNot (Dependency name ver) [] =-         "There is no available version of " ++ display name-      ++ " that satisfies " ++ displayVer ver--    whyNot dep conflicts =-         "For the dependency on " ++ displayDep dep-      ++ " there are these packages: " ++ listOf display pkgs-      ++ ". However none of them are available.\n"-      ++ unlines [ showExclusionReason (packageId pkg') reason-                 | (pkg', reasons) <- conflicts, reason <- reasons ]--      where pkgs = map fst conflicts--showFailure (DependencyConflict pkg dep installedConstraint conflicts) =-     "dependencies conflict: "-  ++ displayPkg pkg ++ " requires "-  ++ (if installedConstraint then "an installed instance of " else "")-  ++ displayDep dep ++ " however:\n"-  ++ unlines [ showExclusionReason (packageId pkg') reason-             | (pkg', reasons) <- conflicts, reason <- reasons ]--showFailure (TopLevelVersionConstraintConflict name ver conflicts) =-     "constraints conflict: we have the top level constraint "-  ++ displayDep (Dependency name ver) ++ ", but\n"-  ++ unlines [ showExclusionReason (packageId pkg') reason-             | (pkg', reasons) <- conflicts, reason <- reasons ]--showFailure (TopLevelVersionConstraintUnsatisfiable name ver) =-     "There is no available version of " ++ display name-      ++ " that satisfies " ++ displayVer ver--showFailure (TopLevelInstallConstraintConflict name InstalledConstraint conflicts) =-     "constraints conflict: "-  ++ "top level constraint '" ++ display name ++ " installed' however\n"-  ++ unlines [ showExclusionReason (packageId pkg') reason-             | (pkg', reasons) <- conflicts, reason <- reasons ]--showFailure (TopLevelInstallConstraintUnsatisfiable name InstalledConstraint) =-     "There is no installed version of " ++ display name--showFailure (TopLevelInstallConstraintConflict name SourceConstraint conflicts) =-     "constraints conflict: "-  ++ "top level constraint '" ++ display name ++ " source' however\n"-  ++ unlines [ showExclusionReason (packageId pkg') reason-             | (pkg', reasons) <- conflicts, reason <- reasons ]--showFailure (TopLevelInstallConstraintUnsatisfiable name SourceConstraint) =-     "There is no available source version of " ++ display name--displayVer :: VersionRange -> String-displayVer = display . simplifyVersionRange--displayDep :: Dependency -> String-displayDep = display . simplifyDependency----- --------------------------------------------------------------- * Utils--- --------------------------------------------------------------impossible :: String -> a-impossible msg = internalError $ "assertion failure: " ++ msg--internalError :: String -> a-internalError msg = error $ "internal error: " ++ msg--displayPkg :: Package pkg => pkg -> String-displayPkg = display . packageId--listOf :: (a -> String) -> [a] -> String-listOf _    []   = []-listOf disp [x0] = disp x0-listOf disp (x0:x1:xs) = disp x0 ++ go x1 xs-  where go x []       = " and " ++ disp x-        go x (x':xs') = ", " ++ disp x ++ go x' xs'---- --------------------------------------------------------------- * Construct a dependency graph--- ---------------------------------------------------------------- | Builds a graph of the package dependencies.------ Dependencies on other packages that are not in the index are discarded.--- You can check if there are any such dependencies with 'brokenPackages'.------ The top-down solver gets its own implementation, because both--- `dependencyGraph` in `Distribution.Client.PlanIndex` (in cabal-install) and--- `dependencyGraph` in `Distribution.Simple.PackageIndex` (in Cabal) both work--- with `PackageIndex` from `Cabal` (that is, a package index indexed by--- installed package IDs rather than package names).------ Ideally we would switch the top-down solver over to use that too, so that--- this duplication could be avoided, but that's a bit of work and the top-down--- solver is legacy code anyway.------ (NOTE: This is called at two types: InstalledPackage and FinalSelectedPackage.)-dependencyGraph :: PackageSourceDeps pkg-                => PackageIndex pkg-                -> (Graph.Graph,-                    Graph.Vertex -> pkg,-                    PackageId -> Maybe Graph.Vertex)-dependencyGraph index = (graph, vertexToPkg, pkgIdToVertex)-  where-    graph = Array.listArray bounds $-            map (catMaybes . map pkgIdToVertex . sourceDeps) pkgs-    vertexToPkg vertex = pkgTable Array.! vertex-    pkgIdToVertex = binarySearch 0 topBound--    pkgTable   = Array.listArray bounds pkgs-    pkgIdTable = Array.listArray bounds (map packageId pkgs)-    pkgs = sortBy (comparing packageId) (PackageIndex.allPackages index)-    topBound = length pkgs - 1-    bounds = (0, topBound)--    binarySearch a b key-      | a > b     = Nothing-      | otherwise = case compare key (pkgIdTable Array.! mid) of-          LT -> binarySearch a (mid-1) key-          EQ -> Just mid-          GT -> binarySearch (mid+1) b key-      where mid = (a + b) `div` 2
− Distribution/Client/Dependency/TopDown/Constraints.hs
@@ -1,599 +0,0 @@-{-# LANGUAGE CPP #-}--------------------------------------------------------------------------------- |--- Module      :  Distribution.Client.Dependency.TopDown.Constraints--- Copyright   :  (c) Duncan Coutts 2008--- License     :  BSD-like------ Maintainer  :  duncan@community.haskell.org--- Stability   :  provisional--- Portability :  portable------ A set of satisfiable constraints on a set of packages.-------------------------------------------------------------------------------module Distribution.Client.Dependency.TopDown.Constraints (-  Constraints,-  empty,-  packages,-  choices,-  isPaired,--  addTarget,-  constrain,-  Satisfiable(..),-  conflicting,-  ) where--import Distribution.Client.Dependency.TopDown.Types-import qualified Distribution.Client.PackageIndex as PackageIndex-import Distribution.Client.PackageIndex-        ( PackageIndex )-import Distribution.Package-         ( PackageName, PackageId, PackageIdentifier(..)-         , Package(packageId), packageName, packageVersion-         , Dependency )-import Distribution.Version-         ( Version )-import Distribution.Client.Utils-         ( mergeBy, MergeResult(..) )--#if !MIN_VERSION_base(4,8,0)-import Data.Monoid-         ( Monoid(mempty) )-#endif-import Data.Either-         ( partitionEithers )-import qualified Data.Map as Map-import Data.Map (Map)-import qualified Data.Set as Set-import Data.Set (Set)-import Control.Exception-         ( assert )----- | A set of satisfiable constraints on a set of packages.------ The 'Constraints' type keeps track of a set of targets (identified by--- package name) that we know that we need. It also keeps track of a set of--- constraints over all packages in the environment.------ It maintains the guarantee that, for the target set, the constraints are--- satisfiable, meaning that there is at least one instance available for each--- package name that satisfies the constraints on that package name.------ Note that it is possible to over-constrain a package in the environment that--- is not in the target set -- the satisfiability guarantee is only maintained--- for the target set. This is useful because it allows us to exclude packages--- without needing to know if it would ever be needed or not (e.g. allows--- excluding broken installed packages).------ Adding a constraint for a target package can fail if it would mean that--- there are no remaining choices.------ Adding a constraint for package that is not a target never fails.------ Adding a new target package can fail if that package already has conflicting--- constraints.----data Constraints installed source reason-   = Constraints--       -- | Targets that we know we need. This is the set for which we-       -- guarantee the constraints are satisfiable.-       !(Set PackageName)--       -- | The available/remaining set. These are packages that have available-       -- choices remaining. This is guaranteed to cover the target packages,-       -- but can also cover other packages in the environment. New targets can-       -- only be added if there are available choices remaining for them.-       !(PackageIndex (InstalledOrSource installed source))--       -- | The excluded set. Choices that we have excluded by applying-       -- constraints. Excluded choices are tagged with the reason.-       !(PackageIndex (ExcludedPkg (InstalledOrSource installed source) reason))--       -- | Paired choices, this is an ugly hack.-       !(Map PackageName (Version, Version))--       -- | Purely for the invariant, we keep a copy of the original index-       !(PackageIndex (InstalledOrSource installed source))----- | Reasons for excluding all, or some choices for a package version.------ Each package version can have a source instance, an installed instance or--- both. We distinguish reasons for constraints that excluded both instances,--- from reasons for constraints that excluded just one instance.----data ExcludedPkg pkg reason-   = ExcludedPkg pkg-       [reason] -- ^ reasons for excluding both source and installed instances-       [reason] -- ^ reasons for excluding the installed instance-       [reason] -- ^ reasons for excluding the source instance--instance Package pkg => Package (ExcludedPkg pkg reason) where-  packageId (ExcludedPkg p _ _ _) = packageId p----- | There is a conservation of packages property. Packages are never gained or--- lost, they just transfer from the remaining set to the excluded set.----invariant :: (Package installed, Package source)-          => Constraints installed source a -> Bool-invariant (Constraints targets available excluded _ original) =--    -- Relationship between available, excluded and original-    all check merged--    -- targets is a subset of available- && all (PackageIndex.elemByPackageName available) (Set.elems targets)--  where-    merged = mergeBy (\a b -> packageId a `compare` mergedPackageId b)-                     (PackageIndex.allPackages original)-                     (mergeBy (\a b -> packageId a `compare` packageId b)-                              (PackageIndex.allPackages available)-                              (PackageIndex.allPackages excluded))-      where-        mergedPackageId (OnlyInLeft  p  ) = packageId p-        mergedPackageId (OnlyInRight   p) = packageId p-        mergedPackageId (InBoth      p _) = packageId p--    -- If the package was originally installed only, then-    check (InBoth (InstalledOnly _) cur) = case cur of-      -- now it's either still remaining as installed only-      OnlyInLeft               (InstalledOnly _)              -> True-      -- or it has been excluded-      OnlyInRight (ExcludedPkg (InstalledOnly _) [] (_:_) []) -> True-      _                                                       -> False--    -- If the package was originally available only, then-    check (InBoth (SourceOnly _) cur) = case cur of-      -- now it's either still remaining as source only-      OnlyInLeft               (SourceOnly _)              -> True-      -- or it has been excluded-      OnlyInRight (ExcludedPkg (SourceOnly _) [] [] (_:_)) -> True-      _                                                    -> False--    -- If the package was originally installed and source, then-    check (InBoth (InstalledAndSource _ _) cur) = case cur of-      -- We can have both remaining:-      OnlyInLeft               (InstalledAndSource _ _)        -> True--      -- both excluded, in particular it can have had the just source or-      -- installed excluded and later had both excluded so we do not mind if-      -- the source or installed excluded is empty or non-empty.-      OnlyInRight (ExcludedPkg (InstalledAndSource _ _) _ _ _) -> True--      -- the installed remaining and the source excluded:-      InBoth                   (InstalledOnly _)-                  (ExcludedPkg (SourceOnly _) [] [] (_:_))     -> True--      -- the source remaining and the installed excluded:-      InBoth                   (SourceOnly _)-                  (ExcludedPkg (InstalledOnly _) [] (_:_) [])  -> True-      _                                                        -> False--    check _ = False----- | An update to the constraints can move packages between the two piles--- but not gain or loose packages.-transitionsTo :: (Package installed, Package source)-              => Constraints installed source a-              -> Constraints installed source a -> Bool-transitionsTo constraints @(Constraints _ available  excluded  _ _)-              constraints'@(Constraints _ available' excluded' _ _) =--     invariant constraints && invariant constraints'-  && null availableGained  && null excludedLost-  &&    map (mapInstalledOrSource packageId packageId) availableLost-     == map (mapInstalledOrSource packageId packageId) excludedGained--  where-    (availableLost, availableGained)-      = partitionEithers (foldr lostAndGained [] availableChange)--    (excludedLost, excludedGained)-      = partitionEithers (foldr lostAndGained [] excludedChange)--    availableChange =-      mergeBy (\a b -> packageId a `compare` packageId b)-        (PackageIndex.allPackages available)-        (PackageIndex.allPackages available')--    excludedChange =-      mergeBy (\a b -> packageId a `compare` packageId b)-        [ pkg | ExcludedPkg pkg _ _ _ <- PackageIndex.allPackages excluded  ]-        [ pkg | ExcludedPkg pkg _ _ _ <- PackageIndex.allPackages excluded' ]--    lostAndGained mr rest = case mr of-      OnlyInLeft pkg                    -> Left pkg : rest-      InBoth (InstalledAndSource pkg _)-             (SourceOnly _)             -> Left (InstalledOnly pkg) : rest-      InBoth (InstalledAndSource _ pkg)-             (InstalledOnly _)          -> Left (SourceOnly pkg) : rest-      InBoth (SourceOnly _)-             (InstalledAndSource pkg _) -> Right (InstalledOnly pkg) : rest-      InBoth (InstalledOnly _)-             (InstalledAndSource _ pkg) -> Right (SourceOnly pkg) : rest-      OnlyInRight pkg                   -> Right pkg : rest-      _                                 -> rest--    mapInstalledOrSource f g pkg = case pkg of-      InstalledOnly      a   -> InstalledOnly (f a)-      SourceOnly           b -> SourceOnly    (g b)-      InstalledAndSource a b -> InstalledAndSource (f a) (g b)---- | We construct 'Constraints' with an initial 'PackageIndex' of all the--- packages available.----empty :: PackageIndex InstalledPackageEx-      -> PackageIndex UnconfiguredPackage-      -> Constraints InstalledPackageEx UnconfiguredPackage reason-empty installed source =-    Constraints targets pkgs excluded pairs pkgs-  where-    targets  = mempty-    excluded = mempty-    pkgs = PackageIndex.fromList-         . map toInstalledOrSource-         $ mergeBy (\a b -> packageId a `compare` packageId b)-                   (PackageIndex.allPackages installed)-                   (PackageIndex.allPackages source)-    toInstalledOrSource (OnlyInLeft  i  ) = InstalledOnly      i-    toInstalledOrSource (OnlyInRight   a) = SourceOnly           a-    toInstalledOrSource (InBoth      i a) = InstalledAndSource i a--    -- pick up cases like base-3 and 4 where one version depends on the other:-    pairs = Map.fromList-      [ (name, (packageVersion pkgid1, packageVersion pkgid2))-      | [pkg1, pkg2] <- PackageIndex.allPackagesByName installed-      , let name   = packageName pkg1-            pkgid1 = packageId pkg1-            pkgid2 = packageId pkg2-      ,    any ((pkgid1==) . packageId) (sourceDeps pkg2)-        || any ((pkgid2==) . packageId) (sourceDeps pkg1) ]----- | The package targets.----packages :: Constraints installed source reason-         -> Set PackageName-packages (Constraints ts _ _ _ _) = ts----- | The package choices that are still available.----choices :: Constraints installed source reason-        -> PackageIndex (InstalledOrSource installed source)-choices (Constraints _ available _ _ _) = available--isPaired :: Constraints installed source reason-         -> PackageId -> Maybe PackageId-isPaired (Constraints _ _ _ pairs _) (PackageIdentifier name version) =-  case Map.lookup name pairs of-    Just (v1, v2)-      | version == v1 -> Just (PackageIdentifier name v2)-      | version == v2 -> Just (PackageIdentifier name v1)-    _                 -> Nothing---data Satisfiable constraints discarded reason-       = Satisfiable constraints discarded-       | Unsatisfiable-       | ConflictsWith [(PackageId, [reason])]---addTarget :: (Package installed, Package source)-          => PackageName-          -> Constraints installed source reason-          -> Satisfiable (Constraints installed source reason)-                         () reason-addTarget pkgname-          constraints@(Constraints targets available excluded paired original)--    -- If it's already a target then there's no change-  | pkgname `Set.member` targets-  = Satisfiable constraints ()--    -- If there is some possible choice available for this target then we're ok-  | PackageIndex.elemByPackageName available pkgname-  = let targets'     = Set.insert pkgname targets-        constraints' = Constraints targets' available excluded paired original-     in assert (constraints `transitionsTo` constraints') $-        Satisfiable constraints' ()--    -- If it's not available and it is excluded then we return the conflicts-  | PackageIndex.elemByPackageName excluded pkgname-  = ConflictsWith conflicts--    -- Otherwise, it's not available and it has not been excluded so the-    -- package is simply completely unknown.-  | otherwise-  = Unsatisfiable--  where-    conflicts =-      [ (packageId pkg, reasons)-      | let excludedChoices = PackageIndex.lookupPackageName excluded pkgname-      , ExcludedPkg pkg isReasons iReasons sReasons <- excludedChoices-      , let reasons = isReasons ++ iReasons ++ sReasons ]---constrain :: (Package installed, Package source)-          => PackageName                -- ^ which package to constrain-          -> (Version -> Bool -> Bool)  -- ^ the constraint test-          -> reason                     -- ^ the reason for the constraint-          -> Constraints installed source reason-          -> Satisfiable (Constraints installed source reason)-                         [PackageId] reason-constrain pkgname constraint reason-          constraints@(Constraints targets available excluded paired original)--  | pkgname `Set.member` targets  &&  not anyRemaining-  = if null conflicts then Unsatisfiable-                      else ConflictsWith conflicts--  | otherwise-  = let constraints' = Constraints targets available' excluded' paired original-     in assert (constraints `transitionsTo` constraints') $-        Satisfiable constraints' (map packageId newExcluded)--  where-    -- This tells us if any packages would remain at all for this package name if-    -- we applied this constraint. This amounts to checking if any package-    -- satisfies the given constraint, including version range and installation-    -- status.-    ---    (available', excluded', newExcluded, anyRemaining, conflicts) =-      updatePkgsStatus-        available excluded-        [] False []-        (mergeBy (\pkg pkg' -> packageVersion pkg `compare` packageVersion pkg')-                 (PackageIndex.lookupPackageName available pkgname)-                 (PackageIndex.lookupPackageName excluded  pkgname))--    testConstraint pkg =-      let ver = packageVersion pkg in-      case Map.lookup (packageName pkg) paired of--        Just (v1, v2)-          | ver == v1 || ver == v2-          -> case pkg of-               InstalledOnly ipkg -> InstalledOnly (ipkg, iOk)-               SourceOnly    spkg -> SourceOnly    (spkg, sOk)-               InstalledAndSource ipkg spkg ->-                 InstalledAndSource (ipkg, iOk) (spkg, sOk)-          where-            iOk = constraint v1 True  || constraint v2 True-            sOk = constraint v1 False || constraint v2 False--        _ -> case pkg of-               InstalledOnly ipkg -> InstalledOnly (ipkg, iOk)-               SourceOnly    spkg -> SourceOnly    (spkg, sOk)-               InstalledAndSource ipkg spkg ->-                 InstalledAndSource (ipkg, iOk) (spkg, sOk)-          where-            iOk = constraint ver True-            sOk = constraint ver False--    -- For the info about available and excluded versions of the package in-    -- question, update the info given the current constraint-    ---    -- We update the available package map and the excluded package map-    -- we also collect:-    --   * the change in available packages (for logging)-    --   * whether there are any remaining choices-    --   * any constraints that conflict with the current constraint--    updatePkgsStatus _ _ nePkgs ok cs _-      | seq nePkgs $ seq ok $ seq cs False = undefined--    updatePkgsStatus aPkgs ePkgs nePkgs ok cs []-      = (aPkgs, ePkgs, reverse nePkgs, ok, reverse cs)--    updatePkgsStatus aPkgs ePkgs nePkgs ok cs (pkg:pkgs) =-        let (aPkgs', ePkgs', mnePkg, ok', mc) = updatePkgStatus aPkgs ePkgs pkg-            nePkgs' = maybeCons mnePkg nePkgs-            cs'     = maybeCons mc cs-         in updatePkgsStatus aPkgs' ePkgs' nePkgs' (ok' || ok) cs' pkgs--    maybeCons Nothing  xs = xs-    maybeCons (Just x) xs = x:xs---    -- For the info about an available or excluded version of the package in-    -- question, update the info given the current constraint.-    ---    updatePkgStatus aPkgs ePkgs pkg =-      case viewPackageStatus pkg of-        AllAvailable (InstalledOnly (aiPkg, False)) ->-          removeAvailable False-            (InstalledOnly aiPkg)-            (PackageIndex.deletePackageId pkgid)-            (ExcludedPkg (InstalledOnly aiPkg) [] [reason] [])-            Nothing--        AllAvailable (SourceOnly (asPkg, False)) ->-          removeAvailable False-            (SourceOnly asPkg)-            (PackageIndex.deletePackageId pkgid)-            (ExcludedPkg (SourceOnly asPkg) [] [] [reason])-            Nothing--        AllAvailable (InstalledAndSource (aiPkg, False) (asPkg, False)) ->-          removeAvailable False-            (InstalledAndSource aiPkg asPkg)-            (PackageIndex.deletePackageId pkgid)-            (ExcludedPkg (InstalledAndSource aiPkg asPkg) [reason] [] [])-            Nothing--        AllAvailable (InstalledAndSource (aiPkg, True) (asPkg, False)) ->-          removeAvailable True-            (SourceOnly asPkg)-            (PackageIndex.insert (InstalledOnly aiPkg))-            (ExcludedPkg (SourceOnly asPkg) [] [] [reason])-            Nothing--        AllAvailable (InstalledAndSource (aiPkg, False) (asPkg, True)) ->-          removeAvailable True-            (InstalledOnly aiPkg)-            (PackageIndex.insert (SourceOnly asPkg))-            (ExcludedPkg (InstalledOnly aiPkg) [] [reason] [])-            Nothing--        AllAvailable _ -> noChange True Nothing--        AvailableExcluded (aiPkg, False) (ExcludedPkg (esPkg, False) _ _ srs) ->-          removeAvailable False-            (InstalledOnly aiPkg)-            (PackageIndex.deletePackageId pkgid)-            (ExcludedPkg (InstalledAndSource aiPkg esPkg) [reason] [] srs)-            Nothing--        AvailableExcluded (_aiPkg, True) (ExcludedPkg (esPkg, False) _ _ srs) ->-          addExtraExclusion True-            (ExcludedPkg (SourceOnly esPkg) [] [] (reason:srs))-            Nothing--        AvailableExcluded (aiPkg, False) (ExcludedPkg (esPkg, True) _ _ srs) ->-          removeAvailable  True-            (InstalledOnly aiPkg)-            (PackageIndex.deletePackageId pkgid)-            (ExcludedPkg (InstalledAndSource aiPkg esPkg) [] [reason] srs)-            (Just (pkgid, srs))--        AvailableExcluded (_aiPkg, True) (ExcludedPkg (_esPkg, True) _ _ srs) ->-          noChange True-            (Just (pkgid, srs))--        ExcludedAvailable (ExcludedPkg (eiPkg, False) _ irs _) (asPkg, False) ->-          removeAvailable  False-            (SourceOnly asPkg)-            (PackageIndex.deletePackageId pkgid)-            (ExcludedPkg (InstalledAndSource eiPkg asPkg) [reason] irs [])-            Nothing--        ExcludedAvailable (ExcludedPkg (eiPkg, True) _ irs _) (asPkg, False) ->-          removeAvailable False-            (SourceOnly asPkg)-            (PackageIndex.deletePackageId pkgid)-            (ExcludedPkg (InstalledAndSource eiPkg asPkg) [] irs [reason])-            (Just (pkgid, irs))--        ExcludedAvailable (ExcludedPkg (eiPkg, False) _ irs _) (_asPkg, True) ->-          addExtraExclusion True-            (ExcludedPkg (InstalledOnly eiPkg) [] (reason:irs) [])-            Nothing--        ExcludedAvailable (ExcludedPkg (_eiPkg, True) _ irs _) (_asPkg, True) ->-          noChange True-            (Just (pkgid, irs))--        AllExcluded (ExcludedPkg (InstalledOnly (eiPkg, False)) _ irs _) ->-          addExtraExclusion False-            (ExcludedPkg (InstalledOnly eiPkg) [] (reason:irs) [])-            Nothing--        AllExcluded (ExcludedPkg (InstalledOnly (_eiPkg, True)) _ irs _) ->-          noChange False-            (Just (pkgid, irs))--        AllExcluded (ExcludedPkg (SourceOnly (esPkg, False)) _ _ srs) ->-          addExtraExclusion False-            (ExcludedPkg (SourceOnly esPkg) [] [] (reason:srs))-            Nothing--        AllExcluded (ExcludedPkg (SourceOnly (_esPkg, True)) _ _ srs) ->-          noChange False-            (Just (pkgid, srs))--        AllExcluded (ExcludedPkg (InstalledAndSource (eiPkg, False) (esPkg, False)) isrs irs srs) ->-          addExtraExclusion False-            (ExcludedPkg (InstalledAndSource eiPkg esPkg) (reason:isrs) irs srs)-            Nothing--        AllExcluded (ExcludedPkg (InstalledAndSource (eiPkg, True) (esPkg, False)) isrs irs srs) ->-          addExtraExclusion False-            (ExcludedPkg (InstalledAndSource eiPkg esPkg) isrs irs (reason:srs))-            (Just (pkgid, irs))--        AllExcluded (ExcludedPkg (InstalledAndSource (eiPkg, False) (esPkg, True)) isrs irs srs) ->-          addExtraExclusion False-            (ExcludedPkg (InstalledAndSource eiPkg esPkg) isrs (reason:irs) srs)-            (Just (pkgid, srs))--        AllExcluded (ExcludedPkg (InstalledAndSource (_eiPkg, True) (_esPkg, True)) isrs irs srs) ->-          noChange False-            (Just (pkgid, isrs ++ irs ++ srs))--      where-        removeAvailable ok nePkg adjustAvailable ePkg c =-          let aPkgs' = adjustAvailable aPkgs-              ePkgs' = PackageIndex.insert ePkg ePkgs-           in aPkgs' `seq` ePkgs' `seq`-              (aPkgs', ePkgs', Just nePkg, ok, c)--        addExtraExclusion ok ePkg c =-          let ePkgs' = PackageIndex.insert ePkg ePkgs-           in ePkgs' `seq`-              (aPkgs, ePkgs', Nothing, ok, c)--        noChange ok c =-          (aPkgs, ePkgs, Nothing, ok, c)--        pkgid = case pkg of OnlyInLeft  p   -> packageId p-                            OnlyInRight p   -> packageId p-                            InBoth      p _ -> packageId p---    viewPackageStatus-      :: (Package installed, Package source)-      => MergeResult (InstalledOrSource installed source)-                     (ExcludedPkg (InstalledOrSource installed source) reason)-      -> PackageStatus (installed, Bool) (source, Bool) reason-    viewPackageStatus merged =-        case merged of-          OnlyInLeft aPkg ->-            AllAvailable (testConstraint aPkg)--          OnlyInRight (ExcludedPkg ePkg isrs irs srs) ->-            AllExcluded (ExcludedPkg (testConstraint ePkg) isrs irs srs)--          InBoth (InstalledOnly aiPkg)-                 (ExcludedPkg (SourceOnly esPkg) [] [] srs) ->-            case testConstraint (InstalledAndSource aiPkg esPkg) of-              InstalledAndSource (aiPkg', iOk) (esPkg', sOk) ->-                AvailableExcluded (aiPkg', iOk) (ExcludedPkg (esPkg', sOk) [] [] srs)-              _ -> impossible--          InBoth (SourceOnly asPkg)-                 (ExcludedPkg (InstalledOnly eiPkg) [] irs []) ->-            case testConstraint (InstalledAndSource eiPkg asPkg) of-              InstalledAndSource (eiPkg', iOk) (asPkg', sOk) ->-                ExcludedAvailable (ExcludedPkg (eiPkg', iOk) [] irs []) (asPkg', sOk)-              _ -> impossible-          _ -> impossible-      where-        impossible = error "impossible: viewPackageStatus invariant violation"---- A intermediate structure that enumerates all the possible cases given the--- invariant. This helps us to get simpler and complete pattern matching in--- updatePkg above----data PackageStatus installed source reason-   = AllAvailable (InstalledOrSource installed source)-   | AllExcluded  (ExcludedPkg (InstalledOrSource installed source) reason)-   | AvailableExcluded installed (ExcludedPkg source reason)-   | ExcludedAvailable (ExcludedPkg installed reason) source---conflicting :: (Package installed, Package source)-            => Constraints installed source reason-            -> Dependency-            -> [(PackageId, [reason])]-conflicting (Constraints _ _ excluded _ _) dep =-  [ (packageId pkg, reasonsAll ++ reasonsAvail ++ reasonsInstalled) --TODO-  | ExcludedPkg pkg reasonsAll reasonsAvail reasonsInstalled <--      PackageIndex.lookupDependency excluded dep ]
− Distribution/Client/Dependency/TopDown/Types.hs
@@ -1,143 +0,0 @@--------------------------------------------------------------------------------- |--- Module      :  Distribution.Client.Dependency.TopDown.Types--- Copyright   :  (c) Duncan Coutts 2008--- License     :  BSD-like------ Maintainer  :  cabal-devel@haskell.org--- Stability   :  provisional--- Portability :  portable------ Types for the top-down dependency resolver.-------------------------------------------------------------------------------{-# LANGUAGE CPP #-}-module Distribution.Client.Dependency.TopDown.Types where--import Distribution.Client.Types-         ( SourcePackage(..), ConfiguredPackage(..)-         , OptionalStanza, ConfiguredId(..) )-import Distribution.InstalledPackageInfo-         ( InstalledPackageInfo )-import qualified Distribution.Client.ComponentDeps as CD--import Distribution.Package-         ( PackageId, PackageIdentifier, Dependency-         , Package(packageId) )-import Distribution.PackageDescription-         ( FlagAssignment )---- --------------------------------------------------------------- * The various kinds of packages--- --------------------------------------------------------------type SelectablePackage-   = InstalledOrSource InstalledPackageEx UnconfiguredPackage--type SelectedPackage-   = InstalledOrSource InstalledPackageEx SemiConfiguredPackage--data InstalledOrSource installed source-   = InstalledOnly      installed-   | SourceOnly                   source-   | InstalledAndSource installed source-  deriving Eq--data FinalSelectedPackage-   = SelectedInstalled InstalledPackage-   | SelectedSource    ConfiguredPackage--type TopologicalSortNumber = Int---- | InstalledPackage caches its dependencies as source package IDs.-data InstalledPackage-   = InstalledPackage-       InstalledPackageInfo-       [PackageId]--data InstalledPackageEx-   = InstalledPackageEx-       InstalledPackage-       !TopologicalSortNumber-       [PackageIdentifier]    -- transitive closure of installed deps--data UnconfiguredPackage-   = UnconfiguredPackage-       SourcePackage-       !TopologicalSortNumber-       FlagAssignment-       [OptionalStanza]--data SemiConfiguredPackage-   = SemiConfiguredPackage-       SourcePackage     -- package info-       FlagAssignment    -- total flag assignment for the package-       [OptionalStanza]  -- enabled optional stanzas-       [Dependency]      -- dependencies we end up with when we apply-                         -- the flag assignment--instance Package InstalledPackage where-  packageId (InstalledPackage pkg _) = packageId pkg--instance Package InstalledPackageEx where-  packageId (InstalledPackageEx p _ _) = packageId p--instance Package UnconfiguredPackage where-  packageId (UnconfiguredPackage p _ _ _) = packageId p--instance Package SemiConfiguredPackage where-  packageId (SemiConfiguredPackage p _ _ _) = packageId p--instance (Package installed, Package source)-      => Package (InstalledOrSource installed source) where-  packageId (InstalledOnly      p  ) = packageId p-  packageId (SourceOnly         p  ) = packageId p-  packageId (InstalledAndSource p _) = packageId p--instance Package FinalSelectedPackage where-  packageId (SelectedInstalled pkg) = packageId pkg-  packageId (SelectedSource    pkg) = packageId pkg----- | We can have constraints on selecting just installed or just source--- packages.------ In particular, installed packages can only depend on other installed--- packages while packages that are not yet installed but which we plan to--- install can depend on installed or other not-yet-installed packages.----data InstalledConstraint = InstalledConstraint-                         | SourceConstraint-  deriving (Eq, Show)---- | Package dependencies------ The top-down solver uses its down type class for package dependencies,--- because it wants to know these dependencies as PackageIds, rather than as--- ComponentIds (so it cannot use PackageFixedDeps).------ Ideally we would switch the top-down solver over to use ComponentIds--- throughout; that means getting rid of this type class, and changing over the--- package index type to use Cabal's rather than cabal-install's. That will--- avoid the need for the local definitions of dependencyGraph and--- reverseTopologicalOrder in the top-down solver.------ Note that the top-down solver does not (and probably will never) make a--- distinction between the various kinds of dependencies, so we return a flat--- list here. If we get rid of this type class then any use of `sourceDeps`--- should be replaced by @fold . depends@.-class Package a => PackageSourceDeps a where-  sourceDeps :: a -> [PackageIdentifier]--instance PackageSourceDeps InstalledPackageEx where-  sourceDeps (InstalledPackageEx _ _ deps) = deps--instance PackageSourceDeps ConfiguredPackage where-  sourceDeps (ConfiguredPackage _ _ _ deps) = map confSrcId $ CD.nonSetupDeps deps--instance PackageSourceDeps InstalledPackage where-  sourceDeps (InstalledPackage _ deps) = deps--instance PackageSourceDeps FinalSelectedPackage where-  sourceDeps (SelectedInstalled pkg) = sourceDeps pkg-  sourceDeps (SelectedSource    pkg) = sourceDeps pkg-
Distribution/Client/Dependency/Types.hs view
@@ -1,193 +1,45 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE DeriveGeneric #-}--------------------------------------------------------------------------------- |--- Module      :  Distribution.Client.Dependency.Types--- Copyright   :  (c) Duncan Coutts 2008--- License     :  BSD-like------ Maintainer  :  cabal-devel@haskell.org--- Stability   :  provisional--- Portability :  portable------ Common types for dependency resolution.------------------------------------------------------------------------------ module Distribution.Client.Dependency.Types (     PreSolver(..),     Solver(..),-    DependencyResolver,-    ResolverPackage(..), -    PackageConstraint(..),-    showPackageConstraint,-    PackagePreferences(..),-    InstalledPreference(..),     PackagesPreferenceDefault(..), -    Progress(..),-    foldProgress,--    LabeledPackageConstraint(..),-    ConstraintSource(..),-    unlabelPackageConstraint,-    showConstraintSource-   ) where -#if !MIN_VERSION_base(4,8,0)-import Control.Applicative-         ( Applicative(..) )-#endif-import Control.Applicative-         ( Alternative(..) )- import Data.Char          ( isAlpha, toLower )-#if !MIN_VERSION_base(4,8,0)-import Data.Monoid-         ( Monoid(..) )-#endif -import Distribution.Client.PkgConfigDb-         ( PkgConfigDb )-import Distribution.Client.Types-         ( OptionalStanza(..), SourcePackage(..), ConfiguredPackage )- import qualified Distribution.Compat.ReadP as Parse          ( pfail, munch1 )-import Distribution.PackageDescription-         ( FlagAssignment, FlagName(..) )-import Distribution.InstalledPackageInfo-         ( InstalledPackageInfo )-import qualified Distribution.Client.PackageIndex as PackageIndex-         ( PackageIndex )-import Distribution.Simple.PackageIndex ( InstalledPackageIndex )-import Distribution.Package-         ( PackageName )-import Distribution.Version-         ( VersionRange, simplifyVersionRange )-import Distribution.Compiler-         ( CompilerInfo )-import Distribution.System-         ( Platform ) import Distribution.Text-         ( Text(..), display )+         ( Text(..) )  import Text.PrettyPrint          ( text ) import GHC.Generics (Generic) import Distribution.Compat.Binary (Binary(..)) -import Prelude hiding (fail) - -- | All the solvers that can be selected.-data PreSolver = AlwaysTopDown | AlwaysModular | Choose+data PreSolver = AlwaysModular   deriving (Eq, Ord, Show, Bounded, Enum, Generic)  -- | All the solvers that can be used.-data Solver = TopDown | Modular+data Solver = Modular   deriving (Eq, Ord, Show, Bounded, Enum, Generic)  instance Binary PreSolver instance Binary Solver  instance Text PreSolver where-  disp AlwaysTopDown = text "topdown"   disp AlwaysModular = text "modular"-  disp Choose        = text "choose"   parse = do     name <- Parse.munch1 isAlpha     case map toLower name of-      "topdown" -> return AlwaysTopDown       "modular" -> return AlwaysModular-      "choose"  -> return Choose       _         -> Parse.pfail --- | A dependency resolver is a function that works out an installation plan--- given the set of installed and available packages and a set of deps to--- solve for.------ The reason for this interface is because there are dozens of approaches to--- solving the package dependency problem and we want to make it easy to swap--- in alternatives.----type DependencyResolver = Platform-                       -> CompilerInfo-                       -> InstalledPackageIndex-                       ->          PackageIndex.PackageIndex SourcePackage-                       -> PkgConfigDb-                       -> (PackageName -> PackagePreferences)-                       -> [LabeledPackageConstraint]-                       -> [PackageName]-                       -> Progress String String [ResolverPackage]---- | The dependency resolver picks either pre-existing installed packages--- or it picks source packages along with package configuration.------ This is like the 'InstallPlan.PlanPackage' but with fewer cases.----data ResolverPackage = PreExisting InstalledPackageInfo-                     | Configured  ConfiguredPackage---- | Per-package constraints. Package constraints must be respected by the--- solver. Multiple constraints for each package can be given, though obviously--- it is possible to construct conflicting constraints (eg impossible version--- range or inconsistent flag assignment).----data PackageConstraint-   = PackageConstraintVersion   PackageName VersionRange-   | PackageConstraintInstalled PackageName-   | PackageConstraintSource    PackageName-   | PackageConstraintFlags     PackageName FlagAssignment-   | PackageConstraintStanzas   PackageName [OptionalStanza]-  deriving (Eq,Show,Generic)--instance Binary PackageConstraint---- | Provide a textual representation of a package constraint--- for debugging purposes.----showPackageConstraint :: PackageConstraint -> String-showPackageConstraint (PackageConstraintVersion pn vr) =-  display pn ++ " " ++ display (simplifyVersionRange vr)-showPackageConstraint (PackageConstraintInstalled pn) =-  display pn ++ " installed"-showPackageConstraint (PackageConstraintSource pn) =-  display pn ++ " source"-showPackageConstraint (PackageConstraintFlags pn fs) =-  "flags " ++ display pn ++ " " ++ unwords (map (uncurry showFlag) fs)-  where-    showFlag (FlagName f) True  = "+" ++ f-    showFlag (FlagName f) False = "-" ++ f-showPackageConstraint (PackageConstraintStanzas pn ss) =-  "stanzas " ++ display pn ++ " " ++ unwords (map showStanza ss)-  where-    showStanza TestStanzas  = "test"-    showStanza BenchStanzas = "bench"---- | Per-package preferences on the version. It is a soft constraint that the--- 'DependencyResolver' should try to respect where possible. It consists of--- an 'InstalledPreference' which says if we prefer versions of packages--- that are already installed. It also has (possibly multiple)--- 'PackageVersionPreference's which are suggested constraints on the version--- number. The resolver should try to use package versions that satisfy--- the maximum number of the suggested version constraints.------ It is not specified if preferences on some packages are more important than--- others.----data PackagePreferences = PackagePreferences [VersionRange]-                                             InstalledPreference-                                             [OptionalStanza]---- | Whether we prefer an installed version of a package or simply the latest--- version.----data InstalledPreference = PreferInstalled | PreferLatest-  deriving Show- -- | Global policy for all packages to say if we prefer package versions that -- are already installed locally or if we just prefer the latest available. --@@ -212,107 +64,3 @@      --    | PreferLatestForSelected   deriving Show---- | A type to represent the unfolding of an expensive long running--- calculation that may fail. We may get intermediate steps before the final--- result which may be used to indicate progress and\/or logging messages.----data Progress step fail done = Step step (Progress step fail done)-                             | Fail fail-                             | Done done-  deriving (Functor)---- | Consume a 'Progress' calculation. Much like 'foldr' for lists but with two--- base cases, one for a final result and one for failure.------ Eg to convert into a simple 'Either' result use:------ > foldProgress (flip const) Left Right----foldProgress :: (step -> a -> a) -> (fail -> a) -> (done -> a)-             -> Progress step fail done -> a-foldProgress step fail done = fold-  where fold (Step s p) = step s (fold p)-        fold (Fail f)   = fail f-        fold (Done r)   = done r--instance Monad (Progress step fail) where-  return   = pure-  p >>= f  = foldProgress Step Fail f p--instance Applicative (Progress step fail) where-  pure a  = Done a-  p <*> x = foldProgress Step Fail (flip fmap x) p--instance Monoid fail => Alternative (Progress step fail) where-  empty   = Fail mempty-  p <|> q = foldProgress Step (const q) Done p---- | 'PackageConstraint' labeled with its source.-data LabeledPackageConstraint-   = LabeledPackageConstraint PackageConstraint ConstraintSource--unlabelPackageConstraint :: LabeledPackageConstraint -> PackageConstraint-unlabelPackageConstraint (LabeledPackageConstraint pc _) = pc---- | Source of a 'PackageConstraint'.-data ConstraintSource =--  -- | Main config file, which is ~/.cabal/config by default.-  ConstraintSourceMainConfig FilePath--  -- | Local cabal.project file-  | ConstraintSourceProjectConfig FilePath--  -- | Sandbox config file, which is ./cabal.sandbox.config by default.-  | ConstraintSourceSandboxConfig FilePath--  -- | User config file, which is ./cabal.config by default.-  | ConstraintSourceUserConfig FilePath--  -- | Flag specified on the command line.-  | ConstraintSourceCommandlineFlag--  -- | Target specified by the user, e.g., @cabal install package-0.1.0.0@-  -- implies @package==0.1.0.0@.-  | ConstraintSourceUserTarget--  -- | Internal requirement to use installed versions of packages like ghc-prim.-  | ConstraintSourceNonUpgradeablePackage--  -- | Internal requirement to use the add-source version of a package when that-  -- version is installed and the source is modified.-  | ConstraintSourceModifiedAddSourceDep--  -- | Internal constraint used by @cabal freeze@.-  | ConstraintSourceFreeze--  -- | Constraint specified by a config file, a command line flag, or a user-  -- target, when a more specific source is not known.-  | ConstraintSourceConfigFlagOrTarget--  -- | The source of the constraint is not specified.-  | ConstraintSourceUnknown-  deriving (Eq, Show, Generic)--instance Binary ConstraintSource---- | Description of a 'ConstraintSource'.-showConstraintSource :: ConstraintSource -> String-showConstraintSource (ConstraintSourceMainConfig path) =-    "main config " ++ path-showConstraintSource (ConstraintSourceProjectConfig path) =-    "project config " ++ path-showConstraintSource (ConstraintSourceSandboxConfig path) =-    "sandbox config " ++ path-showConstraintSource (ConstraintSourceUserConfig path)= "user config " ++ path-showConstraintSource ConstraintSourceCommandlineFlag = "command line flag"-showConstraintSource ConstraintSourceUserTarget = "user target"-showConstraintSource ConstraintSourceNonUpgradeablePackage =-    "non-upgradeable package"-showConstraintSource ConstraintSourceModifiedAddSourceDep =-    "modified add-source dependency"-showConstraintSource ConstraintSourceFreeze = "cabal freeze"-showConstraintSource ConstraintSourceConfigFlagOrTarget =-    "config file, command line flag, or user target"-showConstraintSource ConstraintSourceUnknown = "unknown source"
Distribution/Client/DistDirLayout.hs view
@@ -5,37 +5,83 @@ -- The layout of the .\/dist\/ directory where cabal keeps all of it's state -- and build artifacts. ---module Distribution.Client.DistDirLayout where+module Distribution.Client.DistDirLayout (+    -- * 'DistDirLayout'+    DistDirLayout(..),+    DistDirParams(..),+    defaultDistDirLayout,+    ProjectRoot(..), +    -- * 'StoreDirLayout'+    StoreDirLayout(..),+    defaultStoreDirLayout,++    -- * 'CabalDirLayout'+    CabalDirLayout(..),+    defaultCabalDirLayout,+) where++import Data.Maybe (fromMaybe) import System.FilePath+ import Distribution.Package-         ( PackageId )+         ( PackageId, ComponentId, UnitId ) import Distribution.Compiler-import Distribution.Simple.Compiler (PackageDB(..))+import Distribution.Simple.Compiler+         ( PackageDB(..), PackageDBStack, OptimisationLevel(..) ) import Distribution.Text-import Distribution.Client.Types-         ( InstalledPackageId )+import Distribution.Types.ComponentName+import Distribution.System  +-- | Information which can be used to construct the path to+-- the build directory of a build.  This is LESS fine-grained+-- than what goes into the hashed 'InstalledPackageId',+-- and for good reason: we don't want this path to change if+-- the user, say, adds a dependency to their project.+data DistDirParams = DistDirParams {+    distParamUnitId         :: UnitId,+    distParamPackageId      :: PackageId,+    distParamComponentId    :: ComponentId,+    distParamComponentName  :: Maybe ComponentName,+    distParamCompilerId     :: CompilerId,+    distParamPlatform       :: Platform,+    distParamOptimization   :: OptimisationLevel+    -- TODO (see #3343):+    --  Flag assignments+    --  Optimization+    } + -- | The layout of the project state directory. Traditionally this has been -- called the @dist@ directory. -- data DistDirLayout = DistDirLayout { -        -- | The dist directory, which is the root of where cabal keeps all its-       -- state including the build artifacts from each package we build.+       -- | The root directory of the project. Many other files are relative to+       -- this location. In particular, the @cabal.project@ lives here.        --+       distProjectRootDirectory     :: FilePath,++       -- | The @cabal.project@ file and related like @cabal.project.freeze@.+       -- The parameter is for the extension, like \"freeze\", or \"\" for the+       -- main file.+       --+       distProjectFile              :: String -> FilePath,++       -- | The \"dist\" directory, which is the root of where cabal keeps all+       -- its state including the build artifacts from each package we build.+       --        distDirectory                :: FilePath,         -- | The directory under dist where we keep the build artifacts for a        -- package we're building from a local directory.        ---       -- This uses a 'PackageId' not just a 'PackageName' because technically+       -- This uses a 'UnitId' not just a 'PackageName' because technically        -- we can have multiple instances of the same package in a solution        -- (e.g. setup deps).        ---       distBuildDirectory           :: PackageId -> FilePath,+       distBuildDirectory           :: DistDirParams -> FilePath,        distBuildRootDirectory       :: FilePath,         -- | The directory under dist where we put the unpacked sources of@@ -55,8 +101,8 @@        -- | The location for package-specific cache files (e.g. state used in        -- incremental rebuilds).        ---       distPackageCacheFile         :: PackageId -> String -> FilePath,-       distPackageCacheDirectory    :: PackageId -> FilePath,+       distPackageCacheFile         :: DistDirParams -> String -> FilePath,+       distPackageCacheDirectory    :: DistDirParams -> FilePath,         distTempDirectory            :: FilePath,        distBinDirectory             :: FilePath,@@ -65,30 +111,91 @@      }  +-- | The layout of a cabal nix-style store.+--+data StoreDirLayout = StoreDirLayout {+       storeDirectory         :: CompilerId -> FilePath,+       storePackageDirectory  :: CompilerId -> UnitId -> FilePath,+       storePackageDBPath     :: CompilerId -> FilePath,+       storePackageDB         :: CompilerId -> PackageDB,+       storePackageDBStack    :: CompilerId -> PackageDBStack,+       storeIncomingDirectory :: CompilerId -> FilePath,+       storeIncomingLock      :: CompilerId -> UnitId -> FilePath+     } + --TODO: move to another module, e.g. CabalDirLayout?+-- or perhaps rename this module to DirLayouts.++-- | The layout of the user-wide cabal directory, that is the @~/.cabal@ dir+-- on unix, and equivalents on other systems.+--+-- At the moment this is just a partial specification, but the idea is+-- eventually to cover it all.+-- data CabalDirLayout = CabalDirLayout {-       cabalStoreDirectory        :: CompilerId -> FilePath,-       cabalStorePackageDirectory :: CompilerId -> InstalledPackageId-                                                -> FilePath,-       cabalStorePackageDBPath    :: CompilerId -> FilePath,-       cabalStorePackageDB        :: CompilerId -> PackageDB,+       cabalStoreDirLayout        :: StoreDirLayout, -       cabalPackageCacheDirectory :: FilePath,        cabalLogsDirectory         :: FilePath,        cabalWorldFile             :: FilePath      }  -defaultDistDirLayout :: FilePath -> DistDirLayout-defaultDistDirLayout projectRootDirectory =+-- | Information about the root directory of the project.+--+-- It can either be an implict project root in the current dir if no+-- @cabal.project@ file is found, or an explicit root if the file is found.+--+data ProjectRoot =+       -- | -- ^ An implict project root. It contains the absolute project+       -- root dir.+       ProjectRootImplicit FilePath++       -- | -- ^ An explicit project root. It contains the absolute project+       -- root dir and the relative @cabal.project@ file (or explicit override)+     | ProjectRootExplicit FilePath FilePath+  deriving (Eq, Show)++-- | Make the default 'DistDirLayout' based on the project root dir and+-- optional overrides for the location of the @dist@ directory and the+-- @cabal.project@ file.+--+defaultDistDirLayout :: ProjectRoot    -- ^ the project root+                     -> Maybe FilePath -- ^ the @dist@ directory or default+                                       -- (absolute or relative to the root)+                     -> DistDirLayout+defaultDistDirLayout projectRoot mdistDirectory =     DistDirLayout {..}   where-    distDirectory = projectRootDirectory </> "dist-newstyle"+    (projectRootDir, projectFile) = case projectRoot of+      ProjectRootImplicit dir      -> (dir, dir </> "cabal.project")+      ProjectRootExplicit dir file -> (dir, dir </> file)++    distProjectRootDirectory = projectRootDir+    distProjectFile ext      = projectFile <.> ext++    distDirectory = distProjectRootDirectory+                </> fromMaybe "dist-newstyle" mdistDirectory     --TODO: switch to just dist at some point, or some other new name      distBuildRootDirectory   = distDirectory </> "build"-    distBuildDirectory pkgid = distBuildRootDirectory </> display pkgid+    distBuildDirectory params =+        distBuildRootDirectory </>+        display (distParamPlatform params) </>+        display (distParamCompilerId params) </>+        display (distParamPackageId params) </>+        (case fmap componentNameString (distParamComponentName params) of+            Nothing          -> ""+            Just Nothing     -> ""+            Just (Just name) -> "c" </> display name) </>+        (case distParamOptimization params of+            NoOptimisation -> "noopt"+            NormalOptimisation -> ""+            MaximumOptimisation -> "opt") </>+        (let uid_str = display (distParamUnitId params)+         in if uid_str == display (distParamComponentId params)+                then ""+                else uid_str)      distUnpackedSrcRootDirectory   = distDirectory </> "src"     distUnpackedSrcDirectory pkgid = distUnpackedSrcRootDirectory@@ -97,8 +204,8 @@     distProjectCacheDirectory = distDirectory </> "cache"     distProjectCacheFile name = distProjectCacheDirectory </> name -    distPackageCacheDirectory pkgid = distBuildDirectory pkgid </> "cache"-    distPackageCacheFile pkgid name = distPackageCacheDirectory pkgid </> name+    distPackageCacheDirectory params = distBuildDirectory params </> "cache"+    distPackageCacheFile params name = distPackageCacheDirectory params </> name      distTempDirectory = distDirectory </> "tmp" @@ -108,25 +215,38 @@     distPackageDB = SpecificPackageDB . distPackageDBPath  --defaultCabalDirLayout :: FilePath -> CabalDirLayout-defaultCabalDirLayout cabalDir =-    CabalDirLayout {..}+defaultStoreDirLayout :: FilePath -> StoreDirLayout+defaultStoreDirLayout storeRoot =+    StoreDirLayout {..}   where+    storeDirectory compid =+      storeRoot </> display compid -    cabalStoreDirectory compid =-      cabalDir </> "store" </> display compid+    storePackageDirectory compid ipkgid =+      storeDirectory compid </> display ipkgid -    cabalStorePackageDirectory compid ipkgid = -      cabalStoreDirectory compid </> display ipkgid+    storePackageDBPath compid =+      storeDirectory compid </> "package.db" -    cabalStorePackageDBPath compid =-      cabalStoreDirectory compid </> "package.db"+    storePackageDB compid =+      SpecificPackageDB (storePackageDBPath compid) -    cabalStorePackageDB =-      SpecificPackageDB . cabalStorePackageDBPath+    storePackageDBStack compid =+      [GlobalPackageDB, storePackageDB compid] -    cabalPackageCacheDirectory = cabalDir </> "packages"+    storeIncomingDirectory compid =+      storeDirectory compid </> "incoming"++    storeIncomingLock compid unitid =+      storeIncomingDirectory compid </> display unitid <.> "lock"+++defaultCabalDirLayout :: FilePath -> CabalDirLayout+defaultCabalDirLayout cabalDir =+    CabalDirLayout {..}+  where++    cabalStoreDirLayout = defaultStoreDirLayout (cabalDir </> "store")      cabalLogsDirectory = cabalDir </> "logs" 
Distribution/Client/Exec.hs view
@@ -1,4 +1,3 @@-{-# LANGUAGE CPP #-} ----------------------------------------------------------------------------- -- | -- Module      :  Distribution.Client.Exec@@ -12,7 +11,8 @@ module Distribution.Client.Exec ( exec                                 ) where -import Control.Monad (unless)+import Prelude ()+import Distribution.Client.Compat.Prelude  import qualified Distribution.Simple.GHC   as GHC import qualified Distribution.Simple.GHCJS as GHCJS@@ -27,17 +27,14 @@ import Distribution.Simple.Program.Find (ProgramSearchPathEntry(..)) import Distribution.Simple.Program.Run (programInvocation, runProgramInvocation) import Distribution.Simple.Program.Types ( simpleProgram, ConfiguredProgram(..) )-import Distribution.Simple.Utils       (die, warn)+import Distribution.Simple.Utils       (die', warn) -import Distribution.System    (Platform)+import Distribution.System    (Platform(..), OS(..), buildOS) import Distribution.Verbosity (Verbosity)  import System.Directory ( doesDirectoryExist )+import System.Environment (lookupEnv) import System.FilePath (searchPathSeparator, (</>))-#if !MIN_VERSION_base(4,8,0)-import Control.Applicative ((<$>))-import Data.Monoid (mempty)-#endif   -- | Execute the given command in the package's environment.@@ -55,19 +52,19 @@     case extraArgs of         (exe:args) -> do             program <- requireProgram' verbosity useSandbox programDb exe-            env <- ((++) (programOverrideEnv program)) <$> environmentOverrides+            env <- environmentOverrides (programOverrideEnv program)             let invocation = programInvocation                                  program { programOverrideEnv = env }                                  args             runProgramInvocation verbosity invocation -        [] -> die "Please specify an executable to run"+        [] -> die' verbosity "Please specify an executable to run"   where-    environmentOverrides =+    environmentOverrides env =         case useSandbox of-            NoSandbox -> return []+            NoSandbox -> return env             (UseSandbox sandboxDir) ->-                sandboxEnvironment verbosity sandboxDir comp platform programDb+                sandboxEnvironment verbosity sandboxDir comp platform programDb env   -- | Return the package's sandbox environment.@@ -78,13 +75,19 @@                    -> Compiler                    -> Platform                    -> ProgramDb+                   -> [(String, Maybe String)] -- environment overrides so far                    -> IO [(String, Maybe String)]-sandboxEnvironment verbosity sandboxDir comp platform programDb =+sandboxEnvironment verbosity sandboxDir comp platform programDb iEnv =     case compilerFlavor comp of       GHC   -> env GHC.getGlobalPackageDB   ghcProgram   "GHC_PACKAGE_PATH"       GHCJS -> env GHCJS.getGlobalPackageDB ghcjsProgram "GHCJS_PACKAGE_PATH"-      _     -> die "exec only works with GHC and GHCJS"+      _     -> die' verbosity "exec only works with GHC and GHCJS"   where+    (Platform _ os) = platform+    ldPath = case os of+               OSX     -> "DYLD_LIBRARY_PATH"+               Windows -> "PATH"+               _       -> "LD_LIBRARY_PATH"     env getGlobalPackageDB hcProgram packagePathEnvVar = do         let Just program = lookupProgram hcProgram programDb         gDb <- getGlobalPackageDB verbosity program@@ -96,14 +99,64 @@         exists <- doesDirectoryExist sandboxPackagePath         unless exists $ warn verbosity $ "Package database is not a directory: "                                            ++ sandboxPackagePath+        -- MASSIVE HACK.  We need this to be synchronized with installLibDir+        -- in defaultInstallDirs' in Distribution.Simple.InstallDirs,+        -- which has a special case for Windows (WHY? Who knows; it's been+        -- around as long as Windows exists.)  The sane thing to do here+        -- would be to read out the actual install dirs that were associated+        -- with the package in question, but that's not a well-formed question+        -- here because there is not actually install directory for the+        -- "entire" sandbox.  Since we want to kill this code in favor of+        -- new-build, I decided it wasn't worth fixing this "properly."+        -- Also, this doesn't handle LHC correctly but I don't care -- ezyang+        let extraLibPath =+                case buildOS of+                    Windows -> sandboxDir+                    _ -> sandboxDir </> "lib"+        -- 2016-11-26 Apologies for the spaghetti code here.+        -- Essentially we just want to add the sandbox's lib/ dir to+        -- whatever the library search path environment variable is:+        -- this allows running existing executables against foreign+        -- libraries (meaning Haskell code with a bunch of foreign+        -- exports). However, on Windows this variable is equal to the+        -- executable search path env var. And we try to keep not only+        -- what was already set in the environment, but also the+        -- additional directories we add below in requireProgram'. So+        -- the strategy is that we first take the environment+        -- overrides from requireProgram' below. If the library search+        -- path env is overridden (e.g. because we're on windows), we+        -- prepend the lib/ dir to the relevant override. If not, we+        -- want to avoid wiping the user's own settings, so we first+        -- read the env var's current value, and then prefix ours if+        -- the user had any set.+        iEnv' <-+          if any ((==ldPath) . fst) iEnv+            then return $ updateLdPath extraLibPath iEnv+            else do+              currentLibraryPath <- lookupEnv ldPath+              let updatedLdPath =+                    case currentLibraryPath of+                      Nothing -> Just extraLibPath+                      Just paths ->+                        Just $ extraLibPath ++ [searchPathSeparator] ++ paths+              return $ (ldPath, updatedLdPath) : iEnv+         -- Build the environment-        return [ (packagePathEnvVar, Just compilerPackagePaths)-               , ("CABAL_SANDBOX_PACKAGE_PATH", Just compilerPackagePaths)-               , ("CABAL_SANDBOX_CONFIG", Just sandboxConfigFilePath)-               ]+        return $ [ (packagePathEnvVar, Just compilerPackagePaths)+                 , ("CABAL_SANDBOX_PACKAGE_PATH", Just compilerPackagePaths)+                 , ("CABAL_SANDBOX_CONFIG", Just sandboxConfigFilePath)+                 ] ++ iEnv'      prependToSearchPath path newValue =         newValue ++ [searchPathSeparator] ++ path++    updateLdPath path = map update+      where+        update (name, Just current)+          | name == ldPath = (ldPath, Just $ path ++ [searchPathSeparator] ++ current)+        update (name, Nothing)+          | name == ldPath = (ldPath, Just path)+        update x = x   -- | Check that a program is configured and available to be run. If
Distribution/Client/Fetch.hs view
@@ -21,23 +21,25 @@ import Distribution.Client.Dependency import Distribution.Client.IndexUtils as IndexUtils          ( getSourcePackages, getInstalledPackages )-import qualified Distribution.Client.InstallPlan as InstallPlan-import Distribution.Client.PkgConfigDb-         ( PkgConfigDb, readPkgConfigDb )+import qualified Distribution.Client.SolverInstallPlan as SolverInstallPlan import Distribution.Client.Setup          ( GlobalFlags(..), FetchFlags(..), RepoContext(..) ) +import Distribution.Solver.Types.PkgConfigDb ( PkgConfigDb, readPkgConfigDb )+import Distribution.Solver.Types.SolverPackage+import Distribution.Solver.Types.SourcePackage+ import Distribution.Package          ( packageId ) import Distribution.Simple.Compiler          ( Compiler, compilerInfo, PackageDBStack ) import Distribution.Simple.PackageIndex (InstalledPackageIndex) import Distribution.Simple.Program-         ( ProgramConfiguration )+         ( ProgramDb ) import Distribution.Simple.Setup          ( fromFlag ) import Distribution.Simple.Utils-         ( die, notice, debug )+         ( die', notice, debug ) import Distribution.System          ( Platform ) import Distribution.Text@@ -69,7 +71,7 @@       -> RepoContext       -> Compiler       -> Platform-      -> ProgramConfiguration+      -> ProgramDb       -> GlobalFlags       -> FetchFlags       -> [UserTarget]@@ -77,14 +79,14 @@ fetch verbosity _ _ _ _ _ _ _ [] =     notice verbosity "No packages requested. Nothing to do." -fetch verbosity packageDBs repoCtxt comp platform conf+fetch verbosity packageDBs repoCtxt comp platform progdb       globalFlags fetchFlags userTargets = do -    mapM_ checkTarget userTargets+    mapM_ (checkTarget verbosity) userTargets -    installedPkgIndex <- getInstalledPackages verbosity comp packageDBs conf+    installedPkgIndex <- getInstalledPackages verbosity comp packageDBs progdb     sourcePkgDb       <- getSourcePackages    verbosity repoCtxt-    pkgConfigDb       <- readPkgConfigDb      verbosity conf+    pkgConfigDb       <- readPkgConfigDb      verbosity progdb      pkgSpecifiers <- resolveUserTargets verbosity repoCtxt                        (fromFlag $ globalWorldFile globalFlags)@@ -120,8 +122,8 @@              -> InstalledPackageIndex              -> SourcePackageDb              -> PkgConfigDb-             -> [PackageSpecifier SourcePackage]-             -> IO [SourcePackage]+             -> [PackageSpecifier UnresolvedSourcePackage]+             -> IO [UnresolvedSourcePackage] planPackages verbosity comp platform fetchFlags              installedPkgIndex sourcePkgDb pkgConfigDb pkgSpecifiers @@ -129,7 +131,7 @@       solver <- chooseSolver verbosity                 (fromFlag (fetchSolver fetchFlags)) (compilerInfo comp)       notice verbosity "Resolving dependencies..."-      installPlan <- foldProgress logMsg die return $+      installPlan <- foldProgress logMsg (die' verbosity) return $                        resolveDependencies                          platform (compilerInfo comp) pkgConfigDb                          solver@@ -138,12 +140,12 @@       -- The packages we want to fetch are those packages the 'InstallPlan'       -- that are in the 'InstallPlan.Configured' state.       return-        [ pkg-        | (InstallPlan.Configured (ConfiguredPackage pkg _ _ _))-            <- InstallPlan.toList installPlan ]+        [ solverPkgSource cpkg+        | (SolverInstallPlan.Configured cpkg)+            <- SolverInstallPlan.toList installPlan ]    | otherwise =-      either (die . unlines . map show) return $+      either (die' verbosity . unlines . map show) return $         resolveWithoutDependencies resolverParams    where@@ -156,10 +158,16 @@        . setReorderGoals reorderGoals +      . setCountConflicts countConflicts+       . setShadowPkgs shadowPkgs        . setStrongFlags strongFlags +      . setAllowBootLibInstalls allowBootLibInstalls++      . setSolverVerbosity verbosity+         -- Reinstall the targets given on the command line so that the dep         -- resolver will decide that they need fetching, even if they're         -- already installed. Since we want to get the source packages of@@ -172,16 +180,18 @@     logMsg message rest = debug verbosity message >> rest      reorderGoals     = fromFlag (fetchReorderGoals     fetchFlags)+    countConflicts   = fromFlag (fetchCountConflicts   fetchFlags)     independentGoals = fromFlag (fetchIndependentGoals fetchFlags)     shadowPkgs       = fromFlag (fetchShadowPkgs       fetchFlags)     strongFlags      = fromFlag (fetchStrongFlags      fetchFlags)     maxBackjumps     = fromFlag (fetchMaxBackjumps     fetchFlags)+    allowBootLibInstalls = fromFlag (fetchAllowBootLibInstalls fetchFlags)  -checkTarget :: UserTarget -> IO ()-checkTarget target = case target of+checkTarget :: Verbosity -> UserTarget -> IO ()+checkTarget verbosity target = case target of     UserTargetRemoteTarball _uri-      -> die $ "The 'fetch' command does not yet support remote tarballs. "+      -> die' verbosity $ "The 'fetch' command does not yet support remote tarballs. "             ++ "In the meantime you can use the 'unpack' commands."     _ -> return () @@ -191,7 +201,7 @@     LocalTarballPackage  _file -> return ()      RemoteTarballPackage _uri _ ->-      die $ "The 'fetch' command does not yet support remote tarballs. "+      die' verbosity $ "The 'fetch' command does not yet support remote tarballs. "          ++ "In the meantime you can use the 'unpack' commands."      RepoTarballPackage repo pkgid _ -> do
Distribution/Client/FetchUtils.hs view
@@ -23,6 +23,11 @@     checkRepoTarballFetched,     fetchRepoTarball, +    -- ** fetching packages asynchronously+    asyncFetchPackages,+    waitAsyncFetchPackage,+    AsyncFetchMap,+     -- * fetching other things     downloadIndex,   ) where@@ -35,15 +40,21 @@ import Distribution.Package          ( PackageId, packageName, packageVersion ) import Distribution.Simple.Utils-         ( notice, info, setupMessage )+         ( notice, info, debug, setupMessage ) import Distribution.Text          ( display ) import Distribution.Verbosity-         ( Verbosity )+         ( Verbosity, verboseUnmarkOutput ) import Distribution.Client.GlobalFlags          ( RepoContext(..) )  import Data.Maybe+import Data.Map (Map)+import qualified Data.Map as Map+import Control.Monad+import Control.Exception+import Control.Concurrent.Async+import Control.Concurrent.MVar import System.Directory          ( doesFileExist, createDirectoryIfMissing, getTemporaryDirectory ) import System.IO@@ -64,20 +75,19 @@ -- | Returns @True@ if the package has already been fetched -- or does not need fetching. ---isFetched :: PackageLocation (Maybe FilePath) -> IO Bool+isFetched :: UnresolvedPkgLoc -> IO Bool isFetched loc = case loc of     LocalUnpackedPackage _dir       -> return True     LocalTarballPackage  _file      -> return True     RemoteTarballPackage _uri local -> return (isJust local)     RepoTarballPackage repo pkgid _ -> doesFileExist (packageFile repo pkgid) - -- | Checks if the package has already been fetched (or does not need -- fetching) and if so returns evidence in the form of a 'PackageLocation' -- with a resolved local file location. ---checkFetched :: PackageLocation (Maybe FilePath)-             -> IO (Maybe (PackageLocation FilePath))+checkFetched :: UnresolvedPkgLoc+             -> IO (Maybe ResolvedPkgLoc) checkFetched loc = case loc of     LocalUnpackedPackage dir  ->       return (Just $ LocalUnpackedPackage dir)@@ -109,8 +119,8 @@ -- fetchPackage :: Verbosity              -> RepoContext-             -> PackageLocation (Maybe FilePath)-             -> IO (PackageLocation FilePath)+             -> UnresolvedPkgLoc+             -> IO ResolvedPkgLoc fetchPackage verbosity repoCtxt loc = case loc of     LocalUnpackedPackage dir  ->       return (LocalUnpackedPackage dir)@@ -130,7 +140,7 @@   where     downloadTarballPackage uri = do       transport <- repoContextGetTransport repoCtxt-      transportCheckHttps transport uri+      transportCheckHttps verbosity transport uri       notice verbosity ("Downloading " ++ show uri)       tmpdir <- getTemporaryDirectory       (path, hnd) <- openTempFile tmpdir "cabal-.tar.gz"@@ -155,7 +165,7 @@        RepoRemote{..} -> do         transport <- repoContextGetTransport repoCtxt-        remoteRepoCheckHttps transport repoRemote+        remoteRepoCheckHttps verbosity transport repoRemote         let uri  = packageURI  repoRemote pkgid             dir  = packageDir  repo       pkgid             path = packageFile repo       pkgid@@ -172,11 +182,13 @@           Sec.downloadPackage' rep pkgid path         return path --- | Downloads an index file to [config-dir/packages/serv-id].+-- | Downloads an index file to [config-dir/packages/serv-id] without+-- hackage-security. You probably don't want to call this directly;+-- use 'updateRepo' instead. -- downloadIndex :: HttpTransport -> Verbosity -> RemoteRepo -> FilePath -> IO DownloadResult downloadIndex transport verbosity remoteRepo cacheDir = do-  remoteRepoCheckHttps transport remoteRepo+  remoteRepoCheckHttps verbosity transport remoteRepo   let uri = (remoteRepoURI remoteRepo) {               uriPath = uriPath (remoteRepoURI remoteRepo)                           `FilePath.Posix.combine` "00-index.tar.gz"@@ -184,6 +196,69 @@       path = cacheDir </> "00-index" <.> "tar.gz"   createDirectoryIfMissing True cacheDir   downloadURI transport verbosity uri path+++-- ------------------------------------------------------------+-- * Async fetch wrapper utilities+-- ------------------------------------------------------------++type AsyncFetchMap = Map UnresolvedPkgLoc+                         (MVar (Either SomeException ResolvedPkgLoc))++-- | Fork off an async action to download the given packages (by location).+--+-- The downloads are initiated in order, so you can arrange for packages that+-- will likely be needed sooner to be earlier in the list.+--+-- The body action is passed a map from those packages (identified by their+-- location) to a completion var for that package. So the body action should+-- lookup the location and use 'asyncFetchPackage' to get the result.+--+asyncFetchPackages :: Verbosity+                   -> RepoContext+                   -> [UnresolvedPkgLoc]+                   -> (AsyncFetchMap -> IO a)+                   -> IO a+asyncFetchPackages verbosity repoCtxt pkglocs body = do+    --TODO: [nice to have] use parallel downloads?++    asyncDownloadVars <- sequence [ do v <- newEmptyMVar+                                       return (pkgloc, v)+                                  | pkgloc <- pkglocs ]++    let fetchPackages :: IO ()+        fetchPackages =+          forM_ asyncDownloadVars $ \(pkgloc, var) -> do+            -- Suppress marking here, because 'withAsync' means+            -- that we get nondeterministic interleaving+            result <- try $ fetchPackage (verboseUnmarkOutput verbosity)+                                repoCtxt pkgloc+            putMVar var result++    withAsync fetchPackages $ \_ ->+      body (Map.fromList asyncDownloadVars)+++-- | Expect to find a download in progress in the given 'AsyncFetchMap'+-- and wait on it to finish.+--+-- If the download failed with an exception then this will be thrown.+--+-- Note: This function is supposed to be idempotent, as our install plans+-- can now use the same tarball for many builds, e.g. different+-- components and/or qualified goals, and these all go through the+-- download phase so we end up using 'waitAsyncFetchPackage' twice on+-- the same package. C.f. #4461.+waitAsyncFetchPackage :: Verbosity+                      -> AsyncFetchMap+                      -> UnresolvedPkgLoc+                      -> IO ResolvedPkgLoc+waitAsyncFetchPackage verbosity downloadMap srcloc =+    case Map.lookup srcloc downloadMap of+      Just hnd -> do+        debug verbosity $ "Waiting for download of " ++ show srcloc+        either throwIO return =<< readMVar hnd+      Nothing -> fail "waitAsyncFetchPackage: package not being downloaded"   -- ------------------------------------------------------------
Distribution/Client/FileMonitor.hs view
@@ -15,6 +15,7 @@   monitorFile,   monitorFileHashed,   monitorNonExistentFile,+  monitorFileExistence,   monitorDirectory,   monitorNonExistentDirectory,   monitorDirectoryExistence,@@ -35,23 +36,18 @@   beginUpdateFileMonitor,   ) where +import Prelude ()+import Distribution.Client.Compat.Prelude  #if MIN_VERSION_containers(0,5,0)-import           Data.Map.Strict (Map) import qualified Data.Map.Strict as Map #else-import           Data.Map        (Map) import qualified Data.Map        as Map #endif import qualified Data.ByteString.Lazy as BS-import           Distribution.Compat.Binary import qualified Distribution.Compat.Binary as Binary import qualified Data.Hashable as Hashable-import           Data.List (sort) -#if !MIN_VERSION_base(4,8,0)-import           Control.Applicative-#endif import           Control.Monad import           Control.Monad.Trans (MonadIO, liftIO) import           Control.Monad.State (StateT, mapStateT)@@ -60,7 +56,7 @@                                        throwError) import           Control.Exception -import           Distribution.Client.Compat.Time+import           Distribution.Compat.Time import           Distribution.Client.Glob import           Distribution.Simple.Utils (handleDoesNotExist, writeFileAtomic) import           Distribution.Client.Utils (mergeBy, MergeResult(..))@@ -68,9 +64,7 @@ import           System.FilePath import           System.Directory import           System.IO-import           GHC.Generics (Generic) - ------------------------------------------------------------------------------ -- Types for specifying files to monitor --@@ -130,6 +124,12 @@ monitorNonExistentFile :: FilePath -> MonitorFilePath monitorNonExistentFile = MonitorFile FileNotExists DirNotExists +-- | Monitor a single file for existence only. The monitored file is+-- considered to have changed if it no longer exists.+--+monitorFileExistence :: FilePath -> MonitorFilePath+monitorFileExistence = MonitorFile FileExists DirNotExists+ -- | Monitor a single directory for changes, based on its modification -- time. The monitored directory is considered to have changed if it no -- longer exists or if its modification time has changed.@@ -199,8 +199,13 @@ -- files to be monitored (index by their path), and a list of -- globs, which monitor may files at once. data MonitorStateFileSet-   = MonitorStateFileSet !(Map FilePath MonitorStateFile)+   = MonitorStateFileSet ![MonitorStateFile]                          ![MonitorStateGlob]+     -- Morally this is not actually a set but a bag (represented by lists).+     -- There is no principled reason to use a bag here rather than a set, but+     -- there is also no particular gain either. That said, we do preserve the+     -- order of the lists just to reduce confusion (and have predictable I/O+     -- patterns).   deriving Show  type Hash = Int@@ -216,7 +221,7 @@ -- no longer exists at all. -- data MonitorStateFile = MonitorStateFile !MonitorKindFile !MonitorKindDir-                                         !MonitorStateFileStatus+                                         !FilePath !MonitorStateFileStatus   deriving (Show, Generic)  data MonitorStateFileStatus@@ -262,11 +267,10 @@ -- reconstructMonitorFilePaths :: MonitorStateFileSet -> [MonitorFilePath] reconstructMonitorFilePaths (MonitorStateFileSet singlePaths globPaths) =-    Map.foldrWithKey (\k x r -> getSinglePath k x : r)-                     (map getGlobPath globPaths)-                     singlePaths+    map getSinglePath singlePaths+ ++ map getGlobPath globPaths   where-    getSinglePath filepath (MonitorStateFile kindfile kinddir _) =+    getSinglePath (MonitorStateFile kindfile kinddir filepath _) =       MonitorFile kindfile kinddir filepath      getGlobPath (MonitorStateGlob kindfile kinddir root gstate) =@@ -516,7 +520,7 @@   runChangedM $ do     sequence_       [ probeMonitorStateFileStatus root file status-      | (file, MonitorStateFile _ _ status) <- Map.toList singlePaths ]+      | MonitorStateFile _ _ file status <- singlePaths ]     -- The glob monitors can require state changes     globPaths' <-       sequence@@ -793,19 +797,19 @@                                               --   relative to root                          -> IO MonitorStateFileSet buildMonitorStateFileSet mstartTime hashcache root =-    go Map.empty []+    go [] []   where-    go :: Map FilePath MonitorStateFile -> [MonitorStateGlob]+    go :: [MonitorStateFile] -> [MonitorStateGlob]        -> [MonitorFilePath] -> IO MonitorStateFileSet     go !singlePaths !globPaths [] =-      return (MonitorStateFileSet singlePaths globPaths)+      return (MonitorStateFileSet (reverse singlePaths) (reverse globPaths))      go !singlePaths !globPaths        (MonitorFile kindfile kinddir path : monitors) = do-      monitorState <- MonitorStateFile kindfile kinddir+      monitorState <- MonitorStateFile kindfile kinddir path                   <$> buildMonitorStateFile mstartTime hashcache                                             kindfile kinddir root path-      go (Map.insert path monitorState singlePaths) globPaths monitors+      go (monitorState : singlePaths) globPaths monitors      go !singlePaths !globPaths        (MonitorFileGlob kindfile kinddir globPath : monitors) = do@@ -976,15 +980,15 @@                     collectAllFileHashes singlePaths         `Map.union` collectAllGlobHashes globPaths -    collectAllFileHashes =-      Map.mapMaybe $ \(MonitorStateFile _ _ fstate) -> case fstate of-        MonitorStateFileHashed mtime hash -> Just (mtime, hash)-        _                                 -> Nothing+    collectAllFileHashes singlePaths =+      Map.fromList [ (fpath, (mtime, hash))+                   | MonitorStateFile _ _ fpath+                       (MonitorStateFileHashed mtime hash) <- singlePaths ]      collectAllGlobHashes globPaths =-      Map.fromList [ (fpath, hash)+      Map.fromList [ (fpath, (mtime, hash))                    | MonitorStateGlob _ _ _ gstate <- globPaths-                   , (fpath, hash) <- collectGlobHashes "" gstate ]+                   , (fpath, (mtime, hash)) <- collectGlobHashes "" gstate ]      collectGlobHashes dir (MonitorStateGlobDirs _ _ _ entries) =       [ res
Distribution/Client/Freeze.hs view
@@ -1,4 +1,3 @@-{-# LANGUAGE CPP #-} ----------------------------------------------------------------------------- -- | -- Module      :  Distribution.Client.Freeze@@ -16,19 +15,18 @@     freeze, getFreezePkgs   ) where +import Prelude ()+import Distribution.Client.Compat.Prelude+ import Distribution.Client.Config ( SavedConfig(..) ) import Distribution.Client.Types import Distribution.Client.Targets import Distribution.Client.Dependency-import Distribution.Client.Dependency.Types-         ( ConstraintSource(..), LabeledPackageConstraint(..) ) import Distribution.Client.IndexUtils as IndexUtils          ( getSourcePackages, getInstalledPackages )-import Distribution.Client.InstallPlan-         ( InstallPlan, PlanPackage )-import qualified Distribution.Client.InstallPlan as InstallPlan-import Distribution.Client.PkgConfigDb-         ( PkgConfigDb, readPkgConfigDb )+import Distribution.Client.SolverInstallPlan+         ( SolverInstallPlan, SolverPlanPackage )+import qualified Distribution.Client.SolverInstallPlan as SolverInstallPlan import Distribution.Client.Setup          ( GlobalFlags(..), FreezeFlags(..), ConfigExFlags(..)          , RepoContext(..) )@@ -38,17 +36,23 @@ import Distribution.Client.Sandbox.Types          ( SandboxPackageInfo(..) ) +import Distribution.Solver.Types.ConstraintSource+import Distribution.Solver.Types.LabeledPackageConstraint+import Distribution.Solver.Types.OptionalStanza+import Distribution.Solver.Types.PkgConfigDb+import Distribution.Solver.Types.SolverId+ import Distribution.Package          ( Package, packageId, packageName, packageVersion ) import Distribution.Simple.Compiler          ( Compiler, compilerInfo, PackageDBStack ) import Distribution.Simple.PackageIndex (InstalledPackageIndex) import Distribution.Simple.Program-         ( ProgramConfiguration )+         ( ProgramDb ) import Distribution.Simple.Setup          ( fromFlag, fromFlagOrDefault, flagToMaybe ) import Distribution.Simple.Utils-         ( die, notice, debug, writeFileAtomic )+         ( die', notice, debug, writeFileAtomic ) import Distribution.System          ( Platform ) import Distribution.Text@@ -56,15 +60,7 @@ import Distribution.Verbosity          ( Verbosity ) -import Control.Monad-         ( when ) import qualified Data.ByteString.Lazy.Char8 as BS.Char8-#if !MIN_VERSION_base(4,8,0)-import Data.Monoid-         ( mempty )-#endif-import Data.Version-         ( showVersion ) import Distribution.Version          ( thisVersion ) @@ -76,20 +72,20 @@ -- constraining each dependency to an exact version. -- freeze :: Verbosity-      -> PackageDBStack-      -> RepoContext-      -> Compiler-      -> Platform-      -> ProgramConfiguration-      -> Maybe SandboxPackageInfo-      -> GlobalFlags-      -> FreezeFlags-      -> IO ()-freeze verbosity packageDBs repoCtxt comp platform conf mSandboxPkgInfo+       -> PackageDBStack+       -> RepoContext+       -> Compiler+       -> Platform+       -> ProgramDb+       -> Maybe SandboxPackageInfo+       -> GlobalFlags+       -> FreezeFlags+       -> IO ()+freeze verbosity packageDBs repoCtxt comp platform progdb mSandboxPkgInfo       globalFlags freezeFlags = do      pkgs  <- getFreezePkgs-               verbosity packageDBs repoCtxt comp platform conf mSandboxPkgInfo+               verbosity packageDBs repoCtxt comp platform progdb mSandboxPkgInfo                globalFlags freezeFlags      if null pkgs@@ -112,17 +108,17 @@               -> RepoContext               -> Compiler               -> Platform-              -> ProgramConfiguration+              -> ProgramDb               -> Maybe SandboxPackageInfo               -> GlobalFlags               -> FreezeFlags-              -> IO [PlanPackage]-getFreezePkgs verbosity packageDBs repoCtxt comp platform conf mSandboxPkgInfo+              -> IO [SolverPlanPackage]+getFreezePkgs verbosity packageDBs repoCtxt comp platform progdb mSandboxPkgInfo       globalFlags freezeFlags = do -    installedPkgIndex <- getInstalledPackages verbosity comp packageDBs conf+    installedPkgIndex <- getInstalledPackages verbosity comp packageDBs progdb     sourcePkgDb       <- getSourcePackages    verbosity repoCtxt-    pkgConfigDb       <- readPkgConfigDb      verbosity conf+    pkgConfigDb       <- readPkgConfigDb      verbosity progdb      pkgSpecifiers <- resolveUserTargets verbosity repoCtxt                        (fromFlag $ globalWorldFile globalFlags)@@ -136,10 +132,10 @@   where     sanityCheck pkgSpecifiers = do       when (not . null $ [n | n@(NamedPackage _ _) <- pkgSpecifiers]) $-        die $ "internal error: 'resolveUserTargets' returned "+        die' verbosity $ "internal error: 'resolveUserTargets' returned "            ++ "unexpected named package specifiers!"       when (length pkgSpecifiers /= 1) $-        die $ "internal error: 'resolveUserTargets' returned "+        die' verbosity $ "internal error: 'resolveUserTargets' returned "            ++ "unexpected source package specifiers!"  planPackages :: Verbosity@@ -150,8 +146,8 @@              -> InstalledPackageIndex              -> SourcePackageDb              -> PkgConfigDb-             -> [PackageSpecifier SourcePackage]-             -> IO [PlanPackage]+             -> [PackageSpecifier UnresolvedSourcePackage]+             -> IO [SolverPlanPackage] planPackages verbosity comp platform mSandboxPkgInfo freezeFlags              installedPkgIndex sourcePkgDb pkgConfigDb pkgSpecifiers = do @@ -159,7 +155,7 @@             (fromFlag (freezeSolver freezeFlags)) (compilerInfo comp)   notice verbosity "Resolving dependencies..." -  installPlan <- foldProgress logMsg die return $+  installPlan <- foldProgress logMsg (die' verbosity) return $                    resolveDependencies                      platform (compilerInfo comp) pkgConfigDb                      solver@@ -177,13 +173,20 @@        . setReorderGoals reorderGoals +      . setCountConflicts countConflicts+       . setShadowPkgs shadowPkgs        . setStrongFlags strongFlags +      . setAllowBootLibInstalls allowBootLibInstalls++      . setSolverVerbosity verbosity+       . addConstraints           [ let pkg = pkgSpecifierTarget pkgSpecifier-                pc = PackageConstraintStanzas pkg stanzas+                pc = PackageConstraint (scopeToplevel pkg)+                                       (PackagePropertyStanzas stanzas)             in LabeledPackageConstraint pc ConstraintSourceFreeze           | pkgSpecifier <- pkgSpecifiers ] @@ -199,10 +202,12 @@     benchmarksEnabled = fromFlagOrDefault False $ freezeBenchmarks freezeFlags      reorderGoals     = fromFlag (freezeReorderGoals     freezeFlags)+    countConflicts   = fromFlag (freezeCountConflicts   freezeFlags)     independentGoals = fromFlag (freezeIndependentGoals freezeFlags)     shadowPkgs       = fromFlag (freezeShadowPkgs       freezeFlags)     strongFlags      = fromFlag (freezeStrongFlags      freezeFlags)     maxBackjumps     = fromFlag (freezeMaxBackjumps     freezeFlags)+    allowBootLibInstalls = fromFlag (freezeAllowBootLibInstalls freezeFlags)   -- | Remove all unneeded packages from an install plan.@@ -214,14 +219,17 @@ -- 2) not a dependency (directly or transitively) of the package we are --    freezing.  This is useful for removing previously installed packages --    which are no longer required from the install plan.-pruneInstallPlan :: InstallPlan-                 -> [PackageSpecifier SourcePackage]-                 -> [PlanPackage]+--+-- Invariant: @pkgSpecifiers@ must refer to packages which are not+-- 'PreExisting' in the 'SolverInstallPlan'.+pruneInstallPlan :: SolverInstallPlan+                 -> [PackageSpecifier UnresolvedSourcePackage]+                 -> [SolverPlanPackage] pruneInstallPlan installPlan pkgSpecifiers =     removeSelf pkgIds $-    InstallPlan.dependencyClosure installPlan (map fakeUnitId pkgIds)+    SolverInstallPlan.dependencyClosure installPlan pkgIds   where-    pkgIds = [ packageId pkg+    pkgIds = [ PlannedId (packageId pkg)              | SpecificSourcePackage pkg <- pkgSpecifiers ]     removeSelf [thisPkg] = filter (\pp -> packageId pp /= packageId thisPkg)     removeSelf _  = error $ "internal error: 'pruneInstallPlan' given "@@ -232,7 +240,8 @@ freezePackages verbosity globalFlags pkgs = do      pkgEnv <- fmap (createPkgEnv . addFrozenConstraints) $-                   loadUserConfig verbosity ""  (flagToMaybe . globalConstraintsFile $ globalFlags)+                   loadUserConfig verbosity ""+                   (flagToMaybe . globalConstraintsFile $ globalFlags)     writeFileAtomic userPackageEnvironmentFile $ showPkgEnv pkgEnv   where     addFrozenConstraints config =@@ -242,11 +251,12 @@             }         }     constraint pkg =-        (pkgIdToConstraint $ packageId pkg, ConstraintSourceUserConfig userPackageEnvironmentFile)+        (pkgIdToConstraint $ packageId pkg+        ,ConstraintSourceUserConfig userPackageEnvironmentFile)       where         pkgIdToConstraint pkgId =-            UserConstraintVersion (packageName pkgId)-                                  (thisVersion $ packageVersion pkgId)+            UserConstraint (UserQualified UserQualToplevel (packageName pkgId))+                           (PackagePropertyVersion $ thisVersion (packageVersion pkgId))     createPkgEnv config = mempty { pkgEnvSavedConfig = config }     showPkgEnv = BS.Char8.pack . showPackageEnvironment @@ -256,4 +266,4 @@   where     showPkg pid = name pid ++ " == " ++ version pid     name = display . packageName-    version = showVersion . packageVersion+    version = display . packageVersion
Distribution/Client/GenBounds.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE CPP #-} ----------------------------------------------------------------------------- -- | -- Module      :  Distribution.Client.GenBounds@@ -14,8 +15,6 @@     genBounds   ) where -import Data.Version-         ( Version(..), showVersion ) import Distribution.Client.Init          ( incVersion ) import Distribution.Client.Freeze@@ -25,26 +24,36 @@ import Distribution.Client.Setup          ( GlobalFlags(..), FreezeFlags(..), RepoContext ) import Distribution.Package-         ( Package(..), Dependency(..), PackageName(..)-         , packageName, packageVersion )+         ( Package(..), unPackageName, packageName, packageVersion ) import Distribution.PackageDescription          ( buildDepends ) import Distribution.PackageDescription.Configuration-         ( finalizePackageDescription )+         ( finalizePD )+#ifdef CABAL_PARSEC+import Distribution.PackageDescription.Parsec+         ( readGenericPackageDescription )+#else import Distribution.PackageDescription.Parse-         ( readPackageDescription )+         ( readGenericPackageDescription )+#endif+import Distribution.Types.ComponentRequestedSpec+         ( defaultComponentRequestedSpec )+import Distribution.Types.Dependency import Distribution.Simple.Compiler          ( Compiler, PackageDBStack, compilerInfo ) import Distribution.Simple.Program-         ( ProgramConfiguration )+         ( ProgramDb ) import Distribution.Simple.Utils          ( tryFindPackageDesc ) import Distribution.System          ( Platform )+import Distribution.Text+         ( display ) import Distribution.Verbosity          ( Verbosity ) import Distribution.Version-         ( LowerBound(..), UpperBound(..), VersionRange(..), asVersionIntervals+         ( Version, alterVersion+         , LowerBound(..), UpperBound(..), VersionRange(..), asVersionIntervals          , orLaterVersion, earlierVersion, intersectVersionRanges ) import System.Directory          ( getCurrentDirectory )@@ -69,7 +78,7 @@            `intersectVersionRanges`            earlierVersion (incVersion 1 (vn 2))   where-    vn n = (v { versionBranch = take n (versionBranch v) })+    vn n = alterVersion (take n) v  -- | Show the PVP-mandated version range for this package. The @padTo@ parameter -- specifies the width of the package name column.@@ -85,7 +94,7 @@     showInterval (LowerBound _ _, NoUpperBound) =       error "Error: expected upper bound...this should never happen!"     showInterval (LowerBound l _, UpperBound u _) =-      unwords [">=", showVersion l, "&& <", showVersion u]+      unwords [">=", display l, "&& <", display u]  -- | Entry point for the @gen-bounds@ command. genBounds@@ -94,22 +103,25 @@     -> RepoContext     -> Compiler     -> Platform-    -> ProgramConfiguration+    -> ProgramDb     -> Maybe SandboxPackageInfo     -> GlobalFlags     -> FreezeFlags     -> IO ()-genBounds verbosity packageDBs repoCtxt comp platform conf mSandboxPkgInfo+genBounds verbosity packageDBs repoCtxt comp platform progdb mSandboxPkgInfo       globalFlags freezeFlags = do      let cinfo = compilerInfo comp      cwd <- getCurrentDirectory     path <- tryFindPackageDesc cwd-    gpd <- readPackageDescription verbosity path-    let epd = finalizePackageDescription [] (const True) platform cinfo [] gpd+    gpd <- readGenericPackageDescription verbosity path+    -- NB: We don't enable tests or benchmarks, since often they+    -- don't really have useful bounds.+    let epd = finalizePD [] defaultComponentRequestedSpec+                    (const True) platform cinfo [] gpd     case epd of-      Left _ -> putStrLn "finalizePackageDescription failed"+      Left _ -> putStrLn "finalizePD failed"       Right (pd,_) -> do         let needBounds = filter (not . hasUpperBound . depVersion) $                          buildDepends pd@@ -121,7 +133,7 @@   where      go needBounds = do        pkgs  <- getFreezePkgs-                  verbosity packageDBs repoCtxt comp platform conf+                  verbosity packageDBs repoCtxt comp platform progdb                   mSandboxPkgInfo globalFlags freezeFlags         putStrLn boundsNeededMsg@@ -134,7 +146,7 @@        mapM_ (putStrLn . (++",") . showBounds padTo) thePkgs       depName :: Dependency -> String-     depName (Dependency (PackageName nm) _) = nm+     depName (Dependency pn _) = unPackageName pn       depVersion :: Dependency -> VersionRange      depVersion (Dependency _ vr) = vr
Distribution/Client/Get.hs view
@@ -1,4 +1,3 @@-{-# LANGUAGE CPP #-} ----------------------------------------------------------------------------- -- | -- Module      :  Distribution.Client.Get@@ -18,12 +17,15 @@     get   ) where +import Prelude ()+import Distribution.Client.Compat.Prelude hiding (get)+ import Distribution.Package          ( PackageId, packageId, packageName ) import Distribution.Simple.Setup          ( Flag(..), fromFlag, fromFlagOrDefault ) import Distribution.Simple.Utils-         ( notice, die, info, writeFileAtomic )+         ( notice, die', info, rawSystemExitCode, writeFileAtomic ) import Distribution.Verbosity          ( Verbosity ) import Distribution.Text(display)@@ -37,25 +39,19 @@ import Distribution.Client.FetchUtils import qualified Distribution.Client.Tar as Tar (extractTarGzFile) import Distribution.Client.IndexUtils as IndexUtils-        ( getSourcePackages )+        ( getSourcePackagesAtIndexState, IndexState(..) ) import Distribution.Client.Compat.Process         ( readProcessWithExitCode ) import Distribution.Compat.Exception         ( catchIO ) +import Distribution.Solver.Types.SourcePackage+ import Control.Exception          ( finally ) import Control.Monad-         ( filterM, forM_, unless, when )-import Data.List-         ( sortBy )+         ( forM_, mapM_ ) import qualified Data.Map-import Data.Maybe-         ( listToMaybe, mapMaybe )-#if !MIN_VERSION_base(4,8,0)-import Data.Monoid-         ( mempty )-#endif import Data.Ord          ( comparing ) import System.Directory@@ -66,8 +62,6 @@          ( ExitCode(..) ) import System.FilePath          ( (</>), (<.>), addTrailingPathSeparator )-import System.Process-         ( rawSystem )   -- | Entry point for the 'cabal get' command.@@ -86,16 +80,19 @@         _      -> True    unless useFork $-    mapM_ checkTarget userTargets+    mapM_ (checkTarget verbosity) userTargets -  sourcePkgDb <- getSourcePackages verbosity repoCtxt+  let idxState = fromFlagOrDefault IndexStateHead $+                       getIndexState getFlags +  sourcePkgDb <- getSourcePackagesAtIndexState verbosity repoCtxt idxState+   pkgSpecifiers <- resolveUserTargets verbosity repoCtxt                    (fromFlag $ globalWorldFile globalFlags)                    (packageIndex sourcePkgDb)                    userTargets -  pkgs <- either (die . unlines . map show) return $+  pkgs <- either (die' verbosity . unlines . map show) return $             resolveWithoutDependencies               (resolverParams sourcePkgDb pkgSpecifiers) @@ -113,13 +110,13 @@      prefix = fromFlagOrDefault "" (getDestDir getFlags) -    fork :: [SourcePackage] -> IO ()+    fork :: [UnresolvedSourcePackage] -> IO ()     fork pkgs = do       let kind = fromFlag . getSourceRepository $ getFlags       branchers <- findUsableBranchers       mapM_ (forkPackage verbosity branchers prefix kind) pkgs -    unpack :: [SourcePackage] -> IO ()+    unpack :: [UnresolvedSourcePackage] -> IO ()     unpack pkgs = do       forM_ pkgs $ \pkg -> do         location <- fetchPackage verbosity repoCtxt (packageSource pkg)@@ -141,10 +138,10 @@       where         usePristine = fromFlagOrDefault False (getPristine getFlags) -checkTarget :: UserTarget -> IO ()-checkTarget target = case target of-    UserTargetLocalDir       dir  -> die (notTarball dir)-    UserTargetLocalCabalFile file -> die (notTarball file)+checkTarget :: Verbosity -> UserTarget -> IO ()+checkTarget verbosity target = case target of+    UserTargetLocalDir       dir  -> die' verbosity (notTarball dir)+    UserTargetLocalCabalFile file -> die' verbosity (notTarball file)     _                             -> return ()   where     notTarball t =@@ -163,10 +160,10 @@         pkgdir     = prefix </> pkgdirname         pkgdir'    = addTrailingPathSeparator pkgdir     existsDir  <- doesDirectoryExist pkgdir-    when existsDir $ die $+    when existsDir $ die' verbosity $      "The directory \"" ++ pkgdir' ++ "\" already exists, not unpacking."     existsFile  <- doesFileExist pkgdir-    when existsFile $ die $+    when existsFile $ die' verbosity $      "A file \"" ++ pkgdir ++ "\" is in the way, not unpacking."     notice verbosity $ "Unpacking to " ++ pkgdir'     Tar.extractTarGzFile prefix pkgdirname pkgPath@@ -226,7 +223,7 @@                -- be created.             -> (Maybe PD.RepoKind)                -- ^ Which repo to choose.-            -> SourcePackage+            -> SourcePackage loc                -- ^ The package to fork.             -> IO () forkPackage verbosity branchers prefix kind src = do@@ -237,11 +234,11 @@      destDirExists <- doesDirectoryExist destdir     when destDirExists $ do-        die ("The directory " ++ show destdir ++ " already exists, not forking.")+        die' verbosity ("The directory " ++ show destdir ++ " already exists, not forking.")      destFileExists  <- doesFileExist destdir     when destFileExists $ do-        die ("A file " ++ show destdir ++ " is in the way, not forking.")+        die' verbosity ("A file " ++ show destdir ++ " is in the way, not forking.")      let repos = PD.sourceRepos desc     case findBranchCmd branchers repos kind of@@ -249,11 +246,11 @@             exitCode <- io verbosity destdir             case exitCode of                 ExitSuccess -> return ()-                ExitFailure _ -> die ("Couldn't fork package " ++ pkgid)+                ExitFailure _ -> die' verbosity ("Couldn't fork package " ++ pkgid)         Nothing -> case repos of-            [] -> die ("Package " ++ pkgid+            [] -> die' verbosity ("Package " ++ pkgid                        ++ " does not have any source repositories.")-            _ -> die ("Package " ++ pkgid+            _ -> die' verbosity ("Package " ++ pkgid                       ++ " does not have any usable source repositories.")  -- | Given a set of possible branchers, and a set of possible source@@ -295,7 +292,7 @@          Nothing -> ["branch", src, dst]     return $ BranchCmd $ \verbosity dst -> do         notice verbosity ("bzr: branch " ++ show src)-        rawSystem "bzr" (args dst)+        rawSystemExitCode verbosity "bzr" (args dst)  -- | Branch driver for Darcs. branchDarcs :: Brancher@@ -306,29 +303,29 @@          Nothing -> ["get", src, dst]     return $ BranchCmd $ \verbosity dst -> do         notice verbosity ("darcs: get " ++ show src)-        rawSystem "darcs" (args dst)+        rawSystemExitCode verbosity "darcs" (args dst)  -- | Branch driver for Git. branchGit :: Brancher branchGit = Brancher "git" $ \repo -> do     src <- PD.repoLocation repo-    let branchArgs = case PD.repoBranch repo of-         Just b -> ["--branch", b]-         Nothing -> []-    let postClone dst = case PD.repoTag repo of+    let postClone verbosity dst = case PD.repoTag repo of          Just t -> do              cwd <- getCurrentDirectory              setCurrentDirectory dst              finally-                 (rawSystem "git" (["checkout", t] ++ branchArgs))+                 (rawSystemExitCode verbosity "git" ["checkout", t])                  (setCurrentDirectory cwd)          Nothing -> return ExitSuccess     return $ BranchCmd $ \verbosity dst -> do         notice verbosity ("git: clone " ++ show src)-        code <- rawSystem "git" (["clone", src, dst] ++ branchArgs)+        code <- rawSystemExitCode verbosity "git" (["clone", src, dst] +++                    case PD.repoBranch repo of+                        Nothing -> []+                        Just b -> ["--branch", b])         case code of             ExitFailure _ -> return code-            ExitSuccess -> postClone dst+            ExitSuccess -> postClone verbosity  dst  -- | Branch driver for Mercurial. branchHg :: Brancher@@ -343,7 +340,7 @@     let args dst = ["clone", src, dst] ++ branchArgs ++ tagArgs     return $ BranchCmd $ \verbosity dst -> do         notice verbosity ("hg: clone " ++ show src)-        rawSystem "hg" (args dst)+        rawSystemExitCode verbosity "hg" (args dst)  -- | Branch driver for Subversion. branchSvn :: Brancher@@ -352,4 +349,4 @@     let args dst = ["checkout", src, dst]     return $ BranchCmd $ \verbosity dst -> do         notice verbosity ("svn: checkout " ++ show src)-        rawSystem "svn" (args dst)+        rawSystemExitCode verbosity "svn" (args dst)
Distribution/Client/Glob.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE CPP, DeriveGeneric #-}+{-# LANGUAGE DeriveGeneric #-}  --TODO: [code cleanup] plausibly much of this module should be merged with -- similar functionality in Cabal.@@ -15,14 +15,11 @@     , getFilePathRootDirectory     ) where -import           Data.Char (toUpper)+import Prelude ()+import Distribution.Client.Compat.Prelude+ import           Data.List (stripPrefix)-#if !MIN_VERSION_base(4,8,0)-import           Control.Applicative-#endif-import           Control.Monad-import           Distribution.Compat.Binary-import           GHC.Generics (Generic)+import           Control.Monad (mapM)  import           Distribution.Text import           Distribution.Compat.ReadP (ReadP, (<++), (+++))
Distribution/Client/GlobalFlags.hs view
@@ -4,6 +4,7 @@ {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE RecordWildCards #-}+ module Distribution.Client.GlobalFlags (     GlobalFlags(..)   , defaultGlobalFlags@@ -12,9 +13,11 @@   , withRepoContext'   ) where +import Prelude ()+import Distribution.Client.Compat.Prelude+ import Distribution.Client.Types          ( Repo(..), RemoteRepo(..) )-import Distribution.Compat.Semigroup import Distribution.Simple.Setup          ( Flag(..), fromFlag, flagToMaybe ) import Distribution.Utils.NubList@@ -26,22 +29,15 @@ import Distribution.Simple.Utils          ( info ) -import Data.Maybe-         ( fromMaybe ) import Control.Concurrent          ( MVar, newMVar, modifyMVar ) import Control.Exception          ( throwIO )-import Control.Monad-         ( when ) import System.FilePath          ( (</>) ) import Network.URI-         ( uriScheme, uriPath )-import Data.Map-         ( Map )+         ( URI, uriScheme, uriPath ) import qualified Data.Map as Map-import GHC.Generics ( Generic )  import qualified Hackage.Security.Client                    as Sec import qualified Hackage.Security.Util.Path                 as Sec@@ -50,6 +46,7 @@ import qualified Hackage.Security.Client.Repository.Local   as Sec.Local import qualified Hackage.Security.Client.Repository.Remote  as Sec.Remote import qualified Distribution.Client.Security.HTTP          as Sec.HTTP+import qualified Distribution.Client.Security.DNS           as Sec.DNS  -- ------------------------------------------------------------ -- * Global flags@@ -70,7 +67,8 @@     globalRequireSandbox    :: Flag Bool,     globalIgnoreSandbox     :: Flag Bool,     globalIgnoreExpiry      :: Flag Bool,    -- ^ Ignore security expiry dates-    globalHttpTransport     :: Flag String+    globalHttpTransport     :: Flag String,+    globalNix               :: Flag Bool  -- ^ Integrate with Nix   } deriving Generic  defaultGlobalFlags :: GlobalFlags@@ -88,7 +86,8 @@     globalRequireSandbox    = Flag False,     globalIgnoreSandbox     = Flag False,     globalIgnoreExpiry      = Flag False,-    globalHttpTransport     = mempty+    globalHttpTransport     = mempty,+    globalNix               = Flag False   }  instance Monoid GlobalFlags where@@ -219,8 +218,19 @@                -> (SecureRepo -> IO a)  -- ^ Callback                -> IO a initSecureRepo verbosity httpLib RemoteRepo{..} cachePath = \callback -> do-    withRepo $ \r -> do-      requiresBootstrap <- Sec.requiresBootstrap r+    requiresBootstrap <- withRepo [] Sec.requiresBootstrap++    mirrors <- if requiresBootstrap+               then do+                   info verbosity $ "Trying to locate mirrors via DNS for " +++                                    "initial bootstrap of secure " +++                                    "repository '" ++ show remoteRepoURI +++                                    "' ..."++                   Sec.DNS.queryBootstrapMirrors verbosity remoteRepoURI+               else pure []++    withRepo mirrors $ \r -> do       when requiresBootstrap $ Sec.uncheckClientErrors $         Sec.bootstrap r           (map Sec.KeyId    remoteRepoRootKeys)@@ -228,8 +238,8 @@       callback $ SecureRepo r   where     -- Initialize local or remote repo depending on the URI-    withRepo :: (forall down. Sec.Repository down -> IO a) -> IO a-    withRepo callback | uriScheme remoteRepoURI == "file:" = do+    withRepo :: [URI] -> (forall down. Sec.Repository down -> IO a) -> IO a+    withRepo _ callback | uriScheme remoteRepoURI == "file:" = do       dir <- Sec.makeAbsolute $ Sec.fromFilePath (uriPath remoteRepoURI)       Sec.Local.withRepository dir                                cache@@ -237,9 +247,9 @@                                Sec.hackageIndexLayout                                logTUF                                callback-    withRepo callback =+    withRepo mirrors callback =       Sec.Remote.withRepository httpLib-                                [remoteRepoURI]+                                (remoteRepoURI:mirrors)                                 Sec.Remote.defaultRepoOpts                                 cache                                 Sec.hackageRepoLayout
Distribution/Client/Haddock.hs view
@@ -23,9 +23,9 @@ import Distribution.Package          ( packageVersion ) import Distribution.Simple.Haddock (haddockPackagePaths)-import Distribution.Simple.Program (haddockProgram, ProgramConfiguration-                                   , rawSystemProgram, requireProgramVersion)-import Distribution.Version (Version(Version), orLaterVersion)+import Distribution.Simple.Program (haddockProgram, ProgramDb+                                   , runProgram, requireProgramVersion)+import Distribution.Version (mkVersion, orLaterVersion) import Distribution.Verbosity (Verbosity) import Distribution.Simple.PackageIndex          ( InstalledPackageIndex, allPackagesByName )@@ -35,17 +35,17 @@          ( InstalledPackageInfo(exposed) )  regenerateHaddockIndex :: Verbosity-                       -> InstalledPackageIndex -> ProgramConfiguration+                       -> InstalledPackageIndex -> ProgramDb                        -> FilePath                        -> IO ()-regenerateHaddockIndex verbosity pkgs conf index = do+regenerateHaddockIndex verbosity pkgs progdb index = do       (paths, warns) <- haddockPackagePaths pkgs' Nothing       let paths' = [ (interface, html) | (interface, Just html) <- paths]       forM_ warns (debug verbosity)        (confHaddock, _, _) <-           requireProgramVersion verbosity haddockProgram-                                    (orLaterVersion (Version [0,6] [])) conf+                                    (orLaterVersion (mkVersion [0,6])) progdb        createDirectoryIfMissing True destDir @@ -57,7 +57,7 @@                     , "--title=Haskell modules on this system" ]                  ++ [ "--read-interface=" ++ html ++ "," ++ interface                     | (interface, html) <- paths' ]-        rawSystemProgram verbosity confHaddock flags+        runProgram verbosity confHaddock flags         renameFile (tempDir </> "index.html") (tempDir </> destFile)         installDirectoryContents verbosity tempDir destDir 
Distribution/Client/HttpUtils.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE CPP, BangPatterns #-}+{-# LANGUAGE BangPatterns #-} ----------------------------------------------------------------------------- -- | Separate module for HTTP actions, using a proxy server if one exists. -----------------------------------------------------------------------------@@ -14,6 +14,9 @@     isOldHackageURI   ) where +import Prelude ()+import Distribution.Client.Compat.Prelude+ import Network.HTTP          ( Request (..), Response (..), RequestMethod (..)          , Header(..), HeaderName(..), lookupHeader )@@ -23,39 +26,36 @@ import Network.Browser          ( browse, setOutHandler, setErrHandler, setProxy          , setAuthorityGen, request, setAllowBasicAuth, setUserAgent )-#if !MIN_VERSION_base(4,8,0)-import Control.Applicative-#endif import qualified Control.Exception as Exception+import Control.Exception+         ( evaluate )+import Control.DeepSeq+         ( force ) import Control.Monad-         ( when, guard )+         ( guard ) import qualified Data.ByteString.Lazy.Char8 as BS-import Data.List-         ( isPrefixOf, find, intercalate )-import Data.Maybe-         ( listToMaybe, maybeToList, fromMaybe ) import qualified Paths_cabal_install (version) import Distribution.Verbosity (Verbosity) import Distribution.Simple.Utils-         ( die, info, warn, debug, notice, writeFileAtomic+         ( die', info, warn, debug, notice, writeFileAtomic          , copyFileVerbose,  withTempFile          , rawSystemStdInOut, toUTF8, fromUTF8, normaliseLineEndings ) import Distribution.Client.Utils-         ( readMaybe, withTempFileName )+         ( withTempFileName ) import Distribution.Client.Types          ( RemoteRepo(..) ) import Distribution.System          ( buildOS, buildArch ) import Distribution.Text          ( display )-import Data.Char-         ( isSpace ) import qualified System.FilePath.Posix as FilePath.Posix          ( splitDirectories ) import System.FilePath          ( (<.>) ) import System.Directory          ( doesFileExist, renameFile )+import System.IO+         ( withFile, IOMode(ReadMode), hGetContents, hClose ) import System.IO.Error          ( isDoesNotExistError ) import Distribution.Simple.Program@@ -70,7 +70,6 @@         ( IOEncoding(..), getEffectiveEnvironment ) import Numeric (showHex) import System.Directory (canonicalizePath)-import System.IO (hClose) import System.FilePath (takeFileName, takeDirectory) import System.Random (randomRIO) import System.Exit (ExitCode(..))@@ -132,26 +131,26 @@         304 -> do             notice verbosity "Skipping download: local and remote files match."             return FileAlreadyInCache-        errCode ->  die $ "Failed to download " ++ show uri+        errCode ->  die' verbosity $ "Failed to download " ++ show uri                        ++ " : HTTP code " ++ show errCode  ------------------------------------------------------------------------------ -- Utilities for repo url management -- -remoteRepoCheckHttps :: HttpTransport -> RemoteRepo -> IO ()-remoteRepoCheckHttps transport repo+remoteRepoCheckHttps :: Verbosity -> HttpTransport -> RemoteRepo -> IO ()+remoteRepoCheckHttps verbosity transport repo   | uriScheme (remoteRepoURI repo) == "https:"   , not (transportSupportsHttps transport)-              = die $ "The remote repository '" ++ remoteRepoName repo+              = die' verbosity $ "The remote repository '" ++ remoteRepoName repo                    ++ "' specifies a URL that " ++ requiresHttpsErrorMessage   | otherwise = return () -transportCheckHttps :: HttpTransport -> URI -> IO ()-transportCheckHttps transport uri+transportCheckHttps :: Verbosity -> HttpTransport -> URI -> IO ()+transportCheckHttps verbosity transport uri   | uriScheme uri == "https:"   , not (transportSupportsHttps transport)-              = die $ "The URL " ++ show uri+              = die' verbosity $ "The URL " ++ show uri                    ++ " " ++ requiresHttpsErrorMessage   | otherwise = return () @@ -165,13 +164,13 @@    ++ "external program is available, or one can be selected specifically "    ++ "with the global flag --http-transport=" -remoteRepoTryUpgradeToHttps :: HttpTransport -> RemoteRepo -> IO RemoteRepo-remoteRepoTryUpgradeToHttps transport repo+remoteRepoTryUpgradeToHttps :: Verbosity -> HttpTransport -> RemoteRepo -> IO RemoteRepo+remoteRepoTryUpgradeToHttps verbosity transport repo   | remoteRepoShouldTryHttps repo   , uriScheme (remoteRepoURI repo) == "http:"   , not (transportSupportsHttps transport)   , not (transportManuallySelected transport)-  = die $ "The builtin HTTP implementation does not support HTTPS, but using "+  = die' verbosity $ "The builtin HTTP implementation does not support HTTPS, but using "        ++ "HTTPS for authenticated uploads is recommended. "        ++ "The transport implementations with HTTPS support are "        ++ intercalate ", " [ name | (name, _, True, _ ) <- supportedTransports ]@@ -247,7 +246,7 @@  noPostYet :: Verbosity -> URI -> String -> Maybe (String, String)           -> IO (Int, String)-noPostYet _ _ _ _ = die "Posting (for report upload) is not implemented yet"+noPostYet verbosity _ _ _ = die' verbosity "Posting (for report upload) is not implemented yet"  supportedTransports :: [(String, Maybe Program, Bool,                          ProgramDb -> Maybe HttpTransport)]@@ -285,7 +284,7 @@         let Just transport = mkTrans progdb         return transport { transportManuallySelected = True } -      Nothing -> die $ "Unknown HTTP transport specified: " ++ name+      Nothing -> die' verbosity $ "Unknown HTTP transport specified: " ++ name                     ++ ". The supported transports are "                     ++ intercalate ", "                          [ name' | (name', _, _, _ ) <- supportedTransports ]@@ -340,9 +339,10 @@            resp <- getProgramInvocationOutput verbosity                     (programInvocation prog args)-          headers <- readFile tmpFile-          (code, _err, etag') <- parseResponse uri resp headers-          return (code, etag')+          withFile tmpFile ReadMode $ \hnd -> do+            headers <- hGetContents hnd+            (code, _err, etag') <- parseResponse verbosity uri resp headers+            evaluate $ force (code, etag')      posthttp = noPostYet @@ -367,7 +367,7 @@                    ]         resp <- getProgramInvocationOutput verbosity $ addAuthConfig auth                   (programInvocation prog args)-        (code, err, _etag) <- parseResponse uri resp ""+        (code, err, _etag) <- parseResponse verbosity uri resp ""         return (code, err)      puthttpfile verbosity uri path auth headers = do@@ -384,12 +384,13 @@                    | Header name value <- headers ]         resp <- getProgramInvocationOutput verbosity $ addAuthConfig auth                   (programInvocation prog args)-        (code, err, _etag) <- parseResponse uri resp ""+        (code, err, _etag) <- parseResponse verbosity uri resp ""         return (code, err) -    -- on success these curl involcations produces an output like "200"+    -- on success these curl invocations produces an output like "200"     -- and on failure it has the server error response first-    parseResponse uri resp headers =+    parseResponse :: Verbosity -> URI -> String -> String -> IO (Int, String, Maybe ETag)+    parseResponse verbosity uri resp headers =       let codeerr =             case reverse (lines resp) of               (codeLine:rerrLines) ->@@ -408,16 +409,29 @@         in case codeerr of             Just (i, err) -> return (i, err, mb_etag)-            _             -> statusParseFail uri resp+            _             -> statusParseFail verbosity uri resp   wgetTransport :: ConfiguredProgram -> HttpTransport wgetTransport prog =-    HttpTransport gethttp posthttp posthttpfile puthttpfile True False+  HttpTransport gethttp posthttp posthttpfile puthttpfile True False   where-    gethttp verbosity uri etag destPath reqHeaders = do+    gethttp verbosity uri etag destPath reqHeaders =  do         resp <- runWGet verbosity uri args-        (code, etag') <- parseOutput uri resp++        -- wget doesn't support range requests.+        -- so, we not only ignore range request headers,+        -- but we also dispay a warning message when we see them.+        let hasRangeHeader =  any (\hdr -> isRangeHeader hdr) reqHeaders+            warningMsg     =  "the 'wget' transport currently doesn't support"+                           ++ " range requests, which wastes network bandwidth."+                           ++ " To fix this, set 'http-transport' to 'curl' or"+                           ++ " 'plain-http' in '~/.cabal/config'."+                           ++ " Note that the 'plain-http' transport doesn't"+                           ++ " support HTTPS.\n"++        when (hasRangeHeader) $ warn verbosity warningMsg+        (code, etag') <- parseOutput verbosity uri resp         return (code, etag')       where         args = [ "--output-document=" ++ destPath@@ -429,14 +443,22 @@                [ ["--header", "If-None-Match: " ++ t]                | t <- maybeToList etag ]             ++ [ "--header=" ++ show name ++ ": " ++ value-               | Header name value <- reqHeaders ]+               | hdr@(Header name value) <- reqHeaders+               , (not (isRangeHeader hdr)) ] +        -- wget doesn't support range requests.+        -- so, we ignore range request headers, lest we get errors.+        isRangeHeader :: Header -> Bool+        isRangeHeader (Header HdrRange _) = True+        isRangeHeader _ = False+     posthttp = noPostYet      posthttpfile verbosity  uri path auth =         withTempFile (takeDirectory path)                      (takeFileName path) $ \tmpFile tmpHandle ->-        withTempFile (takeDirectory path) "response" $ \responseFile responseHandle -> do+        withTempFile (takeDirectory path) "response" $+        \responseFile responseHandle -> do           hClose responseHandle           (body, boundary) <- generateMultipartBody path           BS.hPut tmpHandle body@@ -449,12 +471,14 @@                      , "--header=Content-type: multipart/form-data; " ++                                               "boundary=" ++ boundary ]           out <- runWGet verbosity (addUriAuth auth uri) args-          (code, _etag) <- parseOutput uri out-          resp <- readFile responseFile-          return (code, resp)+          (code, _etag) <- parseOutput verbosity uri out+          withFile responseFile ReadMode $ \hnd -> do+            resp <- hGetContents hnd+            evaluate $ force (code, resp)      puthttpfile verbosity uri path auth headers =-        withTempFile (takeDirectory path) "response" $ \responseFile responseHandle -> do+        withTempFile (takeDirectory path) "response" $+        \responseFile responseHandle -> do             hClose responseHandle             let args = [ "--method=PUT", "--body-file="++path                        , "--user-agent=" ++ userAgent@@ -465,9 +489,10 @@                        | Header name value <- headers ]              out <- runWGet verbosity (addUriAuth auth uri) args-            (code, _etag) <- parseOutput uri out-            resp <- readFile responseFile-            return (code, resp)+            (code, _etag) <- parseOutput verbosity uri out+            withFile responseFile ReadMode $ \hnd -> do+              resp <- hGetContents hnd+              evaluate $ force (code, resp)      addUriAuth Nothing uri = uri     addUriAuth (Just (user, pass)) uri = uri@@ -490,14 +515,14 @@         -- wget returns exit code 8 for server "errors" like "304 not modified"         if exitCode == ExitSuccess || exitCode == ExitFailure 8           then return resp-          else die $ "'" ++ programPath prog+          else die' verbosity $ "'" ++ programPath prog                   ++ "' exited with an error:\n" ++ resp      -- With the --server-response flag, wget produces output with the full     -- http server response with all headers, we want to find a line like     -- "HTTP/1.1 200 OK", but only the last one, since we can have multiple     -- requests due to redirects.-    parseOutput uri resp =+    parseOutput verbosity uri resp =       let parsedCode = listToMaybe                      [ code                      | (protocol:codestr:_err) <- map words (reverse (lines resp))@@ -509,7 +534,7 @@                     | ["ETag:", etag] <- map words (reverse (lines resp)) ]        in case parsedCode of             Just i -> return (i, mb_etag)-            _      -> statusParseFail uri resp+            _      -> statusParseFail verbosity uri resp   powershellTransport :: ConfiguredProgram -> HttpTransport@@ -529,7 +554,7 @@       where         parseResponse x = case readMaybe . unlines . take 1 . lines $ trim x of           Just i  -> return (i, Nothing) -- TODO extract real etag-          Nothing -> statusParseFail uri x+          Nothing -> statusParseFail verbosity uri x         etagHeader = [ Header HdrIfNoneMatch t | t <- maybeToList etag ]      posthttp = noPostYet@@ -547,14 +572,14 @@         resp <- runPowershellScript verbosity $ webclientScript           (setupHeaders (contentHeader : extraHeaders) ++ setupAuth auth)           (uploadFileAction "POST" uri fullPath)-        parseUploadResponse uri resp+        parseUploadResponse verbosity uri resp      puthttpfile verbosity uri path auth headers = do       fullPath <- canonicalizePath path       resp <- runPowershellScript verbosity $ webclientScript         (setupHeaders (extraHeaders ++ headers) ++ setupAuth auth)         (uploadFileAction "PUT" uri fullPath)-      parseUploadResponse uri resp+      parseUploadResponse verbosity uri resp      runPowershellScript verbosity script = do       let args =@@ -593,10 +618,10 @@       , "Write-Host (-join [System.Text.Encoding]::UTF8.GetChars($bodyBytes));"       ] -    parseUploadResponse uri resp = case lines (trim resp) of+    parseUploadResponse verbosity uri resp = case lines (trim resp) of       (codeStr : message)         | Just code <- readMaybe codeStr -> return (code, unlines message)-      _ -> statusParseFail uri resp+      _ -> statusParseFail verbosity uri resp      webclientScript setup action = unlines       [ "$wc = new-object system.net.webclient;"@@ -641,6 +666,7 @@       (_, resp) <- cabalBrowse verbosity Nothing (request req)       let code  = convertRspCode (rspCode resp)           etag' = lookupHeader HdrETag (rspHeaders resp)+      -- 206 Partial Content is a normal response to a range request; see #3385.       when (code==200 || code==206) $         writeFileAtomic destPath $ rspBody resp       return (code, etag')@@ -689,7 +715,7 @@       p <- fixupEmptyProxy <$> fetchProxy True       Exception.handleJust         (guard . isDoesNotExistError)-        (const . die $ "Couldn't establish HTTP connection. "+        (const . die' verbosity $ "Couldn't establish HTTP connection. "                     ++ "Possible cause: HTTP proxy server is down.") $         browse $ do           setProxy p@@ -713,9 +739,9 @@                    , " (", display buildOS, "; ", display buildArch, ")"                    ] -statusParseFail :: URI -> String -> IO a-statusParseFail uri r =-    die $ "Failed to download " ++ show uri ++ " : "+statusParseFail :: Verbosity -> URI -> String -> IO a+statusParseFail verbosity uri r =+    die' verbosity $ "Failed to download " ++ show uri ++ " : "        ++ "No Status Code could be parsed from response: " ++ r  -- Trim
Distribution/Client/IndexUtils.hs view
@@ -1,7 +1,9 @@ {-# LANGUAGE CPP #-}+{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE BangPatterns #-} {-# LANGUAGE GADTs #-}+ ----------------------------------------------------------------------------- -- | -- Module      :  Distribution.Client.IndexUtils@@ -21,62 +23,74 @@   getSourcePackages,   getSourcePackagesMonitorFiles, +  IndexState(..),+  getSourcePackagesAtIndexState,+   Index(..),   PackageEntry(..),   parsePackageIndex,   updateRepoIndexCache,   updatePackageIndexCacheFile,-  readCacheStrict,+  readCacheStrict, -- only used by soon-to-be-obsolete sandbox code    BuildTreeRefType(..), refTypeFromTypeCode, typeCodeFromRefType   ) where +import Prelude ()+import Distribution.Client.Compat.Prelude+ import qualified Codec.Archive.Tar       as Tar import qualified Codec.Archive.Tar.Entry as Tar import qualified Codec.Archive.Tar.Index as Tar import qualified Distribution.Client.Tar as Tar+import Distribution.Client.IndexUtils.Timestamp import Distribution.Client.Types+import Distribution.Verbosity  import Distribution.Package-         ( PackageId, PackageIdentifier(..), PackageName(..)-         , Package(..), packageVersion, packageName-         , Dependency(Dependency) )-import Distribution.Client.PackageIndex (PackageIndex)-import qualified Distribution.Client.PackageIndex      as PackageIndex+         ( PackageId, PackageIdentifier(..), mkPackageName+         , Package(..), packageVersion, packageName )+import Distribution.Types.Dependency import Distribution.Simple.PackageIndex (InstalledPackageIndex)-import qualified Distribution.PackageDescription.Parse as PackageDesc.Parse import Distribution.PackageDescription          ( GenericPackageDescription )-import Distribution.PackageDescription.Parse-         ( parsePackageDescription ) import Distribution.Simple.Compiler          ( Compiler, PackageDBStack ) import Distribution.Simple.Program-         ( ProgramConfiguration )+         ( ProgramDb ) import qualified Distribution.Simple.Configure as Configure          ( getInstalledPackages, getInstalledPackagesMonitorFiles )-import Distribution.ParseUtils-         ( ParseResult(..) ) import Distribution.Version-         ( Version(Version), intersectVersionRanges )+         ( mkVersion, intersectVersionRanges ) import Distribution.Text          ( display, simpleParse )-import Distribution.Verbosity-         ( Verbosity, normal, lessVerbose ) import Distribution.Simple.Utils-         ( die, warn, info, fromUTF8, ignoreBOM )+         ( die', warn, info ) import Distribution.Client.Setup          ( RepoContext(..) ) -import Data.Char   (isAlphaNum)-import Data.Maybe  (mapMaybe, catMaybes, maybeToList)-import Data.List   (isPrefixOf)-#if !MIN_VERSION_base(4,8,0)-import Data.Monoid (Monoid(..))+#ifdef CABAL_PARSEC+import Distribution.PackageDescription.Parsec+         ( parseGenericPackageDescriptionMaybe )+import qualified Distribution.PackageDescription.Parsec as PackageDesc.Parse+#else+import Distribution.ParseUtils+         ( ParseResult(..) )+import Distribution.PackageDescription.Parse+         ( parseGenericPackageDescription )+import Distribution.Simple.Utils+         ( fromUTF8, ignoreBOM )+import qualified Distribution.PackageDescription.Parse as PackageDesc.Parse #endif++import           Distribution.Solver.Types.PackageIndex (PackageIndex)+import qualified Distribution.Solver.Types.PackageIndex as PackageIndex+import           Distribution.Solver.Types.SourcePackage+ import qualified Data.Map as Map-import Control.Monad (when, liftM)-import Control.Exception (evaluate)+import Control.DeepSeq+import Control.Monad+import Control.Exception import qualified Data.ByteString.Lazy as BS import qualified Data.ByteString.Lazy.Char8 as BS.Char8 import qualified Data.ByteString.Char8 as BSS@@ -84,8 +98,9 @@ import Distribution.Client.GZipUtils (maybeDecompress) import Distribution.Client.Utils ( byteStringToFilePath                                  , tryFindAddSourcePackageDesc )+import Distribution.Compat.Binary import Distribution.Compat.Exception (catchIO)-import Distribution.Client.Compat.Time (getFileAge, getModTime)+import Distribution.Compat.Time (getFileAge, getModTime) import System.Directory (doesFileExist, doesDirectoryExist) import System.FilePath          ( (</>), (<.>), takeExtension, replaceExtension, splitDirectories, normalise )@@ -100,10 +115,10 @@  -- | Reduced-verbosity version of 'Configure.getInstalledPackages' getInstalledPackages :: Verbosity -> Compiler-                     -> PackageDBStack -> ProgramConfiguration+                     -> PackageDBStack -> ProgramDb                      -> IO InstalledPackageIndex-getInstalledPackages verbosity comp packageDbs conf =-    Configure.getInstalledPackages verbosity' comp packageDbs conf+getInstalledPackages verbosity comp packageDbs progdb =+    Configure.getInstalledPackages verbosity' comp packageDbs progdb   where     verbosity'  = lessVerbose verbosity @@ -128,6 +143,48 @@ -- Reading the source package index -- +-- Note: 'data IndexState' is defined in+-- "Distribution.Client.IndexUtils.Timestamp" to avoid import cycles++-- | 'IndexStateInfo' contains meta-information about the resulting+-- filtered 'Cache' 'after applying 'filterCache' according to a+-- requested 'IndexState'.+data IndexStateInfo = IndexStateInfo+    { isiMaxTime  :: !Timestamp+    -- ^ 'Timestamp' of maximum/latest 'Timestamp' in the current+    -- filtered view of the cache.+    --+    -- The following property holds+    --+    -- > filterCache (IndexState (isiMaxTime isi)) cache == (cache, isi)+    --++    , isiHeadTime :: !Timestamp+    -- ^ 'Timestamp' equivalent to 'IndexStateHead', i.e. the latest+    -- known 'Timestamp'; 'isiHeadTime' is always greater or equal to+    -- 'isiMaxTime'.+    }++emptyStateInfo :: IndexStateInfo+emptyStateInfo = IndexStateInfo nullTimestamp nullTimestamp++-- | Filters a 'Cache' according to an 'IndexState'+-- specification. Also returns 'IndexStateInfo' describing the+-- resulting index cache.+--+-- Note: 'filterCache' is idempotent in the 'Cache' value+filterCache :: IndexState -> Cache -> (Cache, IndexStateInfo)+filterCache IndexStateHead cache = (cache, IndexStateInfo{..})+  where+    isiMaxTime  = cacheHeadTs cache+    isiHeadTime = cacheHeadTs cache+filterCache (IndexStateTime ts0) cache0 = (cache, IndexStateInfo{..})+  where+    cache = Cache { cacheEntries = ents, cacheHeadTs = isiMaxTime }+    isiHeadTime = cacheHeadTs cache0+    isiMaxTime  = maximumTimestamp (map cacheEntryTimestamp ents)+    ents = filter ((<= ts0) . cacheEntryTimestamp) (cacheEntries cache0)+ -- | Read a repository index from disk, from the local files specified by -- a list of 'Repo's. --@@ -136,16 +193,70 @@ -- -- This is a higher level wrapper used internally in cabal-install. getSourcePackages :: Verbosity -> RepoContext -> IO SourcePackageDb-getSourcePackages verbosity repoCtxt | null (repoContextRepos repoCtxt) = do-  warn verbosity $ "No remote package servers have been specified. Usually "-                ++ "you would have one specified in the config file."-  return SourcePackageDb {-    packageIndex       = mempty,-    packagePreferences = mempty-  }-getSourcePackages verbosity repoCtxt = do-  info verbosity "Reading available packages..."-  pkgss <- mapM (\r -> readRepoIndex verbosity repoCtxt r) (repoContextRepos repoCtxt)+getSourcePackages verbosity repoCtxt =+    getSourcePackagesAtIndexState verbosity repoCtxt IndexStateHead++-- | Variant of 'getSourcePackages' which allows getting the source+-- packages at a particular 'IndexState'.+--+-- Current choices are either the latest (aka HEAD), or the index as+-- it was at a particular time.+--+-- TODO: Enhance to allow specifying per-repo 'IndexState's and also+-- report back per-repo 'IndexStateInfo's (in order for @new-freeze@+-- to access it)+getSourcePackagesAtIndexState :: Verbosity -> RepoContext -> IndexState+                           -> IO SourcePackageDb+getSourcePackagesAtIndexState verbosity repoCtxt _+  | null (repoContextRepos repoCtxt) = do+      -- In the test suite, we routinely don't have any remote package+      -- servers, so don't bleat about it+      warn (verboseUnmarkOutput verbosity) $+        "No remote package servers have been specified. Usually " +++        "you would have one specified in the config file."+      return SourcePackageDb {+        packageIndex       = mempty,+        packagePreferences = mempty+      }+getSourcePackagesAtIndexState verbosity repoCtxt idxState = do+  case idxState of+      IndexStateHead      -> info verbosity "Reading available packages..."+      IndexStateTime time ->+          info verbosity ("Reading available packages (for index-state as of "+                          ++ display time ++ ")...")++  pkgss <- forM (repoContextRepos repoCtxt) $ \r -> do+      let rname = maybe "" remoteRepoName $ maybeRepoRemote r+      unless (idxState == IndexStateHead) $+          case r of+            RepoLocal path -> warn verbosity ("index-state ignored for old-format repositories (local repository '" ++ path ++ "')")+            RepoRemote {} -> warn verbosity ("index-state ignored for old-format (remote repository '" ++ rname ++ "')")+            RepoSecure {} -> pure ()+++      let idxState' = case r of+            RepoSecure {} -> idxState+            _             -> IndexStateHead++      (pis,deps,isi) <- readRepoIndex verbosity repoCtxt r idxState'++      case idxState' of+        IndexStateHead -> do+            info verbosity ("index-state("++rname++") = " +++                              display (isiHeadTime isi))+            return ()+        IndexStateTime ts0 -> do+            when (isiMaxTime isi /= ts0) $+                warn verbosity ("Requested index-state " ++ display ts0+                                ++ " does not exist in '"++rname++"'!"+                                ++ " Falling back to older state ("+                                ++ display (isiMaxTime isi) ++ ").")+            info verbosity ("index-state("++rname++") = " +++                              display (isiMaxTime isi) ++ " (HEAD = " +++                              display (isiHeadTime isi) ++ ")")++      pure (pis,deps)+   let (pkgs, prefs) = mconcat pkgss       prefs' = Map.fromListWith intersectVersionRanges                  [ (name, range) | Dependency name range <- prefs ]@@ -159,9 +270,9 @@ readCacheStrict :: Verbosity -> Index -> (PackageEntry -> pkg) -> IO ([pkg], [Dependency]) readCacheStrict verbosity index mkPkg = do     updateRepoIndexCache verbosity index-    cache <- liftM readIndexCache $ BSS.readFile (cacheFile index)+    cache <- readIndexCache verbosity index     withFile (indexFile index) ReadMode $ \indexHnd ->-      packageListFromCache mkPkg indexHnd cache ReadPackageIndexStrict+      packageListFromCache verbosity mkPkg indexHnd cache ReadPackageIndexStrict  -- | Read a repository index from disk, from the local file specified by -- the 'Repo'.@@ -170,13 +281,15 @@ -- -- This is a higher level wrapper used internally in cabal-install. ---readRepoIndex :: Verbosity -> RepoContext -> Repo-              -> IO (PackageIndex SourcePackage, [Dependency])-readRepoIndex verbosity repoCtxt repo =+readRepoIndex :: Verbosity -> RepoContext -> Repo -> IndexState+              -> IO (PackageIndex UnresolvedSourcePackage, [Dependency], IndexStateInfo)+readRepoIndex verbosity repoCtxt repo idxState =   handleNotFound $ do     warnIfIndexIsOld =<< getIndexFileAge repo     updateRepoIndexCache verbosity (RepoIndex repoCtxt repo)-    readPackageIndexCacheFile mkAvailablePackage (RepoIndex repoCtxt repo)+    readPackageIndexCacheFile verbosity mkAvailablePackage+                              (RepoIndex repoCtxt repo)+                              idxState    where     mkAvailablePackage pkgEntry =@@ -201,7 +314,7 @@           RepoLocal{..}  -> warn verbosity $                "The package list for the local repo '" ++ repoLocalDir             ++ "' is missing. The repo is invalid."-        return mempty+        return (mempty,mempty,emptyStateInfo)       else ioError e      isOldThreshold = 15 --days@@ -260,8 +373,10 @@  -- | A build tree reference is either a link or a snapshot. data BuildTreeRefType = SnapshotRef | LinkRef-                      deriving Eq+                      deriving (Eq,Generic) +instance Binary BuildTreeRefType+ refTypeFromTypeCode :: Tar.TypeCode -> BuildTreeRefType refTypeFromTypeCode t   | t == Tar.buildTreeRefTypeCode      = LinkRef@@ -295,14 +410,14 @@ -- function over this to translate it to a list of IO actions returning -- 'PackageOrDep's. We can use 'lazySequence' to turn this into a list of -- 'PackageOrDep's, still maintaining the lazy nature of the original tar read.-parsePackageIndex :: ByteString -> [IO (Maybe PackageOrDep)]-parsePackageIndex = concatMap (uncurry extract) . tarEntriesList . Tar.read+parsePackageIndex :: Verbosity -> ByteString -> [IO (Maybe PackageOrDep)]+parsePackageIndex verbosity = concatMap (uncurry extract) . tarEntriesList . Tar.read   where     extract :: BlockNo -> Tar.Entry -> [IO (Maybe PackageOrDep)]     extract blockNo entry = tryExtractPkg ++ tryExtractPrefs       where         tryExtractPkg = do-          mkPkgEntry <- maybeToList $ extractPkg entry blockNo+          mkPkgEntry <- maybeToList $ extractPkg verbosity entry blockNo           return $ fmap (fmap Pkg) mkPkgEntry          tryExtractPrefs = do@@ -321,21 +436,29 @@     go !_ (Tar.Fail e)     = error ("tarEntriesList: " ++ show e)     go !n (Tar.Next e es') = (n, e) : go (Tar.nextEntryOffset e n) es' -extractPkg :: Tar.Entry -> BlockNo -> Maybe (IO (Maybe PackageEntry))-extractPkg entry blockNo = case Tar.entryContent entry of+extractPkg :: Verbosity -> Tar.Entry -> BlockNo -> Maybe (IO (Maybe PackageEntry))+extractPkg verbosity entry blockNo = case Tar.entryContent entry of   Tar.NormalFile content _      | takeExtension fileName == ".cabal"     -> case splitDirectories (normalise fileName) of         [pkgname,vers,_] -> case simpleParse vers of           Just ver -> Just . return $ Just (NormalPackage pkgid descr content blockNo)             where-              pkgid  = PackageIdentifier (PackageName pkgname) ver-              parsed = parsePackageDescription . ignoreBOM . fromUTF8 . BS.Char8.unpack+              pkgid  = PackageIdentifier (mkPackageName pkgname) ver+#ifdef CABAL_PARSEC+              parsed = parseGenericPackageDescriptionMaybe (BS.toStrict content)+              descr = case parsed of+                  Just d  -> d+                  Nothing -> error $ "Couldn't read cabal file "+                                    ++ show fileName+#else+              parsed = parseGenericPackageDescription . ignoreBOM . fromUTF8 . BS.Char8.unpack                                                $ content               descr  = case parsed of                 ParseOk _ d -> d                 _           -> error $ "Couldn't read cabal file "                                     ++ show fileName+#endif           _ -> Nothing         _ -> Nothing @@ -346,8 +469,8 @@         dirExists <- doesDirectoryExist path         result <- if not dirExists then return Nothing                   else do-                    cabalFile <- tryFindAddSourcePackageDesc path "Error reading package index."-                    descr     <- PackageDesc.Parse.readPackageDescription normal cabalFile+                    cabalFile <- tryFindAddSourcePackageDesc verbosity path "Error reading package index."+                    descr     <- PackageDesc.Parse.readGenericPackageDescription normal cabalFile                     return . Just $ BuildTreeRef (refTypeFromTypeCode typeCode) (packageId descr)                                                  descr path blockNo         return result@@ -390,6 +513,19 @@                    xs' <- lazySequence xs                    return (x' : xs') +-- | A lazy unfolder for lookup operations which return the current+-- value and (possibly) the next key+lazyUnfold :: (k -> IO (v, Maybe k)) -> k -> IO [(k,v)]+lazyUnfold step = goLazy . Just+  where+    goLazy s = unsafeInterleaveIO (go s)++    go Nothing  = return []+    go (Just k) = do+        (v, mk') <- step k+        vs' <- goLazy mk'+        return ((k,v):vs')+ -- | Which index do we mean? data Index =     -- | The main index for the specified repository@@ -407,12 +543,26 @@ cacheFile (RepoIndex _ctxt repo) = indexBaseName repo <.> "cache" cacheFile (SandboxIndex index)   = index `replaceExtension` "cache" +-- | Return 'True' if 'Index' uses 01-index format (aka secure repo)+is01Index :: Index -> Bool+is01Index (RepoIndex _ repo) = case repo of+                                 RepoSecure {} -> True+                                 RepoRemote {} -> False+                                 RepoLocal  {} -> False+is01Index (SandboxIndex _)   = False++ updatePackageIndexCacheFile :: Verbosity -> Index -> IO () updatePackageIndexCacheFile verbosity index = do-    info verbosity ("Updating index cache file " ++ cacheFile index)-    withIndexEntries index $ \entries -> do-      let cache = Cache { cacheEntries = entries }-      writeFile (cacheFile index) (showIndexCache cache)+    info verbosity ("Updating index cache file " ++ cacheFile index ++ " ...")+    withIndexEntries verbosity index $ \entries -> do+      let !maxTs = maximumTimestamp (map cacheEntryTimestamp entries)+          cache = Cache { cacheHeadTs  = maxTs+                        , cacheEntries = entries+                        }+      writeIndexCache index cache+      info verbosity ("Index cache updated to index-state "+                      ++ display (cacheHeadTs cache))  -- | Read the index (for the purpose of building a cache) --@@ -434,73 +584,94 @@ -- TODO: It would be nicer if we actually incrementally updated @cabal@'s -- cache, rather than reconstruct it from zero on each update. However, this -- would require a change in the cache format.-withIndexEntries :: Index -> ([IndexCacheEntry] -> IO a) -> IO a-withIndexEntries (RepoIndex repoCtxt repo@RepoSecure{..}) callback =+withIndexEntries :: Verbosity -> Index -> ([IndexCacheEntry] -> IO a) -> IO a+withIndexEntries _ (RepoIndex repoCtxt repo@RepoSecure{..}) callback =     repoContextWithSecureRepo repoCtxt repo $ \repoSecure ->       Sec.withIndex repoSecure $ \Sec.IndexCallbacks{..} -> do-        let mk :: (Sec.DirectoryEntry, fp, Maybe (Sec.Some Sec.IndexFile))-               -> IO [IndexCacheEntry]-            mk (_, _fp, Nothing) =-              return [] -- skip unrecognized file-            mk (_, _fp, Just (Sec.Some (Sec.IndexPkgMetadata _pkgId))) =-              return [] -- skip metadata-            mk (dirEntry, _fp, Just (Sec.Some (Sec.IndexPkgCabal pkgId))) = do-              let blockNo = fromIntegral (Sec.directoryEntryBlockNo dirEntry)-              return [CachePackageId pkgId blockNo]-            mk (dirEntry, _fp, Just (Sec.Some file@(Sec.IndexPkgPrefs _pkgName))) = do-              content <- Sec.indexEntryContent `fmap` indexLookupFileEntry dirEntry file-              return $ map CachePreference (parsePreferredVersions content)-        entriess <- lazySequence $ map mk (Sec.directoryEntries indexDirectory)-        callback $ concat entriess-withIndexEntries index callback = do+        -- Incrementally (lazily) read all the entries in the tar file in order,+        -- including all revisions, not just the last revision of each file+        indexEntries <- lazyUnfold indexLookupEntry (Sec.directoryFirst indexDirectory)+        callback [ cacheEntry+                 | (dirEntry, indexEntry) <- indexEntries+                 , cacheEntry <- toCacheEntries dirEntry indexEntry ]+  where+    toCacheEntries :: Sec.DirectoryEntry -> Sec.Some Sec.IndexEntry+                   -> [IndexCacheEntry]+    toCacheEntries dirEntry (Sec.Some sie) =+        case Sec.indexEntryPathParsed sie of+          Nothing                            -> [] -- skip unrecognized file+          Just (Sec.IndexPkgMetadata _pkgId) -> [] -- skip metadata+          Just (Sec.IndexPkgCabal pkgId)     -> force+              [CachePackageId pkgId blockNo timestamp]+          Just (Sec.IndexPkgPrefs _pkgName)  -> force+              [ CachePreference dep blockNo timestamp+              | dep <- parsePreferredVersions (Sec.indexEntryContent sie)+              ]+      where+        blockNo = Sec.directoryEntryBlockNo dirEntry+        timestamp = fromMaybe (error "withIndexEntries: invalid timestamp") $+                              epochTimeToTimestamp $ Sec.indexEntryTime sie++withIndexEntries verbosity index callback = do -- non-secure repositories     withFile (indexFile index) ReadMode $ \h -> do       bs          <- maybeDecompress `fmap` BS.hGetContents h-      pkgsOrPrefs <- lazySequence $ parsePackageIndex bs+      pkgsOrPrefs <- lazySequence $ parsePackageIndex verbosity bs       callback $ map toCache (catMaybes pkgsOrPrefs)   where     toCache :: PackageOrDep -> IndexCacheEntry-    toCache (Pkg (NormalPackage pkgid _ _ blockNo)) = CachePackageId pkgid blockNo+    toCache (Pkg (NormalPackage pkgid _ _ blockNo)) = CachePackageId pkgid blockNo nullTimestamp     toCache (Pkg (BuildTreeRef refType _ _ _ blockNo)) = CacheBuildTreeRef refType blockNo-    toCache (Dep d) = CachePreference d+    toCache (Dep d) = CachePreference d 0 nullTimestamp  data ReadPackageIndexMode = ReadPackageIndexStrict                           | ReadPackageIndexLazyIO  readPackageIndexCacheFile :: Package pkg-                          => (PackageEntry -> pkg)+                          => Verbosity+                          -> (PackageEntry -> pkg)                           -> Index-                          -> IO (PackageIndex pkg, [Dependency])-readPackageIndexCacheFile mkPkg index = do-  cache    <- liftM readIndexCache $ BSS.readFile (cacheFile index)-  indexHnd <- openFile (indexFile index) ReadMode-  packageIndexFromCache mkPkg indexHnd cache ReadPackageIndexLazyIO+                          -> IndexState+                          -> IO (PackageIndex pkg, [Dependency], IndexStateInfo)+readPackageIndexCacheFile verbosity mkPkg index idxState = do+    cache0    <- readIndexCache verbosity index+    indexHnd <- openFile (indexFile index) ReadMode+    let (cache,isi) = filterCache idxState cache0+    (pkgs,deps) <- packageIndexFromCache verbosity mkPkg indexHnd cache ReadPackageIndexLazyIO+    pure (pkgs,deps,isi) + packageIndexFromCache :: Package pkg-                      => (PackageEntry -> pkg)+                      => Verbosity+                     -> (PackageEntry -> pkg)                       -> Handle                       -> Cache                       -> ReadPackageIndexMode                       -> IO (PackageIndex pkg, [Dependency])-packageIndexFromCache mkPkg hnd cache mode = do-     (pkgs, prefs) <- packageListFromCache mkPkg hnd cache mode+packageIndexFromCache verbosity mkPkg hnd cache mode = do+     (pkgs, prefs) <- packageListFromCache verbosity mkPkg hnd cache mode      pkgIndex <- evaluate $ PackageIndex.fromList pkgs      return (pkgIndex, prefs)  -- | Read package list ----- The result packages (though not the preferences) are guaranteed to be listed--- in the same order as they are in the tar file (because later entries in a tar--- file mask earlier ones).-packageListFromCache :: (PackageEntry -> pkg)+-- The result package releases and preference entries are guaranteed+-- to be unique.+--+-- Note: 01-index.tar is an append-only index and therefore contains+-- all .cabal edits and preference-updates. The masking happens+-- here, i.e. the semantics that later entries in a tar file mask+-- earlier ones is resolved in this function.+packageListFromCache :: Verbosity+                     -> (PackageEntry -> pkg)                      -> Handle                      -> Cache                      -> ReadPackageIndexMode                      -> IO ([pkg], [Dependency])-packageListFromCache mkPkg hnd Cache{..} mode = accum mempty [] cacheEntries+packageListFromCache verbosity mkPkg hnd Cache{..} mode = accum mempty [] mempty cacheEntries   where-    accum srcpkgs prefs [] = return (reverse srcpkgs, prefs)+    accum !srcpkgs btrs !prefs [] = return (Map.elems srcpkgs ++ btrs, Map.elems prefs) -    accum srcpkgs prefs (CachePackageId pkgid blockno : entries) = do+    accum srcpkgs btrs prefs (CachePackageId pkgid blockno _ : entries) = do       -- Given the cache entry, make a package index entry.       -- The magic here is that we use lazy IO to read the .cabal file       -- from the index tarball if it turns out that we need it.@@ -509,27 +680,25 @@         pkgtxt <- getEntryContent blockno         pkg    <- readPackageDescription pkgtxt         return (pkg, pkgtxt)-      let srcpkg = case mode of-            ReadPackageIndexLazyIO ->-              mkPkg (NormalPackage pkgid pkg pkgtxt blockno)-            ReadPackageIndexStrict ->-              pkg `seq` pkgtxt `seq` mkPkg (NormalPackage pkgid pkg-                                            pkgtxt blockno)-      accum (srcpkg:srcpkgs) prefs entries+      case mode of+        ReadPackageIndexLazyIO -> pure ()+        ReadPackageIndexStrict -> evaluate pkg *> evaluate pkgtxt *> pure ()+      let srcpkg = mkPkg (NormalPackage pkgid pkg pkgtxt blockno)+      accum (Map.insert pkgid srcpkg srcpkgs) btrs prefs entries -    accum srcpkgs prefs (CacheBuildTreeRef refType blockno : entries) = do+    accum srcpkgs btrs prefs (CacheBuildTreeRef refType blockno : entries) = do       -- We have to read the .cabal file eagerly here because we can't cache the       -- package id for build tree references - the user might edit the .cabal       -- file after the reference was added to the index.       path <- liftM byteStringToFilePath . getEntryContent $ blockno       pkg  <- do let err = "Error reading package index from cache."-                 file <- tryFindAddSourcePackageDesc path err-                 PackageDesc.Parse.readPackageDescription normal file+                 file <- tryFindAddSourcePackageDesc verbosity path err+                 PackageDesc.Parse.readGenericPackageDescription normal file       let srcpkg = mkPkg (BuildTreeRef refType (packageId pkg) pkg path blockno)-      accum (srcpkg:srcpkgs) prefs entries+      accum srcpkgs (srcpkg:btrs) prefs entries -    accum srcpkgs prefs (CachePreference pref : entries) =-      accum srcpkgs (pref:prefs) entries+    accum srcpkgs btrs prefs (CachePreference pref@(Dependency pn _) _ _ : entries) =+      accum srcpkgs btrs (Map.insert pn pref prefs) entries      getEntryContent :: BlockNo -> IO ByteString     getEntryContent blockno = do@@ -543,11 +712,18 @@      readPackageDescription :: ByteString -> IO GenericPackageDescription     readPackageDescription content =-      case parsePackageDescription . ignoreBOM . fromUTF8 . BS.Char8.unpack $ content of+#ifdef CABAL_PARSEC+      case parseGenericPackageDescriptionMaybe (BS.toStrict content) of+        Just gpd -> return gpd+        Nothing  -> interror "failed to parse .cabal file"+#else+      case parseGenericPackageDescription . ignoreBOM . fromUTF8 . BS.Char8.unpack $ content of         ParseOk _ d -> return d         _           -> interror "failed to parse .cabal file"+#endif -    interror msg = die $ "internal error when reading package index: " ++ msg+    interror :: String -> IO a+    interror msg = die' verbosity $ "internal error when reading package index: " ++ msg                       ++ "The package index or index cache is probably "                       ++ "corrupt. Running cabal update might fix it." @@ -555,31 +731,156 @@ -- Index cache data structure -- +-- | Read the 'Index' cache from the filesystem+--+-- If a corrupted index cache is detected this function regenerates+-- the index cache and then reattempt to read the index once (and+-- 'die's if it fails again).+readIndexCache :: Verbosity -> Index -> IO Cache+readIndexCache verbosity index = do+    cacheOrFail <- readIndexCache' index+    case cacheOrFail of+      Left msg -> do+          warn verbosity $ concat+              [ "Parsing the index cache failed (", msg, "). "+              , "Trying to regenerate the index cache..."+              ]++          updatePackageIndexCacheFile verbosity index++          either (die' verbosity) (return . hashConsCache) =<< readIndexCache' index++      Right res -> return (hashConsCache res)++-- | Read the 'Index' cache from the filesystem without attempting to+-- regenerate on parsing failures.+readIndexCache' :: Index -> IO (Either String Cache)+readIndexCache' index+  | is01Index index = decodeFileOrFail' (cacheFile index)+  | otherwise       = liftM (Right .read00IndexCache) $+                      BSS.readFile (cacheFile index)++-- | Write the 'Index' cache to the filesystem+writeIndexCache :: Index -> Cache -> IO ()+writeIndexCache index cache+  | is01Index index = encodeFile (cacheFile index) cache+  | otherwise       = writeFile (cacheFile index) (show00IndexCache cache)++-- | Optimise sharing of equal values inside 'Cache'+--+-- c.f. https://en.wikipedia.org/wiki/Hash_consing+hashConsCache :: Cache -> Cache+hashConsCache cache0+    = cache0 { cacheEntries = go mempty mempty (cacheEntries cache0) }+  where+    -- TODO/NOTE:+    --+    -- If/when we redo the binary serialisation via e.g. CBOR and we+    -- are able to use incremental decoding, we may want to move the+    -- hash-consing into the incremental deserialisation, or+    -- alterantively even do something like+    -- http://cbor.schmorp.de/value-sharing+    --+    go _ _ [] = []+    -- for now we only optimise only CachePackageIds since those+    -- represent the vast majority+    go !pns !pvs (CachePackageId pid bno ts : rest)+        = CachePackageId pid' bno ts : go pns' pvs' rest+      where+        !pid' = PackageIdentifier pn' pv'+        (!pn',!pns') = mapIntern pn pns+        (!pv',!pvs') = mapIntern pv pvs+        PackageIdentifier pn pv = pid++    go pns pvs (x:xs) = x : go pns pvs xs++    mapIntern :: Ord k => k -> Map.Map k k -> (k,Map.Map k k)+    mapIntern k m = maybe (k,Map.insert k k m) (\k' -> (k',m)) (Map.lookup k m)++-- | Cabal caches various information about the Hackage index+data Cache = Cache+    { cacheHeadTs  :: Timestamp+      -- ^ maximum/latest 'Timestamp' among 'cacheEntries'; unless the+      -- invariant of 'cacheEntries' being in chronological order is+      -- violated, this corresponds to the last (seen) 'Timestamp' in+      -- 'cacheEntries'+    , cacheEntries :: [IndexCacheEntry]+    }++instance NFData Cache where+    rnf = rnf . cacheEntries+ -- | Tar files are block structured with 512 byte blocks. Every header and file -- content starts on a block boundary. ---type BlockNo = Tar.TarEntryOffset+type BlockNo = Word32 -- Tar.TarEntryOffset -data IndexCacheEntry = CachePackageId PackageId BlockNo-                     | CacheBuildTreeRef BuildTreeRefType BlockNo-                     | CachePreference Dependency-  deriving (Eq) -installedUnitId, blocknoKey, buildTreeRefKey, preferredVersionKey :: String-installedUnitId = "pkg:"+data IndexCacheEntry+    = CachePackageId PackageId !BlockNo !Timestamp+    | CachePreference Dependency !BlockNo !Timestamp+    | CacheBuildTreeRef !BuildTreeRefType !BlockNo+      -- NB: CacheBuildTreeRef is irrelevant for 01-index & new-build+  deriving (Eq,Generic)++instance NFData IndexCacheEntry where+    rnf (CachePackageId pkgid _ _) = rnf pkgid+    rnf (CachePreference dep _ _) = rnf dep+    rnf (CacheBuildTreeRef _ _) = ()++cacheEntryTimestamp :: IndexCacheEntry -> Timestamp+cacheEntryTimestamp (CacheBuildTreeRef _ _)  = nullTimestamp+cacheEntryTimestamp (CachePreference _ _ ts) = ts+cacheEntryTimestamp (CachePackageId _ _ ts)  = ts++----------------------------------------------------------------------------+-- new binary 01-index.cache format++instance Binary Cache where+    put (Cache headTs ents) = do+        -- magic / format version+        --+        -- NB: this currently encodes word-size implicitly; when we+        -- switch to CBOR encoding, we will have a platform+        -- independent binary encoding+        put (0xcaba1002::Word)+        put headTs+        put ents++    get = do+        magic <- get+        when (magic /= (0xcaba1002::Word)) $+            fail ("01-index.cache: unexpected magic marker encountered: " ++ show magic)+        Cache <$> get <*> get++instance Binary IndexCacheEntry++----------------------------------------------------------------------------+-- legacy 00-index.cache format++packageKey, blocknoKey, buildTreeRefKey, preferredVersionKey :: String+packageKey = "pkg:" blocknoKey = "b#" buildTreeRefKey     = "build-tree-ref:" preferredVersionKey = "pref-ver:" -readIndexCacheEntry :: BSS.ByteString -> Maybe IndexCacheEntry-readIndexCacheEntry = \line ->+-- legacy 00-index.cache format+read00IndexCache :: BSS.ByteString -> Cache+read00IndexCache bs = Cache+  { cacheHeadTs  = nullTimestamp+  , cacheEntries = mapMaybe read00IndexCacheEntry $ BSS.lines bs+  }++read00IndexCacheEntry :: BSS.ByteString -> Maybe IndexCacheEntry+read00IndexCacheEntry = \line ->   case BSS.words line of     [key, pkgnamestr, pkgverstr, sep, blocknostr]-      | key == BSS.pack installedUnitId && sep == BSS.pack blocknoKey ->+      | key == BSS.pack packageKey && sep == BSS.pack blocknoKey ->       case (parseName pkgnamestr, parseVer pkgverstr [],             parseBlockNo blocknostr) of         (Just pkgname, Just pkgver, Just blockno)-          -> Just (CachePackageId (PackageIdentifier pkgname pkgver) blockno)+          -> Just (CachePackageId (PackageIdentifier pkgname pkgver)+                                  blockno nullTimestamp)         _ -> Nothing     [key, typecodestr, blocknostr] | key == BSS.pack buildTreeRefKey ->       case (parseRefType typecodestr, parseBlockNo blocknostr) of@@ -587,13 +888,15 @@           -> Just (CacheBuildTreeRef refType blockno)         _ -> Nothing -    (key: remainder) | key == BSS.pack preferredVersionKey ->-      fmap CachePreference (simpleParse (BSS.unpack (BSS.unwords remainder)))+    (key: remainder) | key == BSS.pack preferredVersionKey -> do+      pref <- simpleParse (BSS.unpack (BSS.unwords remainder))+      return $ CachePreference pref 0 nullTimestamp+     _  -> Nothing   where     parseName str       | BSS.all (\c -> isAlphaNum c || c == '-') str-                  = Just (PackageName (BSS.unpack str))+                  = Just (mkPackageName (BSS.unpack str))       | otherwise = Nothing      parseVer str vs =@@ -602,7 +905,7 @@         Just (v, str') -> case BSS.uncons str' of           Just ('.', str'') -> parseVer str'' (v:vs)           Just _            -> Nothing-          Nothing           -> Just (Version (reverse (v:vs)) [])+          Nothing           -> Just (mkVersion (reverse (v:vs)))      parseBlockNo str =       case BSS.readInt str of@@ -617,31 +920,22 @@             -> Just (refTypeFromTypeCode typeCode)         _   -> Nothing -showIndexCacheEntry :: IndexCacheEntry -> String-showIndexCacheEntry entry = unwords $ case entry of-   CachePackageId pkgid b -> [ installedUnitId-                             , display (packageName pkgid)-                             , display (packageVersion pkgid)-                             , blocknoKey-                             , show b-                             ]-   CacheBuildTreeRef t b  -> [ buildTreeRefKey-                             , [typeCodeFromRefType t]-                             , show b-                             ]-   CachePreference dep    -> [ preferredVersionKey-                             , display dep-                             ]---- | Cabal caches various information about the Hackage index-data Cache = Cache {-    cacheEntries :: [IndexCacheEntry]-  }--readIndexCache :: BSS.ByteString -> Cache-readIndexCache bs = Cache {-    cacheEntries = mapMaybe readIndexCacheEntry $ BSS.lines bs-  }+-- legacy 00-index.cache format+show00IndexCache :: Cache -> String+show00IndexCache Cache{..} = unlines $ map show00IndexCacheEntry cacheEntries -showIndexCache :: Cache -> String-showIndexCache Cache{..} = unlines $ map showIndexCacheEntry cacheEntries+show00IndexCacheEntry :: IndexCacheEntry -> String+show00IndexCacheEntry entry = unwords $ case entry of+   CachePackageId pkgid b _ -> [ packageKey+                               , display (packageName pkgid)+                               , display (packageVersion pkgid)+                               , blocknoKey+                               , show b+                               ]+   CacheBuildTreeRef tr b   -> [ buildTreeRefKey+                               , [typeCodeFromRefType tr]+                               , show b+                               ]+   CachePreference dep _ _  -> [ preferredVersionKey+                               , display dep+                               ]
+ Distribution/Client/IndexUtils/Timestamp.hs view
@@ -0,0 +1,192 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE RecordWildCards #-}++-----------------------------------------------------------------------------+-- |+-- Module      :  Distribution.Client.IndexUtils.Timestamp+-- Copyright   :  (c) 2016 Herbert Valerio Riedel+-- License     :  BSD3+--+-- Timestamp type used in package indexes++module Distribution.Client.IndexUtils.Timestamp+    ( Timestamp+    , nullTimestamp+    , epochTimeToTimestamp+    , timestampToUTCTime+    , utcTimeToTimestamp+    , maximumTimestamp++    , IndexState(..)+    ) where++import qualified Codec.Archive.Tar.Entry    as Tar+import           Control.DeepSeq+import           Control.Monad+import           Data.Char                  (isDigit)+import           Data.Int                   (Int64)+import           Data.Time                  (UTCTime (..), fromGregorianValid,+                                             makeTimeOfDayValid, showGregorian,+                                             timeOfDayToTime, timeToTimeOfDay)+import           Data.Time.Clock.POSIX      (posixSecondsToUTCTime,+                                             utcTimeToPOSIXSeconds)+import           Distribution.Compat.Binary+import qualified Distribution.Compat.ReadP  as ReadP+import           Distribution.Text+import qualified Text.PrettyPrint           as Disp+import           GHC.Generics (Generic)++-- | UNIX timestamp (expressed in seconds since unix epoch, i.e. 1970).+newtype Timestamp = TS Int64 -- Tar.EpochTime+                  deriving (Eq,Ord,Enum,NFData,Show)++epochTimeToTimestamp :: Tar.EpochTime -> Maybe Timestamp+epochTimeToTimestamp et+  | ts == nullTimestamp  = Nothing+  | otherwise            = Just ts+  where+    ts = TS et++timestampToUTCTime :: Timestamp -> Maybe UTCTime+timestampToUTCTime (TS t)+  | t == minBound  = Nothing+  | otherwise      = Just $ posixSecondsToUTCTime (fromIntegral t)++utcTimeToTimestamp :: UTCTime -> Maybe Timestamp+utcTimeToTimestamp utct+  | minTime <= t, t <= maxTime  = Just (TS (fromIntegral t))+  | otherwise                   = Nothing+  where+    maxTime = toInteger (maxBound :: Int64)+    minTime = toInteger (succ minBound :: Int64)+    t :: Integer+    t = round . utcTimeToPOSIXSeconds $ utct++-- | Compute the maximum 'Timestamp' value+--+-- Returns 'nullTimestamp' for the empty list.  Also note that+-- 'nullTimestamp' compares as smaller to all non-'nullTimestamp'+-- values.+maximumTimestamp :: [Timestamp] -> Timestamp+maximumTimestamp [] = nullTimestamp+maximumTimestamp xs@(_:_) = maximum xs++-- returns 'Nothing' if not representable as 'Timestamp'+posixSecondsToTimestamp :: Integer -> Maybe Timestamp+posixSecondsToTimestamp pt+  | minTs <= pt, pt <= maxTs  = Just (TS (fromInteger pt))+  | otherwise                 = Nothing+  where+    maxTs = toInteger (maxBound :: Int64)+    minTs = toInteger (succ minBound :: Int64)++-- | Pretty-prints 'Timestamp' in ISO8601/RFC3339 format+-- (e.g. @"2017-12-31T23:59:59Z"@)+--+-- Returns empty string for 'nullTimestamp' in order for+--+-- > null (display nullTimestamp) == True+--+-- to hold.+showTimestamp :: Timestamp -> String+showTimestamp ts = case timestampToUTCTime ts of+    Nothing          -> ""+    -- Note: we don't use 'formatTime' here to avoid incurring a+    -- dependency on 'old-locale' for older `time` libs+    Just UTCTime{..} -> showGregorian utctDay ++ ('T':showTOD utctDayTime) ++ "Z"+  where+    showTOD = show . timeToTimeOfDay++instance Binary Timestamp where+    put (TS t) = put t+    get = TS `fmap` get++instance Text Timestamp where+    disp = Disp.text . showTimestamp++    parse = parsePosix ReadP.+++ parseUTC+      where+        -- | Parses unix timestamps, e.g. @"\@1474626019"@+        parsePosix = do+            _ <- ReadP.char '@'+            t <- parseInteger+            maybe ReadP.pfail return $ posixSecondsToTimestamp t++        -- | Parses ISO8601/RFC3339-style UTC timestamps,+        -- e.g. @"2017-12-31T23:59:59Z"@+        --+        -- TODO: support numeric tz offsets; allow to leave off seconds+        parseUTC = do+            -- Note: we don't use 'Data.Time.Format.parseTime' here since+            -- we want more control over the accepted formats.++            ye <- parseYear+            _ <- ReadP.char '-'+            mo   <- parseTwoDigits+            _ <- ReadP.char '-'+            da   <- parseTwoDigits+            _ <- ReadP.char 'T'++            utctDay <- maybe ReadP.pfail return $+                       fromGregorianValid ye mo da++            ho   <- parseTwoDigits+            _ <- ReadP.char ':'+            mi   <- parseTwoDigits+            _ <- ReadP.char ':'+            se   <- parseTwoDigits+            _ <- ReadP.char 'Z'++            utctDayTime <- maybe ReadP.pfail (return . timeOfDayToTime) $+                           makeTimeOfDayValid ho mi (realToFrac (se::Int))++            maybe ReadP.pfail return $ utcTimeToTimestamp (UTCTime{..})++        parseTwoDigits = do+            d1 <- ReadP.satisfy isDigit+            d2 <- ReadP.satisfy isDigit+            return (read [d1,d2])++        -- A year must have at least 4 digits; e.g. "0097" is fine,+        -- while "97" is not c.f. RFC3339 which+        -- deprecates 2-digit years+        parseYear = do+            sign <- ReadP.option ' ' (ReadP.char '-')+            ds <- ReadP.munch1 isDigit+            when (length ds < 4) ReadP.pfail+            return (read (sign:ds))++        parseInteger = do+            sign <- ReadP.option ' ' (ReadP.char '-')+            ds <- ReadP.munch1 isDigit+            return (read (sign:ds) :: Integer)++-- | Special timestamp value to be used when 'timestamp' is+-- missing/unknown/invalid+nullTimestamp :: Timestamp+nullTimestamp = TS minBound++----------------------------------------------------------------------------+-- defined here for now to avoid import cycles++-- | Specification of the state of a specific repo package index+data IndexState = IndexStateHead -- ^ Use all available entries+                | IndexStateTime !Timestamp -- ^ Use all entries that existed at+                                            -- the specified time+                deriving (Eq,Generic,Show)++instance Binary IndexState+instance NFData IndexState++instance Text IndexState where+    disp IndexStateHead = Disp.text "HEAD"+    disp (IndexStateTime ts) = disp ts++    parse = parseHead ReadP.+++ parseTime+      where+        parseHead = do+            _ <- ReadP.string "HEAD"+            return IndexStateHead++        parseTime = IndexStateTime `fmap` parse
Distribution/Client/Init.hs view
@@ -1,4 +1,3 @@-{-# LANGUAGE CPP #-} ----------------------------------------------------------------------------- -- | -- Module      :  Distribution.Client.Init@@ -23,48 +22,40 @@    ) where +import Prelude ()+import Distribution.Client.Compat.Prelude hiding (empty)+ import System.IO   ( hSetBuffering, stdout, BufferMode(..) ) import System.Directory   ( getCurrentDirectory, doesDirectoryExist, doesFileExist, copyFile   , getDirectoryContents, createDirectoryIfMissing ) import System.FilePath-  ( (</>), (<.>), takeBaseName )+  ( (</>), (<.>), takeBaseName, equalFilePath ) import Data.Time   ( getCurrentTime, utcToLocalTime, toGregorian, localDay, getCurrentTimeZone ) -import Data.Char-  ( toUpper ) import Data.List-  ( intercalate, nub, groupBy, (\\) )-import Data.Maybe-  ( fromMaybe, isJust, catMaybes, listToMaybe )+  ( groupBy, (\\) ) import Data.Function   ( on ) import qualified Data.Map as M-#if !MIN_VERSION_base(4,8,0)-import Control.Applicative-  ( (<$>) )-import Data.Traversable-  ( traverse )-#endif import Control.Monad-  ( when, unless, (>=>), join, forM_ )+  ( (>=>), join, forM_, mapM, mapM_ ) import Control.Arrow   ( (&&&), (***) )  import Text.PrettyPrint hiding (mode, cat) -import Data.Version-  ( Version(..) ) import Distribution.Version-  ( orLaterVersion, earlierVersion, intersectVersionRanges, VersionRange )+  ( Version, mkVersion, alterVersion+  , orLaterVersion, earlierVersion, intersectVersionRanges, VersionRange ) import Distribution.Verbosity   ( Verbosity ) import Distribution.ModuleName-  ( ModuleName, fromString )  -- And for the Text instance+  ( ModuleName )  -- And for the Text instance import Distribution.InstalledPackageInfo-  ( InstalledPackageInfo, sourcePackageId, exposed )+  ( InstalledPackageInfo, exposed ) import qualified Distribution.Package as P import Language.Haskell.Extension ( Language(..) ) @@ -89,14 +80,15 @@ import Distribution.Simple.Compiler   ( PackageDBStack, Compiler ) import Distribution.Simple.Program-  ( ProgramConfiguration )+  ( ProgramDb ) import Distribution.Simple.PackageIndex   ( InstalledPackageIndex, moduleNameIndex ) import Distribution.Text   ( display, Text(..) ) -import Distribution.Client.PackageIndex+import Distribution.Solver.Types.PackageIndex   ( elemByPackageName )+ import Distribution.Client.IndexUtils   ( getSourcePackages ) import Distribution.Client.Types@@ -108,12 +100,12 @@           -> PackageDBStack           -> RepoContext           -> Compiler-          -> ProgramConfiguration+          -> ProgramDb           -> InitFlags           -> IO ()-initCabal verbosity packageDBs repoCtxt comp conf initFlags = do+initCabal verbosity packageDBs repoCtxt comp progdb initFlags = do -  installedPkgIndex <- getInstalledPackages verbosity comp packageDBs conf+  installedPkgIndex <- getInstalledPackages verbosity comp packageDBs progdb   sourcePkgDb <- getSourcePackages verbosity repoCtxt    hSetBuffering stdout NoBuffering@@ -202,7 +194,7 @@ --  if possible. getVersion :: InitFlags -> IO InitFlags getVersion flags = do-  let v = Just $ Version [0,1,0,0] []+  let v = Just $ mkVersion [0,1,0,0]   v' <-     return (flagToMaybe $ version flags)         ?>> maybePrompt flags (prompt "Package version" v)         ?>> return v@@ -380,14 +372,21 @@              then Just "src"              else Nothing +-- | Check whether a potential source file is located in one of the+--   source directories.+isSourceFile :: Maybe [FilePath] -> SourceFileEntry -> Bool+isSourceFile Nothing        sf = isSourceFile (Just ["."]) sf+isSourceFile (Just srcDirs) sf = any (equalFilePath (relativeSourcePath sf)) srcDirs+ -- | Get the list of exposed modules and extra tools needed to build them. getModulesBuildToolsAndDeps :: InstalledPackageIndex -> InitFlags -> IO InitFlags getModulesBuildToolsAndDeps pkgIx flags = do   dir <- maybe getCurrentDirectory return . flagToMaybe $ packageDir flags -  -- TODO: really should use guessed source roots.-  sourceFiles <- scanForModules dir+  sourceFiles0 <- scanForModules dir +  let sourceFiles = filter (isSourceFile (sourceDirs flags)) sourceFiles0+   Just mods <-      return (exposedModules flags)            ?>> (return . Just . map moduleName $ sourceFiles) @@ -455,7 +454,7 @@                   message flags "You will need to pick one and manually add it to the Build-depends: field."                   return Nothing   where-    pkgGroups = groupBy ((==) `on` P.pkgName) (map sourcePackageId ps)+    pkgGroups = groupBy ((==) `on` P.pkgName) (map P.packageId ps)      -- Given a list of available versions of the same package, pick a dependency.     toDep :: [P.PackageIdentifier] -> IO P.Dependency@@ -477,11 +476,11 @@ pvpize v = orLaterVersion v'            `intersectVersionRanges`            earlierVersion (incVersion 1 v')-  where v' = (v { versionBranch = take 2 (versionBranch v) })+  where v' = alterVersion (take 2) v  -- | Increment the nth version component (counting from 0). incVersion :: Int -> Version -> Version-incVersion n (Version vlist tags) = Version (incVersion' n vlist) tags+incVersion n = alterVersion (incVersion' n)   where     incVersion' 0 []     = [1]     incVersion' 0 (v:_)  = [v+1]@@ -603,11 +602,6 @@                                   = return . Right $ choices !! (n-1)                    | otherwise    = Left `fmap` promptStr "Please specify" Nothing -readMaybe :: (Read a) => String -> Maybe a-readMaybe s = case reads s of-                [(a,"")] -> Just a-                _        -> Nothing- --------------------------------------------------------------------------- --  File generation  ------------------------------------------------------ ---------------------------------------------------------------------------@@ -625,28 +619,28 @@           Flag BSD3             -> Just $ bsd3 authors year -          Flag (GPL (Just (Version {versionBranch = [2]})))+          Flag (GPL (Just v)) | v == mkVersion [2]             -> Just gplv2 -          Flag (GPL (Just (Version {versionBranch = [3]})))+          Flag (GPL (Just v)) | v == mkVersion [3]             -> Just gplv3 -          Flag (LGPL (Just (Version {versionBranch = [2, 1]})))+          Flag (LGPL (Just v)) | v == mkVersion [2,1]             -> Just lgpl21 -          Flag (LGPL (Just (Version {versionBranch = [3]})))+          Flag (LGPL (Just v)) | v == mkVersion [3]             -> Just lgpl3 -          Flag (AGPL (Just (Version {versionBranch = [3]})))+          Flag (AGPL (Just v)) | v == mkVersion [3]             -> Just agplv3 -          Flag (Apache (Just (Version {versionBranch = [2, 0]})))+          Flag (Apache (Just v)) | v == mkVersion [2,0]             -> Just apache20            Flag MIT             -> Just $ mit authors year -          Flag (MPL (Version {versionBranch = [2, 0]}))+          Flag (MPL v) | v == mkVersion [2,0]             -> Just mpl20            Flag ISC@@ -850,7 +844,7 @@                 (Just "Extra files to be distributed with the package, such as examples or a README.")                 True -       , field  "cabal-version" (Flag $ orLaterVersion (Version [1,10] []))+       , field  "cabal-version" (Flag $ orLaterVersion (mkVersion [1,10]))                 (Just "Constraint on the version of Cabal needed to build this package.")                 False @@ -924,9 +918,9 @@                         (True, _, _)      -> (showComment com $$) . ($$ text "")                         (False, _, _)     -> ($$ text "")                       $-                      comment f <> text s <> colon-                                <> text (replicate (20 - length s) ' ')-                                <> text (fromMaybe "" . flagToMaybe $ f)+                      comment f <<>> text s <<>> colon+                                <<>> text (replicate (20 - length s) ' ')+                                <<>> text (fromMaybe "" . flagToMaybe $ f)    comment NoFlag    = text "-- "    comment (Flag "") = text "-- "    comment _         = text ""
Distribution/Client/Init/Heuristics.hs view
@@ -1,4 +1,3 @@-{-# LANGUAGE CPP #-} ----------------------------------------------------------------------------- -- | -- Module      :  Distribution.Client.Init.Heuristics@@ -20,32 +19,31 @@     guessAuthorNameMail,     knownCategories, ) where++import Prelude ()+import Distribution.Client.Compat.Prelude+ import Distribution.Text         (simpleParse) import Distribution.Simple.Setup (Flag(..), flagToMaybe) import Distribution.ModuleName     ( ModuleName, toFilePath )-import Distribution.Client.PackageIndex-    ( allPackagesByName ) import qualified Distribution.Package as P import qualified Distribution.PackageDescription as PD     ( category, packageDescription )-import Distribution.Simple.Utils-         ( intercalate ) import Distribution.Client.Utils          ( tryCanonicalizePath ) import Language.Haskell.Extension ( Extension ) -import Distribution.Client.Types ( packageDescription, SourcePackageDb(..) )-#if !MIN_VERSION_base(4,8,0)-import Control.Applicative ( pure, (<$>), (<*>) )-import Data.Monoid         ( mempty, mappend, mconcat )-#endif-import Control.Arrow ( first )-import Control.Monad ( liftM )-import Data.Char   ( isAlphaNum, isNumber, isUpper, isLower, isSpace )+import Distribution.Solver.Types.PackageIndex+    ( allPackagesByName )+import Distribution.Solver.Types.SourcePackage+    ( packageDescription )++import Distribution.Client.Types ( SourcePackageDb(..) )+import Control.Monad ( mapM )+import Data.Char   ( isNumber, isLower ) import Data.Either ( partitionEithers )-import Data.List   ( isInfixOf, isPrefixOf, isSuffixOf, sortBy )-import Data.Maybe  ( mapMaybe, catMaybes, maybeToList )+import Data.List   ( isInfixOf ) import Data.Ord    ( comparing ) import qualified Data.Set as Set ( fromList, toList ) import System.Directory ( getCurrentDirectory, getDirectoryContents,@@ -88,7 +86,7 @@  -- | Guess the package name based on the given root directory. guessPackageName :: FilePath -> IO P.PackageName-guessPackageName = liftM (P.PackageName . repair . last . splitDirectories)+guessPackageName = liftM (P.mkPackageName . repair . last . splitDirectories)                  . tryCanonicalizePath   where     -- Treat each span of non-alphanumeric characters as a hyphen. Each@@ -251,7 +249,11 @@ -- Ordered in increasing preference, since Flag-as-monoid is identical to -- Last. authorGuessPure :: AuthorGuessIO -> AuthorGuess-authorGuessPure (AuthorGuessIO env darcsLocalF darcsGlobalF gitLocal gitGlobal)+authorGuessPure (AuthorGuessIO { authorGuessEnv = env+                               , authorGuessLocalDarcs = darcsLocalF+                               , authorGuessGlobalDarcs = darcsGlobalF+                               , authorGuessLocalGit = gitLocal+                               , authorGuessGlobalGit = gitGlobal })     = mconcat         [ emailEnv env         , gitGlobal@@ -275,12 +277,13 @@ type AuthorGuess   = (Flag String, Flag String) type Enviro        = [(String, String)] data GitLoc        = Local | Global-data AuthorGuessIO = AuthorGuessIO-    Enviro         -- ^ Environment lookup table-    (Maybe String) -- ^ Contents of local darcs author info-    (Maybe String) -- ^ Contents of global darcs author info-    AuthorGuess    -- ^ Git config --local-    AuthorGuess    -- ^ Git config --global+data AuthorGuessIO = AuthorGuessIO {+    authorGuessEnv         :: Enviro,         -- ^ Environment lookup table+    authorGuessLocalDarcs  :: (Maybe String), -- ^ Contents of local darcs author info+    authorGuessGlobalDarcs :: (Maybe String), -- ^ Contents of global darcs author info+    authorGuessLocalGit    :: AuthorGuess,   -- ^ Git config --local+    authorGuessGlobalGit   :: AuthorGuess    -- ^ Git config --global+  }  darcsEnv :: Enviro -> AuthorGuess darcsEnv = maybe mempty nameAndMail . lookup "DARCS_EMAIL"
Distribution/Client/Init/Types.hs view
@@ -18,6 +18,7 @@ import Distribution.Simple.Setup   ( Flag(..) ) +import Distribution.Types.Dependency as P import Distribution.Compat.Semigroup import Distribution.Version import Distribution.Verbosity@@ -82,7 +83,7 @@  instance Text PackageType where   disp = Disp.text . show-  parse = Parse.choice $ map (fmap read . Parse.string . show) [Library, Executable]+  parse = Parse.choice $ map (fmap read . Parse.string . show) [Library, Executable] -- TODO: eradicateNoParse  instance Monoid InitFlags where   mempty = gmempty@@ -114,5 +115,5 @@  instance Text Category where   disp  = Disp.text . show-  parse = Parse.choice $ map (fmap read . Parse.string . show) [Codec .. ]+  parse = Parse.choice $ map (fmap read . Parse.string . show) [Codec .. ] -- TODO: eradicateNoParse 
Distribution/Client/Install.hs view
@@ -29,14 +29,13 @@     pruneInstallPlan   ) where -import Data.Foldable-         ( traverse_ )+import Prelude ()+import Distribution.Client.Compat.Prelude+ import Data.List-         ( isPrefixOf, unfoldr, nub, sort, (\\) )+         ( (\\) ) import qualified Data.Map as Map import qualified Data.Set as S-import Data.Maybe-         ( catMaybes, isJust, isNothing, fromMaybe, mapMaybe ) import Control.Exception as Exception          ( Exception(toException), bracket, catches          , Handler(Handler), handleJust, IOException, SomeException )@@ -48,17 +47,12 @@          ( ExitCode(..) ) import Distribution.Compat.Exception          ( catchIO, catchExit )-#if !MIN_VERSION_base(4,8,0)-import Control.Applicative-         ( (<$>) )-import Data.Traversable-         ( traverse )-#endif import Control.Monad-         ( filterM, forM_, when, unless )+         ( forM_, mapM ) import System.Directory          ( getTemporaryDirectory, doesDirectoryExist, doesFileExist,-           createDirectoryIfMissing, removeFile, renameDirectory )+           createDirectoryIfMissing, removeFile, renameDirectory,+           getDirectoryContents ) import System.FilePath          ( (</>), (<.>), equalFilePath, takeDirectory ) import System.IO@@ -71,15 +65,18 @@          ( chooseCabalVersion, configureSetupScript, checkConfigExFlags ) import Distribution.Client.Dependency import Distribution.Client.Dependency.Types-         ( Solver(..), ConstraintSource(..), LabeledPackageConstraint(..) )+         ( Solver(..) ) import Distribution.Client.FetchUtils import Distribution.Client.HttpUtils          ( HttpTransport (..) )+import Distribution.Solver.Types.PackageFixedDeps import qualified Distribution.Client.Haddock as Haddock (regenerateHaddockIndex) import Distribution.Client.IndexUtils as IndexUtils-         ( getSourcePackages, getInstalledPackages )+         ( getSourcePackagesAtIndexState, IndexState(..), getInstalledPackages ) import qualified Distribution.Client.InstallPlan as InstallPlan+import qualified Distribution.Client.SolverInstallPlan as SolverInstallPlan import Distribution.Client.InstallPlan (InstallPlan)+import Distribution.Client.SolverInstallPlan (SolverInstallPlan) import Distribution.Client.Setup          ( GlobalFlags(..), RepoContext(..)          , ConfigFlags(..), configureCommand, filterConfigureFlags@@ -102,29 +99,33 @@          ( storeAnonymous, storeLocal, fromInstallPlan, fromPlanningFailure ) import qualified Distribution.Client.InstallSymlink as InstallSymlink          ( symlinkBinaries )-import qualified Distribution.Client.PackageIndex as SourcePackageIndex import qualified Distribution.Client.Win32SelfUpgrade as Win32SelfUpgrade import qualified Distribution.Client.World as World import qualified Distribution.InstalledPackageInfo as Installed-import Distribution.Client.Compat.ExecutablePath import Distribution.Client.JobControl-import qualified Distribution.Client.ComponentDeps as CD +import qualified Distribution.Solver.Types.ComponentDeps as CD+import           Distribution.Solver.Types.ConstraintSource+import           Distribution.Solver.Types.Settings+import           Distribution.Solver.Types.LabeledPackageConstraint+import           Distribution.Solver.Types.OptionalStanza+import qualified Distribution.Solver.Types.PackageIndex as SourcePackageIndex+import           Distribution.Solver.Types.PkgConfigDb+                   ( PkgConfigDb, readPkgConfigDb )+import           Distribution.Solver.Types.SourcePackage as SourcePackage+ import Distribution.Utils.NubList import Distribution.Simple.Compiler          ( CompilerId(..), Compiler(compilerId), compilerFlavor          , CompilerInfo(..), compilerInfo, PackageDB(..), PackageDBStack )-import Distribution.Simple.Program (ProgramConfiguration,-                                    defaultProgramConfiguration)+import Distribution.Simple.Program (ProgramDb) import qualified Distribution.Simple.InstallDirs as InstallDirs import qualified Distribution.Simple.PackageIndex as PackageIndex import Distribution.Simple.PackageIndex (InstalledPackageIndex)-import Distribution.Simple.LocalBuildInfo (ComponentName(CLibName))-import qualified Distribution.Simple.Configure as Configure import Distribution.Simple.Setup          ( haddockCommand, HaddockFlags(..)          , buildCommand, BuildFlags(..), emptyBuildFlags-         , AllowNewer(..)+         , AllowNewer(..), AllowOlder(..), RelaxDeps(..)          , toFlag, fromFlag, fromFlagOrDefault, flagToMaybe, defaultDistPref ) import qualified Distribution.Simple.Setup as Cabal          ( Flag(..)@@ -132,41 +133,42 @@          , registerCommand, RegisterFlags(..), emptyRegisterFlags          , testCommand, TestFlags(..), emptyTestFlags ) import Distribution.Simple.Utils-         ( createDirectoryIfMissingVerbose, rawSystemExit, comparing-         , writeFileAtomic, withTempFile , withUTF8FileContents )+         ( createDirectoryIfMissingVerbose, comparing+         , writeFileAtomic, withUTF8FileContents ) import Distribution.Simple.InstallDirs as InstallDirs          ( PathTemplate, fromPathTemplate, toPathTemplate, substPathTemplate          , initialPathTemplateEnv, installDirsTemplateEnv )+import Distribution.Simple.Configure (interpretPackageDbFlags)+import Distribution.Simple.Register (registerPackage, defaultRegisterOptions) import Distribution.Package          ( PackageIdentifier(..), PackageId, packageName, packageVersion-         , Package(..)-         , Dependency(..), thisPackageVersion-         , UnitId(..), mkUnitId-         , HasUnitId(..) )+         , Package(..), HasMungedPackageId(..), HasUnitId(..)+         , UnitId )+import Distribution.Types.Dependency+         ( Dependency(..), thisPackageVersion )+import Distribution.Types.MungedPackageId import qualified Distribution.PackageDescription as PackageDescription import Distribution.PackageDescription          ( PackageDescription, GenericPackageDescription(..), Flag(..)-         , FlagName(..), FlagAssignment )+         , FlagAssignment, showFlagValue ) import Distribution.PackageDescription.Configuration-         ( finalizePackageDescription )-import Distribution.Client.PkgConfigDb-         ( PkgConfigDb, readPkgConfigDb )+         ( finalizePD ) import Distribution.ParseUtils          ( showPWarning ) import Distribution.Version          ( Version, VersionRange, foldVersionRange ) import Distribution.Simple.Utils as Utils-         ( notice, info, warn, debug, debugNoWrap, die-         , intercalate, withTempDirectory )+         ( notice, info, warn, debug, debugNoWrap, die'+         , withTempDirectory ) import Distribution.Client.Utils-         ( determineNumJobs, inDir, logDirChange, mergeBy, MergeResult(..)+         ( determineNumJobs, logDirChange, mergeBy, MergeResult(..)          , tryCanonicalizePath ) import Distribution.System          ( Platform, OS(Windows), buildOS ) import Distribution.Text          ( display ) import Distribution.Verbosity as Verbosity-         ( Verbosity, showForCabal, normal, verbose )+         ( Verbosity, normal, verbose ) import Distribution.Simple.BuildPaths ( exeExtension )  --TODO:@@ -193,7 +195,7 @@   -> RepoContext   -> Compiler   -> Platform-  -> ProgramConfiguration+  -> ProgramDb   -> UseSandbox   -> Maybe SandboxPackageInfo   -> GlobalFlags@@ -203,10 +205,22 @@   -> HaddockFlags   -> [UserTarget]   -> IO ()-install verbosity packageDBs repos comp platform conf useSandbox mSandboxPkgInfo+install verbosity packageDBs repos comp platform progdb useSandbox mSandboxPkgInfo   globalFlags configFlags configExFlags installFlags haddockFlags   userTargets0 = do +    unless (installRootCmd installFlags == Cabal.NoFlag) $+        warn verbosity $ "--root-cmd is no longer supported, "+        ++ "see https://github.com/haskell/cabal/issues/3353"+        ++ " (if you didn't type --root-cmd, comment out root-cmd"+        ++ " in your ~/.cabal/config file)"+    let userOrSandbox = fromFlag (configUserInstall configFlags)+                     || isUseSandbox useSandbox+    unless userOrSandbox $+        warn verbosity $ "the --global flag is deprecated -- "+        ++ "it is generally considered a bad idea to install packages "+        ++ "into the global store"+     installContext <- makeInstallContext verbosity args (Just userTargets0)     planResult     <- foldProgress logMsg (return . Left) (return . Right) =<<                       makeInstallPlan verbosity args installContext@@ -214,16 +228,16 @@     case planResult of         Left message -> do             reportPlanningFailure verbosity args installContext message-            die' message+            die'' message         Right installPlan ->             processInstallPlan verbosity args installContext installPlan   where     args :: InstallArgs-    args = (packageDBs, repos, comp, platform, conf, useSandbox, mSandboxPkgInfo,-            globalFlags, configFlags, configExFlags, installFlags,-            haddockFlags)+    args = (packageDBs, repos, comp, platform, progdb, useSandbox,+            mSandboxPkgInfo, globalFlags, configFlags, configExFlags,+            installFlags, haddockFlags) -    die' message = die (message ++ if isUseSandbox useSandbox+    die'' message = die' verbosity (message ++ if isUseSandbox useSandbox                                    then installFailedInSandbox else [])     -- TODO: use a better error message, remove duplication.     installFailedInSandbox =@@ -237,7 +251,7 @@ -- | Common context for makeInstallPlan and processInstallPlan. type InstallContext = ( InstalledPackageIndex, SourcePackageDb                       , PkgConfigDb-                      , [UserTarget], [PackageSpecifier SourcePackage]+                      , [UserTarget], [PackageSpecifier UnresolvedSourcePackage]                       , HttpTransport )  -- TODO: Make InstallArgs a proper data type with documented fields or just get@@ -247,7 +261,7 @@                    , RepoContext                    , Compiler                    , Platform-                   , ProgramConfiguration+                   , ProgramDb                    , UseSandbox                    , Maybe SandboxPackageInfo                    , GlobalFlags@@ -260,13 +274,16 @@ makeInstallContext :: Verbosity -> InstallArgs -> Maybe [UserTarget]                       -> IO InstallContext makeInstallContext verbosity-  (packageDBs, repoCtxt, comp, _, conf,_,_,-   globalFlags, _, configExFlags, _, _) mUserTargets = do+  (packageDBs, repoCtxt, comp, _, progdb,_,_,+   globalFlags, _, configExFlags, installFlags, _) mUserTargets = do -    installedPkgIndex <- getInstalledPackages verbosity comp packageDBs conf-    sourcePkgDb       <- getSourcePackages    verbosity repoCtxt-    pkgConfigDb       <- readPkgConfigDb      verbosity conf+    let idxState = fromFlagOrDefault IndexStateHead $+                       installIndexState installFlags +    installedPkgIndex <- getInstalledPackages verbosity comp packageDBs progdb+    sourcePkgDb       <- getSourcePackagesAtIndexState verbosity repoCtxt idxState+    pkgConfigDb       <- readPkgConfigDb      verbosity progdb+     checkConfigExFlags verbosity installedPkgIndex                        (packageIndex sourcePkgDb) configExFlags     transport <- repoContextGetTransport repoCtxt@@ -294,7 +311,7 @@  -- | Make an install plan given install context and install arguments. makeInstallPlan :: Verbosity -> InstallArgs -> InstallContext-                -> IO (Progress String String InstallPlan)+                -> IO (Progress String String SolverInstallPlan) makeInstallPlan verbosity   (_, _, comp, platform, _, _, mSandboxPkgInfo,    _, configFlags, configExFlags, installFlags,@@ -305,34 +322,37 @@     solver <- chooseSolver verbosity (fromFlag (configSolver configExFlags))               (compilerInfo comp)     notice verbosity "Resolving dependencies..."-    return $ planPackages comp platform mSandboxPkgInfo solver-      configFlags configExFlags installFlags-      installedPkgIndex sourcePkgDb pkgConfigDb pkgSpecifiers+    return $ planPackages verbosity comp platform mSandboxPkgInfo solver+          configFlags configExFlags installFlags+          installedPkgIndex sourcePkgDb pkgConfigDb pkgSpecifiers  -- | Given an install plan, perform the actual installations. processInstallPlan :: Verbosity -> InstallArgs -> InstallContext-                   -> InstallPlan+                   -> SolverInstallPlan                    -> IO () processInstallPlan verbosity-  args@(_,_, _, _, _, _, _, _, _, _, installFlags, _)+  args@(_,_, _, _, _, _, _, _, configFlags, _, installFlags, _)   (installedPkgIndex, sourcePkgDb, _,-   userTargets, pkgSpecifiers, _) installPlan = do+   userTargets, pkgSpecifiers, _) installPlan0 = do+     checkPrintPlan verbosity installedPkgIndex installPlan sourcePkgDb       installFlags pkgSpecifiers      unless (dryRun || nothingToInstall) $ do-      installPlan' <- performInstallations verbosity-                      args installedPkgIndex installPlan-      postInstallActions verbosity args userTargets installPlan'+      buildOutcomes <- performInstallations verbosity+                         args installedPkgIndex installPlan+      postInstallActions verbosity args userTargets installPlan buildOutcomes   where+    installPlan = InstallPlan.configureInstallPlan configFlags installPlan0     dryRun = fromFlag (installDryRun installFlags)-    nothingToInstall = null (InstallPlan.ready installPlan)+    nothingToInstall = null (fst (InstallPlan.ready installPlan))  -- ------------------------------------------------------------ -- * Installation planning -- ------------------------------------------------------------ -planPackages :: Compiler+planPackages :: Verbosity+             -> Compiler              -> Platform              -> Maybe SandboxPackageInfo              -> Solver@@ -342,9 +362,9 @@              -> InstalledPackageIndex              -> SourcePackageDb              -> PkgConfigDb-             -> [PackageSpecifier SourcePackage]-             -> Progress String String InstallPlan-planPackages comp platform mSandboxPkgInfo solver+             -> [PackageSpecifier UnresolvedSourcePackage]+             -> Progress String String SolverInstallPlan+planPackages verbosity comp platform mSandboxPkgInfo solver              configFlags configExFlags installFlags              installedPkgIndex sourcePkgDb pkgConfigDb pkgSpecifiers = @@ -365,15 +385,22 @@        . setReorderGoals reorderGoals +      . setCountConflicts countConflicts+       . setAvoidReinstalls avoidReinstalls        . setShadowPkgs shadowPkgs        . setStrongFlags strongFlags +      . setAllowBootLibInstalls allowBootLibInstalls++      . setSolverVerbosity verbosity+       . setPreferenceDefault (if upgradeDeps then PreferAllLatest                                              else PreferLatestForSelected) +      . removeLowerBounds allowOlder       . removeUpperBounds allowNewer        . addPreferences@@ -389,16 +416,18 @@       . addConstraints           --FIXME: this just applies all flags to all targets which           -- is silly. We should check if the flags are appropriate-          [ let pc = PackageConstraintFlags-                     (pkgSpecifierTarget pkgSpecifier) flags+          [ let pc = PackageConstraint+                     (scopeToplevel $ pkgSpecifierTarget pkgSpecifier)+                     (PackagePropertyFlags flags)             in LabeledPackageConstraint pc ConstraintSourceConfigFlagOrTarget           | let flags = configConfigurationsFlags configFlags           , not (null flags)           , pkgSpecifier <- pkgSpecifiers ]        . addConstraints-          [ let pc = PackageConstraintStanzas-                     (pkgSpecifierTarget pkgSpecifier) stanzas+          [ let pc = PackageConstraint+                     (scopeToplevel $ pkgSpecifierTarget pkgSpecifier)+                     (PackagePropertyStanzas stanzas)             in LabeledPackageConstraint pc ConstraintSourceConfigFlagOrTarget           | pkgSpecifier <- pkgSpecifiers ] @@ -406,6 +435,10 @@        . (if reinstall then reinstallTargets else id) +        -- Don't solve for executables, the legacy install codepath+        -- doesn't understand how to install them+      . setSolveExecutables (SolveExecutables False)+       $ standardInstallPolicy         installedPkgIndex sourcePkgDb pkgSpecifiers @@ -417,28 +450,33 @@     reinstall        = fromFlag (installOverrideReinstall installFlags) ||                        fromFlag (installReinstall         installFlags)     reorderGoals     = fromFlag (installReorderGoals      installFlags)+    countConflicts   = fromFlag (installCountConflicts    installFlags)     independentGoals = fromFlag (installIndependentGoals  installFlags)     avoidReinstalls  = fromFlag (installAvoidReinstalls   installFlags)     shadowPkgs       = fromFlag (installShadowPkgs        installFlags)     strongFlags      = fromFlag (installStrongFlags       installFlags)     maxBackjumps     = fromFlag (installMaxBackjumps      installFlags)+    allowBootLibInstalls = fromFlag (installAllowBootLibInstalls installFlags)     upgradeDeps      = fromFlag (installUpgradeDeps       installFlags)     onlyDeps         = fromFlag (installOnlyDeps          installFlags)-    allowNewer       = fromMaybe AllowNewerNone (configAllowNewer configFlags)+    allowOlder       = fromMaybe (AllowOlder RelaxDepsNone)+                                 (configAllowOlder configFlags)+    allowNewer       = fromMaybe (AllowNewer RelaxDepsNone)+                                 (configAllowNewer configFlags)  -- | Remove the provided targets from the install plan. pruneInstallPlan :: Package targetpkg                  => [PackageSpecifier targetpkg]-                 -> InstallPlan-                 -> Progress String String InstallPlan+                 -> SolverInstallPlan+                 -> Progress String String SolverInstallPlan pruneInstallPlan pkgSpecifiers =   -- TODO: this is a general feature and should be moved to D.C.Dependency   -- Also, the InstallPlan.remove should return info more precise to the   -- problem, rather than the very general PlanProblem type.   either (Fail . explain) Done-  . InstallPlan.remove (\pkg -> packageName pkg `elem` targetnames)+  . SolverInstallPlan.remove (\pkg -> packageName pkg `elem` targetnames)   where-    explain :: [InstallPlan.PlanProblem ipkg srcpkg iresult ifailure] -> String+    explain :: [SolverInstallPlan.SolverPlanProblem] -> String     explain problems =       "Cannot select only the dependencies (as requested by the "       ++ "'--only-dependencies' flag), "@@ -450,7 +488,7 @@       where         pkgids =           nub [ depid-              | InstallPlan.PackageMissingDeps _ depids <- problems+              | SolverInstallPlan.PackageMissingDeps _ depids <- problems               , depid <- depids               , packageName depid `elem` targetnames ] @@ -467,7 +505,7 @@                -> InstallPlan                -> SourcePackageDb                -> InstallFlags-               -> [PackageSpecifier SourcePackage]+               -> [PackageSpecifier UnresolvedSourcePackage]                -> IO () checkPrintPlan verbosity installed installPlan sourcePkgDb   installFlags pkgSpecifiers = do@@ -486,9 +524,15 @@        : map (display . packageId) preExistingTargets       ++ ["Use --reinstall if you want to reinstall anyway."] -  let lPlan = linearizeInstallPlan installed installPlan+  let lPlan =+        [ (pkg, status)+        | pkg <- InstallPlan.executionOrder installPlan+        , let status = packageStatus installed pkg ]   -- Are any packages classified as reinstalls?-  let reinstalledPkgs = concatMap (extractReinstalls . snd) lPlan+  let reinstalledPkgs =+        [ ipkg+        | (_pkg, status) <- lPlan+        , ipkg <- extractReinstalls status ]   -- Packages that are already broken.   let oldBrokenPkgs =           map Installed.installedUnitId@@ -521,9 +565,9 @@   when containsReinstalls $ do     if breaksPkgs       then do-        (if dryRun || overrideReinstall then warn verbosity else die) $ unlines $+        (if dryRun || overrideReinstall then warn else die') verbosity $ unlines $             "The following packages are likely to be broken by the reinstalls:"-          : map (display . Installed.sourcePackageId) newBrokenPkgs+          : map (display . mungedId) newBrokenPkgs           ++ if overrideReinstall                then if dryRun then [] else                  ["Continuing even though " ++@@ -537,54 +581,28 @@   -- are already fetched.   let offline = fromFlagOrDefault False (installOfflineMode installFlags)   when offline $ do-    let pkgs = [ sourcePkg-               | InstallPlan.Configured (ConfiguredPackage sourcePkg _ _ _)-                 <- InstallPlan.toList installPlan ]+    let pkgs = [ confPkgSource cpkg+               | InstallPlan.Configured cpkg <- InstallPlan.toList installPlan ]     notFetched <- fmap (map packageInfoId)                   . filterM (fmap isNothing . checkFetched . packageSource)                   $ pkgs     unless (null notFetched) $-      die $ "Can't download packages in offline mode. "+      die' verbosity $ "Can't download packages in offline mode. "       ++ "Must download the following packages to proceed:\n"       ++ intercalate ", " (map display notFetched)       ++ "\nTry using 'cabal fetch'."    where-    nothingToInstall = null (InstallPlan.ready installPlan)+    nothingToInstall = null (fst (InstallPlan.ready installPlan))      dryRun            = fromFlag (installDryRun            installFlags)     overrideReinstall = fromFlag (installOverrideReinstall installFlags) ---TODO: this type is too specific-linearizeInstallPlan :: InstalledPackageIndex-                     -> InstallPlan-                     -> [(ReadyPackage, PackageStatus)]-linearizeInstallPlan installedPkgIndex plan =-    unfoldr next plan-  where-    next plan' = case InstallPlan.ready plan' of-      []      -> Nothing-      (pkg:_) -> Just ((pkg, status), plan'')-        where-          pkgid  = installedUnitId pkg-          status = packageStatus installedPkgIndex pkg-          ipkg   = Installed.emptyInstalledPackageInfo {-                     Installed.sourcePackageId    = packageId pkg,-                     Installed.installedUnitId = pkgid-                   }-          plan'' = InstallPlan.completed pkgid (Just ipkg)-                     (BuildOk DocsNotTried TestsNotTried (Just ipkg))-                     (InstallPlan.processing [pkg] plan')-          --FIXME: This is a bit of a hack,-          -- pretending that each package is installed-          -- It's doubly a hack because the installed package ID-          -- didn't get updated...- data PackageStatus = NewPackage                    | NewVersion [Version]                    | Reinstall  [UnitId] [PackageChange] -type PackageChange = MergeResult PackageIdentifier PackageIdentifier+type PackageChange = MergeResult MungedPackageId MungedPackageId  extractReinstalls :: PackageStatus -> [UnitId] extractReinstalls (Reinstall ipids _) = ipids@@ -597,8 +615,8 @@   case PackageIndex.lookupPackageName installedPkgIndex                                       (packageName cpkg) of     [] -> NewPackage-    ps ->  case filter ((== packageId cpkg)-                        . Installed.sourcePackageId) (concatMap snd ps) of+    ps ->  case filter ((== mungedId cpkg)+                        . mungedId) (concatMap snd ps) of       []           -> NewVersion (map fst ps)       pkgs@(pkg:_) -> Reinstall (map Installed.installedUnitId pkgs)                                 (changes pkg cpkg)@@ -607,20 +625,20 @@      changes :: Installed.InstalledPackageInfo             -> ReadyPackage-            -> [MergeResult PackageIdentifier PackageIdentifier]-    changes pkg pkg' = filter changed $-      mergeBy (comparing packageName)+            -> [PackageChange]+    changes pkg (ReadyPackage pkg') = filter changed $+      mergeBy (comparing mungedName)         -- deps of installed pkg         (resolveInstalledIds $ Installed.depends pkg)         -- deps of configured pkg         (resolveInstalledIds $ CD.nonSetupDeps (depends pkg'))      -- convert to source pkg ids via index-    resolveInstalledIds :: [UnitId] -> [PackageIdentifier]+    resolveInstalledIds :: [UnitId] -> [MungedPackageId]     resolveInstalledIds =         nub       . sort-      . map Installed.sourcePackageId+      . map mungedId       . catMaybes       . map (PackageIndex.lookupUnitId installedPkgIndex) @@ -635,7 +653,7 @@ printPlan dryRun verbosity plan sourcePkgDb = case plan of   []   -> return ()   pkgs-    | verbosity >= Verbosity.verbose -> putStr $ unlines $+    | verbosity >= Verbosity.verbose -> notice verbosity $ unlines $         ("In order, the following " ++ wouldWill ++ " be installed:")       : map showPkgAndReason pkgs     | otherwise -> notice verbosity $ unlines $@@ -649,10 +667,10 @@     showPkg (pkg, _) = display (packageId pkg) ++                        showLatest (pkg) -    showPkgAndReason (ReadyPackage pkg' _, pr) = display (packageId pkg') +++    showPkgAndReason (ReadyPackage pkg', pr) = display (packageId pkg') ++           showLatest pkg' ++           showFlagAssignment (nonDefaultFlags pkg') ++-          showStanzas (stanzas pkg') +++          showStanzas (confPkgStanzas pkg') ++           showDep pkg' ++           case pr of             NewPackage     -> " (new package)"@@ -680,30 +698,23 @@     toFlagAssignment :: [Flag] -> FlagAssignment     toFlagAssignment = map (\ f -> (flagName f, flagDefault f)) -    nonDefaultFlags :: ConfiguredPackage -> FlagAssignment-    nonDefaultFlags (ConfiguredPackage spkg fa _ _) =+    nonDefaultFlags :: ConfiguredPackage loc -> FlagAssignment+    nonDefaultFlags cpkg =       let defaultAssignment =             toFlagAssignment-             (genPackageFlags (Source.packageDescription spkg))-      in  fa \\ defaultAssignment--    stanzas :: ConfiguredPackage -> [OptionalStanza]-    stanzas (ConfiguredPackage _ _ sts _) = sts+             (genPackageFlags (SourcePackage.packageDescription $+                               confPkgSource cpkg))+      in  confPkgFlags cpkg \\ defaultAssignment      showStanzas :: [OptionalStanza] -> String-    showStanzas = concatMap ((' ' :) . showStanza)-    showStanza TestStanzas  = "*test"-    showStanza BenchStanzas = "*bench"+    showStanzas = concatMap ((" *" ++) . showStanza)      showFlagAssignment :: FlagAssignment -> String     showFlagAssignment = concatMap ((' ' :) . showFlagValue)-    showFlagValue (f, True)   = '+' : showFlagName f-    showFlagValue (f, False)  = '-' : showFlagName f-    showFlagName (FlagName f) = f      change (OnlyInLeft pkgid)        = display pkgid ++ " removed"     change (InBoth     pkgid pkgid') = display pkgid ++ " -> "-                                    ++ display (packageVersion pkgid')+                                    ++ display (mungedVersion pkgid')     change (OnlyInRight      pkgid') = display pkgid' ++ " added"      showDep pkg | Just rdeps <- Map.lookup (packageId pkg) revDeps@@ -711,9 +722,10 @@                 | otherwise = ""      revDepGraphEdges :: [(PackageId, PackageId)]-    revDepGraphEdges = [ (rpid, packageId pkg)-                       | (pkg@(ReadyPackage _ deps), _) <- plan-                       , rpid <- Installed.sourcePackageId <$> CD.flatDeps deps ]+    revDepGraphEdges = [ (rpid, packageId cpkg)+                       | (ReadyPackage cpkg, _) <- plan+                       , ConfiguredId rpid (Just PackageDescription.CLibName) _+                        <- CD.flatDeps (confPkgDeps cpkg) ]      revDeps :: Map.Map PackageId [PackageId]     revDeps = Map.fromListWith (++) (map (fmap (:[])) revDepGraphEdges)@@ -776,8 +788,8 @@ theSpecifiedPackage :: Package pkg => PackageSpecifier pkg -> Maybe PackageId theSpecifiedPackage pkgSpec =   case pkgSpec of-    NamedPackage name [PackageConstraintVersion name' version]-      | name == name' -> PackageIdentifier name <$> trivialRange version+    NamedPackage name [PackagePropertyVersion version]+      -> PackageIdentifier name <$> trivialRange version     NamedPackage _ _ -> Nothing     SpecificSourcePackage pkg -> Just $ packageId pkg   where@@ -804,11 +816,12 @@                    -> InstallArgs                    -> [UserTarget]                    -> InstallPlan+                   -> BuildOutcomes                    -> IO () postInstallActions verbosity-  (packageDBs, _, comp, platform, conf, useSandbox, mSandboxPkgInfo+  (packageDBs, _, comp, platform, progdb, useSandbox, mSandboxPkgInfo   ,globalFlags, configFlags, _, installFlags, _)-  targets installPlan = do+  targets installPlan buildOutcomes = do    unless oneShot $     World.insert verbosity worldFile@@ -817,7 +830,7 @@       | UserTargetNamed dep <- targets ]    let buildReports = BuildReports.fromInstallPlan platform (compilerId comp)-                                                  installPlan+                                                  installPlan buildOutcomes   BuildReports.storeLocal (compilerInfo comp)                           (fromNubList $ installSummaryFile installFlags)                           buildReports@@ -827,15 +840,16 @@   when (reportingLevel == DetailedReports) $     storeDetailedBuildReports verbosity logsDir buildReports -  regenerateHaddockIndex verbosity packageDBs comp platform conf useSandbox-                         configFlags installFlags installPlan+  regenerateHaddockIndex verbosity packageDBs comp platform progdb useSandbox+                         configFlags installFlags buildOutcomes -  symlinkBinaries verbosity platform comp configFlags installFlags installPlan+  symlinkBinaries verbosity platform comp configFlags installFlags+                  installPlan buildOutcomes -  printBuildFailures installPlan+  printBuildFailures verbosity buildOutcomes -  updateSandboxTimestampsFile useSandbox mSandboxPkgInfo-                              comp platform installPlan+  updateSandboxTimestampsFile verbosity useSandbox mSandboxPkgInfo+                              comp platform installPlan buildOutcomes    where     reportingLevel = fromFlag (installBuildReports installFlags)@@ -881,14 +895,14 @@                        -> [PackageDB]                        -> Compiler                        -> Platform-                       -> ProgramConfiguration+                       -> ProgramDb                        -> UseSandbox                        -> ConfigFlags                        -> InstallFlags-                       -> InstallPlan+                       -> BuildOutcomes                        -> IO ()-regenerateHaddockIndex verbosity packageDBs comp platform conf useSandbox-                       configFlags installFlags installPlan+regenerateHaddockIndex verbosity packageDBs comp platform progdb useSandbox+                       configFlags installFlags buildOutcomes   | haddockIndexFileIsRequested && shouldRegenerateHaddockIndex = do    defaultDirs <- InstallDirs.defaultInstallDirs@@ -902,8 +916,8 @@      "Updating documentation index " ++ indexFile    --TODO: might be nice if the install plan gave us the new InstalledPackageInfo-  installedPkgIndex <- getInstalledPackages verbosity comp packageDBs conf-  Haddock.regenerateHaddockIndex verbosity installedPkgIndex conf indexFile+  installedPkgIndex <- getInstalledPackages verbosity comp packageDBs progdb+  Haddock.regenerateHaddockIndex verbosity installedPkgIndex progdb indexFile    | otherwise = return ()   where@@ -916,14 +930,14 @@     -- #1337), we don't do it for global installs or special cases where we're     -- installing into a specific db.     shouldRegenerateHaddockIndex = (isUseSandbox useSandbox || normalUserInstall)-                                && someDocsWereInstalled installPlan+                                && someDocsWereInstalled buildOutcomes       where-        someDocsWereInstalled = any installedDocs . InstallPlan.toList+        someDocsWereInstalled = any installedDocs . Map.elems+        installedDocs (Right (BuildResult DocsOk _ _)) = True+        installedDocs _                                = False+         normalUserInstall     = (UserPackageDB `elem` packageDBs)                              && all (not . isSpecificPackageDB) packageDBs--        installedDocs (InstallPlan.Installed _ _ (BuildOk DocsOk _ _)) = True-        installedDocs _                                                = False         isSpecificPackageDB (SpecificPackageDB _) = True         isSpecificPackageDB _                     = False @@ -945,24 +959,26 @@                 -> ConfigFlags                 -> InstallFlags                 -> InstallPlan+                -> BuildOutcomes                 -> IO ()-symlinkBinaries verbosity platform comp configFlags installFlags plan = do+symlinkBinaries verbosity platform comp configFlags installFlags+                plan buildOutcomes = do   failed <- InstallSymlink.symlinkBinaries platform comp                                            configFlags installFlags-                                           plan+                                           plan buildOutcomes   case failed of     [] -> return ()     [(_, exe, path)] ->       warn verbosity $            "could not create a symlink in " ++ bindir ++ " for "-        ++ exe ++ " because the file exists there already but is not "+        ++ display exe ++ " because the file exists there already but is not "         ++ "managed by cabal. You can create a symlink for this executable "         ++ "manually if you wish. The executable file has been installed at "         ++ path     exes ->       warn verbosity $            "could not create symlinks in " ++ bindir ++ " for "-        ++ intercalate ", " [ exe | (_, exe, _) <- exes ]+        ++ intercalate ", " [ display exe | (_, exe, _) <- exes ]         ++ " because the files exist there already and are not "         ++ "managed by cabal. You can create symlinks for these executables "         ++ "manually if you wish. The executable files have been installed at "@@ -971,16 +987,15 @@     bindir = fromFlag (installSymlinkBinDir installFlags)  -printBuildFailures :: InstallPlan-                   -> IO ()-printBuildFailures plan =-  case [ (pkg, reason)-       | InstallPlan.Failed pkg reason <- InstallPlan.toList plan ] of+printBuildFailures :: Verbosity -> BuildOutcomes -> IO ()+printBuildFailures verbosity buildOutcomes =+  case [ (pkgid, failure)+       | (pkgid, Left failure) <- Map.toList buildOutcomes ] of     []     -> return ()-    failed -> die . unlines+    failed -> die' verbosity . unlines             $ "Error: some packages failed to install:"-            : [ display (packageId pkg) ++ printFailureReason reason-              | (pkg, reason) <- failed ]+            : [ display pkgid ++ printFailureReason reason+              | (pkgid, reason) <- failed ]   where     printFailureReason reason = case reason of       DependentFailed pkgid -> " depends on " ++ display pkgid@@ -1015,31 +1030,35 @@  -- | If we're working inside a sandbox and some add-source deps were installed, -- update the timestamps of those deps.-updateSandboxTimestampsFile :: UseSandbox -> Maybe SandboxPackageInfo+updateSandboxTimestampsFile :: Verbosity -> UseSandbox -> Maybe SandboxPackageInfo                             -> Compiler -> Platform                             -> InstallPlan+                            -> BuildOutcomes                             -> IO ()-updateSandboxTimestampsFile (UseSandbox sandboxDir)+updateSandboxTimestampsFile verbosity (UseSandbox sandboxDir)                             (Just (SandboxPackageInfo _ _ _ allAddSourceDeps))-                            comp platform installPlan =-  withUpdateTimestamps sandboxDir (compilerId comp) platform $ \_ -> do-    let allInstalled = [ pkg | InstallPlan.Installed pkg _ _-                            <- InstallPlan.toList installPlan ]-        allSrcPkgs   = [ pkg | ReadyPackage (ConfiguredPackage pkg _ _ _) _-                            <- allInstalled ]+                            comp platform installPlan buildOutcomes =+  withUpdateTimestamps verbosity sandboxDir (compilerId comp) platform $ \_ -> do+    let allInstalled = [ pkg+                       | InstallPlan.Configured pkg+                            <- InstallPlan.toList installPlan+                       , case InstallPlan.lookupBuildOutcome pkg buildOutcomes of+                           Just (Right _success) -> True+                           _                     -> False+                       ]+        allSrcPkgs   = [ confPkgSource cpkg | cpkg <- allInstalled ]         allPaths     = [ pth | LocalUnpackedPackage pth                             <- map packageSource allSrcPkgs]     allPathsCanonical <- mapM tryCanonicalizePath allPaths     return $! filter (`S.member` allAddSourceDeps) allPathsCanonical -updateSandboxTimestampsFile _ _ _ _ _ = return ()+updateSandboxTimestampsFile _ _ _ _ _ _ _ = return ()  -- ------------------------------------------------------------ -- * Actually do the installations -- ------------------------------------------------------------  data InstallMisc = InstallMisc {-    rootCmd    :: Maybe FilePath,     libVersion :: Maybe Version   } @@ -1051,9 +1070,9 @@                      -> InstallArgs                      -> InstalledPackageIndex                      -> InstallPlan-                     -> IO InstallPlan+                     -> IO BuildOutcomes performInstallations verbosity-  (packageDBs, repoCtxt, comp, platform, conf, useSandbox, _,+  (packageDBs, repoCtxt, comp, platform, progdb, useSandbox, _,    globalFlags, configFlags, configExFlags, installFlags, haddockFlags)   installedPkgIndex installPlan = do @@ -1062,26 +1081,26 @@     when parallelInstall $       notice verbosity $ "Notice: installing into a sandbox located at "                          ++ sandboxDir+  info verbosity $ "Number of threads used: " ++ (show numJobs) ++ "." -  jobControl   <- if parallelInstall then newParallelJobControl+  jobControl   <- if parallelInstall then newParallelJobControl numJobs                                      else newSerialJobControl-  buildLimit   <- newJobLimit numJobs   fetchLimit   <- newJobLimit (min numJobs numFetchJobs)   installLock  <- newLock -- serialise installation   cacheLock    <- newLock -- serialise access to setup exe cache -  executeInstallPlan verbosity comp jobControl useLogFile installPlan $ \rpkg ->+  executeInstallPlan verbosity jobControl keepGoing useLogFile+                     installPlan $ \rpkg ->     installReadyPackage platform cinfo configFlags                         rpkg $ \configFlags' src pkg pkgoverride ->       fetchSourcePackage verbosity repoCtxt fetchLimit src $ \src' ->-        installLocalPackage verbosity buildLimit-                            (packageId pkg) src' distPref $ \mpath ->-          installUnpackedPackage verbosity buildLimit installLock numJobs+        installLocalPackage verbosity (packageId pkg) src' distPref $ \mpath ->+          installUnpackedPackage verbosity installLock numJobs                                  (setupScriptOptions installedPkgIndex                                   cacheLock rpkg)-                                 miscOptions configFlags'-                                 installFlags haddockFlags-                                 cinfo platform pkg rpkg pkgoverride mpath useLogFile+                                 configFlags'+                                 installFlags haddockFlags comp progdb+                                 platform pkg rpkg pkgoverride mpath useLogFile    where     cinfo = compilerInfo comp@@ -1089,6 +1108,7 @@     numJobs         = determineNumJobs (installNumJobs installFlags)     numFetchJobs    = 2     parallelInstall = numJobs >= 2+    keepGoing       = fromFlag (installKeepGoing installFlags)     distPref        = fromFlagOrDefault (useDistPref defaultSetupScriptOptions)                       (configDistPref configFlags) @@ -1097,7 +1117,7 @@         packageDBs         comp         platform-        conf+        progdb         distPref         (chooseCabalVersion configFlags (libVersion miscOptions))         (Just lock)@@ -1114,7 +1134,8 @@                  logFileTemplate       where         installLogFile' = flagToMaybe $ installLogFile installFlags-        defaultTemplate = toPathTemplate $ logsDir </> "$pkgid" <.> "log"+        defaultTemplate = toPathTemplate $+                            logsDir </> "$compiler" </> "$libname" <.> "log"          -- If the user has specified --remote-build-reporting=detailed, use the         -- default log file location. If the --build-log option is set, use the@@ -1146,82 +1167,38 @@           | otherwise                         = False      substLogFileName :: PathTemplate -> PackageIdentifier -> UnitId -> FilePath-    substLogFileName template pkg ipid = fromPathTemplate+    substLogFileName template pkg uid = fromPathTemplate                                   . substPathTemplate env                                   $ template-      where env = initialPathTemplateEnv (packageId pkg)-                  ipid-                  (compilerInfo comp) platform+      where env = initialPathTemplateEnv (packageId pkg) uid+                    (compilerInfo comp) platform      miscOptions  = InstallMisc {-      rootCmd    = if fromFlag (configUserInstall configFlags)-                      || (isUseSandbox useSandbox)-                     then Nothing      -- ignore --root-cmd if --user-                                       -- or working inside a sandbox.-                     else flagToMaybe (installRootCmd installFlags),       libVersion = flagToMaybe (configCabalVersion configExFlags)     }   executeInstallPlan :: Verbosity-                   -> Compiler-                   -> JobControl IO (PackageId, UnitId, BuildResult)+                   -> JobControl IO (UnitId, BuildOutcome)+                   -> Bool                    -> UseLogFile                    -> InstallPlan-                   -> (ReadyPackage -> IO BuildResult)-                   -> IO InstallPlan-executeInstallPlan verbosity _comp jobCtl useLogFile plan0 installPkg =-    tryNewTasks 0 plan0-  where-    tryNewTasks taskCount plan = do-      case InstallPlan.ready plan of-        [] | taskCount == 0 -> return plan-           | otherwise      -> waitForTasks taskCount plan-        pkgs                -> do-          sequence_-            [ do info verbosity $ "Ready to install " ++ display pkgid-                 spawnJob jobCtl $ do-                   buildResult <- installPkg pkg-                   let ipid = case buildResult of-                                Right (BuildOk _ _ (Just ipi)) ->-                                     Installed.installedUnitId ipi-                                _ -> mkUnitId (display (packageId pkg))-                   return (packageId pkg, ipid, buildResult)-            | pkg <- pkgs-            , let pkgid = packageId pkg ]--          let taskCount' = taskCount + length pkgs-              plan'      = InstallPlan.processing pkgs plan-          waitForTasks taskCount' plan'--    waitForTasks taskCount plan = do-      info verbosity $ "Waiting for install task to finish..."-      (pkgid, ipid, buildResult) <- collectJob jobCtl-      printBuildResult pkgid ipid buildResult-      let taskCount' = taskCount-1-          plan'      = updatePlan pkgid buildResult plan-      tryNewTasks taskCount' plan'--    updatePlan :: PackageIdentifier -> BuildResult -> InstallPlan-               -> InstallPlan-    updatePlan pkgid (Right buildSuccess@(BuildOk _ _ mipkg)) =-        InstallPlan.completed (Source.fakeUnitId pkgid)-                              mipkg buildSuccess+                   -> (ReadyPackage -> IO BuildOutcome)+                   -> IO BuildOutcomes+executeInstallPlan verbosity jobCtl keepGoing useLogFile plan0 installPkg =+    InstallPlan.execute+      jobCtl keepGoing depsFailure plan0 $ \pkg -> do+        buildOutcome <- installPkg pkg+        printBuildResult (packageId pkg) (installedUnitId pkg) buildOutcome+        return buildOutcome -    updatePlan pkgid (Left buildFailure) =-        InstallPlan.failed (Source.fakeUnitId pkgid)-                           buildFailure depsFailure-      where-        depsFailure = DependentFailed pkgid-        -- So this first pkgid failed for whatever reason (buildFailure).-        -- All the other packages that depended on this pkgid, which we-        -- now cannot build, we mark as failing due to 'DependentFailed'-        -- which kind of means it was not their fault.+  where+    depsFailure = DependentFailed . packageId      -- Print build log if something went wrong, and 'Installed $PKGID'     -- otherwise.-    printBuildResult :: PackageId -> UnitId -> BuildResult -> IO ()-    printBuildResult pkgid ipid buildResult = case buildResult of+    printBuildResult :: PackageId -> UnitId -> BuildOutcome -> IO ()+    printBuildResult pkgid uid buildOutcome = case buildOutcome of         (Right _) -> notice verbosity $ "Installed " ++ display pkgid         (Left _)  -> do           notice verbosity $ "Failed to install " ++ display pkgid@@ -1229,7 +1206,7 @@             case useLogFile of               Nothing                 -> return ()               Just (mkLogFileName, _) -> do-                let logName = mkLogFileName pkgid ipid+                let logName = mkLogFileName pkgid uid                 putStr $ "Build log ( " ++ logName ++ " ):\n"                 printFile logName @@ -1247,46 +1224,47 @@ installReadyPackage :: Platform -> CompilerInfo                     -> ConfigFlags                     -> ReadyPackage-                    -> (ConfigFlags -> PackageLocation (Maybe FilePath)+                    -> (ConfigFlags -> UnresolvedPkgLoc                                     -> PackageDescription                                     -> PackageDescriptionOverride                                     -> a)                     -> a installReadyPackage platform cinfo configFlags-                    (ReadyPackage (ConfiguredPackage+                    (ReadyPackage (ConfiguredPackage ipid                                     (SourcePackage _ gpkg source pkgoverride)-                                    flags stanzas _)-                                   deps)+                                    flags stanzas deps))                     installPkg =   installPkg configFlags {+    configIPID = toFlag (display ipid),     configConfigurationsFlags = flags,     -- We generate the legacy constraints as well as the new style precise deps.     -- In the end only one set gets passed to Setup.hs configure, depending on     -- the Cabal version we are talking to.-    configConstraints  = [ thisPackageVersion (packageId deppkg)-                         | deppkg <- CD.nonSetupDeps deps ],-    configDependencies = [ (packageName (Installed.sourcePackageId deppkg),-                            Installed.installedUnitId deppkg)-                         | deppkg <- CD.nonSetupDeps deps ],+    configConstraints  = [ thisPackageVersion srcid+                         | ConfiguredId srcid (Just PackageDescription.CLibName) _ipid+                            <- CD.nonSetupDeps deps ],+    configDependencies = [ (packageName srcid, dep_ipid)+                         | ConfiguredId srcid (Just PackageDescription.CLibName) dep_ipid+                            <- CD.nonSetupDeps deps ],     -- Use '--exact-configuration' if supported.     configExactConfiguration = toFlag True,     configBenchmarks         = toFlag False,     configTests              = toFlag (TestStanzas `elem` stanzas)   } source pkg pkgoverride   where-    pkg = case finalizePackageDescription flags+    pkg = case finalizePD flags (enableStanzas stanzas)            (const True)-           platform cinfo [] (enableStanzas stanzas gpkg) of-      Left _ -> error "finalizePackageDescription ReadyPackage failed"+           platform cinfo [] gpkg of+      Left _ -> error "finalizePD ReadyPackage failed"       Right (desc, _) -> desc  fetchSourcePackage   :: Verbosity   -> RepoContext   -> JobLimit-  -> PackageLocation (Maybe FilePath)-  -> (PackageLocation FilePath -> IO BuildResult)-  -> IO BuildResult+  -> UnresolvedPkgLoc+  -> (ResolvedPkgLoc -> IO BuildOutcome)+  -> IO BuildOutcome fetchSourcePackage verbosity repoCtxt fetchLimit src installPkg = do   fetched <- checkFetched src   case fetched of@@ -1299,11 +1277,10 @@  installLocalPackage   :: Verbosity-  -> JobLimit-  -> PackageIdentifier -> PackageLocation FilePath -> FilePath-  -> (Maybe FilePath -> IO BuildResult)-  -> IO BuildResult-installLocalPackage verbosity jobLimit pkgid location distPref installPkg =+  -> PackageIdentifier -> ResolvedPkgLoc -> FilePath+  -> (Maybe FilePath -> IO BuildOutcome)+  -> IO BuildOutcome+installLocalPackage verbosity pkgid location distPref installPkg =    case location of @@ -1311,25 +1288,24 @@       installPkg (Just dir)      LocalTarballPackage tarballPath ->-      installLocalTarballPackage verbosity jobLimit+      installLocalTarballPackage verbosity         pkgid tarballPath distPref installPkg      RemoteTarballPackage _ tarballPath ->-      installLocalTarballPackage verbosity jobLimit+      installLocalTarballPackage verbosity         pkgid tarballPath distPref installPkg      RepoTarballPackage _ _ tarballPath ->-      installLocalTarballPackage verbosity jobLimit+      installLocalTarballPackage verbosity         pkgid tarballPath distPref installPkg   installLocalTarballPackage   :: Verbosity-  -> JobLimit   -> PackageIdentifier -> FilePath -> FilePath-  -> (Maybe FilePath -> IO BuildResult)-  -> IO BuildResult-installLocalTarballPackage verbosity jobLimit pkgid+  -> (Maybe FilePath -> IO BuildOutcome)+  -> IO BuildOutcome+installLocalTarballPackage verbosity pkgid                            tarballPath distPref installPkg = do   tmp <- getTemporaryDirectory   withTempDirectory verbosity tmp "cabal-tmp" $ \tmpDirPath ->@@ -1338,15 +1314,13 @@           absUnpackedPath = tmpDirPath </> relUnpackedPath           descFilePath = absUnpackedPath                      </> display (packageName pkgid) <.> "cabal"-      withJobLimit jobLimit $ do-        info verbosity $ "Extracting " ++ tarballPath-                      ++ " to " ++ tmpDirPath ++ "..."-        extractTarGzFile tmpDirPath relUnpackedPath tarballPath-        exists <- doesFileExist descFilePath-        when (not exists) $-          die $ "Package .cabal file not found: " ++ show descFilePath-        maybeRenameDistDir absUnpackedPath-+      info verbosity $ "Extracting " ++ tarballPath+                    ++ " to " ++ tmpDirPath ++ "..."+      extractTarGzFile tmpDirPath relUnpackedPath tarballPath+      exists <- doesFileExist descFilePath+      when (not exists) $+        die' verbosity $ "Package .cabal file not found: " ++ show descFilePath+      maybeRenameDistDir absUnpackedPath       installPkg (Just absUnpackedPath)    where@@ -1379,27 +1353,25 @@  installUnpackedPackage   :: Verbosity-  -> JobLimit   -> Lock   -> Int   -> SetupScriptOptions-  -> InstallMisc   -> ConfigFlags   -> InstallFlags   -> HaddockFlags-  -> CompilerInfo+  -> Compiler+  -> ProgramDb   -> Platform   -> PackageDescription   -> ReadyPackage   -> PackageDescriptionOverride   -> Maybe FilePath -- ^ Directory to change to before starting the installation.   -> UseLogFile -- ^ File to log output to (if any)-  -> IO BuildResult-installUnpackedPackage verbosity buildLimit installLock numJobs-                       scriptOptions miscOptions-                       configFlags installFlags haddockFlags-                       cinfo platform pkg rpkg pkgoverride workingDir useLogFile = do-+  -> IO BuildOutcome+installUnpackedPackage verbosity installLock numJobs+                       scriptOptions+                       configFlags installFlags haddockFlags comp progdb+                       platform pkg rpkg pkgoverride workingDir useLogFile = do   -- Override the .cabal file if necessary   case pkgoverride of     Nothing     -> return ()@@ -1411,15 +1383,9 @@                     ++ " with the latest revision from the index."       writeFileAtomic descFilePath pkgtxt -  -- Compute the IPID-  let flags (ReadyPackage (ConfiguredPackage _ x _ _) _) = x-      cid = Configure.computeComponentId (PackageDescription.package pkg) CLibName-                (map (\(SimpleUnitId cid0) -> cid0) (CD.libraryDeps (depends rpkg))) (flags rpkg)-      ipid = SimpleUnitId cid-   -- Make sure that we pass --libsubdir etc to 'setup configure' (necessary if   -- the setup script was compiled against an old version of the Cabal lib).-  configFlags' <- addDefaultInstallDirs ipid configFlags+  configFlags' <- addDefaultInstallDirs configFlags   -- Filter out flags not supported by the old versions of the Cabal lib.   let configureFlags :: Version -> ConfigFlags       configureFlags  = filterConfigureFlags configFlags' {@@ -1427,11 +1393,11 @@       }    -- Path to the optional log file.-  mLogPath <- maybeLogPath ipid+  mLogPath <- maybeLogPath -  logDirChange (maybe putStr appendFile mLogPath) workingDir $ do+  logDirChange (maybe (const (return ())) appendFile mLogPath) workingDir $ do     -- Configure phase-    onFailure ConfigureFailed $ withJobLimit buildLimit $ do+    onFailure ConfigureFailed $ do       when (numJobs > 1) $ notice verbosity $         "Configuring " ++ display pkgid ++ "..."       setup configureCommand configureFlags mLogPath@@ -1460,23 +1426,32 @@          -- Install phase           onFailure InstallFailed $ criticalSection installLock $ do-            -- Capture installed package configuration file-            maybePkgConf <- maybeGenPkgConf mLogPath-             -- Actual installation-            withWin32SelfUpgrade verbosity ipid configFlags+            withWin32SelfUpgrade verbosity uid configFlags                                  cinfo platform pkg $ do-              case rootCmd miscOptions of-                (Just cmd) -> reexec cmd-                Nothing    -> do-                  setup Cabal.copyCommand copyFlags mLogPath-                  when shouldRegister $ do-                    setup Cabal.registerCommand registerFlags mLogPath-            return (Right (BuildOk docsResult testsResult maybePkgConf))+              setup Cabal.copyCommand copyFlags mLogPath +            -- Capture installed package configuration file, so that+            -- it can be incorporated into the final InstallPlan+            ipkgs <- genPkgConfs mLogPath+            let ipkgs' = case ipkgs of+                            [ipkg] -> [ipkg { Installed.installedUnitId = uid }]+                            _ -> ipkgs+            let packageDBs = interpretPackageDbFlags+                                (fromFlag (configUserInstall configFlags))+                                (configPackageDBs configFlags)+            forM_ ipkgs' $ \ipkg' ->+                registerPackage verbosity comp progdb+                                packageDBs ipkg'+                                defaultRegisterOptions++            return (Right (BuildResult docsResult testsResult (find ((==uid).installedUnitId) ipkgs')))+   where     pkgid            = packageId pkg-    buildCommand'    = buildCommand defaultProgramConfiguration+    uid              = installedUnitId rpkg+    cinfo            = compilerInfo comp+    buildCommand'    = buildCommand progdb     buildFlags   _   = emptyBuildFlags {       buildDistPref  = configDistPref configFlags,       buildVerbosity = toFlag verbosity'@@ -1504,8 +1479,8 @@     verbosity' = maybe verbosity snd useLogFile     tempTemplate name = name ++ "-" ++ display pkgid -    addDefaultInstallDirs :: UnitId -> ConfigFlags -> IO ConfigFlags-    addDefaultInstallDirs ipid configFlags' = do+    addDefaultInstallDirs :: ConfigFlags -> IO ConfigFlags+    addDefaultInstallDirs configFlags' = do       defInstallDirs <- InstallDirs.defaultInstallDirs flavor userInstall False       return $ configFlags' {           configInstallDirs = fmap Cabal.Flag .@@ -1515,41 +1490,54 @@           }         where           CompilerId flavor _ = compilerInfoId cinfo-          env         = initialPathTemplateEnv pkgid ipid cinfo platform+          env         = initialPathTemplateEnv pkgid uid cinfo platform           userInstall = fromFlagOrDefault defaultUserInstall                         (configUserInstall configFlags') -    maybeGenPkgConf :: Maybe FilePath-                    -> IO (Maybe Installed.InstalledPackageInfo)-    maybeGenPkgConf mLogPath =+    genPkgConfs :: Maybe FilePath+                     -> IO [Installed.InstalledPackageInfo]+    genPkgConfs mLogPath =       if shouldRegister then do         tmp <- getTemporaryDirectory-        withTempFile tmp (tempTemplate "pkgConf") $ \pkgConfFile handle -> do-          hClose handle-          let registerFlags' version = (registerFlags version) {-                Cabal.regGenPkgConf = toFlag (Just pkgConfFile)+        withTempDirectory verbosity tmp (tempTemplate "pkgConf") $ \dir -> do+          let pkgConfDest = dir </> "pkgConf"+              registerFlags' version = (registerFlags version) {+                Cabal.regGenPkgConf = toFlag (Just pkgConfDest)               }           setup Cabal.registerCommand registerFlags' mLogPath-          withUTF8FileContents pkgConfFile $ \pkgConfText ->-            case Installed.parseInstalledPackageInfo pkgConfText of-              Installed.ParseFailed perror    -> pkgConfParseFailed perror-              Installed.ParseOk warns pkgConf -> do-                unless (null warns) $-                  warn verbosity $ unlines (map (showPWarning pkgConfFile) warns)-                return (Just pkgConf)-      else return Nothing+          is_dir <- doesDirectoryExist pkgConfDest+          let notHidden = not . isHidden+              isHidden name = "." `isPrefixOf` name+          if is_dir+            -- Sort so that each prefix of the package+            -- configurations is well formed+            then mapM (readPkgConf pkgConfDest) . sort . filter notHidden+                    =<< getDirectoryContents pkgConfDest+            else fmap (:[]) $ readPkgConf "." pkgConfDest+      else return [] +    readPkgConf :: FilePath -> FilePath+                -> IO Installed.InstalledPackageInfo+    readPkgConf pkgConfDir pkgConfFile =+      (withUTF8FileContents (pkgConfDir </> pkgConfFile) $ \pkgConfText ->+        case Installed.parseInstalledPackageInfo pkgConfText of+          Installed.ParseFailed perror    -> pkgConfParseFailed perror+          Installed.ParseOk warns pkgConf -> do+            unless (null warns) $+              warn verbosity $ unlines (map (showPWarning pkgConfFile) warns)+            return pkgConf)+     pkgConfParseFailed :: Installed.PError -> IO a     pkgConfParseFailed perror =-      die $ "Couldn't parse the output of 'setup register --gen-pkg-config':"+      die' verbosity $ "Couldn't parse the output of 'setup register --gen-pkg-config':"             ++ show perror -    maybeLogPath :: UnitId -> IO (Maybe FilePath)-    maybeLogPath ipid =+    maybeLogPath :: IO (Maybe FilePath)+    maybeLogPath =       case useLogFile of          Nothing                 -> return Nothing          Just (mkLogFileName, _) -> do-           let logFileName = mkLogFileName (packageId pkg) ipid+           let logFileName = mkLogFileName (packageId pkg) uid                logDir      = takeDirectory logFileName            unless (null logDir) $ createDirectoryIfMissing True logDir            logFileExists <- doesFileExist logFileName@@ -1567,28 +1555,16 @@           (Just pkg)           cmd flags []) -    reexec cmd = do-      -- look for our own executable file and re-exec ourselves using a helper-      -- program like sudo to elevate privileges:-      self <- getExecutablePath-      weExist <- doesFileExist self-      if weExist-        then inDir workingDir $-               rawSystemExit verbosity cmd-                 [self, "install", "--only"-                 ,"--verbose=" ++ showForCabal verbosity]-        else die $ "Unable to find cabal executable at: " ++ self - -- helper-onFailure :: (SomeException -> BuildFailure) -> IO BuildResult -> IO BuildResult+onFailure :: (SomeException -> BuildFailure) -> IO BuildOutcome -> IO BuildOutcome onFailure result action =   action `catches`     [ Handler $ \ioe  -> handler (ioe  :: IOException)     , Handler $ \exit -> handler (exit :: ExitCode)     ]   where-    handler :: Exception e => e -> IO BuildResult+    handler :: Exception e => e -> IO BuildOutcome     handler = return . Left . result . toException  @@ -1604,7 +1580,7 @@                      -> PackageDescription                      -> IO a -> IO a withWin32SelfUpgrade _ _ _ _ _ _ action | buildOS /= Windows = action-withWin32SelfUpgrade verbosity ipid configFlags cinfo platform pkg action = do+withWin32SelfUpgrade verbosity uid configFlags cinfo platform pkg action = do    defaultDirs <- InstallDirs.defaultInstallDirs                    compFlavor@@ -1622,7 +1598,7 @@       [ InstallDirs.bindir absoluteDirs </> exeName <.> exeExtension       | exe <- PackageDescription.executables pkg       , PackageDescription.buildable (PackageDescription.buildInfo exe)-      , let exeName = prefix ++ PackageDescription.exeName exe ++ suffix+      , let exeName = prefix ++ display (PackageDescription.exeName exe) ++ suffix             prefix  = substTemplate prefixTemplate             suffix  = substTemplate suffixTemplate ]       where@@ -1632,10 +1608,10 @@         templateDirs   = InstallDirs.combineInstallDirs fromFlagOrDefault                            defaultDirs (configInstallDirs configFlags)         absoluteDirs   = InstallDirs.absoluteInstallDirs-                           pkgid ipid+                           pkgid uid                            cinfo InstallDirs.NoCopyDest                            platform templateDirs         substTemplate  = InstallDirs.fromPathTemplate                        . InstallDirs.substPathTemplate env-          where env = InstallDirs.initialPathTemplateEnv pkgid ipid+          where env = InstallDirs.initialPathTemplateEnv pkgid uid                       cinfo platform
Distribution/Client/InstallPlan.hs view
@@ -1,788 +1,943 @@-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE DeriveGeneric #-}--------------------------------------------------------------------------------- |--- Module      :  Distribution.Client.InstallPlan--- Copyright   :  (c) Duncan Coutts 2008--- License     :  BSD-like------ Maintainer  :  duncan@community.haskell.org--- Stability   :  provisional--- Portability :  portable------ Package installation plan----------------------------------------------------------------------------------module Distribution.Client.InstallPlan (-  InstallPlan,-  GenericInstallPlan,-  PlanPackage,-  GenericPlanPackage(..),--  -- * Operations on 'InstallPlan's-  new,-  toList,-  mapPreservingGraph,--  ready,-  processing,-  completed,-  failed,-  remove,-  preexisting,-  preinstalled,--  showPlanIndex,-  showInstallPlan,--  -- * Checking validity of plans-  valid,-  closed,-  consistent,-  acyclic,--  -- ** Details on invalid plans-  PlanProblem(..),-  showPlanProblem,-  problems,--  -- ** Querying the install plan-  dependencyClosure,-  reverseDependencyClosure,-  topologicalOrder,-  reverseTopologicalOrder,-  ) where--import Distribution.InstalledPackageInfo-         ( InstalledPackageInfo )-import Distribution.Package-         ( PackageIdentifier(..), PackageName(..), Package(..)-         , HasUnitId(..), UnitId(..) )-import Distribution.Client.Types-         ( BuildSuccess, BuildFailure-         , PackageFixedDeps(..), ConfiguredPackage-         , GenericReadyPackage(..), fakeUnitId )-import Distribution.Version-         ( Version )-import Distribution.Client.ComponentDeps (ComponentDeps)-import qualified Distribution.Client.ComponentDeps as CD-import Distribution.Simple.PackageIndex-         ( PackageIndex )-import qualified Distribution.Simple.PackageIndex as PackageIndex-import Distribution.Client.PlanIndex-         ( FakeMap )-import qualified Distribution.Client.PlanIndex as PlanIndex-import Distribution.Text-         ( display )--import Data.List-         ( foldl', intercalate )-import Data.Maybe-         ( fromMaybe, catMaybes )-import qualified Data.Graph as Graph-import Data.Graph (Graph)-import qualified Data.Tree as Tree-import Distribution.Compat.Binary (Binary(..))-import GHC.Generics-import Control.Exception-         ( assert )-import qualified Data.Map as Map-import qualified Data.Traversable as T----- When cabal tries to install a number of packages, including all their--- dependencies it has a non-trivial problem to solve.------ The Problem:------ In general we start with a set of installed packages and a set of source--- packages.------ Installed packages have fixed dependencies. They have already been built and--- we know exactly what packages they were built against, including their exact--- versions.------ Source package have somewhat flexible dependencies. They are specified as--- version ranges, though really they're predicates. To make matters worse they--- have conditional flexible dependencies. Configuration flags can affect which--- packages are required and can place additional constraints on their--- versions.------ These two sets of package can and usually do overlap. There can be installed--- packages that are also available as source packages which means they could--- be re-installed if required, though there will also be packages which are--- not available as source and cannot be re-installed. Very often there will be--- extra versions available than are installed. Sometimes we may like to prefer--- installed packages over source ones or perhaps always prefer the latest--- available version whether installed or not.------ The goal is to calculate an installation plan that is closed, acyclic and--- consistent and where every configured package is valid.------ An installation plan is a set of packages that are going to be used--- together. It will consist of a mixture of installed packages and source--- packages along with their exact version dependencies. An installation plan--- is closed if for every package in the set, all of its dependencies are--- also in the set. It is consistent if for every package in the set, all--- dependencies which target that package have the same version.---- Note that plans do not necessarily compose. You might have a valid plan for--- package A and a valid plan for package B. That does not mean the composition--- is simultaneously valid for A and B. In particular you're most likely to--- have problems with inconsistent dependencies.--- On the other hand it is true that every closed sub plan is valid.---- | Packages in an install plan------ NOTE: 'ConfiguredPackage', 'GenericReadyPackage' and 'GenericPlanPackage'--- intentionally have no 'PackageInstalled' instance. `This is important:--- PackageInstalled returns only library dependencies, but for package that--- aren't yet installed we know many more kinds of dependencies (setup--- dependencies, exe, test-suite, benchmark, ..). Any functions that operate on--- dependencies in cabal-install should consider what to do with these--- dependencies; if we give a 'PackageInstalled' instance it would be too easy--- to get this wrong (and, for instance, call graph traversal functions from--- Cabal rather than from cabal-install). Instead, see 'PackageFixedDeps'.-data GenericPlanPackage ipkg srcpkg iresult ifailure-   = PreExisting ipkg-   | Configured  srcpkg-   | Processing  (GenericReadyPackage srcpkg ipkg)-   | Installed   (GenericReadyPackage srcpkg ipkg) (Maybe ipkg) iresult-   | Failed      srcpkg ifailure-  deriving (Eq, Show, Generic)--instance (Binary ipkg, Binary srcpkg, Binary  iresult, Binary  ifailure)-      => Binary (GenericPlanPackage ipkg srcpkg iresult ifailure)--type PlanPackage = GenericPlanPackage-                   InstalledPackageInfo ConfiguredPackage-                   BuildSuccess BuildFailure--instance (Package ipkg, Package srcpkg) =>-         Package (GenericPlanPackage ipkg srcpkg iresult ifailure) where-  packageId (PreExisting ipkg)     = packageId ipkg-  packageId (Configured  spkg)     = packageId spkg-  packageId (Processing  rpkg)     = packageId rpkg-  packageId (Installed   rpkg _ _) = packageId rpkg-  packageId (Failed      spkg   _) = packageId spkg--instance (PackageFixedDeps srcpkg,-          PackageFixedDeps ipkg, HasUnitId ipkg) =>-         PackageFixedDeps (GenericPlanPackage ipkg srcpkg iresult ifailure) where-  depends (PreExisting pkg)     = depends pkg-  depends (Configured  pkg)     = depends pkg-  depends (Processing  pkg)     = depends pkg-  depends (Installed   pkg _ _) = depends pkg-  depends (Failed      pkg   _) = depends pkg--instance (HasUnitId ipkg, HasUnitId srcpkg) =>-         HasUnitId-         (GenericPlanPackage ipkg srcpkg iresult ifailure) where-  installedUnitId (PreExisting ipkg ) = installedUnitId ipkg-  installedUnitId (Configured  spkg)  = installedUnitId spkg-  installedUnitId (Processing  rpkg)  = installedUnitId rpkg-  -- NB: defer to the actual installed package info in this case-  installedUnitId (Installed _ (Just ipkg) _) = installedUnitId ipkg-  installedUnitId (Installed rpkg _        _) = installedUnitId rpkg-  installedUnitId (Failed      spkg        _) = installedUnitId spkg---data GenericInstallPlan ipkg srcpkg iresult ifailure = GenericInstallPlan {-    planIndex      :: !(PlanIndex ipkg srcpkg iresult ifailure),-    planFakeMap    :: !FakeMap,-    planIndepGoals :: !Bool,--    -- | Cached (lazily) graph-    ---    -- The 'Graph' representaion works in terms of integer node ids, so we-    -- have to keep mapping to and from our meaningful nodes, which of course-    -- are package ids.-    ---    planGraph      :: Graph,-    planGraphRev   :: Graph,  -- ^ Reverse deps, transposed-    planPkgIdOf    :: Graph.Vertex -> UnitId, -- ^ mapping back to package ids-    planVertexOf   :: UnitId -> Graph.Vertex  -- ^ mapping into node ids-  }---- | Much like 'planPkgIdOf', but mapping back to full packages.-planPkgOf :: GenericInstallPlan ipkg srcpkg iresult ifailure-          -> Graph.Vertex-          -> GenericPlanPackage ipkg srcpkg iresult ifailure-planPkgOf plan v =-    case PackageIndex.lookupUnitId (planIndex plan)-                                   (planPkgIdOf plan v) of-      Just pkg -> pkg-      Nothing  -> error "InstallPlan: internal error: planPkgOf lookup failed"----- | 'GenericInstallPlan' specialised to most commonly used types.-type InstallPlan = GenericInstallPlan-                   InstalledPackageInfo ConfiguredPackage-                   BuildSuccess BuildFailure--type PlanIndex ipkg srcpkg iresult ifailure =-     PackageIndex (GenericPlanPackage ipkg srcpkg iresult ifailure)--invariant :: (HasUnitId ipkg,   PackageFixedDeps ipkg,-              HasUnitId srcpkg, PackageFixedDeps srcpkg)-          => GenericInstallPlan ipkg srcpkg iresult ifailure -> Bool-invariant plan =-    valid (planFakeMap plan)-          (planIndepGoals plan)-          (planIndex plan)---- | Smart constructor that deals with caching the 'Graph' representation.----mkInstallPlan :: (HasUnitId ipkg,   PackageFixedDeps ipkg,-                  HasUnitId srcpkg, PackageFixedDeps srcpkg)-              => PlanIndex ipkg srcpkg iresult ifailure-              -> FakeMap-              -> Bool-              -> GenericInstallPlan ipkg srcpkg iresult ifailure-mkInstallPlan index fakeMap indepGoals =-    GenericInstallPlan {-      planIndex      = index,-      planFakeMap    = fakeMap,-      planIndepGoals = indepGoals,--      -- lazily cache the graph stuff:-      planGraph      = graph,-      planGraphRev   = Graph.transposeG graph,-      planPkgIdOf    = vertexToPkgId,-      planVertexOf   = fromMaybe noSuchPkgId . pkgIdToVertex-    }-  where-    (graph, vertexToPkgId, pkgIdToVertex) =-      PlanIndex.dependencyGraph fakeMap index-    noSuchPkgId = internalError "package is not in the graph"--internalError :: String -> a-internalError msg = error $ "InstallPlan: internal error: " ++ msg--instance (HasUnitId ipkg,   PackageFixedDeps ipkg,-          HasUnitId srcpkg, PackageFixedDeps srcpkg,-          Binary ipkg, Binary srcpkg, Binary iresult, Binary ifailure)-       => Binary (GenericInstallPlan ipkg srcpkg iresult ifailure) where-    put GenericInstallPlan {-              planIndex      = index,-              planFakeMap    = fakeMap,-              planIndepGoals = indepGoals-        } = put (index, fakeMap, indepGoals)--    get = do-      (index, fakeMap, indepGoals) <- get-      return $! mkInstallPlan index fakeMap indepGoals--showPlanIndex :: (HasUnitId ipkg, HasUnitId srcpkg)-              => PlanIndex ipkg srcpkg iresult ifailure -> String-showPlanIndex index =-    intercalate "\n" (map showPlanPackage (PackageIndex.allPackages index))-  where showPlanPackage p =-            showPlanPackageTag p ++ " "-                ++ display (packageId p) ++ " ("-                ++ display (installedUnitId p) ++ ")"--showInstallPlan :: (HasUnitId ipkg, HasUnitId srcpkg)-                => GenericInstallPlan ipkg srcpkg iresult ifailure -> String-showInstallPlan plan =-    showPlanIndex (planIndex plan) ++ "\n" ++-    "fake map:\n  " ++-    intercalate "\n  " (map showKV (Map.toList (planFakeMap plan)))-  where showKV (k,v) = display k ++ " -> " ++ display v--showPlanPackageTag :: GenericPlanPackage ipkg srcpkg iresult ifailure -> String-showPlanPackageTag (PreExisting _)   = "PreExisting"-showPlanPackageTag (Configured  _)   = "Configured"-showPlanPackageTag (Processing  _)   = "Processing"-showPlanPackageTag (Installed _ _ _) = "Installed"-showPlanPackageTag (Failed    _   _) = "Failed"---- | Build an installation plan from a valid set of resolved packages.----new :: (HasUnitId ipkg,   PackageFixedDeps ipkg,-        HasUnitId srcpkg, PackageFixedDeps srcpkg)-    => Bool-    -> PlanIndex ipkg srcpkg iresult ifailure-    -> Either [PlanProblem ipkg srcpkg iresult ifailure]-              (GenericInstallPlan ipkg srcpkg iresult ifailure)-new indepGoals index =-  -- NB: Need to pre-initialize the fake-map with pre-existing-  -- packages-  let isPreExisting (PreExisting _) = True-      isPreExisting _ = False-      fakeMap = Map.fromList-              . map (\p -> (fakeUnitId (packageId p)-                           ,installedUnitId p))-              . filter isPreExisting-              $ PackageIndex.allPackages index in-  case problems fakeMap indepGoals index of-    []    -> Right (mkInstallPlan index fakeMap indepGoals)-    probs -> Left probs--toList :: GenericInstallPlan ipkg srcpkg iresult ifailure-       -> [GenericPlanPackage ipkg srcpkg iresult ifailure]-toList = PackageIndex.allPackages . planIndex---- | Remove packages from the install plan. This will result in an--- error if there are remaining packages that depend on any matching--- package. This is primarily useful for obtaining an install plan for--- the dependencies of a package or set of packages without actually--- installing the package itself, as when doing development.----remove :: (HasUnitId ipkg,   PackageFixedDeps ipkg,-           HasUnitId srcpkg, PackageFixedDeps srcpkg)-       => (GenericPlanPackage ipkg srcpkg iresult ifailure -> Bool)-       -> GenericInstallPlan ipkg srcpkg iresult ifailure-       -> Either [PlanProblem ipkg srcpkg iresult ifailure]-                 (GenericInstallPlan ipkg srcpkg iresult ifailure)-remove shouldRemove plan =-    new (planIndepGoals plan) newIndex-  where-    newIndex = PackageIndex.fromList $-                 filter (not . shouldRemove) (toList plan)---- | The packages that are ready to be installed. That is they are in the--- configured state and have all their dependencies installed already.--- The plan is complete if the result is @[]@.----ready :: forall ipkg srcpkg iresult ifailure. PackageFixedDeps srcpkg-      => GenericInstallPlan ipkg srcpkg iresult ifailure-      -> [GenericReadyPackage srcpkg ipkg]-ready plan = assert check readyPackages-  where-    check = if null readyPackages && null processingPackages-              then null configuredPackages-              else True-    configuredPackages = [ pkg | Configured pkg <- toList plan ]-    processingPackages = [ pkg | Processing pkg <- toList plan]--    readyPackages :: [GenericReadyPackage srcpkg ipkg]-    readyPackages = catMaybes (map (lookupReadyPackage plan) configuredPackages)--lookupReadyPackage :: forall ipkg srcpkg iresult ifailure.-                      PackageFixedDeps srcpkg-                   => GenericInstallPlan ipkg srcpkg iresult ifailure-                   -> srcpkg-                   -> Maybe (GenericReadyPackage srcpkg ipkg)-lookupReadyPackage plan pkg = do-    deps <- hasAllInstalledDeps pkg-    return (ReadyPackage pkg deps)-  where--    hasAllInstalledDeps :: srcpkg -> Maybe (ComponentDeps [ipkg])-    hasAllInstalledDeps = T.mapM (mapM isInstalledDep) . depends--    isInstalledDep :: UnitId -> Maybe ipkg-    isInstalledDep pkgid =-      -- NB: Need to check if the ID has been updated in planFakeMap, in which-      -- case we might be dealing with an old pointer-      case PlanIndex.fakeLookupUnitId-           (planFakeMap plan) (planIndex plan) pkgid-      of-        Just (PreExisting ipkg)            -> Just ipkg-        Just (Configured  _)               -> Nothing-        Just (Processing  _)               -> Nothing-        Just (Installed   _ (Just ipkg) _) -> Just ipkg-        Just (Installed   _ Nothing     _) -> internalError depOnNonLib-        Just (Failed      _             _) -> internalError depOnFailed-        Nothing                            -> internalError incomplete-    incomplete  = "install plan is not closed"-    depOnFailed = "configured package depends on failed package"-    depOnNonLib = "configured package depends on a non-library package"---- | Marks packages in the graph as currently processing (e.g. building).------ * The package must exist in the graph and be in the configured state.----processing :: (HasUnitId ipkg,   PackageFixedDeps ipkg,-               HasUnitId srcpkg, PackageFixedDeps srcpkg)-           => [GenericReadyPackage srcpkg ipkg]-           -> GenericInstallPlan ipkg srcpkg iresult ifailure-           -> GenericInstallPlan ipkg srcpkg iresult ifailure-processing pkgs plan = assert (invariant plan') plan'-  where-    plan' = plan {-              planIndex = PackageIndex.merge (planIndex plan) processingPkgs-            }-    processingPkgs = PackageIndex.fromList [Processing pkg | pkg <- pkgs]---- | Marks a package in the graph as completed. Also saves the build result for--- the completed package in the plan.------ * The package must exist in the graph and be in the processing state.--- * The package must have had no uninstalled dependent packages.----completed :: (HasUnitId ipkg,   PackageFixedDeps ipkg,-              HasUnitId srcpkg, PackageFixedDeps srcpkg)-          => UnitId-          -> Maybe ipkg -> iresult-          -> GenericInstallPlan ipkg srcpkg iresult ifailure-          -> GenericInstallPlan ipkg srcpkg iresult ifailure-completed pkgid mipkg buildResult plan = assert (invariant plan') plan'-  where-    plan'     = plan {-                  -- NB: installation can change the IPID, so better-                  -- record it in the fake mapping...-                  planFakeMap = insert_fake_mapping mipkg-                              $ planFakeMap plan,-                  planIndex = PackageIndex.insert installed-                            . PackageIndex.deleteUnitId pkgid-                            $ planIndex plan-                }-    -- ...but be sure to use the *old* IPID for the lookup for the-    -- preexisting record-    installed = Installed (lookupProcessingPackage plan pkgid) mipkg buildResult-    insert_fake_mapping (Just ipkg) = Map.insert pkgid (installedUnitId ipkg)-    insert_fake_mapping  _          = id---- | Marks a package in the graph as having failed. It also marks all the--- packages that depended on it as having failed.------ * The package must exist in the graph and be in the processing--- state.----failed :: (HasUnitId ipkg,   PackageFixedDeps ipkg,-           HasUnitId srcpkg, PackageFixedDeps srcpkg)-       => UnitId         -- ^ The id of the package that failed to install-       -> ifailure           -- ^ The build result to use for the failed package-       -> ifailure           -- ^ The build result to use for its dependencies-       -> GenericInstallPlan ipkg srcpkg iresult ifailure-       -> GenericInstallPlan ipkg srcpkg iresult ifailure-failed pkgid buildResult buildResult' plan = assert (invariant plan') plan'-  where-    -- NB: failures don't update IPIDs-    plan'    = plan {-                 planIndex = PackageIndex.merge (planIndex plan) failures-               }-    ReadyPackage srcpkg _deps = lookupProcessingPackage plan pkgid-    failures = PackageIndex.fromList-             $ Failed srcpkg buildResult-             : [ Failed pkg' buildResult'-               | Just pkg' <- map checkConfiguredPackage-                            $ packagesThatDependOn plan pkgid ]---- | Lookup the reachable packages in the reverse dependency graph.----packagesThatDependOn :: GenericInstallPlan ipkg srcpkg iresult ifailure-                     -> UnitId-                     -> [GenericPlanPackage ipkg srcpkg iresult ifailure]-packagesThatDependOn plan pkgid = map (planPkgOf plan)-                          . tail-                          . Graph.reachable (planGraphRev plan)-                          . planVertexOf plan-                          $ Map.findWithDefault pkgid pkgid (planFakeMap plan)---- | Lookup a package that we expect to be in the processing state.----lookupProcessingPackage :: GenericInstallPlan ipkg srcpkg iresult ifailure-                        -> UnitId-                        -> GenericReadyPackage srcpkg ipkg-lookupProcessingPackage plan pkgid =-  -- NB: processing packages are guaranteed to not indirect through-  -- planFakeMap-  case PackageIndex.lookupUnitId (planIndex plan) pkgid of-    Just (Processing pkg) -> pkg-    _  -> internalError $ "not in processing state or no such pkg " ++-                          display pkgid---- | Check a package that we expect to be in the configured or failed state.----checkConfiguredPackage :: (Package srcpkg, Package ipkg)-                       => GenericPlanPackage ipkg srcpkg iresult ifailure-                       -> Maybe srcpkg-checkConfiguredPackage (Configured pkg) = Just pkg-checkConfiguredPackage (Failed     _ _) = Nothing-checkConfiguredPackage pkg                =-  internalError $ "not configured or no such pkg " ++ display (packageId pkg)---- | Replace a ready package with a pre-existing one. The pre-existing one--- must have exactly the same dependencies as the source one was configured--- with.----preexisting :: (HasUnitId ipkg,   PackageFixedDeps ipkg,-                HasUnitId srcpkg, PackageFixedDeps srcpkg)-            => UnitId-            -> ipkg-            -> GenericInstallPlan ipkg srcpkg iresult ifailure-            -> GenericInstallPlan ipkg srcpkg iresult ifailure-preexisting pkgid ipkg plan = assert (invariant plan') plan'-  where-    plan' = plan {-                    -- NB: installation can change the IPID, so better-                    -- record it in the fake mapping...-      planFakeMap = Map.insert pkgid-                               (installedUnitId ipkg)-                               (planFakeMap plan),-      planIndex   = PackageIndex.insert (PreExisting ipkg)-                    -- ...but be sure to use the *old* IPID for the lookup for-                    -- the preexisting record-                  . PackageIndex.deleteUnitId pkgid-                  $ planIndex plan-    }---- | Replace a ready package with an installed one. The installed one--- must have exactly the same dependencies as the source one was configured--- with.----preinstalled :: (HasUnitId ipkg,   PackageFixedDeps ipkg,-                 HasUnitId srcpkg, PackageFixedDeps srcpkg)-             => UnitId-             -> Maybe ipkg -> iresult-             -> GenericInstallPlan ipkg srcpkg iresult ifailure-             -> GenericInstallPlan ipkg srcpkg iresult ifailure-preinstalled pkgid mipkg buildResult plan = assert (invariant plan') plan'-  where-    plan' = plan { planIndex = PackageIndex.insert installed (planIndex plan) }-    Just installed = do-      Configured pkg <- PackageIndex.lookupUnitId (planIndex plan) pkgid-      rpkg <- lookupReadyPackage plan pkg-      return (Installed rpkg mipkg buildResult)---- | Transform an install plan by mapping a function over all the packages in--- the plan. It can consistently change the 'UnitId' of all the packages,--- while preserving the same overall graph structure.------ The mapping function has a few constraints on it for correct operation.--- The mapping function /may/ change the 'UnitId' of the package, but it--- /must/ also remap the 'UnitId's of its dependencies using ths supplied--- remapping function. Apart from this consistent remapping it /may not/--- change the structure of the dependencies.----mapPreservingGraph :: (HasUnitId ipkg,-                       HasUnitId srcpkg,-                       HasUnitId ipkg',   PackageFixedDeps ipkg',-                       HasUnitId srcpkg', PackageFixedDeps srcpkg')-                   => (  (UnitId -> UnitId)-                      -> GenericPlanPackage ipkg  srcpkg  iresult  ifailure-                      -> GenericPlanPackage ipkg' srcpkg' iresult' ifailure')-                   -> GenericInstallPlan ipkg  srcpkg  iresult  ifailure-                   -> GenericInstallPlan ipkg' srcpkg' iresult' ifailure'-mapPreservingGraph f plan =-    mkInstallPlan (PackageIndex.fromList pkgs')-                  Map.empty -- empty fakeMap-                  (planIndepGoals plan)-  where-    -- The package mapping function may change the UnitId. So we-    -- walk over the packages in dependency order keeping track of these-    -- package id changes and use it to supply the correct set of package-    -- dependencies as an extra input to the package mapping function.-    ---    -- Having fully remapped all the deps this also means we can use an empty-    -- FakeMap for the resulting install plan.--    (_, pkgs') = foldl' f' (Map.empty, []) (reverseTopologicalOrder plan)--    f' (ipkgidMap, pkgs) pkg = (ipkgidMap', pkg' : pkgs)-      where-       pkg' = f (mapDep ipkgidMap) pkg--       ipkgidMap'-         | ipkgid /= ipkgid' = Map.insert ipkgid ipkgid' ipkgidMap-         | otherwise         =                           ipkgidMap-         where-           ipkgid  = installedUnitId pkg-           ipkgid' = installedUnitId pkg'--    mapDep ipkgidMap ipkgid = Map.findWithDefault ipkgid ipkgid ipkgidMap----- --------------------------------------------------------------- * Checking validity of plans--- ---------------------------------------------------------------- | A valid installation plan is a set of packages that is 'acyclic',--- 'closed' and 'consistent'. Also, every 'ConfiguredPackage' in the--- plan has to have a valid configuration (see 'configuredPackageValid').------ * if the result is @False@ use 'problems' to get a detailed list.----valid :: (HasUnitId ipkg,   PackageFixedDeps ipkg,-          HasUnitId srcpkg, PackageFixedDeps srcpkg)-      => FakeMap -> Bool-      -> PlanIndex ipkg srcpkg iresult ifailure-      -> Bool-valid fakeMap indepGoals index =-    null $ problems fakeMap indepGoals index--data PlanProblem ipkg srcpkg iresult ifailure =-     PackageMissingDeps   (GenericPlanPackage ipkg srcpkg iresult ifailure)-                          [PackageIdentifier]-   | PackageCycle         [GenericPlanPackage ipkg srcpkg iresult ifailure]-   | PackageInconsistency PackageName [(PackageIdentifier, Version)]-   | PackageStateInvalid  (GenericPlanPackage ipkg srcpkg iresult ifailure)-                          (GenericPlanPackage ipkg srcpkg iresult ifailure)--showPlanProblem :: (Package ipkg, Package srcpkg)-                => PlanProblem ipkg srcpkg iresult ifailure -> String-showPlanProblem (PackageMissingDeps pkg missingDeps) =-     "Package " ++ display (packageId pkg)-  ++ " depends on the following packages which are missing from the plan: "-  ++ intercalate ", " (map display missingDeps)--showPlanProblem (PackageCycle cycleGroup) =-     "The following packages are involved in a dependency cycle "-  ++ intercalate ", " (map (display.packageId) cycleGroup)--showPlanProblem (PackageInconsistency name inconsistencies) =-     "Package " ++ display name-  ++ " is required by several packages,"-  ++ " but they require inconsistent versions:\n"-  ++ unlines [ "  package " ++ display pkg ++ " requires "-                            ++ display (PackageIdentifier name ver)-             | (pkg, ver) <- inconsistencies ]--showPlanProblem (PackageStateInvalid pkg pkg') =-     "Package " ++ display (packageId pkg)-  ++ " is in the " ++ showPlanState pkg-  ++ " state but it depends on package " ++ display (packageId pkg')-  ++ " which is in the " ++ showPlanState pkg'-  ++ " state"-  where-    showPlanState (PreExisting _)   = "pre-existing"-    showPlanState (Configured  _)   = "configured"-    showPlanState (Processing  _)   = "processing"-    showPlanState (Installed _ _ _) = "installed"-    showPlanState (Failed    _   _) = "failed"---- | For an invalid plan, produce a detailed list of problems as human readable--- error messages. This is mainly intended for debugging purposes.--- Use 'showPlanProblem' for a human readable explanation.----problems :: (HasUnitId ipkg,   PackageFixedDeps ipkg,-             HasUnitId srcpkg, PackageFixedDeps srcpkg)-         => FakeMap -> Bool-         -> PlanIndex ipkg srcpkg iresult ifailure-         -> [PlanProblem ipkg srcpkg iresult ifailure]-problems fakeMap indepGoals index =--     [ PackageMissingDeps pkg-       (catMaybes-        (map-         (fmap packageId . PlanIndex.fakeLookupUnitId fakeMap index)-         missingDeps))-     | (pkg, missingDeps) <- PlanIndex.brokenPackages fakeMap index ]--  ++ [ PackageCycle cycleGroup-     | cycleGroup <- PlanIndex.dependencyCycles fakeMap index ]--  ++ [ PackageInconsistency name inconsistencies-     | (name, inconsistencies) <--       PlanIndex.dependencyInconsistencies fakeMap indepGoals index ]--  ++ [ PackageStateInvalid pkg pkg'-     | pkg <- PackageIndex.allPackages index-     , Just pkg' <- map (PlanIndex.fakeLookupUnitId fakeMap index)-                    (CD.flatDeps (depends pkg))-     , not (stateDependencyRelation pkg pkg') ]---- | The graph of packages (nodes) and dependencies (edges) must be acyclic.------ * if the result is @False@ use 'PackageIndex.dependencyCycles' to find out---   which packages are involved in dependency cycles.----acyclic :: (HasUnitId ipkg,   PackageFixedDeps ipkg,-            HasUnitId srcpkg, PackageFixedDeps srcpkg)-        => FakeMap -> PlanIndex ipkg srcpkg iresult ifailure -> Bool-acyclic fakeMap = null . PlanIndex.dependencyCycles fakeMap---- | An installation plan is closed if for every package in the set, all of--- its dependencies are also in the set. That is, the set is closed under the--- dependency relation.------ * if the result is @False@ use 'PackageIndex.brokenPackages' to find out---   which packages depend on packages not in the index.----closed :: (HasUnitId ipkg, PackageFixedDeps ipkg,-           PackageFixedDeps srcpkg)-       => FakeMap -> PlanIndex ipkg srcpkg iresult ifailure -> Bool-closed fakeMap = null . PlanIndex.brokenPackages fakeMap---- | An installation plan is consistent if all dependencies that target a--- single package name, target the same version.------ This is slightly subtle. It is not the same as requiring that there be at--- most one version of any package in the set. It only requires that of--- packages which have more than one other package depending on them. We could--- actually make the condition even more precise and say that different--- versions are OK so long as they are not both in the transitive closure of--- any other package (or equivalently that their inverse closures do not--- intersect). The point is we do not want to have any packages depending--- directly or indirectly on two different versions of the same package. The--- current definition is just a safe approximation of that.------ * if the result is @False@ use 'PackageIndex.dependencyInconsistencies' to---   find out which packages are.----consistent :: (HasUnitId ipkg,   PackageFixedDeps ipkg,-               HasUnitId srcpkg, PackageFixedDeps srcpkg)-           => FakeMap -> PlanIndex ipkg srcpkg iresult ifailure -> Bool-consistent fakeMap = null . PlanIndex.dependencyInconsistencies fakeMap False---- | The states of packages have that depend on each other must respect--- this relation. That is for very case where package @a@ depends on--- package @b@ we require that @dependencyStatesOk a b = True@.----stateDependencyRelation :: GenericPlanPackage ipkg srcpkg iresult ifailure-                        -> GenericPlanPackage ipkg srcpkg iresult ifailure-                        -> Bool-stateDependencyRelation (PreExisting _) (PreExisting _)   = True--stateDependencyRelation (Configured  _) (PreExisting _)   = True-stateDependencyRelation (Configured  _) (Configured  _)   = True-stateDependencyRelation (Configured  _) (Processing  _)   = True-stateDependencyRelation (Configured  _) (Installed _ _ _) = True--stateDependencyRelation (Processing  _) (PreExisting _)   = True-stateDependencyRelation (Processing  _) (Installed _ _ _) = True--stateDependencyRelation (Installed _ _ _) (PreExisting _)   = True-stateDependencyRelation (Installed _ _ _) (Installed _ _ _) = True--stateDependencyRelation (Failed    _ _) (PreExisting _)   = True--- failed can depends on configured because a package can depend on--- several other packages and if one of the deps fail then we fail--- but we still depend on the other ones that did not fail:-stateDependencyRelation (Failed    _ _) (Configured  _)   = True-stateDependencyRelation (Failed    _ _) (Processing  _)   = True-stateDependencyRelation (Failed    _ _) (Installed _ _ _) = True-stateDependencyRelation (Failed    _ _) (Failed    _   _) = True--stateDependencyRelation _               _                 = False----- | Compute the dependency closure of a package in a install plan----dependencyClosure :: GenericInstallPlan ipkg srcpkg iresult ifailure-                  -> [UnitId]-                  -> [GenericPlanPackage ipkg srcpkg iresult ifailure]-dependencyClosure plan =-    map (planPkgOf plan)-  . concatMap Tree.flatten-  . Graph.dfs (planGraph plan)-  . map (planVertexOf plan)---reverseDependencyClosure :: GenericInstallPlan ipkg srcpkg iresult ifailure-                         -> [UnitId]-                         -> [GenericPlanPackage ipkg srcpkg iresult ifailure]-reverseDependencyClosure plan =-    map (planPkgOf plan)-  . concatMap Tree.flatten-  . Graph.dfs (planGraphRev plan)-  . map (planVertexOf plan)---topologicalOrder :: GenericInstallPlan ipkg srcpkg iresult ifailure-                 -> [GenericPlanPackage ipkg srcpkg iresult ifailure]-topologicalOrder plan =-    map (planPkgOf plan)-  . Graph.topSort-  $ planGraph plan---reverseTopologicalOrder :: GenericInstallPlan ipkg srcpkg iresult ifailure-                        -> [GenericPlanPackage ipkg srcpkg iresult ifailure]-reverseTopologicalOrder plan =-    map (planPkgOf plan)-  . Graph.topSort-  $ planGraphRev plan+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE FlexibleContexts #-}+-----------------------------------------------------------------------------+-- |+-- Module      :  Distribution.Client.InstallPlan+-- Copyright   :  (c) Duncan Coutts 2008+-- License     :  BSD-like+--+-- Maintainer  :  duncan@community.haskell.org+-- Stability   :  provisional+-- Portability :  portable+--+-- Package installation plan+--+-----------------------------------------------------------------------------+module Distribution.Client.InstallPlan (+  InstallPlan,+  GenericInstallPlan,+  PlanPackage,+  GenericPlanPackage(..),+  foldPlanPackage,+  IsUnit,++  -- * Operations on 'InstallPlan's+  new,+  toGraph,+  toList,+  toMap,+  keys,+  keysSet,+  planIndepGoals,+  depends,++  fromSolverInstallPlan,+  fromSolverInstallPlanWithProgress,+  configureInstallPlan,+  remove,+  installed,+  lookup,+  directDeps,+  revDirectDeps,++  -- * Traversal+  executionOrder,+  execute,+  BuildOutcomes,+  lookupBuildOutcome,+  -- ** Traversal helpers+  -- $traversal+  Processing,+  ready,+  completed,+  failed,++  -- * Display+  showPlanGraph,+  showInstallPlan,++  -- * Graph-like operations+  reverseTopologicalOrder,+  reverseDependencyClosure,+  ) where++import Distribution.Client.Types hiding (BuildOutcomes)+import qualified Distribution.PackageDescription as PD+import qualified Distribution.Simple.Configure as Configure+import qualified Distribution.Simple.Setup as Cabal++import Distribution.InstalledPackageInfo+         ( InstalledPackageInfo )+import Distribution.Package+         ( Package(..), HasMungedPackageId(..)+         , HasUnitId(..), UnitId )+import Distribution.Solver.Types.SolverPackage+import Distribution.Client.JobControl+import Distribution.Text+import Text.PrettyPrint+import qualified Distribution.Client.SolverInstallPlan as SolverInstallPlan+import Distribution.Client.SolverInstallPlan (SolverInstallPlan)++import qualified Distribution.Solver.Types.ComponentDeps as CD+import           Distribution.Solver.Types.Settings+import           Distribution.Solver.Types.SolverId+import           Distribution.Solver.Types.InstSolverPackage++import           Distribution.Utils.LogProgress++-- TODO: Need this when we compute final UnitIds+-- import qualified Distribution.Simple.Configure as Configure++import Data.List+         ( foldl', intercalate )+import qualified Data.Foldable as Foldable (all)+import Data.Maybe+         ( fromMaybe, catMaybes )+import qualified Distribution.Compat.Graph as Graph+import Distribution.Compat.Graph (Graph, IsNode(..))+import Distribution.Compat.Binary (Binary(..))+import GHC.Generics+import Data.Typeable+import Control.Monad+import Control.Exception+         ( assert )+import qualified Data.Map as Map+import           Data.Map (Map)+import qualified Data.Set as Set+import           Data.Set (Set)++import Prelude hiding (lookup)+++-- When cabal tries to install a number of packages, including all their+-- dependencies it has a non-trivial problem to solve.+--+-- The Problem:+--+-- In general we start with a set of installed packages and a set of source+-- packages.+--+-- Installed packages have fixed dependencies. They have already been built and+-- we know exactly what packages they were built against, including their exact+-- versions.+--+-- Source package have somewhat flexible dependencies. They are specified as+-- version ranges, though really they're predicates. To make matters worse they+-- have conditional flexible dependencies. Configuration flags can affect which+-- packages are required and can place additional constraints on their+-- versions.+--+-- These two sets of package can and usually do overlap. There can be installed+-- packages that are also available as source packages which means they could+-- be re-installed if required, though there will also be packages which are+-- not available as source and cannot be re-installed. Very often there will be+-- extra versions available than are installed. Sometimes we may like to prefer+-- installed packages over source ones or perhaps always prefer the latest+-- available version whether installed or not.+--+-- The goal is to calculate an installation plan that is closed, acyclic and+-- consistent and where every configured package is valid.+--+-- An installation plan is a set of packages that are going to be used+-- together. It will consist of a mixture of installed packages and source+-- packages along with their exact version dependencies. An installation plan+-- is closed if for every package in the set, all of its dependencies are+-- also in the set. It is consistent if for every package in the set, all+-- dependencies which target that package have the same version.++-- Note that plans do not necessarily compose. You might have a valid plan for+-- package A and a valid plan for package B. That does not mean the composition+-- is simultaneously valid for A and B. In particular you're most likely to+-- have problems with inconsistent dependencies.+-- On the other hand it is true that every closed sub plan is valid.++-- | Packages in an install plan+--+-- NOTE: 'ConfiguredPackage', 'GenericReadyPackage' and 'GenericPlanPackage'+-- intentionally have no 'PackageInstalled' instance. `This is important:+-- PackageInstalled returns only library dependencies, but for package that+-- aren't yet installed we know many more kinds of dependencies (setup+-- dependencies, exe, test-suite, benchmark, ..). Any functions that operate on+-- dependencies in cabal-install should consider what to do with these+-- dependencies; if we give a 'PackageInstalled' instance it would be too easy+-- to get this wrong (and, for instance, call graph traversal functions from+-- Cabal rather than from cabal-install). Instead, see 'PackageInstalled'.+data GenericPlanPackage ipkg srcpkg+   = PreExisting ipkg+   | Configured  srcpkg+   | Installed   srcpkg+  deriving (Eq, Show, Generic)++-- | Convenience combinator for destructing 'GenericPlanPackage'.+-- This is handy because if you case manually, you have to handle+-- 'Configured' and 'Installed' separately (where often you want+-- them to be the same.)+foldPlanPackage :: (ipkg -> a)+                -> (srcpkg -> a)+                -> GenericPlanPackage ipkg srcpkg+                -> a+foldPlanPackage f _ (PreExisting ipkg)  = f ipkg+foldPlanPackage _ g (Configured srcpkg) = g srcpkg+foldPlanPackage _ g (Installed  srcpkg) = g srcpkg++type IsUnit a = (IsNode a, Key a ~ UnitId)++depends :: IsUnit a => a -> [UnitId]+depends = nodeNeighbors++-- NB: Expanded constraint synonym here to avoid undecidable+-- instance errors in GHC 7.8 and earlier.+instance (IsNode ipkg, IsNode srcpkg, Key ipkg ~ UnitId, Key srcpkg ~ UnitId)+         => IsNode (GenericPlanPackage ipkg srcpkg) where+    type Key (GenericPlanPackage ipkg srcpkg) = UnitId+    nodeKey (PreExisting ipkg) = nodeKey ipkg+    nodeKey (Configured  spkg) = nodeKey spkg+    nodeKey (Installed   spkg) = nodeKey spkg+    nodeNeighbors (PreExisting ipkg) = nodeNeighbors ipkg+    nodeNeighbors (Configured  spkg) = nodeNeighbors spkg+    nodeNeighbors (Installed   spkg) = nodeNeighbors spkg++instance (Binary ipkg, Binary srcpkg)+      => Binary (GenericPlanPackage ipkg srcpkg)++type PlanPackage = GenericPlanPackage+                   InstalledPackageInfo (ConfiguredPackage UnresolvedPkgLoc)++instance (Package ipkg, Package srcpkg) =>+         Package (GenericPlanPackage ipkg srcpkg) where+  packageId (PreExisting ipkg)     = packageId ipkg+  packageId (Configured  spkg)     = packageId spkg+  packageId (Installed   spkg)     = packageId spkg++instance (HasMungedPackageId ipkg, HasMungedPackageId srcpkg) =>+         HasMungedPackageId (GenericPlanPackage ipkg srcpkg) where+  mungedId (PreExisting ipkg)     = mungedId ipkg+  mungedId (Configured  spkg)     = mungedId spkg+  mungedId (Installed   spkg)     = mungedId spkg++instance (HasUnitId ipkg, HasUnitId srcpkg) =>+         HasUnitId+         (GenericPlanPackage ipkg srcpkg) where+  installedUnitId (PreExisting ipkg) = installedUnitId ipkg+  installedUnitId (Configured  spkg) = installedUnitId spkg+  installedUnitId (Installed   spkg) = installedUnitId spkg++instance (HasConfiguredId ipkg, HasConfiguredId srcpkg) =>+          HasConfiguredId (GenericPlanPackage ipkg srcpkg) where+    configuredId (PreExisting ipkg) = configuredId ipkg+    configuredId (Configured  spkg) = configuredId spkg+    configuredId (Installed   spkg) = configuredId spkg++data GenericInstallPlan ipkg srcpkg = GenericInstallPlan {+    planGraph      :: !(Graph (GenericPlanPackage ipkg srcpkg)),+    planIndepGoals :: !IndependentGoals+  }+  deriving (Typeable)++-- | 'GenericInstallPlan' specialised to most commonly used types.+type InstallPlan = GenericInstallPlan+                   InstalledPackageInfo (ConfiguredPackage UnresolvedPkgLoc)++-- | Smart constructor that deals with caching the 'Graph' representation.+--+mkInstallPlan :: (IsUnit ipkg, IsUnit srcpkg)+              => String+              -> Graph (GenericPlanPackage ipkg srcpkg)+              -> IndependentGoals+              -> GenericInstallPlan ipkg srcpkg+mkInstallPlan loc graph indepGoals =+    assert (valid loc graph)+    GenericInstallPlan {+      planGraph      = graph,+      planIndepGoals = indepGoals+    }++internalError :: String -> String -> a+internalError loc msg = error $ "internal error in InstallPlan." ++ loc+                             ++ if null msg then "" else ": " ++ msg++instance (IsNode ipkg, Key ipkg ~ UnitId, IsNode srcpkg, Key srcpkg ~ UnitId,+          Binary ipkg, Binary srcpkg)+       => Binary (GenericInstallPlan ipkg srcpkg) where+    put GenericInstallPlan {+              planGraph      = graph,+              planIndepGoals = indepGoals+        } = put (graph, indepGoals)++    get = do+      (graph, indepGoals) <- get+      return $! mkInstallPlan "(instance Binary)" graph indepGoals++showPlanGraph :: (Package ipkg, Package srcpkg,+                  IsUnit ipkg, IsUnit srcpkg)+              => Graph (GenericPlanPackage ipkg srcpkg) -> String+showPlanGraph graph = renderStyle defaultStyle $+    vcat (map dispPlanPackage (Graph.toList graph))+  where dispPlanPackage p =+            hang (hsep [ text (showPlanPackageTag p)+                       , disp (packageId p)+                       , parens (disp (nodeKey p))]) 2+                 (vcat (map disp (nodeNeighbors p)))++showInstallPlan :: (Package ipkg, Package srcpkg,+                    IsUnit ipkg, IsUnit srcpkg)+                => GenericInstallPlan ipkg srcpkg -> String+showInstallPlan = showPlanGraph . planGraph++showPlanPackageTag :: GenericPlanPackage ipkg srcpkg -> String+showPlanPackageTag (PreExisting _)   = "PreExisting"+showPlanPackageTag (Configured  _)   = "Configured"+showPlanPackageTag (Installed   _)   = "Installed"++-- | Build an installation plan from a valid set of resolved packages.+--+new :: (IsUnit ipkg, IsUnit srcpkg)+    => IndependentGoals+    -> Graph (GenericPlanPackage ipkg srcpkg)+    -> GenericInstallPlan ipkg srcpkg+new indepGoals graph = mkInstallPlan "new" graph indepGoals++toGraph :: GenericInstallPlan ipkg srcpkg+        -> Graph (GenericPlanPackage ipkg srcpkg)+toGraph = planGraph++toList :: GenericInstallPlan ipkg srcpkg+       -> [GenericPlanPackage ipkg srcpkg]+toList = Graph.toList . planGraph++toMap :: GenericInstallPlan ipkg srcpkg+      -> Map UnitId (GenericPlanPackage ipkg srcpkg)+toMap = Graph.toMap . planGraph++keys :: GenericInstallPlan ipkg srcpkg -> [UnitId]+keys = Graph.keys . planGraph++keysSet :: GenericInstallPlan ipkg srcpkg -> Set UnitId+keysSet = Graph.keysSet . planGraph++-- | Remove packages from the install plan. This will result in an+-- error if there are remaining packages that depend on any matching+-- package. This is primarily useful for obtaining an install plan for+-- the dependencies of a package or set of packages without actually+-- installing the package itself, as when doing development.+--+remove :: (IsUnit ipkg, IsUnit srcpkg)+       => (GenericPlanPackage ipkg srcpkg -> Bool)+       -> GenericInstallPlan ipkg srcpkg+       -> GenericInstallPlan ipkg srcpkg+remove shouldRemove plan =+    mkInstallPlan "remove" newGraph (planIndepGoals plan)+  where+    newGraph = Graph.fromDistinctList $+                 filter (not . shouldRemove) (toList plan)++-- | Change a number of packages in the 'Configured' state to the 'Installed'+-- state.+--+-- To preserve invariants, the package must have all of its dependencies+-- already installed too (that is 'PreExisting' or 'Installed').+--+installed :: (IsUnit ipkg, IsUnit srcpkg)+          => (srcpkg -> Bool)+          -> GenericInstallPlan ipkg srcpkg+          -> GenericInstallPlan ipkg srcpkg+installed shouldBeInstalled installPlan =+    foldl' markInstalled installPlan+      [ pkg+      | Configured pkg <- reverseTopologicalOrder installPlan+      , shouldBeInstalled pkg ]+  where+    markInstalled plan pkg =+      assert (all isInstalled (directDeps plan (nodeKey pkg))) $+      plan {+        planGraph = Graph.insert (Installed pkg) (planGraph plan)+      }++-- | Lookup a package in the plan.+--+lookup :: (IsUnit ipkg, IsUnit srcpkg)+       => GenericInstallPlan ipkg srcpkg+       -> UnitId+       -> Maybe (GenericPlanPackage ipkg srcpkg)+lookup plan pkgid = Graph.lookup pkgid (planGraph plan)++-- | Find all the direct dependencies of the given package.+--+-- Note that the package must exist in the plan or it is an error.+--+directDeps :: GenericInstallPlan ipkg srcpkg+           -> UnitId+           -> [GenericPlanPackage ipkg srcpkg]+directDeps plan pkgid =+  case Graph.neighbors (planGraph plan) pkgid of+    Just deps -> deps+    Nothing   -> internalError "directDeps" "package not in graph"++-- | Find all the direct reverse dependencies of the given package.+--+-- Note that the package must exist in the plan or it is an error.+--+revDirectDeps :: GenericInstallPlan ipkg srcpkg+              -> UnitId+              -> [GenericPlanPackage ipkg srcpkg]+revDirectDeps plan pkgid =+  case Graph.revNeighbors (planGraph plan) pkgid of+    Just deps -> deps+    Nothing   -> internalError "revDirectDeps" "package not in graph"++-- | Return all the packages in the 'InstallPlan' in reverse topological order.+-- That is, for each package, all dependencies of the package appear first.+--+-- Compared to 'executionOrder', this function returns all the installed and+-- source packages rather than just the source ones. Also, while both this+-- and 'executionOrder' produce reverse topological orderings of the package+-- dependency graph, it is not necessarily exactly the same order.+--+reverseTopologicalOrder :: GenericInstallPlan ipkg srcpkg+                        -> [GenericPlanPackage ipkg srcpkg]+reverseTopologicalOrder plan = Graph.revTopSort (planGraph plan)+++-- | Return the packages in the plan that depend directly or indirectly on the+-- given packages.+--+reverseDependencyClosure :: GenericInstallPlan ipkg srcpkg+                         -> [UnitId]+                         -> [GenericPlanPackage ipkg srcpkg]+reverseDependencyClosure plan = fromMaybe []+                              . Graph.revClosure (planGraph plan)+++-- Alert alert!   Why does SolverId map to a LIST of plan packages?+-- The sordid story has to do with 'build-depends' on a package+-- with libraries and executables.  In an ideal world, we would+-- ONLY depend on the library in this situation.  But c.f. #3661+-- some people rely on the build-depends to ALSO implicitly+-- depend on an executable.+--+-- I don't want to commit to a strategy yet, so the only possible+-- thing you can do in this case is return EVERYTHING and let+-- the client filter out what they want (executables? libraries?+-- etc).  This similarly implies we can't return a 'ConfiguredId'+-- because that's not enough information.++fromSolverInstallPlan ::+      (IsUnit ipkg, IsUnit srcpkg)+    => (   (SolverId -> [GenericPlanPackage ipkg srcpkg])+        -> SolverInstallPlan.SolverPlanPackage+        -> [GenericPlanPackage ipkg srcpkg]         )+    -> SolverInstallPlan+    -> GenericInstallPlan ipkg srcpkg+fromSolverInstallPlan f plan =+    mkInstallPlan "fromSolverInstallPlan"+      (Graph.fromDistinctList pkgs'')+      (SolverInstallPlan.planIndepGoals plan)+  where+    (_, _, pkgs'') = foldl' f' (Map.empty, Map.empty, [])+                        (SolverInstallPlan.reverseTopologicalOrder plan)++    f' (pidMap, ipiMap, pkgs) pkg = (pidMap', ipiMap', pkgs' ++ pkgs)+      where+       pkgs' = f (mapDep pidMap ipiMap) pkg++       (pidMap', ipiMap')+         = case nodeKey pkg of+            PreExistingId _ uid -> (pidMap, Map.insert uid pkgs' ipiMap)+            PlannedId     pid   -> (Map.insert pid pkgs' pidMap, ipiMap)++    mapDep _ ipiMap (PreExistingId _pid uid)+        | Just pkgs <- Map.lookup uid ipiMap = pkgs+        | otherwise = error ("fromSolverInstallPlan: PreExistingId " ++ display uid)+    mapDep pidMap _ (PlannedId pid)+        | Just pkgs <- Map.lookup pid pidMap = pkgs+        | otherwise = error ("fromSolverInstallPlan: PlannedId " ++ display pid)+    -- This shouldn't happen, since mapDep should only be called+    -- on neighbor SolverId, which must have all been done already+    -- by the reverse top-sort (we assume the graph is not broken).+++fromSolverInstallPlanWithProgress ::+      (IsUnit ipkg, IsUnit srcpkg)+    => (   (SolverId -> [GenericPlanPackage ipkg srcpkg])+        -> SolverInstallPlan.SolverPlanPackage+        -> LogProgress [GenericPlanPackage ipkg srcpkg]         )+    -> SolverInstallPlan+    -> LogProgress (GenericInstallPlan ipkg srcpkg)+fromSolverInstallPlanWithProgress f plan = do+    (_, _, pkgs'') <- foldM f' (Map.empty, Map.empty, [])+                        (SolverInstallPlan.reverseTopologicalOrder plan)+    return $ mkInstallPlan "fromSolverInstallPlanWithProgress"+               (Graph.fromDistinctList pkgs'')+               (SolverInstallPlan.planIndepGoals plan)+  where+    f' (pidMap, ipiMap, pkgs) pkg = do+        pkgs' <- f (mapDep pidMap ipiMap) pkg+        let (pidMap', ipiMap')+                 = case nodeKey pkg of+                    PreExistingId _ uid -> (pidMap, Map.insert uid pkgs' ipiMap)+                    PlannedId     pid   -> (Map.insert pid pkgs' pidMap, ipiMap)+        return (pidMap', ipiMap', pkgs' ++ pkgs)++    mapDep _ ipiMap (PreExistingId _pid uid)+        | Just pkgs <- Map.lookup uid ipiMap = pkgs+        | otherwise = error ("fromSolverInstallPlan: PreExistingId " ++ display uid)+    mapDep pidMap _ (PlannedId pid)+        | Just pkgs <- Map.lookup pid pidMap = pkgs+        | otherwise = error ("fromSolverInstallPlan: PlannedId " ++ display pid)+    -- This shouldn't happen, since mapDep should only be called+    -- on neighbor SolverId, which must have all been done already+    -- by the reverse top-sort (we assume the graph is not broken).++-- | Conversion of 'SolverInstallPlan' to 'InstallPlan'.+-- Similar to 'elaboratedInstallPlan'+configureInstallPlan :: Cabal.ConfigFlags -> SolverInstallPlan -> InstallPlan+configureInstallPlan configFlags solverPlan =+    flip fromSolverInstallPlan solverPlan $ \mapDep planpkg ->+      [case planpkg of+        SolverInstallPlan.PreExisting pkg ->+          PreExisting (instSolverPkgIPI pkg)++        SolverInstallPlan.Configured  pkg ->+          Configured (configureSolverPackage mapDep pkg)+      ]+  where+    configureSolverPackage :: (SolverId -> [PlanPackage])+                           -> SolverPackage UnresolvedPkgLoc+                           -> ConfiguredPackage UnresolvedPkgLoc+    configureSolverPackage mapDep spkg =+      ConfiguredPackage {+        confPkgId = Configure.computeComponentId+                        (Cabal.fromFlagOrDefault False+                            (Cabal.configDeterministic configFlags))+                        Cabal.NoFlag+                        Cabal.NoFlag+                        (packageId spkg)+                        PD.CLibName+                        (Just (map confInstId (CD.libraryDeps deps),+                               solverPkgFlags spkg)),+        confPkgSource = solverPkgSource spkg,+        confPkgFlags  = solverPkgFlags spkg,+        confPkgStanzas = solverPkgStanzas spkg,+        confPkgDeps   = deps+        -- NB: no support for executable dependencies+      }+      where+        deps = fmap (concatMap (map configuredId . mapDep)) (solverPkgLibDeps spkg)+++-- ------------------------------------------------------------+-- * Primitives for traversing plans+-- ------------------------------------------------------------++-- $traversal+--+-- Algorithms to traverse or execute an 'InstallPlan', especially in parallel,+-- may make use of the 'Processing' type and the associated operations+-- 'ready', 'completed' and 'failed'.+--+-- The 'Processing' type is used to keep track of the state of a traversal and+-- includes the set of packages that are in the processing state, e.g. in the+-- process of being installed, plus those that have been completed and those+-- where processing failed.+--+-- Traversal algorithms start with an 'InstallPlan':+--+-- * Initially there will be certain packages that can be processed immediately+--   (since they are configured source packages and have all their dependencies+--   installed already). The function 'ready' returns these packages plus a+--   'Processing' state that marks these same packages as being in the+--   processing state.+--+-- * The algorithm must now arrange for these packages to be processed+--   (possibly in parallel). When a package has completed processing, the+--   algorithm needs to know which other packages (if any) are now ready to+--   process as a result. The 'completed' function marks a package as completed+--   and returns any packages that are newly in the processing state (ie ready+--   to process), along with the updated 'Processing' state.+--+-- * If failure is possible then when processing a package fails, the algorithm+--   needs to know which other packages have also failed as a result. The+--   'failed' function marks the given package as failed as well as all the+--   other packages that depend on the failed package. In addition it returns+--   the other failed packages.+++-- | The 'Processing' type is used to keep track of the state of a traversal+-- and includes the set of packages that are in the processing state, e.g. in+-- the process of being installed, plus those that have been completed and+-- those where processing failed.+--+data Processing = Processing !(Set UnitId) !(Set UnitId) !(Set UnitId)+                            -- processing,   completed,    failed++-- | The packages in the plan that are initially ready to be installed.+-- That is they are in the configured state and have all their dependencies+-- installed already.+--+-- The result is both the packages that are now ready to be installed and also+-- a 'Processing' state containing those same packages. The assumption is that+-- all the packages that are ready will now be processed and so we can consider+-- them to be in the processing state.+--+ready :: (IsUnit ipkg, IsUnit srcpkg)+      => GenericInstallPlan ipkg srcpkg+      -> ([GenericReadyPackage srcpkg], Processing)+ready plan =+    assert (processingInvariant plan processing) $+    (readyPackages, processing)+  where+    !processing =+      Processing+        (Set.fromList [ nodeKey pkg | pkg <- readyPackages ])+        (Set.fromList [ nodeKey pkg | pkg <- toList plan, isInstalled pkg ])+        Set.empty+    readyPackages =+      [ ReadyPackage pkg+      | Configured pkg <- toList plan+      , all isInstalled (directDeps plan (nodeKey pkg))+      ]++isInstalled :: GenericPlanPackage a b -> Bool+isInstalled (PreExisting {}) = True+isInstalled (Installed   {}) = True+isInstalled _                = False++-- | Given a package in the processing state, mark the package as completed+-- and return any packages that are newly in the processing state (ie ready to+-- process), along with the updated 'Processing' state.+--+completed :: (IsUnit ipkg, IsUnit srcpkg)+          => GenericInstallPlan ipkg srcpkg+          -> Processing -> UnitId+          -> ([GenericReadyPackage srcpkg], Processing)+completed plan (Processing processingSet completedSet failedSet) pkgid =+    assert (pkgid `Set.member` processingSet) $+    assert (processingInvariant plan processing') $++    ( map asReadyPackage newlyReady+    , processing' )+  where+    completedSet'  = Set.insert pkgid completedSet++    -- each direct reverse dep where all direct deps are completed+    newlyReady     = [ dep+                     | dep <- revDirectDeps plan pkgid+                     , all ((`Set.member` completedSet') . nodeKey)+                           (directDeps plan (nodeKey dep))+                     ]++    processingSet' = foldl' (flip Set.insert)+                            (Set.delete pkgid processingSet)+                            (map nodeKey newlyReady)+    processing'    = Processing processingSet' completedSet' failedSet++    asReadyPackage (Configured pkg) = ReadyPackage pkg+    asReadyPackage _ = internalError "completed" ""++failed :: (IsUnit ipkg, IsUnit srcpkg)+       => GenericInstallPlan ipkg srcpkg+       -> Processing -> UnitId+       -> ([srcpkg], Processing)+failed plan (Processing processingSet completedSet failedSet) pkgid =+    assert (pkgid `Set.member` processingSet) $+    assert (all (`Set.notMember` processingSet) (tail newlyFailedIds)) $+    assert (all (`Set.notMember` completedSet)  (tail newlyFailedIds)) $+    -- but note that some newlyFailed may already be in the failed set+    -- since one package can depend on two packages that both fail and+    -- so would be in the rev-dep closure for both.+    assert (processingInvariant plan processing') $++    ( map asConfiguredPackage (tail newlyFailed)+    , processing' )+  where+    processingSet' = Set.delete pkgid processingSet+    failedSet'     = failedSet `Set.union` Set.fromList newlyFailedIds+    newlyFailedIds = map nodeKey newlyFailed+    newlyFailed    = fromMaybe (internalError "failed" "package not in graph")+                   $ Graph.revClosure (planGraph plan) [pkgid]+    processing'    = Processing processingSet' completedSet failedSet'++    asConfiguredPackage (Configured pkg) = pkg+    asConfiguredPackage _ = internalError "failed" "not in configured state"++processingInvariant :: (IsUnit ipkg, IsUnit srcpkg)+                    => GenericInstallPlan ipkg srcpkg+                    -> Processing -> Bool+processingInvariant plan (Processing processingSet completedSet failedSet) =++    -- All the packages in the three sets are actually in the graph+    assert (Foldable.all (flip Graph.member (planGraph plan)) processingSet) $+    assert (Foldable.all (flip Graph.member (planGraph plan)) completedSet) $+    assert (Foldable.all (flip Graph.member (planGraph plan)) failedSet) $++    -- The processing, completed and failed sets are disjoint from each other+    assert (noIntersection processingSet completedSet) $+    assert (noIntersection processingSet failedSet) $+    assert (noIntersection failedSet     completedSet) $++    -- Packages that depend on a package that's still processing cannot be+    -- completed+    assert (noIntersection (reverseClosure processingSet) completedSet) $++    -- On the other hand, packages that depend on a package that's still+    -- processing /can/ have failed (since they may have depended on multiple+    -- packages that were processing, but it only takes one to fail to cause+    -- knock-on failures) so it is quite possible to have an+    -- intersection (reverseClosure processingSet) failedSet++    -- The failed set is upwards closed, i.e. equal to its own rev dep closure+    assert (failedSet == reverseClosure failedSet) $++    -- All immediate reverse deps of packges that are currently processing+    -- are not currently being processed (ie not in the processing set).+    assert (and [ rdeppkgid `Set.notMember` processingSet+                | pkgid     <- Set.toList processingSet+                , rdeppkgid <- maybe (internalError "processingInvariant" "")+                                     (map nodeKey)+                                     (Graph.revNeighbors (planGraph plan) pkgid)+                ]) $++    -- Packages from the processing or failed sets are only ever in the+    -- configured state.+    assert (and [ case Graph.lookup pkgid (planGraph plan) of+                    Just (Configured  _) -> True+                    Just (PreExisting _) -> False+                    Just (Installed   _) -> False+                    Nothing              -> False+                | pkgid <- Set.toList processingSet ++ Set.toList failedSet ])++    -- We use asserts rather than returning False so that on failure we get+    -- better details on which bit of the invariant was violated.+    True+  where+    reverseClosure    = Set.fromList+                      . map nodeKey+                      . fromMaybe (internalError "processingInvariant" "")+                      . Graph.revClosure (planGraph plan)+                      . Set.toList+    noIntersection a b = Set.null (Set.intersection a b)+++-- ------------------------------------------------------------+-- * Traversing plans+-- ------------------------------------------------------------++-- | Flatten an 'InstallPlan', producing the sequence of source packages in+-- the order in which they would be processed when the plan is executed. This+-- can be used for simultations or presenting execution dry-runs.+--+-- It is guaranteed to give the same order as using 'execute' (with a serial+-- in-order 'JobControl'), which is a reverse topological orderings of the+-- source packages in the dependency graph, albeit not necessarily exactly the+-- same ordering as that produced by 'reverseTopologicalOrder'.+--+executionOrder :: (IsUnit ipkg, IsUnit srcpkg)+               => GenericInstallPlan ipkg srcpkg+               -> [GenericReadyPackage srcpkg]+executionOrder plan =+    let (newpkgs, processing) = ready plan+     in tryNewTasks processing newpkgs+  where+    tryNewTasks _processing []       = []+    tryNewTasks  processing (p:todo) = waitForTasks processing p todo++    waitForTasks processing p todo =+        p : tryNewTasks processing' (todo++nextpkgs)+      where+        (nextpkgs, processing') = completed plan processing (nodeKey p)+++-- ------------------------------------------------------------+-- * Executing plans+-- ------------------------------------------------------------++-- | The set of results we get from executing an install plan.+--+type BuildOutcomes failure result = Map UnitId (Either failure result)++-- | Lookup the build result for a single package.+--+lookupBuildOutcome :: HasUnitId pkg+                   => pkg -> BuildOutcomes failure result+                   -> Maybe (Either failure result)+lookupBuildOutcome = Map.lookup . installedUnitId++-- | Execute an install plan. This traverses the plan in dependency order.+--+-- Executing each individual package can fail and if so all dependents fail+-- too. The result for each package is collected as a 'BuildOutcomes' map.+--+-- Visiting each package happens with optional parallelism, as determined by+-- the 'JobControl'. By default, after any failure we stop as soon as possible+-- (using the 'JobControl' to try to cancel in-progress tasks). This behaviour+-- can be reversed to keep going and build as many packages as possible.+--+-- Note that the 'BuildOutcomes' is /not/ guaranteed to cover all the packages+-- in the plan. In particular in the default mode where we stop as soon as+-- possible after a failure then there may be packages which are skipped and+-- these will have no 'BuildOutcome'.+--+execute :: forall m ipkg srcpkg result failure.+           (IsUnit ipkg, IsUnit srcpkg,+            Monad m)+        => JobControl m (UnitId, Either failure result)+        -> Bool                -- ^ Keep going after failure+        -> (srcpkg -> failure) -- ^ Value for dependents of failed packages+        -> GenericInstallPlan ipkg srcpkg+        -> (GenericReadyPackage srcpkg -> m (Either failure result))+        -> m (BuildOutcomes failure result)+execute jobCtl keepGoing depFailure plan installPkg =+    let (newpkgs, processing) = ready plan+     in tryNewTasks Map.empty False False processing newpkgs+  where+    tryNewTasks :: BuildOutcomes failure result+                -> Bool -> Bool -> Processing+                -> [GenericReadyPackage srcpkg]+                -> m (BuildOutcomes failure result)++    tryNewTasks !results tasksFailed tasksRemaining !processing newpkgs+      -- we were in the process of cancelling and now we're finished+      | tasksFailed && not keepGoing && not tasksRemaining+      = return results++      -- we are still in the process of cancelling, wait for remaining tasks+      | tasksFailed && not keepGoing && tasksRemaining+      = waitForTasks results tasksFailed processing++      -- no new tasks to do and all tasks are done so we're finished+      | null newpkgs && not tasksRemaining+      = return results++      -- no new tasks to do, remaining tasks to wait for+      | null newpkgs+      = waitForTasks results tasksFailed processing++      -- new tasks to do, spawn them, then wait for tasks to complete+      | otherwise+      = do sequence_ [ spawnJob jobCtl $ do+                         result <- installPkg pkg+                         return (nodeKey pkg, result)+                     | pkg <- newpkgs ]+           waitForTasks results tasksFailed processing++    waitForTasks :: BuildOutcomes failure result+                 -> Bool -> Processing+                 -> m (BuildOutcomes failure result)+    waitForTasks !results tasksFailed !processing = do+      (pkgid, result) <- collectJob jobCtl++      case result of++        Right _success -> do+            tasksRemaining <- remainingJobs jobCtl+            tryNewTasks results' tasksFailed tasksRemaining+                        processing' nextpkgs+          where+            results' = Map.insert pkgid result results+            (nextpkgs, processing') = completed plan processing pkgid++        Left _failure -> do+            -- if this is the first failure and we're not trying to keep going+            -- then try to cancel as many of the remaining jobs as possible+            when (not tasksFailed && not keepGoing) $+              cancelJobs jobCtl++            tasksRemaining <- remainingJobs jobCtl+            tryNewTasks results' True tasksRemaining processing' []+          where+            (depsfailed, processing') = failed plan processing pkgid+            results'   = Map.insert pkgid result results `Map.union` depResults+            depResults = Map.fromList+                           [ (nodeKey deppkg, Left (depFailure deppkg))+                           | deppkg <- depsfailed ]++-- ------------------------------------------------------------+-- * Checking validity of plans+-- ------------------------------------------------------------++-- | A valid installation plan is a set of packages that is closed, acyclic+-- and respects the package state relation.+--+-- * if the result is @False@ use 'problems' to get a detailed list.+--+valid :: (IsUnit ipkg, IsUnit srcpkg)+      => String -> Graph (GenericPlanPackage ipkg srcpkg) -> Bool+valid loc graph =+    case problems graph of+      [] -> True+      ps -> internalError loc ('\n' : unlines (map showPlanProblem ps))++data PlanProblem ipkg srcpkg =+     PackageMissingDeps   (GenericPlanPackage ipkg srcpkg) [UnitId]+   | PackageCycle         [GenericPlanPackage ipkg srcpkg]+   | PackageStateInvalid  (GenericPlanPackage ipkg srcpkg)+                          (GenericPlanPackage ipkg srcpkg)++showPlanProblem :: (IsUnit ipkg, IsUnit srcpkg)+                => PlanProblem ipkg srcpkg -> String+showPlanProblem (PackageMissingDeps pkg missingDeps) =+     "Package " ++ display (nodeKey pkg)+  ++ " depends on the following packages which are missing from the plan: "+  ++ intercalate ", " (map display missingDeps)++showPlanProblem (PackageCycle cycleGroup) =+     "The following packages are involved in a dependency cycle "+  ++ intercalate ", " (map (display . nodeKey) cycleGroup)+showPlanProblem (PackageStateInvalid pkg pkg') =+     "Package " ++ display (nodeKey pkg)+  ++ " is in the " ++ showPlanPackageTag pkg+  ++ " state but it depends on package " ++ display (nodeKey pkg')+  ++ " which is in the " ++ showPlanPackageTag pkg'+  ++ " state"++-- | For an invalid plan, produce a detailed list of problems as human readable+-- error messages. This is mainly intended for debugging purposes.+-- Use 'showPlanProblem' for a human readable explanation.+--+problems :: (IsUnit ipkg, IsUnit srcpkg)+         => Graph (GenericPlanPackage ipkg srcpkg)+         -> [PlanProblem ipkg srcpkg]+problems graph =++     [ PackageMissingDeps pkg+       (catMaybes+        (map+         (fmap nodeKey . flip Graph.lookup graph)+         missingDeps))+     | (pkg, missingDeps) <- Graph.broken graph ]++  ++ [ PackageCycle cycleGroup+     | cycleGroup <- Graph.cycles graph ]+{-+  ++ [ PackageInconsistency name inconsistencies+     | (name, inconsistencies) <-+       dependencyInconsistencies indepGoals graph ]+     --TODO: consider re-enabling this one, see SolverInstallPlan+-}+  ++ [ PackageStateInvalid pkg pkg'+     | pkg <- Graph.toList graph+     , Just pkg' <- map (flip Graph.lookup graph)+                    (nodeNeighbors pkg)+     , not (stateDependencyRelation pkg pkg') ]++-- | The states of packages have that depend on each other must respect+-- this relation. That is for very case where package @a@ depends on+-- package @b@ we require that @stateDependencyRelation a b = True@.+--+stateDependencyRelation :: GenericPlanPackage ipkg srcpkg+                        -> GenericPlanPackage ipkg srcpkg -> Bool+stateDependencyRelation PreExisting{} PreExisting{} = True++stateDependencyRelation Installed{}   PreExisting{} = True+stateDependencyRelation Installed{}   Installed{}   = True++stateDependencyRelation Configured{}  PreExisting{} = True+stateDependencyRelation Configured{}  Installed{}   = True+stateDependencyRelation Configured{}  Configured{}  = True++stateDependencyRelation _             _             = False
Distribution/Client/InstallSymlink.hs view
@@ -16,10 +16,12 @@     symlinkBinary,   ) where -#if mingw32_HOST_OS+#ifdef mingw32_HOST_OS  import Distribution.Package (PackageIdentifier)+import Distribution.Types.UnqualComponentName import Distribution.Client.InstallPlan (InstallPlan)+import Distribution.Client.Types (BuildOutcomes) import Distribution.Client.Setup (InstallFlags) import Distribution.Simple.Setup (ConfigFlags) import Distribution.Simple.Compiler@@ -28,33 +30,36 @@ symlinkBinaries :: Platform -> Compiler                 -> ConfigFlags                 -> InstallFlags-                -> InstallPlan -                -> IO [(PackageIdentifier, String, FilePath)]-symlinkBinaries _ _ _ _ _ = return []+                -> InstallPlan+                -> BuildOutcomes+                -> IO [(PackageIdentifier, UnqualComponentName, FilePath)]+symlinkBinaries _ _ _ _ _ _ = return [] -symlinkBinary :: FilePath -> FilePath -> String -> String -> IO Bool+symlinkBinary :: FilePath -> FilePath -> UnqualComponentName -> String -> IO Bool symlinkBinary _ _ _ _ = fail "Symlinking feature not available on Windows"  #else  import Distribution.Client.Types-         ( SourcePackage(..)-         , GenericReadyPackage(..), ReadyPackage, enableStanzas-         , ConfiguredPackage(..) , fakeUnitId)+         ( ConfiguredPackage(..), BuildOutcomes ) import Distribution.Client.Setup          ( InstallFlags(installSymlinkBinDir) ) import qualified Distribution.Client.InstallPlan as InstallPlan import Distribution.Client.InstallPlan (InstallPlan) +import Distribution.Solver.Types.SourcePackage+import Distribution.Solver.Types.OptionalStanza+ import Distribution.Package-         ( PackageIdentifier, Package(packageId), UnitId(..) )+         ( PackageIdentifier, Package(packageId), UnitId, installedUnitId )+import Distribution.Types.UnqualComponentName import Distribution.Compiler          ( CompilerId(..) ) import qualified Distribution.PackageDescription as PackageDescription import Distribution.PackageDescription          ( PackageDescription ) import Distribution.PackageDescription.Configuration-         ( finalizePackageDescription )+         ( finalizePD ) import Distribution.Simple.Setup          ( ConfigFlags(..), fromFlag, fromFlagOrDefault, flagToMaybe ) import qualified Distribution.Simple.InstallDirs as InstallDirs@@ -62,6 +67,8 @@          ( Compiler, compilerInfo, CompilerInfo(..) ) import Distribution.System          ( Platform )+import Distribution.Text+         ( display )  import System.Posix.Files          ( getSymbolicLinkStatus, isSymbolicLink, createSymbolicLink@@ -104,8 +111,9 @@                 -> ConfigFlags                 -> InstallFlags                 -> InstallPlan-                -> IO [(PackageIdentifier, String, FilePath)]-symlinkBinaries platform comp configFlags installFlags plan =+                -> BuildOutcomes+                -> IO [(PackageIdentifier, UnqualComponentName, FilePath)]+symlinkBinaries platform comp configFlags installFlags plan buildOutcomes =   case flagToMaybe (installSymlinkBinDir installFlags) of     Nothing            -> return []     Just symlinkBinDir@@ -123,31 +131,32 @@                then return Nothing                else return (Just (pkgid, publicExeName,                                   privateBinDir </> privateExeName))-        | (ReadyPackage (ConfiguredPackage _ _flags _ _) _, pkg, exe) <- exes+        | (rpkg, pkg, exe) <- exes         , let pkgid  = packageId pkg               -- This is a bit dodgy; probably won't work for Backpack packages-              ipid = fakeUnitId pkgid+              ipid = installedUnitId rpkg               publicExeName  = PackageDescription.exeName exe-              privateExeName = prefix ++ publicExeName ++ suffix+              privateExeName = prefix ++ unUnqualComponentName publicExeName ++ suffix               prefix = substTemplate pkgid ipid prefixTemplate               suffix = substTemplate pkgid ipid suffixTemplate ]   where     exes =       [ (cpkg, pkg, exe)-      | InstallPlan.Installed cpkg _ _ <- InstallPlan.toList plan-      , let pkg   = pkgDescription cpkg+      | InstallPlan.Configured cpkg <- InstallPlan.toList plan+      , case InstallPlan.lookupBuildOutcome cpkg buildOutcomes of+          Just (Right _success) -> True+          _                     -> False+      , let pkg :: PackageDescription+            pkg = pkgDescription cpkg       , exe <- PackageDescription.executables pkg       , PackageDescription.buildable (PackageDescription.buildInfo exe) ] -    pkgDescription :: ReadyPackage -> PackageDescription-    pkgDescription (ReadyPackage (ConfiguredPackage-                                    (SourcePackage _ pkg _ _)-                                    flags stanzas _)-                                  _) =-      case finalizePackageDescription flags+    pkgDescription (ConfiguredPackage _ (SourcePackage _ pkg _ _)+                                      flags stanzas _) =+      case finalizePD flags (enableStanzas stanzas)              (const True)-             platform cinfo [] (enableStanzas stanzas pkg) of-        Left _ -> error "finalizePackageDescription ReadyPackage failed"+             platform cinfo [] pkg of+        Left _ -> error "finalizePD ReadyPackage failed"         Right (desc, _) -> desc      -- This is sadly rather complicated. We're kind of re-doing part of the@@ -177,30 +186,32 @@     cinfo            = compilerInfo comp     (CompilerId compilerFlavor _) = compilerInfoId cinfo -symlinkBinary :: FilePath -- ^ The canonical path of the public bin dir-                          --   eg @/home/user/bin@-              -> FilePath -- ^ The canonical path of the private bin dir-                          --   eg @/home/user/.cabal/bin@-              -> String   -- ^ The name of the executable to go in the public-                          --   bin dir, eg @foo@-              -> String   -- ^ The name of the executable to in the private bin-                          --   dir, eg @foo-1.0@-              -> IO Bool  -- ^ If creating the symlink was successful. @False@-                          --   if there was another file there already that we-                          --   did not own. Other errors like permission errors-                          --   just propagate as exceptions.+symlinkBinary ::+  FilePath               -- ^ The canonical path of the public bin dir eg+                         --   @/home/user/bin@+  -> FilePath            -- ^ The canonical path of the private bin dir eg+                         --   @/home/user/.cabal/bin@+  -> UnqualComponentName -- ^ The name of the executable to go in the public bin+                         --   dir, eg @foo@+  -> String              -- ^ The name of the executable to in the private bin+                         --   dir, eg @foo-1.0@+  -> IO Bool             -- ^ If creating the symlink was successful. @False@ if+                         --   there was another file there already that we did+                         --   not own. Other errors like permission errors just+                         --   propagate as exceptions. symlinkBinary publicBindir privateBindir publicName privateName = do-  ok <- targetOkToOverwrite (publicBindir </> publicName)+  ok <- targetOkToOverwrite (publicBindir </> publicName')                             (privateBindir </> privateName)   case ok of     NotOurFile    ->                     return False     NotExists     ->           mkLink >> return True     OkToOverwrite -> rmLink >> mkLink >> return True   where+    publicName' = display publicName     relativeBindir = makeRelative publicBindir privateBindir     mkLink = createSymbolicLink (relativeBindir </> privateName)-                                (publicBindir   </> publicName)-    rmLink = removeLink (publicBindir </> publicName)+                                (publicBindir   </> publicName')+    rmLink = removeLink (publicBindir </> publicName')  -- | Check a file path of a symlink that we would like to create to see if it -- is OK. For it to be OK to overwrite it must either not already exist yet or
Distribution/Client/JobControl.hs view
@@ -16,6 +16,8 @@     newParallelJobControl,     spawnJob,     collectJob,+    remainingJobs,+    cancelJobs,      JobLimit,     newJobLimit,@@ -27,48 +29,131 @@   ) where  import Control.Monad-import Control.Concurrent hiding (QSem, newQSem, waitQSem, signalQSem)-import Control.Exception (SomeException, bracket_, mask, throw, try)+import Control.Concurrent (forkIO)+import Control.Concurrent.MVar+import Control.Concurrent.STM (STM, atomically)+import Control.Concurrent.STM.TVar+import Control.Concurrent.STM.TChan+import Control.Exception (SomeException, bracket_, throwIO, try) import Distribution.Client.Compat.Semaphore ++-- | A simple concurrency abstraction. Jobs can be spawned and can complete+-- in any order. This allows both serial and parallel implementations.+-- data JobControl m a = JobControl {+       -- | Add a new job to the pool of jobs        spawnJob    :: m a -> m (),-       collectJob  :: m a++       -- | Wait until one job is complete+       collectJob  :: m a,++       -- | Returns True if there are any outstanding jobs+       -- (ie spawned but yet to be collected)+       remainingJobs :: m Bool,++       -- | Try to cancel any outstanding but not-yet-started jobs.+       -- Call 'remainingJobs' after this to find out if any jobs are left+       -- (ie could not be cancelled).+       cancelJobs  :: m ()      }  +-- | Make a 'JobControl' that executes all jobs serially and in order.+-- It only executes jobs on demand when they are collected, not eagerly.+--+-- Cancelling will cancel /all/ jobs that have not been collected yet.+-- newSerialJobControl :: IO (JobControl IO a) newSerialJobControl = do-    queue <- newChan+    qVar <- newTChanIO     return JobControl {-      spawnJob   = spawn queue,-      collectJob = collect queue+      spawnJob      = spawn     qVar,+      collectJob    = collect   qVar,+      remainingJobs = remaining qVar,+      cancelJobs    = cancel    qVar     }   where-    spawn :: Chan (IO a) -> IO a -> IO ()-    spawn = writeChan+    spawn :: TChan (IO a) -> IO a -> IO ()+    spawn qVar job = atomically $ writeTChan qVar job -    collect :: Chan (IO a) -> IO a-    collect = join . readChan+    collect :: TChan (IO a) -> IO a+    collect qVar =+      join $ atomically $ readTChan qVar -newParallelJobControl :: IO (JobControl IO a)-newParallelJobControl = do-    resultVar <- newEmptyMVar+    remaining :: TChan (IO a) -> IO Bool+    remaining qVar  = fmap not $ atomically $ isEmptyTChan qVar++    cancel :: TChan (IO a) -> IO ()+    cancel qVar = do+      _ <- atomically $ readAllTChan qVar+      return ()++-- | Make a 'JobControl' that eagerly executes jobs in parallel, with a given+-- maximum degree of parallelism.+--+-- Cancelling will cancel jobs that have not yet begun executing, but jobs+-- that have already been executed or are currently executing cannot be+-- cancelled.+--+newParallelJobControl :: Int -> IO (JobControl IO a)+newParallelJobControl n | n < 1 || n > 1000 =+  error $ "newParallelJobControl: not a sensible number of jobs: " ++ show n+newParallelJobControl maxJobLimit = do+    inqVar   <- newTChanIO+    outqVar  <- newTChanIO+    countVar <- newTVarIO 0+    replicateM_ maxJobLimit $+      forkIO $+        worker inqVar outqVar     return JobControl {-      spawnJob   = spawn resultVar,-      collectJob = collect resultVar+      spawnJob      = spawn   inqVar  countVar,+      collectJob    = collect outqVar countVar,+      remainingJobs = remaining       countVar,+      cancelJobs    = cancel  inqVar  countVar     }   where-    spawn :: MVar (Either SomeException a) -> IO a -> IO ()-    spawn resultVar job =-      mask $ \restore ->-        forkIO (do res <- try (restore job)-                   putMVar resultVar res)-         >> return ()+    worker ::  TChan (IO a) -> TChan (Either SomeException a) -> IO ()+    worker inqVar outqVar =+      forever $ do+        job <- atomically $ readTChan inqVar+        res <- try job+        atomically $ writeTChan outqVar res -    collect :: MVar (Either SomeException a) -> IO a-    collect resultVar =-      takeMVar resultVar >>= either throw return+    spawn :: TChan (IO a) -> TVar Int -> IO a -> IO ()+    spawn inqVar countVar job =+      atomically $ do+        modifyTVar' countVar (+1)+        writeTChan inqVar job++    collect :: TChan (Either SomeException a) -> TVar Int -> IO a+    collect outqVar countVar = do+      res <- atomically $ do+        modifyTVar' countVar (subtract 1)+        readTChan outqVar+      either throwIO return res++    remaining :: TVar Int -> IO Bool+    remaining countVar = fmap (/=0) $ atomically $ readTVar countVar++    cancel :: TChan (IO a) -> TVar Int -> IO ()+    cancel inqVar countVar =+      atomically $ do+        xs <- readAllTChan inqVar+        modifyTVar' countVar (subtract (length xs))++readAllTChan :: TChan a -> STM [a]+readAllTChan qvar = go []+  where+    go xs = do+      mx <- tryReadTChan qvar+      case mx of+        Nothing -> return (reverse xs)+        Just x  -> go (x:xs)++-------------------------+-- Job limits and locks+--  data JobLimit = JobLimit QSem 
Distribution/Client/List.hs view
@@ -14,38 +14,41 @@   ) where  import Distribution.Package-         ( PackageName(..), Package(..), packageName, packageVersion-         , Dependency(..), simplifyDependency-         , UnitId )+         ( PackageName, Package(..), packageName+         , packageVersion, UnitId )+import Distribution.Types.Dependency+import Distribution.Types.UnqualComponentName import Distribution.ModuleName (ModuleName) import Distribution.License (License) import qualified Distribution.InstalledPackageInfo as Installed import qualified Distribution.PackageDescription   as Source import Distribution.PackageDescription-         ( Flag(..), FlagName(..) )+         ( Flag(..), unFlagName ) import Distribution.PackageDescription.Configuration          ( flattenPackageDescription )  import Distribution.Simple.Compiler         ( Compiler, PackageDBStack )-import Distribution.Simple.Program (ProgramConfiguration)+import Distribution.Simple.Program (ProgramDb) import Distribution.Simple.Utils-        ( equating, comparing, die, notice )+        ( equating, comparing, die', notice ) import Distribution.Simple.Setup (fromFlag) import Distribution.Simple.PackageIndex (InstalledPackageIndex) import qualified Distribution.Simple.PackageIndex as InstalledPackageIndex-import qualified Distribution.Client.PackageIndex as PackageIndex import Distribution.Version-         ( Version(..), VersionRange, withinRange, anyVersion+         ( Version, mkVersion, versionNumbers, VersionRange, withinRange, anyVersion          , intersectVersionRanges, simplifyVersionRange ) import Distribution.Verbosity (Verbosity) import Distribution.Text          ( Text(disp), display ) +import           Distribution.Solver.Types.PackageConstraint+import qualified Distribution.Solver.Types.PackageIndex as PackageIndex+import           Distribution.Solver.Types.SourcePackage+ import Distribution.Client.Types-         ( SourcePackage(..), SourcePackageDb(..) )-import Distribution.Client.Dependency.Types-         ( PackageConstraint(..) )+         ( SourcePackageDb(..)+         , UnresolvedSourcePackage ) import Distribution.Client.Targets          ( UserTarget, resolveUserTargets, PackageSpecifier(..) ) import Distribution.Client.Setup@@ -61,7 +64,7 @@ import Data.List          ( sortBy, groupBy, sort, nub, intersperse, maximumBy, partition ) import Data.Maybe-         ( listToMaybe, fromJust, fromMaybe, isJust )+         ( listToMaybe, fromJust, fromMaybe, isJust, maybeToList ) import qualified Data.Map as Map import Data.Tree as Tree import Control.Monad@@ -78,19 +81,19 @@            -> PackageDBStack            -> RepoContext            -> Compiler-           -> ProgramConfiguration+           -> ProgramDb            -> ListFlags            -> [String]            -> IO [PackageDisplayInfo]-getPkgList verbosity packageDBs repoCtxt comp conf listFlags pats = do-    installedPkgIndex <- getInstalledPackages verbosity comp packageDBs conf+getPkgList verbosity packageDBs repoCtxt comp progdb listFlags pats = do+    installedPkgIndex <- getInstalledPackages verbosity comp packageDBs progdb     sourcePkgDb       <- getSourcePackages verbosity repoCtxt     let sourcePkgIndex = packageIndex sourcePkgDb         prefs name = fromMaybe anyVersion                        (Map.lookup name (packagePreferences sourcePkgDb))          pkgsInfo ::-          [(PackageName, [Installed.InstalledPackageInfo], [SourcePackage])]+          [(PackageName, [Installed.InstalledPackageInfo], [UnresolvedSourcePackage])]         pkgsInfo             -- gather info for all packages           | null pats = mergePackages@@ -101,7 +104,7 @@           | otherwise = pkgsInfoMatching          pkgsInfoMatching ::-          [(PackageName, [Installed.InstalledPackageInfo], [SourcePackage])]+          [(PackageName, [Installed.InstalledPackageInfo], [UnresolvedSourcePackage])]         pkgsInfoMatching =           let matchingInstalled = matchingPackages                                   InstalledPackageIndex.searchByNameSubstring@@ -134,12 +137,12 @@      -> PackageDBStack      -> RepoContext      -> Compiler-     -> ProgramConfiguration+     -> ProgramDb      -> ListFlags      -> [String]      -> IO ()-list verbosity packageDBs repos comp conf listFlags pats = do-    matches <- getPkgList verbosity packageDBs repos comp conf listFlags pats+list verbosity packageDBs repos comp progdb listFlags pats = do+    matches <- getPkgList verbosity packageDBs repos comp progdb listFlags pats      if simpleOutput       then putStr $ unlines@@ -164,7 +167,7 @@      -> PackageDBStack      -> RepoContext      -> Compiler-     -> ProgramConfiguration+     -> ProgramDb      -> GlobalFlags      -> InfoFlags      -> [UserTarget]@@ -172,10 +175,10 @@ info verbosity _ _ _ _ _ _ [] =     notice verbosity "No packages requested. Nothing to do." -info verbosity packageDBs repoCtxt comp conf+info verbosity packageDBs repoCtxt comp progdb      globalFlags _listFlags userTargets = do -    installedPkgIndex <- getInstalledPackages verbosity comp packageDBs conf+    installedPkgIndex <- getInstalledPackages verbosity comp packageDBs progdb     sourcePkgDb       <- getSourcePackages verbosity repoCtxt     let sourcePkgIndex = packageIndex sourcePkgDb         prefs name = fromMaybe anyVersion@@ -194,7 +197,7 @@                        sourcePkgs' userTargets      pkgsinfo      <- sequence-                       [ do pkginfo <- either die return $+                       [ do pkginfo <- either (die' verbosity) return $                                          gatherPkgInfo prefs                                            installedPkgIndex sourcePkgIndex                                            pkgSpecifier@@ -206,11 +209,11 @@   where     gatherPkgInfo :: (PackageName -> VersionRange) ->                      InstalledPackageIndex ->-                     PackageIndex.PackageIndex SourcePackage ->-                     PackageSpecifier SourcePackage ->+                     PackageIndex.PackageIndex UnresolvedSourcePackage ->+                     PackageSpecifier UnresolvedSourcePackage ->                      Either String PackageDisplayInfo     gatherPkgInfo prefs installedPkgIndex sourcePkgIndex-      (NamedPackage name constraints)+      (NamedPackage name props)       | null (selectedInstalledPkgs) && null (selectedSourcePkgs)       = Left $ "There is no available version of " ++ display name             ++ " that satisfies "@@ -235,7 +238,7 @@                          -- supplied a non-trivial version constraint         showPkgVersion = not (null verConstraints)         verConstraint  = foldr intersectVersionRanges anyVersion verConstraints-        verConstraints = [ vr | PackageConstraintVersion _ vr <- constraints ]+        verConstraints = [ vr | PackagePropertyVersion vr <- props ]      gatherPkgInfo prefs installedPkgIndex sourcePkgIndex       (SpecificSourcePackage pkg) =@@ -251,8 +254,8 @@   (PackageName -> VersionRange)   -> PackageName   -> InstalledPackageIndex-  -> PackageIndex.PackageIndex SourcePackage-  -> (VersionRange, [Installed.InstalledPackageInfo], [SourcePackage])+  -> PackageIndex.PackageIndex UnresolvedSourcePackage+  -> (VersionRange, [Installed.InstalledPackageInfo], [UnresolvedSourcePackage]) sourcePkgsInfo prefs name installedPkgIndex sourcePkgIndex =   (pref, installedPkgs, sourcePkgs)   where@@ -268,7 +271,7 @@ data PackageDisplayInfo = PackageDisplayInfo {     pkgName           :: PackageName,     selectedVersion   :: Maybe Version,-    selectedSourcePkg :: Maybe SourcePackage,+    selectedSourcePkg :: Maybe UnresolvedSourcePackage,     installedVersions :: [Version],     sourceVersions    :: [Version],     preferredVersions :: VersionRange,@@ -285,7 +288,7 @@     flags             :: [Flag],     hasLib            :: Bool,     hasExe            :: Bool,-    executables       :: [String],+    executables       :: [UnqualComponentName],     modules           :: [ModuleName],     haddockHtml       :: FilePath,     haveTarball       :: Bool@@ -346,7 +349,7 @@    , entry "Author"        author       hideIfNull     reflowLines    , entry "Maintainer"    maintainer   hideIfNull     reflowLines    , entry "Source repo"   sourceRepo   orNotSpecified text-   , entry "Executables"   executables  hideIfNull     (commaSep text)+   , entry "Executables"   executables  hideIfNull     (commaSep disp)    , entry "Flags"         flags        hideIfNull     (commaSep dispFlag)    , entry "Dependencies"  dependencies hideIfNull     (commaSep dispExtDep)    , entry "Documentation" haddockHtml  showIfInstalled text@@ -378,7 +381,7 @@     orNotSpecified = altText null "[ Not specified ]"      commaSep f = Disp.fsep . Disp.punctuate (Disp.char ',') . map f-    dispFlag f = case flagName f of FlagName n -> text n+    dispFlag = text . unFlagName . flagName     dispYesNo True  = text "Yes"     dispYesNo False = text "No" @@ -417,8 +420,8 @@ -- mergePackageInfo :: VersionRange                  -> [Installed.InstalledPackageInfo]-                 -> [SourcePackage]-                 -> Maybe SourcePackage+                 -> [UnresolvedSourcePackage]+                 -> Maybe UnresolvedSourcePackage                  -> Bool                  -> PackageDisplayInfo mergePackageInfo versionPref installedPkgs sourcePkgs selectedPkg showVer =@@ -462,7 +465,8 @@     executables  = map fst (maybe [] Source.condExecutables sourceGeneric),     modules      = combine (map Installed.exposedName . Installed.exposedModules)                            installed-                           (maybe [] getListOfExposedModules . Source.library)+                           -- NB: only for the PUBLIC library+                           (concatMap getListOfExposedModules . maybeToList . Source.library)                            source,     dependencies =       combine (map (SourceDependency . simplifyDependency)@@ -520,10 +524,10 @@ -- both be empty. -- mergePackages :: [Installed.InstalledPackageInfo]-              -> [SourcePackage]+              -> [UnresolvedSourcePackage]               -> [( PackageName                   , [Installed.InstalledPackageInfo]-                  , [SourcePackage] )]+                  , [UnresolvedSourcePackage] )] mergePackages installedPkgs sourcePkgs =     map collect   $ mergeBy (\i a -> fst i `compare` fst a)@@ -567,13 +571,13 @@ -- interestingVersions :: (Version -> Bool) -> [Version] -> [Version] interestingVersions pref =-      map ((\ns -> Version ns []) . fst) . filter snd+      map (mkVersion . fst) . filter snd     . concat  . Tree.levels     . swizzleTree-    . reorderTree (\(Node (v,_) _) -> pref (Version v []))+    . reorderTree (\(Node (v,_) _) -> pref (mkVersion v))     . reverseTree     . mkTree-    . map versionBranch+    . map versionNumbers    where     swizzleTree = unfoldTree (spine [])
Distribution/Client/Manpage.hs view
@@ -56,7 +56,7 @@   , "installing existing packages and developing new packages. "   , "It can be used to work with local packages or to install packages from online package archives, "   , "including automatically installing dependencies. By default it is configured to use Hackage, "-  , "which is Haskell’s central package archive that contains thousands of libraries and applications "+  , "which is Haskell's central package archive that contains thousands of libraries and applications "   , "in the Cabal package format."   , ".SH OPTIONS"   , "Global options:"
+ Distribution/Client/Nix.hs view
@@ -0,0 +1,207 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE ViewPatterns #-}++module Distribution.Client.Nix+       ( findNixExpr+       , inNixShell+       , nixInstantiate+       , nixShell+       , nixShellIfSandboxed+       ) where++#if !MIN_VERSION_base(4,8,0)+import Control.Applicative ((<$>))+#endif++import Control.Exception (bracket, catch)+import Control.Monad (filterM, when, unless)+import System.Directory+       ( canonicalizePath, createDirectoryIfMissing, doesDirectoryExist+       , doesFileExist, removeDirectoryRecursive, removeFile )+import System.Environment (getArgs, getExecutablePath)+import System.FilePath+       ( (</>), replaceExtension, takeDirectory, takeFileName )+import System.IO (IOMode(..), hClose, openFile)+import System.IO.Error (isDoesNotExistError)+import System.Process (showCommandForUser)++import Distribution.Compat.Environment+       ( lookupEnv, setEnv, unsetEnv )+import Distribution.Compat.Semigroup++import Distribution.Verbosity++import Distribution.Simple.Program+       ( Program(..), ProgramDb+       , addKnownProgram, configureProgram, emptyProgramDb, getDbProgramOutput+       , runDbProgram, simpleProgram )+import Distribution.Simple.Setup (fromFlagOrDefault)+import Distribution.Simple.Utils (debug, existsAndIsMoreRecentThan)++import Distribution.Client.Config (SavedConfig(..))+import Distribution.Client.GlobalFlags (GlobalFlags(..))+import Distribution.Client.Sandbox.Types (UseSandbox(..))+++configureOneProgram :: Verbosity -> Program -> IO ProgramDb+configureOneProgram verb prog =+  configureProgram verb prog (addKnownProgram prog emptyProgramDb)+++touchFile :: FilePath -> IO ()+touchFile path = do+  catch (removeFile path) (\e -> when (isDoesNotExistError e) (return ()))+  createDirectoryIfMissing True (takeDirectory path)+  openFile path WriteMode >>= hClose+++findNixExpr :: GlobalFlags -> SavedConfig -> IO (Maybe FilePath)+findNixExpr globalFlags config = do+  -- criteria for deciding to run nix-shell+  let nixEnabled =+        fromFlagOrDefault False+        (globalNix (savedGlobalFlags config) <> globalNix globalFlags)++  if nixEnabled+    then do+      let exprPaths = [ "shell.nix", "default.nix" ]+      filterM doesFileExist exprPaths >>= \case+        [] -> return Nothing+        (path : _) -> return (Just path)+    else return Nothing+++-- set IN_NIX_SHELL so that builtins.getEnv in Nix works as in nix-shell+inFakeNixShell :: IO a -> IO a+inFakeNixShell f =+  bracket (fakeEnv "IN_NIX_SHELL" "1") (resetEnv "IN_NIX_SHELL") (\_ -> f)+  where+    fakeEnv var new = do+      old <- lookupEnv var+      setEnv var new+      return old+    resetEnv var = maybe (unsetEnv var) (setEnv var)+++nixInstantiate+  :: Verbosity+  -> FilePath+  -> Bool+  -> GlobalFlags+  -> SavedConfig+  -> IO ()+nixInstantiate verb dist force globalFlags config =+  findNixExpr globalFlags config >>= \case+    Nothing -> return ()+    Just shellNix -> do+      alreadyInShell <- inNixShell+      shellDrv <- drvPath dist shellNix+      instantiated <- doesFileExist shellDrv+      -- an extra timestamp file is necessary because the derivation lives in+      -- the store so its mtime is always 1.+      let timestamp = timestampPath dist shellNix+      upToDate <- existsAndIsMoreRecentThan timestamp shellNix++      let ready = alreadyInShell || (instantiated && upToDate && not force)+      unless ready $ do++        let prog = simpleProgram "nix-instantiate"+        progdb <- configureOneProgram verb prog++        removeGCRoots verb dist+        touchFile timestamp++        _ <- inFakeNixShell+             (getDbProgramOutput verb prog progdb+              [ "--add-root", shellDrv, "--indirect", shellNix ])+        return ()+++nixShell+  :: Verbosity+  -> FilePath+  -> GlobalFlags+  -> SavedConfig+  -> IO ()+     -- ^ The action to perform inside a nix-shell. This is also the action+     -- that will be performed immediately if Nix is disabled.+  -> IO ()+nixShell verb dist globalFlags config go = do++  alreadyInShell <- inNixShell++  if alreadyInShell+    then go+    else do+      findNixExpr globalFlags config >>= \case+        Nothing -> go+        Just shellNix -> do++          let prog = simpleProgram "nix-shell"+          progdb <- configureOneProgram verb prog++          cabal <- getExecutablePath++          -- alreadyInShell == True in child process+          setEnv "CABAL_IN_NIX_SHELL" "1"++          -- Run cabal with the same arguments inside nix-shell.+          -- When the child process reaches the top of nixShell, it will+          -- detect that it is running inside the shell and fall back+          -- automatically.+          shellDrv <- drvPath dist shellNix+          args <- getArgs+          runDbProgram verb prog progdb+            [ "--add-root", gcrootPath dist </> "result", "--indirect", shellDrv+            , "--run", showCommandForUser cabal args+            ]+++drvPath :: FilePath -> FilePath -> IO FilePath+drvPath dist path = do+  -- We do not actually care about canonicity, but makeAbsolute is only+  -- available in newer versions of directory.+  -- We expect the path to be a symlink if it exists, so we do not canonicalize+  -- the entire path because that would dereference the symlink.+  distNix <- canonicalizePath (dist </> "nix")+  -- Nix garbage collector roots must be absolute paths+  return (distNix </> replaceExtension (takeFileName path) "drv")+++timestampPath :: FilePath -> FilePath -> FilePath+timestampPath dist path =+  dist </> "nix" </> replaceExtension (takeFileName path) "drv.timestamp"+++gcrootPath :: FilePath -> FilePath+gcrootPath dist = dist </> "nix" </> "gcroots"+++inNixShell :: IO Bool+inNixShell = maybe False (const True) <$> lookupEnv "CABAL_IN_NIX_SHELL"+++removeGCRoots :: Verbosity -> FilePath -> IO ()+removeGCRoots verb dist = do+  let tgt = gcrootPath dist+  exists <- doesDirectoryExist tgt+  when exists $ do+    debug verb ("removing Nix gcroots from " ++ tgt)+    removeDirectoryRecursive tgt+++nixShellIfSandboxed+  :: Verbosity+  -> FilePath+  -> GlobalFlags+  -> SavedConfig+  -> UseSandbox+  -> IO ()+     -- ^ The action to perform inside a nix-shell. This is also the action+     -- that will be performed immediately if Nix is disabled.+  -> IO ()+nixShellIfSandboxed verb dist globalFlags config useSandbox go =+  case useSandbox of+    NoSandbox -> go+    UseSandbox _ -> nixShell verb dist globalFlags config go
+ Distribution/Client/Outdated.hs view
@@ -0,0 +1,204 @@+{-# LANGUAGE CPP #-}+-----------------------------------------------------------------------------+-- |+-- Module      :  Distribution.Client.Outdated+-- Maintainer  :  cabal-devel@haskell.org+-- Portability :  portable+--+-- Implementation of the 'outdated' command. Checks for outdated+-- dependencies in the package description file or freeze file.+-----------------------------------------------------------------------------++module Distribution.Client.Outdated ( outdated+                                    , ListOutdatedSettings(..), listOutdated )+where++import Prelude ()+import Distribution.Client.Config+import Distribution.Client.IndexUtils as IndexUtils+import Distribution.Client.Compat.Prelude+import Distribution.Client.ProjectConfig+import Distribution.Client.DistDirLayout+import Distribution.Client.RebuildMonad+import Distribution.Client.Setup hiding (quiet)+import Distribution.Client.Targets+import Distribution.Client.Types+import Distribution.Solver.Types.PackageConstraint+import Distribution.Solver.Types.PackageIndex+import Distribution.Client.Sandbox.PackageEnvironment++import Distribution.Package                          (PackageName, packageVersion)+import Distribution.PackageDescription               (buildDepends)+import Distribution.PackageDescription.Configuration (finalizePD)+import Distribution.Simple.Compiler                  (Compiler, compilerInfo)+import Distribution.Simple.Setup                     (fromFlagOrDefault)+import Distribution.Simple.Utils+       (die', notice, debug, tryFindPackageDesc)+import Distribution.System                           (Platform)+import Distribution.Text                             (display)+import Distribution.Types.ComponentRequestedSpec     (ComponentRequestedSpec(..))+import Distribution.Types.Dependency+       (Dependency(..), depPkgName, simplifyDependency)+import Distribution.Verbosity                        (Verbosity, silent)+import Distribution.Version+       (Version, LowerBound(..), UpperBound(..)+       ,asVersionIntervals, majorBoundVersion)+#ifdef CABAL_PARSEC+import Distribution.PackageDescription.Parsec+       (readGenericPackageDescription)+#else+import Distribution.PackageDescription.Parse+       (readGenericPackageDescription)+#endif++import qualified Data.Set as S+import System.Directory                              (getCurrentDirectory)+import System.Exit                                   (exitFailure)+import Control.Exception                             (throwIO)++-- | Entry point for the 'outdated' command.+outdated :: Verbosity -> OutdatedFlags -> RepoContext+         -> Compiler -> Platform+         -> IO ()+outdated verbosity0 outdatedFlags repoContext comp platform = do+  let freezeFile    = fromFlagOrDefault False (outdatedFreezeFile outdatedFlags)+      newFreezeFile = fromFlagOrDefault False+                      (outdatedNewFreezeFile outdatedFlags)+      simpleOutput  = fromFlagOrDefault False (outdatedSimpleOutput outdatedFlags)+      quiet         = fromFlagOrDefault False (outdatedQuiet outdatedFlags)+      exitCode      = fromFlagOrDefault quiet (outdatedExitCode outdatedFlags)+      ignorePred    = let ignoreSet = S.fromList (outdatedIgnore outdatedFlags)+                      in \pkgname -> pkgname `S.member` ignoreSet+      minorPred     = case outdatedMinor outdatedFlags of+                        Nothing -> const False+                        Just IgnoreMajorVersionBumpsNone -> const False+                        Just IgnoreMajorVersionBumpsAll  -> const True+                        Just (IgnoreMajorVersionBumpsSome pkgs) ->+                          let minorSet = S.fromList pkgs+                          in \pkgname -> pkgname `S.member` minorSet+      verbosity     = if quiet then silent else verbosity0++  sourcePkgDb <- IndexUtils.getSourcePackages verbosity repoContext+  let pkgIndex = packageIndex sourcePkgDb+  deps <- if freezeFile+          then depsFromFreezeFile verbosity+          else if newFreezeFile+               then depsFromNewFreezeFile verbosity+               else depsFromPkgDesc       verbosity comp platform+  debug verbosity $ "Dependencies loaded: "+    ++ (intercalate ", " $ map display deps)+  let outdatedDeps = listOutdated deps pkgIndex+                     (ListOutdatedSettings ignorePred minorPred)+  when (not quiet) $+    showResult verbosity outdatedDeps simpleOutput+  if (exitCode && (not . null $ outdatedDeps))+    then exitFailure+    else return ()++-- | Print either the list of all outdated dependencies, or a message+-- that there are none.+showResult :: Verbosity -> [(Dependency,Version)] -> Bool -> IO ()+showResult verbosity outdatedDeps simpleOutput =+  if (not . null $ outdatedDeps)+    then+    do when (not simpleOutput) $+         notice verbosity "Outdated dependencies:"+       for_ outdatedDeps $ \(d@(Dependency pn _), v) ->+         let outdatedDep = if simpleOutput then display pn+                           else display d ++ " (latest: " ++ display v ++ ")"+         in notice verbosity outdatedDep+    else notice verbosity "All dependencies are up to date."++-- | Convert a list of 'UserConstraint's to a 'Dependency' list.+userConstraintsToDependencies :: [UserConstraint] -> [Dependency]+userConstraintsToDependencies ucnstrs =+  mapMaybe (packageConstraintToDependency . userToPackageConstraint) ucnstrs++-- | Read the list of dependencies from the freeze file.+depsFromFreezeFile :: Verbosity -> IO [Dependency]+depsFromFreezeFile verbosity = do+  cwd        <- getCurrentDirectory+  userConfig <- loadUserConfig verbosity cwd Nothing+  let ucnstrs = map fst . configExConstraints . savedConfigureExFlags $ userConfig+      deps    = userConstraintsToDependencies ucnstrs+  debug verbosity "Reading the list of dependencies from the freeze file"+  return deps++-- | Read the list of dependencies from the new-style freeze file.+depsFromNewFreezeFile :: Verbosity -> IO [Dependency]+depsFromNewFreezeFile verbosity = do+  projectRoot <- either throwIO return =<<+                 findProjectRoot Nothing {- TODO: Support '--project-file': -} Nothing+  let distDirLayout = defaultDistDirLayout projectRoot {- TODO: Support dist dir override -} Nothing+  projectConfig  <- runRebuild (distProjectRootDirectory distDirLayout) $+                    readProjectLocalFreezeConfig verbosity distDirLayout+  let ucnstrs = map fst . projectConfigConstraints . projectConfigShared+                $ projectConfig+      deps    = userConstraintsToDependencies ucnstrs+  debug verbosity+    "Reading the list of dependencies from the new-style freeze file"+  return deps++-- | Read the list of dependencies from the package description.+depsFromPkgDesc :: Verbosity -> Compiler  -> Platform -> IO [Dependency]+depsFromPkgDesc verbosity comp platform = do+  cwd  <- getCurrentDirectory+  path <- tryFindPackageDesc cwd+  gpd  <- readGenericPackageDescription verbosity path+  let cinfo = compilerInfo comp+      epd = finalizePD [] (ComponentRequestedSpec True True)+            (const True) platform cinfo [] gpd+  case epd of+    Left _        -> die' verbosity "finalizePD failed"+    Right (pd, _) -> do+      let bd = buildDepends pd+      debug verbosity+        "Reading the list of dependencies from the package description"+      return bd++-- | Various knobs for customising the behaviour of 'listOutdated'.+data ListOutdatedSettings = ListOutdatedSettings {+  -- | Should this package be ignored?+  listOutdatedIgnorePred :: PackageName -> Bool,+  -- | Should major version bumps should be ignored for this package?+  listOutdatedMinorPred  :: PackageName -> Bool+  }++-- | Find all outdated dependencies.+listOutdated :: [Dependency]+             -> PackageIndex UnresolvedSourcePackage+             -> ListOutdatedSettings+             -> [(Dependency, Version)]+listOutdated deps pkgIndex (ListOutdatedSettings ignorePred minorPred) =+  mapMaybe isOutdated $ map simplifyDependency deps+  where+    isOutdated :: Dependency -> Maybe (Dependency, Version)+    isOutdated dep+      | ignorePred (depPkgName dep) = Nothing+      | otherwise                   =+          let this   = map packageVersion $ lookupDependency pkgIndex dep+              latest = lookupLatest dep+          in (\v -> (dep, v)) `fmap` isOutdated' this latest++    isOutdated' :: [Version] -> [Version] -> Maybe Version+    isOutdated' [] _  = Nothing+    isOutdated' _  [] = Nothing+    isOutdated' this latest = let this'   = maximum this+                                  latest' = maximum latest+                              in if this' < latest' then Just latest' else Nothing++    lookupLatest :: Dependency -> [Version]+    lookupLatest dep+      | minorPred (depPkgName dep) =+        map packageVersion $ lookupDependency pkgIndex  (relaxMinor dep)+      | otherwise                  =+        map packageVersion $ lookupPackageName pkgIndex (depPkgName dep)++    relaxMinor :: Dependency -> Dependency+    relaxMinor (Dependency pn vr) = (Dependency pn vr')+      where+        vr' = let vis = asVersionIntervals vr+                  (LowerBound v0 _,upper) = last vis+              in case upper of+                   NoUpperBound     -> vr+                   UpperBound _v1 _ -> majorBoundVersion v0
Distribution/Client/PackageHash.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE RecordWildCards, NamedFieldPuns, GeneralizedNewtypeDeriving #-}+{-# LANGUAGE DeriveDataTypeable #-}  -- | Functions to calculate nix-style hashes for package ids. --@@ -29,11 +30,12 @@   ) where  import Distribution.Package-         ( PackageId, PackageIdentifier(..), mkUnitId )+         ( PackageId, PackageIdentifier(..), mkComponentId+         , PkgconfigName ) import Distribution.System          ( Platform, OS(Windows), buildOS ) import Distribution.PackageDescription-         ( FlagName(..), FlagAssignment )+         ( FlagAssignment, showFlagValue ) import Distribution.Simple.Compiler          ( CompilerId, OptimisationLevel(..), DebugInfoLevel(..)          , ProfDetailLevel(..), showProfDetailLevel )@@ -41,8 +43,10 @@          ( PathTemplate, fromPathTemplate ) import Distribution.Text          ( display )+import Distribution.Version import Distribution.Client.Types          ( InstalledPackageId )+import qualified Distribution.Solver.Types.ComponentDeps as CD  import qualified Hackage.Security.Client    as Sec @@ -50,11 +54,14 @@ import qualified Data.ByteString.Base16     as Base16 import qualified Data.ByteString.Char8      as BS import qualified Data.ByteString.Lazy.Char8 as LBS+import qualified Data.Map as Map import qualified Data.Set as Set import           Data.Set (Set) +import Data.Typeable import Data.Maybe        (catMaybes) import Data.List         (sortBy, intercalate)+import Data.Map          (Map) import Data.Function     (on) import Distribution.Compat.Binary (Binary(..)) import Control.Exception (evaluate)@@ -85,7 +92,7 @@ -- hashedInstalledPackageIdLong :: PackageHashInputs -> InstalledPackageId hashedInstalledPackageIdLong pkghashinputs@PackageHashInputs{pkgHashPkgId} =-    mkUnitId $+    mkComponentId $          display pkgHashPkgId   -- to be a bit user friendly       ++ "-"       ++ showHashValue (hashPackageHashInputs pkghashinputs)@@ -105,12 +112,12 @@ -- in the hash. -- -- Truncating the hash size is disappointing but also technically ok. We--- rely on the hash primarily for collision avoidance not for any securty+-- rely on the hash primarily for collision avoidance not for any security -- properties (at least for now). -- hashedInstalledPackageIdShort :: PackageHashInputs -> InstalledPackageId hashedInstalledPackageIdShort pkghashinputs@PackageHashInputs{pkgHashPkgId} =-    mkUnitId $+    mkComponentId $       intercalate "-"         -- max length now 64         [ truncateStr 14 (display name)@@ -133,7 +140,9 @@ -- data PackageHashInputs = PackageHashInputs {        pkgHashPkgId         :: PackageId,+       pkgHashComponent     :: Maybe CD.Component,        pkgHashSourceHash    :: PackageSourceHash,+       pkgHashPkgConfigDeps :: Set (PkgconfigName, Maybe Version),        pkgHashDirectDeps    :: Set InstalledPackageId,        pkgHashOtherConfig   :: PackageHashConfigInputs      }@@ -162,13 +171,13 @@        pkgHashStripLibs           :: Bool,        pkgHashStripExes           :: Bool,        pkgHashDebugInfo           :: DebugInfoLevel,+       pkgHashProgramArgs         :: Map String [String],        pkgHashExtraLibDirs        :: [FilePath],        pkgHashExtraFrameworkDirs  :: [FilePath],        pkgHashExtraIncludeDirs    :: [FilePath],        pkgHashProgPrefix          :: Maybe PathTemplate,        pkgHashProgSuffix          :: Maybe PathTemplate ---     TODO: [required eventually] extra program options --     TODO: [required eventually] pkgHashToolsVersions     ? --     TODO: [required eventually] pkgHashToolsExtraOptions ? --     TODO: [research required] and what about docs?@@ -188,8 +197,10 @@ renderPackageHashInputs :: PackageHashInputs -> LBS.ByteString renderPackageHashInputs PackageHashInputs{                           pkgHashPkgId,+                          pkgHashComponent,                           pkgHashSourceHash,                           pkgHashDirectDeps,+                          pkgHashPkgConfigDeps,                           pkgHashOtherConfig =                             PackageHashConfigInputs{..}                         } =@@ -207,9 +218,16 @@     --TODO: [nice to have] ultimately we probably want to put this config info     -- into the ghc-pkg db. At that point this should probably be changed to     -- use the config file infrastructure so it can be read back in again.-    LBS.pack $ unlines $ catMaybes+    LBS.pack $ unlines $ catMaybes $       [ entry "pkgid"       display pkgHashPkgId+      , mentry "component"  show pkgHashComponent       , entry "src"         showHashValue pkgHashSourceHash+      , entry "pkg-config-deps"+                            (intercalate ", " . map (\(pn, mb_v) -> display pn +++                                                    case mb_v of+                                                        Nothing -> ""+                                                        Just v -> " " ++ display v)+                                              . Set.toList) pkgHashPkgConfigDeps       , entry "deps"        (intercalate ", " . map display                                               . Set.toList) pkgHashDirectDeps         -- and then all the config@@ -236,17 +254,15 @@       , opt   "extra-include-dirs" [] unwords pkgHashExtraIncludeDirs       , opt   "prog-prefix" Nothing (maybe "" fromPathTemplate) pkgHashProgPrefix       , opt   "prog-suffix" Nothing (maybe "" fromPathTemplate) pkgHashProgSuffix-      ]+      ] ++ Map.foldrWithKey (\prog args acc -> opt (prog ++ "-options") [] unwords args : acc) [] pkgHashProgramArgs   where     entry key     format value = Just (key ++ ": " ++ format value)+    mentry key    format value = fmap (\v -> key ++ ": " ++ format v) value     opt   key def format value          | value == def = Nothing          | otherwise    = entry key format value -    showFlagAssignment = unwords . map showEntry . sortBy (compare `on` fst)-      where-        showEntry (FlagName name, False) = '-' : name-        showEntry (FlagName name, True)  = '+' : name+    showFlagAssignment = unwords . map showFlagValue . sortBy (compare `on` fst)  ----------------------------------------------- -- The specific choice of hash implementation@@ -263,7 +279,7 @@ -- package ids.  newtype HashValue = HashValue BS.ByteString-  deriving (Eq, Show)+  deriving (Eq, Show, Typeable)  instance Binary HashValue where   put (HashValue digest) = put digest
− Distribution/Client/PackageIndex.hs
@@ -1,318 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE DeriveFunctor #-}-{-# LANGUAGE DeriveGeneric #-}--------------------------------------------------------------------------------- |--- Module      :  Distribution.Client.PackageIndex--- Copyright   :  (c) David Himmelstrup 2005,---                    Bjorn Bringert 2007,---                    Duncan Coutts 2008------ Maintainer  :  cabal-devel@haskell.org--- Portability :  portable------ An index of packages.----module Distribution.Client.PackageIndex (-  -- * Package index data type-  PackageIndex,--  -- * Creating an index-  fromList,--  -- * Updates-  merge,-  insert,-  deletePackageName,-  deletePackageId,-  deleteDependency,--  -- * Queries--  -- ** Precise lookups-  elemByPackageId,-  elemByPackageName,-  lookupPackageName,-  lookupPackageId,-  lookupDependency,--  -- ** Case-insensitive searches-  searchByName,-  SearchResult(..),-  searchByNameSubstring,--  -- ** Bulk queries-  allPackages,-  allPackagesByName,-  ) where--import Prelude hiding (lookup)-import Control.Exception (assert)-import qualified Data.Map as Map-import Data.Map (Map)-import Data.List (groupBy, sortBy, isInfixOf)-#if !MIN_VERSION_base(4,8,0)-import Data.Monoid (Monoid(..))-#endif-import Data.Maybe (isJust, fromMaybe)-import GHC.Generics (Generic)-import Distribution.Compat.Binary (Binary)-import Distribution.Compat.Semigroup (Semigroup((<>)))--import Distribution.Package-         ( PackageName(..), PackageIdentifier(..)-         , Package(..), packageName, packageVersion-         , Dependency(Dependency) )-import Distribution.Version-         ( withinRange )-import Distribution.Simple.Utils-         ( lowercase, comparing )----- | The collection of information about packages from one or more 'PackageDB's.------ It can be searched efficiently by package name and version.----newtype PackageIndex pkg = PackageIndex-  -- This index package names to all the package records matching that package-  -- name case-sensitively. It includes all versions.-  ---  -- This allows us to find all versions satisfying a dependency.-  -- Most queries are a map lookup followed by a linear scan of the bucket.-  ---  (Map PackageName [pkg])--  deriving (Eq, Show, Read, Functor, Generic)---FIXME: the Functor instance here relies on no package id changes--instance Package pkg => Semigroup (PackageIndex pkg) where-  (<>) = merge--instance Package pkg => Monoid (PackageIndex pkg) where-  mempty  = PackageIndex Map.empty-  mappend = (<>)-  --save one mappend with empty in the common case:-  mconcat [] = mempty-  mconcat xs = foldr1 mappend xs--instance Binary pkg => Binary (PackageIndex pkg)--invariant :: Package pkg => PackageIndex pkg -> Bool-invariant (PackageIndex m) = all (uncurry goodBucket) (Map.toList m)-  where-    goodBucket _    [] = False-    goodBucket name (pkg0:pkgs0) = check (packageId pkg0) pkgs0-      where-        check pkgid []          = packageName pkgid == name-        check pkgid (pkg':pkgs) = packageName pkgid == name-                               && pkgid < pkgid'-                               && check pkgid' pkgs-          where pkgid' = packageId pkg'------- * Internal helpers-----mkPackageIndex :: Package pkg => Map PackageName [pkg] -> PackageIndex pkg-mkPackageIndex index = assert (invariant (PackageIndex index))-                                         (PackageIndex index)--internalError :: String -> a-internalError name = error ("PackageIndex." ++ name ++ ": internal error")---- | Lookup a name in the index to get all packages that match that name--- case-sensitively.----lookup :: PackageIndex pkg -> PackageName -> [pkg]-lookup (PackageIndex m) name = fromMaybe [] $ Map.lookup name m------- * Construction------- | Build an index out of a bunch of packages.------ If there are duplicates, later ones mask earlier ones.----fromList :: Package pkg => [pkg] -> PackageIndex pkg-fromList pkgs = mkPackageIndex-              . Map.map fixBucket-              . Map.fromListWith (++)-              $ [ (packageName pkg, [pkg])-                | pkg <- pkgs ]-  where-    fixBucket = -- out of groups of duplicates, later ones mask earlier ones-                -- but Map.fromListWith (++) constructs groups in reverse order-                map head-                -- Eq instance for PackageIdentifier is wrong, so use Ord:-              . groupBy (\a b -> EQ == comparing packageId a b)-                -- relies on sortBy being a stable sort so we-                -- can pick consistently among duplicates-              . sortBy (comparing packageId)------- * Updates------- | Merge two indexes.------ Packages from the second mask packages of the same exact name--- (case-sensitively) from the first.----merge :: Package pkg => PackageIndex pkg -> PackageIndex pkg -> PackageIndex pkg-merge i1@(PackageIndex m1) i2@(PackageIndex m2) =-  assert (invariant i1 && invariant i2) $-    mkPackageIndex (Map.unionWith mergeBuckets m1 m2)---- | Elements in the second list mask those in the first.-mergeBuckets :: Package pkg => [pkg] -> [pkg] -> [pkg]-mergeBuckets []     ys     = ys-mergeBuckets xs     []     = xs-mergeBuckets xs@(x:xs') ys@(y:ys') =-      case packageId x `compare` packageId y of-        GT -> y : mergeBuckets xs  ys'-        EQ -> y : mergeBuckets xs' ys'-        LT -> x : mergeBuckets xs' ys---- | Inserts a single package into the index.------ This is equivalent to (but slightly quicker than) using 'mappend' or--- 'merge' with a singleton index.----insert :: Package pkg => pkg -> PackageIndex pkg -> PackageIndex pkg-insert pkg (PackageIndex index) = mkPackageIndex $-  Map.insertWith (\_ -> insertNoDup) (packageName pkg) [pkg] index-  where-    pkgid = packageId pkg-    insertNoDup []                = [pkg]-    insertNoDup pkgs@(pkg':pkgs') = case compare pkgid (packageId pkg') of-      LT -> pkg  : pkgs-      EQ -> pkg  : pkgs'-      GT -> pkg' : insertNoDup pkgs'---- | Internal delete helper.----delete :: Package pkg => PackageName -> (pkg -> Bool) -> PackageIndex pkg -> PackageIndex pkg-delete name p (PackageIndex index) = mkPackageIndex $-  Map.update filterBucket name index-  where-    filterBucket = deleteEmptyBucket-                 . filter (not . p)-    deleteEmptyBucket []        = Nothing-    deleteEmptyBucket remaining = Just remaining---- | Removes a single package from the index.----deletePackageId :: Package pkg => PackageIdentifier -> PackageIndex pkg -> PackageIndex pkg-deletePackageId pkgid =-  delete (packageName pkgid) (\pkg -> packageId pkg == pkgid)---- | Removes all packages with this (case-sensitive) name from the index.----deletePackageName :: Package pkg => PackageName -> PackageIndex pkg -> PackageIndex pkg-deletePackageName name =-  delete name (\pkg -> packageName pkg == name)---- | Removes all packages satisfying this dependency from the index.----deleteDependency :: Package pkg => Dependency -> PackageIndex pkg -> PackageIndex pkg-deleteDependency (Dependency name verstionRange) =-  delete name (\pkg -> packageVersion pkg `withinRange` verstionRange)------- * Bulk queries------- | Get all the packages from the index.----allPackages :: PackageIndex pkg -> [pkg]-allPackages (PackageIndex m) = concat (Map.elems m)---- | Get all the packages from the index.------ They are grouped by package name, case-sensitively.----allPackagesByName :: PackageIndex pkg -> [[pkg]]-allPackagesByName (PackageIndex m) = Map.elems m------- * Lookups-----elemByPackageId :: Package pkg => PackageIndex pkg -> PackageIdentifier -> Bool-elemByPackageId index = isJust . lookupPackageId index--elemByPackageName :: Package pkg => PackageIndex pkg -> PackageName -> Bool-elemByPackageName index = not . null . lookupPackageName index----- | Does a lookup by package id (name & version).------ Since multiple package DBs mask each other case-sensitively by package name,--- then we get back at most one package.----lookupPackageId :: Package pkg => PackageIndex pkg -> PackageIdentifier -> Maybe pkg-lookupPackageId index pkgid =-  case [ pkg | pkg <- lookup index (packageName pkgid)-             , packageId pkg == pkgid ] of-    []    -> Nothing-    [pkg] -> Just pkg-    _     -> internalError "lookupPackageIdentifier"---- | Does a case-sensitive search by package name.----lookupPackageName :: Package pkg => PackageIndex pkg -> PackageName -> [pkg]-lookupPackageName index name =-  [ pkg | pkg <- lookup index name-        , packageName pkg == name ]---- | Does a case-sensitive search by package name and a range of versions.------ We get back any number of versions of the specified package name, all--- satisfying the version range constraint.----lookupDependency :: Package pkg => PackageIndex pkg -> Dependency -> [pkg]-lookupDependency index (Dependency name versionRange) =-  [ pkg | pkg <- lookup index name-        , packageName pkg == name-        , packageVersion pkg `withinRange` versionRange ]------- * Case insensitive name lookups------- | Does a case-insensitive search by package name.------ If there is only one package that compares case-insensitively to this name--- then the search is unambiguous and we get back all versions of that package.--- If several match case-insensitively but one matches exactly then it is also--- unambiguous.------ If however several match case-insensitively and none match exactly then we--- have an ambiguous result, and we get back all the versions of all the--- packages. The list of ambiguous results is split by exact package name. So--- it is a non-empty list of non-empty lists.----searchByName :: PackageIndex pkg-             -> String -> [(PackageName, [pkg])]-searchByName (PackageIndex m) name =-    [ pkgs-    | pkgs@(PackageName name',_) <- Map.toList m-    , lowercase name' == lname ]-  where-    lname = lowercase name--data SearchResult a = None | Unambiguous a | Ambiguous [a]---- | Does a case-insensitive substring search by package name.------ That is, all packages that contain the given string in their name.----searchByNameSubstring :: PackageIndex pkg-                      -> String -> [(PackageName, [pkg])]-searchByNameSubstring (PackageIndex m) searchterm =-    [ pkgs-    | pkgs@(PackageName name, _) <- Map.toList m-    , lsearchterm `isInfixOf` lowercase name ]-  where-    lsearchterm = lowercase searchterm
Distribution/Client/PackageUtils.hs view
@@ -15,11 +15,13 @@   ) where  import Distribution.Package-         ( packageVersion, packageName, Dependency(..) )+         ( packageVersion, packageName )+import Distribution.Types.Dependency+import Distribution.Types.UnqualComponentName import Distribution.PackageDescription-         ( PackageDescription(..) )+         ( PackageDescription(..), libName ) import Distribution.Version-         ( withinRange )+         ( withinRange, isAnyVersion )  -- | The list of dependencies that refer to external packages -- rather than internal package components.@@ -30,5 +32,7 @@     -- True if this dependency is an internal one (depends on a library     -- defined in the same package).     internal (Dependency depName versionRange) =-            depName == packageName pkg &&-            packageVersion pkg `withinRange` versionRange+           (depName == packageName pkg &&+            packageVersion pkg `withinRange` versionRange) ||+           (Just (packageNameToUnqualComponentName depName) `elem` map libName (subLibraries pkg) &&+            isAnyVersion versionRange)
− Distribution/Client/PkgConfigDb.hs
@@ -1,146 +0,0 @@-{-# LANGUAGE CPP #-}--------------------------------------------------------------------------------- |--- Module      :  Distribution.Client.PkgConfigDb--- Copyright   :  (c) Iñaki García Etxebarria 2016--- License     :  BSD-like------ Maintainer  :  cabal-devel@haskell.org--- Portability :  portable------ Read the list of packages available to pkg-config.-------------------------------------------------------------------------------module Distribution.Client.PkgConfigDb-    ( PkgConfigDb-    , readPkgConfigDb-    , pkgConfigDbFromList-    , pkgConfigPkgIsPresent-    , getPkgConfigDbDirs-    ) where--#if !MIN_VERSION_base(4,8,0)-import Control.Applicative ((<$>), (<*>))-#endif--import Control.Exception (IOException, handle)-import Data.Char (isSpace)-import qualified Data.Map as M-import Data.Version (parseVersion)-import Text.ParserCombinators.ReadP (readP_to_S)-import System.FilePath (splitSearchPath)--import Distribution.Package-    ( PackageName(..) )-import Distribution.Verbosity-    ( Verbosity )-import Distribution.Version-    ( Version, VersionRange, withinRange )--import Distribution.Compat.Environment-    ( lookupEnv )-import Distribution.Simple.Program-    ( ProgramConfiguration, pkgConfigProgram, getProgramOutput,-      requireProgram )-import Distribution.Simple.Utils-    ( info )---- | The list of packages installed in the system visible to--- @pkg-config@. This is an opaque datatype, to be constructed with--- `readPkgConfigDb` and queried with `pkgConfigPkgPresent`.-data PkgConfigDb =  PkgConfigDb (M.Map PackageName (Maybe Version))-                 -- ^ If an entry is `Nothing`, this means that the-                 -- package seems to be present, but we don't know the-                 -- exact version (because parsing of the version-                 -- number failed).-                 | NoPkgConfigDb-                 -- ^ For when we could not run pkg-config successfully.-     deriving (Show)---- | Query pkg-config for the list of installed packages, together--- with their versions. Return a `PkgConfigDb` encapsulating this--- information.-readPkgConfigDb :: Verbosity -> ProgramConfiguration -> IO PkgConfigDb-readPkgConfigDb verbosity conf = handle ioErrorHandler $ do-  (pkgConfig, _) <- requireProgram verbosity pkgConfigProgram conf-  pkgList <- lines <$> getProgramOutput verbosity pkgConfig ["--list-all"]-  -- The output of @pkg-config --list-all@ also includes a description-  -- for each package, which we do not need.-  let pkgNames = map (takeWhile (not . isSpace)) pkgList-  pkgVersions <- lines <$> getProgramOutput verbosity pkgConfig-                             ("--modversion" : pkgNames)-  (return . pkgConfigDbFromList . zip pkgNames) pkgVersions-      where-        -- For when pkg-config invocation fails (possibly because of a-        -- too long command line).-        ioErrorHandler :: IOException -> IO PkgConfigDb-        ioErrorHandler e = do-          info verbosity ("Failed to query pkg-config, Cabal will continue"-                          ++ " without solving for pkg-config constraints: "-                          ++ show e)-          return NoPkgConfigDb---- | Create a `PkgConfigDb` from a list of @(packageName, version)@ pairs.-pkgConfigDbFromList :: [(String, String)] -> PkgConfigDb-pkgConfigDbFromList pairs = (PkgConfigDb . M.fromList . map convert) pairs-    where-      convert :: (String, String) -> (PackageName, Maybe Version)-      convert (n,vs) = (PackageName n,-                        case (reverse . readP_to_S parseVersion) vs of-                          (v, "") : _ -> Just v-                          _           -> Nothing -- Version not (fully)-                                                 -- understood.-                       )---- | Check whether a given package range is satisfiable in the given--- @pkg-config@ database.-pkgConfigPkgIsPresent :: PkgConfigDb -> PackageName -> VersionRange -> Bool-pkgConfigPkgIsPresent (PkgConfigDb db) pn vr =-    case M.lookup pn db of-      Nothing       -> False    -- Package not present in the DB.-      Just Nothing  -> True     -- Package present, but version unknown.-      Just (Just v) -> withinRange v vr--- If we could not read the pkg-config database successfully we allow--- the check to succeed. The plan found by the solver may fail to be--- executed later on, but we have no grounds for rejecting the plan at--- this stage.-pkgConfigPkgIsPresent NoPkgConfigDb _ _ = True----- | Query pkg-config for the locations of pkg-config's package files. Use this--- to monitor for changes in the pkg-config DB.----getPkgConfigDbDirs :: Verbosity -> ProgramConfiguration -> IO [FilePath]-getPkgConfigDbDirs verbosity conf =-    (++) <$> getEnvPath <*> getDefPath- where-    -- According to @man pkg-config@:-    ---    -- PKG_CONFIG_PATH-    -- A  colon-separated  (on Windows, semicolon-separated) list of directories-    -- to search for .pc files.  The default directory will always be searched-    -- after searching the path-    ---    getEnvPath = maybe [] parseSearchPath-             <$> lookupEnv "PKG_CONFIG_PATH"--    -- Again according to @man pkg-config@:-    ---    -- pkg-config can be used to query itself for the default search path,-    -- version number and other information, for instance using:-    ---    -- > pkg-config --variable pc_path pkg-config-    ---    getDefPath = handle ioErrorHandler $ do-      (pkgConfig, _) <- requireProgram verbosity pkgConfigProgram conf-      parseSearchPath <$>-        getProgramOutput verbosity pkgConfig-                         ["--variable", "pc_path", "pkg-config"]--    parseSearchPath str =-      case lines str of-        [p] | not (null p) -> splitSearchPath p-        _                  -> []--    ioErrorHandler :: IOException -> IO [FilePath]-    ioErrorHandler _e = return []-
− Distribution/Client/PlanIndex.hs
@@ -1,289 +0,0 @@--- | These graph traversal functions mirror the ones in Cabal, but work with--- the more complete (and fine-grained) set of dependencies provided by--- PackageFixedDeps rather than only the library dependencies provided by--- PackageInstalled.-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE CPP #-}-module Distribution.Client.PlanIndex (-    -- * FakeMap and related operations-    FakeMap-  , fakeDepends-  , fakeLookupUnitId-    -- * Graph traversal functions-  , brokenPackages-  , dependencyCycles-  , dependencyGraph-  , dependencyInconsistencies-  ) where--import Prelude hiding (lookup)-import qualified Data.Map as Map-import qualified Data.Graph as Graph-import Data.Array ((!))-import Data.Map (Map)-import Data.Maybe (isNothing)-import Data.Either (rights)--#if !MIN_VERSION_base(4,8,0)-import Data.Monoid (Monoid(..))-#endif--import Distribution.Package-         ( PackageName(..), PackageIdentifier(..), UnitId(..)-         , Package(..), packageName, packageVersion-         )-import Distribution.Version-         ( Version )--import Distribution.Client.ComponentDeps (ComponentDeps)-import qualified Distribution.Client.ComponentDeps as CD-import Distribution.Client.Types-         ( PackageFixedDeps(..) )-import Distribution.Simple.PackageIndex-         ( PackageIndex, allPackages, insert, lookupUnitId )-import Distribution.Package-         ( HasUnitId(..), PackageId )---- Note [FakeMap]--------------------- We'd like to use the PackageIndex defined in this module for cabal-install's--- InstallPlan.  However, at the moment, this data structure is indexed by--- UnitId, which we don't know until after we've compiled a package--- (whereas InstallPlan needs to store not-compiled packages in the index.)--- Eventually, an UnitId will be calculatable prior to actually building--- the package, but at the moment, the "fake installed package ID map" is a--- workaround to solve this problem while reusing PackageIndex.  The basic idea--- is that, since we don't know what an UnitId is beforehand, we just fake--- up one based on the package ID (it only needs to be unique for the particular--- install plan), and fill it out with the actual generated UnitId after--- the package is successfully compiled.------ However, there is a problem: in the index there may be references using the--- old package ID, which are now dangling if we update the UnitId.  We--- could map over the entire index to update these pointers as well (a costly--- operation), but instead, we've chosen to parametrize a variety of important--- functions by a FakeMap, which records what a fake installed package ID was--- actually resolved to post-compilation.  If we do a lookup, we first check and--- see if it's a fake ID in the FakeMap.------ It's a bit grungy, but we expect this to only be temporary anyway.  (Another--- possible workaround would have been to *not* update the installed package ID,--- but I decided this would be hard to understand.)---- | Map from fake package keys to real ones.  See Note [FakeMap]-type FakeMap = Map UnitId UnitId---- | Variant of `depends` which accepts a `FakeMap`------ Analogous to `fakeInstalledDepends`. See Note [FakeMap].-fakeDepends :: PackageFixedDeps pkg => FakeMap -> pkg -> ComponentDeps [UnitId]-fakeDepends fakeMap = fmap (map resolveFakeId) . depends-  where-    resolveFakeId :: UnitId -> UnitId-    resolveFakeId ipid = Map.findWithDefault ipid ipid fakeMap----- | Variant of 'lookupUnitId' which accepts a 'FakeMap'.  See Note---- [FakeMap].-fakeLookupUnitId :: FakeMap -> PackageIndex a -> UnitId-                             -> Maybe a-fakeLookupUnitId fakeMap index pkg =-  lookupUnitId index (Map.findWithDefault pkg pkg fakeMap)---- | All packages that have dependencies that are not in the index.------ Returns such packages along with the dependencies that they're missing.----brokenPackages :: (PackageFixedDeps pkg)-               => FakeMap-               -> PackageIndex pkg-               -> [(pkg, [UnitId])]-brokenPackages fakeMap index =-  [ (pkg, missing)-  | pkg  <- allPackages index-  , let missing =-          [ pkg' | pkg' <- CD.flatDeps (depends pkg)-                 , isNothing (fakeLookupUnitId fakeMap index pkg') ]-  , not (null missing) ]---- | Compute all roots of the install plan, and verify that the transitive--- plans from those roots are all consistent.------ NOTE: This does not check for dependency cycles. Moreover, dependency cycles--- may be absent from the subplans even if the larger plan contains a dependency--- cycle. Such cycles may or may not be an issue; either way, we don't check--- for them here.-dependencyInconsistencies :: forall pkg. (PackageFixedDeps pkg, HasUnitId pkg)-                          => FakeMap-                          -> Bool-                          -> PackageIndex pkg-                          -> [(PackageName, [(PackageIdentifier, Version)])]-dependencyInconsistencies fakeMap indepGoals index  =-    concatMap (dependencyInconsistencies' fakeMap) subplans-  where-    subplans :: [PackageIndex pkg]-    subplans = rights $-                 map (dependencyClosure fakeMap index)-                     (rootSets fakeMap indepGoals index)---- | Compute the root sets of a plan------ A root set is a set of packages whose dependency closure must be consistent.--- This is the set of all top-level library roots (taken together normally, or--- as singletons sets if we are considering them as independent goals), along--- with all setup dependencies of all packages.-rootSets :: (PackageFixedDeps pkg, HasUnitId pkg)-         => FakeMap -> Bool -> PackageIndex pkg -> [[UnitId]]-rootSets fakeMap indepGoals index =-       if indepGoals then map (:[]) libRoots else [libRoots]-    ++ setupRoots index-  where-    libRoots = libraryRoots fakeMap index---- | Compute the library roots of a plan------ The library roots are the set of packages with no reverse dependencies--- (no reverse library dependencies but also no reverse setup dependencies).-libraryRoots :: (PackageFixedDeps pkg, HasUnitId pkg)-             => FakeMap -> PackageIndex pkg -> [UnitId]-libraryRoots fakeMap index =-    map toPkgId roots-  where-    (graph, toPkgId, _) = dependencyGraph fakeMap index-    indegree = Graph.indegree graph-    roots    = filter isRoot (Graph.vertices graph)-    isRoot v = indegree ! v == 0---- | The setup dependencies of each package in the plan-setupRoots :: PackageFixedDeps pkg => PackageIndex pkg -> [[UnitId]]-setupRoots = filter (not . null)-           . map (CD.setupDeps . depends)-           . allPackages---- | Given a package index where we assume we want to use all the packages--- (use 'dependencyClosure' if you need to get such a index subset) find out--- if the dependencies within it use consistent versions of each package.--- Return all cases where multiple packages depend on different versions of--- some other package.------ Each element in the result is a package name along with the packages that--- depend on it and the versions they require. These are guaranteed to be--- distinct.----dependencyInconsistencies' :: forall pkg.-                              (PackageFixedDeps pkg, HasUnitId pkg)-                           => FakeMap-                           -> PackageIndex pkg-                           -> [(PackageName, [(PackageIdentifier, Version)])]-dependencyInconsistencies' fakeMap index =-    [ (name, [ (pid,packageVersion dep) | (dep,pids) <- uses, pid <- pids])-    | (name, ipid_map) <- Map.toList inverseIndex-    , let uses = Map.elems ipid_map-    , reallyIsInconsistent (map fst uses)-    ]-  where-    -- For each package name (of a dependency, somewhere)-    --   and each installed ID of that that package-    --     the associated package instance-    --     and a list of reverse dependencies (as source IDs)-    inverseIndex :: Map PackageName (Map UnitId (pkg, [PackageId]))-    inverseIndex = Map.fromListWith (Map.unionWith (\(a,b) (_,b') -> (a,b++b')))-      [ (packageName dep, Map.fromList [(ipid,(dep,[packageId pkg]))])-      | -- For each package @pkg@-        pkg <- allPackages index-        -- Find out which @ipid@ @pkg@ depends on-      , ipid <- CD.nonSetupDeps (fakeDepends fakeMap pkg)-        -- And look up those @ipid@ (i.e., @ipid@ is the ID of @dep@)-      , Just dep <- [fakeLookupUnitId fakeMap index ipid]-      ]--    -- If, in a single install plan, we depend on more than one version of a-    -- package, then this is ONLY okay in the (rather special) case that we-    -- depend on precisely two versions of that package, and one of them-    -- depends on the other. This is necessary for example for the base where-    -- we have base-3 depending on base-4.-    reallyIsInconsistent :: [pkg] -> Bool-    reallyIsInconsistent []       = False-    reallyIsInconsistent [_p]     = False-    reallyIsInconsistent [p1, p2] =-      let pid1 = installedUnitId p1-          pid2 = installedUnitId p2-      in Map.findWithDefault pid1 pid1 fakeMap `notElem` CD.nonSetupDeps (fakeDepends fakeMap p2)-      && Map.findWithDefault pid2 pid2 fakeMap `notElem` CD.nonSetupDeps (fakeDepends fakeMap p1)-    reallyIsInconsistent _ = True------ | Find if there are any cycles in the dependency graph. If there are no--- cycles the result is @[]@.------ This actually computes the strongly connected components. So it gives us a--- list of groups of packages where within each group they all depend on each--- other, directly or indirectly.----dependencyCycles :: (PackageFixedDeps pkg, HasUnitId pkg)-                 => FakeMap-                 -> PackageIndex pkg-                 -> [[pkg]]-dependencyCycles fakeMap index =-  [ vs | Graph.CyclicSCC vs <- Graph.stronglyConnComp adjacencyList ]-  where-    adjacencyList = [ (pkg, installedUnitId pkg,-                            CD.flatDeps (fakeDepends fakeMap pkg))-                    | pkg <- allPackages index ]----- | Tries to take the transitive closure of the package dependencies.------ If the transitive closure is complete then it returns that subset of the--- index. Otherwise it returns the broken packages as in 'brokenPackages'.------ * Note that if the result is @Right []@ it is because at least one of--- the original given 'PackageIdentifier's do not occur in the index.-dependencyClosure :: (PackageFixedDeps pkg, HasUnitId pkg)-                  => FakeMap-                  -> PackageIndex pkg-                  -> [UnitId]-                  -> Either [(pkg, [UnitId])]-                            (PackageIndex pkg)-dependencyClosure fakeMap index pkgids0 = case closure mempty [] pkgids0 of-  (completed, []) -> Right completed-  (completed, _)  -> Left (brokenPackages fakeMap completed)- where-    closure completed failed []             = (completed, failed)-    closure completed failed (pkgid:pkgids) =-      case fakeLookupUnitId fakeMap index pkgid of-        Nothing   -> closure completed (pkgid:failed) pkgids-        Just pkg  ->-          case fakeLookupUnitId fakeMap completed-               (installedUnitId pkg) of-            Just _  -> closure completed  failed pkgids-            Nothing -> closure completed' failed pkgids'-              where completed' = insert pkg completed-                    pkgids'    = CD.nonSetupDeps (depends pkg) ++ pkgids----- | Builds a graph of the package dependencies.------ Dependencies on other packages that are not in the index are discarded.--- You can check if there are any such dependencies with 'brokenPackages'.----dependencyGraph :: (PackageFixedDeps pkg, HasUnitId pkg)-                => FakeMap-                -> PackageIndex pkg-                -> (Graph.Graph,-                    Graph.Vertex -> UnitId,-                    UnitId -> Maybe Graph.Vertex)-dependencyGraph fakeMap index = (graph, vertexToPkg, idToVertex)-  where-    (graph, vertexToPkg', idToVertex) = Graph.graphFromEdges edges-    vertexToPkg v = case vertexToPkg' v of-                      ((), pkgid, _targets) -> pkgid--    pkgs  = allPackages index-    edges = map edgesFrom pkgs--    resolve   pid = Map.findWithDefault pid pid fakeMap-    edgesFrom pkg = ( ()-                    , resolve (installedUnitId pkg)-                    , CD.flatDeps (fakeDepends fakeMap pkg)-                    )
Distribution/Client/ProjectBuilding.hs view
@@ -1,1292 +1,1328 @@ {-# LANGUAGE CPP, BangPatterns, RecordWildCards, NamedFieldPuns,-             DeriveGeneric, DeriveDataTypeable, GeneralizedNewtypeDeriving,-             ScopedTypeVariables #-}---- | ----module Distribution.Client.ProjectBuilding (-    BuildStatus(..),-    BuildStatusMap,-    BuildStatusRebuild(..),-    BuildReason(..),-    MonitorChangedReason(..),-    rebuildTargetsDryRun,-    rebuildTargets-  ) where--import           Distribution.Client.PackageHash (renderPackageHashInputs)-import           Distribution.Client.RebuildMonad-import           Distribution.Client.ProjectConfig-import           Distribution.Client.ProjectPlanning--import           Distribution.Client.Types-                   ( PackageLocation(..), GenericReadyPackage(..)-                   , PackageFixedDeps(..)-                   , InstalledPackageId, installedPackageId )-import           Distribution.Client.InstallPlan-                   ( GenericInstallPlan, GenericPlanPackage )-import qualified Distribution.Client.InstallPlan as InstallPlan-import qualified Distribution.Client.ComponentDeps as CD-import           Distribution.Client.ComponentDeps (ComponentDeps)-import           Distribution.Client.DistDirLayout-import           Distribution.Client.FileMonitor-import           Distribution.Client.SetupWrapper-import           Distribution.Client.JobControl-import           Distribution.Client.FetchUtils-import           Distribution.Client.GlobalFlags (RepoContext)-import qualified Distribution.Client.Tar as Tar-import           Distribution.Client.Setup (filterConfigureFlags)-import           Distribution.Client.SrcDist (allPackageSourceFiles)-import           Distribution.Client.Utils (removeExistingFile)--import           Distribution.Package hiding (InstalledPackageId, installedPackageId)-import           Distribution.InstalledPackageInfo (InstalledPackageInfo)-import qualified Distribution.InstalledPackageInfo as Installed-import           Distribution.Simple.Program-import qualified Distribution.Simple.Setup as Cabal-import           Distribution.Simple.Command (CommandUI)-import qualified Distribution.Simple.Register as Cabal-import qualified Distribution.Simple.InstallDirs as InstallDirs-import           Distribution.Simple.LocalBuildInfo (ComponentName)--import           Distribution.Simple.Utils hiding (matchFileGlob)-import           Distribution.Version-import           Distribution.Verbosity-import           Distribution.Text-import           Distribution.ParseUtils ( showPWarning )--import           Data.Map (Map)-import qualified Data.Map as Map-import           Data.Set (Set)-import qualified Data.Set as Set-import qualified Data.ByteString.Lazy as LBS--#if !MIN_VERSION_base(4,8,0)-import           Control.Applicative-#endif-import           Control.Monad-import           Control.Exception-import           Control.Concurrent.Async-import           Control.Concurrent.MVar-import           Data.List-import           Data.Maybe--import           System.FilePath-import           System.IO-import           System.Directory-import           System.Exit (ExitCode)------------------------------------------------------------------------------------ * Overall building strategy.------------------------------------------------------------------------------------- We start with an 'ElaboratedInstallPlan' that has already been improved by--- reusing packages from the store. So the remaining packages in the--- 'InstallPlan.Configured' state are ones we either need to build or rebuild.------ First, we do a preliminary dry run phase where we work out which packages--- we really need to (re)build, and for the ones we do need to build which--- build phase to start at.------------------------------------------------------------------------------------ * Dry run: what bits of the 'ElaboratedInstallPlan' will we execute?----------------------------------------------------------------------------------- We split things like this for a couple reasons. Firstly we need to be able--- to do dry runs, and these need to be reasonably accurate in terms of--- letting users know what (and why) things are going to be (re)built.------ Given that we need to be able to do dry runs, it would not be great if--- we had to repeat all the same work when we do it for real. Not only is--- it duplicate work, but it's duplicate code which is likely to get out of--- sync. So we do things only once. We preserve info we discover in the dry--- run phase and rely on it later when we build things for real. This also--- somewhat simplifies the build phase. So this way the dry run can't so--- easily drift out of sync with the real thing since we're relying on the--- info it produces.------ An additional advantage is that it makes it easier to debug rebuild--- errors (ie rebuilding too much or too little), since all the rebuild--- decisions are made without making any state changes at the same time--- (that would make it harder to reproduce the problem sitation).----- | The 'BuildStatus' of every package in the 'ElaboratedInstallPlan'----type BuildStatusMap = Map InstalledPackageId BuildStatus---- | The build status for an individual package. That is, the state that the--- package is in prior to initiating a (re)build.------ It serves two purposes:------  * For dry-run output, it lets us explain to the user if and why a package---    is going to be (re)built.------  * It tell us what step to start or resume building from, and carries---    enough information for us to be able to do so.----data BuildStatus =--     -- | The package is in the 'InstallPlan.PreExisting' state, so does not-     --   need building.-     BuildStatusPreExisting--     -- | The package has not been downloaded yet, so it will have to be-     --   downloaded, unpacked and built.-   | BuildStatusDownload--     -- | The package has not been unpacked yet, so it will have to be-     --   unpacked and built.-   | BuildStatusUnpack FilePath--     -- | The package exists in a local dir already, and just needs building-     --   or rebuilding. So this can only happen for 'BuildInplaceOnly' style-     --   packages.-   | BuildStatusRebuild FilePath BuildStatusRebuild--     -- | The package exists in a local dir already, and is fully up to date.-     --   So this package can be put into the 'InstallPlan.Installed' state-     --   and it does not need to be built.-   | BuildStatusUpToDate (Maybe InstalledPackageInfo) BuildSuccess---- | For a package that is going to be built or rebuilt, the state it's in now.------ So again, this tells us why a package needs to be rebuilt and what build--- phases need to be run. The 'MonitorChangedReason' gives us details like--- which file changed, which is mainly for high verbosity debug output.----data BuildStatusRebuild =--     -- | The package configuration changed, so the configure and build phases-     --   needs to be (re)run.-     BuildStatusConfigure (MonitorChangedReason ())--     -- | The configuration has not changed but the build phase needs to be-     -- rerun. We record the reason the (re)build is needed.-     ---     -- The optional registration info here tells us if we've registered the-     -- package already, or if we stil need to do that after building.-     ---   | BuildStatusBuild (Maybe (Maybe InstalledPackageInfo)) BuildReason--data BuildReason =-     -- | The depencencies of this package have been (re)built so the build-     -- phase needs to be rerun.-     ---     -- The optional registration info here tells us if we've registered the-     -- package already, or if we stil need to do that after building.-     ---     BuildReasonDepsRebuilt--     -- | Changes in files within the package (or first run or corrupt cache) -   | BuildReasonFilesChanged (MonitorChangedReason ())--     -- | An important special case is that no files have changed but the-     -- set of components the /user asked to build/ has changed. We track the-     -- set of components /we have built/, which of course only grows (until-     -- some other change resets it).-     ---     -- The @Set 'ComponentName'@ is the set of components we have built-     -- previously. When we update the monitor we take the union of the ones-     -- we have built previously with the ones the user has asked for this-     -- time and save those. See 'updatePackageBuildFileMonitor'.-     ---   | BuildReasonExtraTargets (Set ComponentName)--     -- | Although we're not going to build any additional targets as a whole,-     -- we're going to build some part of a component or run a repl or any-     -- other action that does not result in additional persistent artifacts.-     -- -   | BuildReasonEphemeralTargets---- | Which 'BuildStatus' values indicate we'll have to do some build work of--- some sort. In particular we use this as part of checking if any of a--- package's deps have changed.----buildStatusRequiresBuild :: BuildStatus -> Bool-buildStatusRequiresBuild BuildStatusPreExisting = False-buildStatusRequiresBuild BuildStatusUpToDate {} = False-buildStatusRequiresBuild _                      = True---- | Do the dry run pass. This is a prerequisite of 'rebuildTargets'.------ It gives us the 'BuildStatusMap' and also gives us an improved version of--- the 'ElaboratedInstallPlan' with packages switched to the--- 'InstallPlan.Installed' state when we find that they're already up to date.----rebuildTargetsDryRun :: DistDirLayout-                     -> ElaboratedInstallPlan-                     -> IO (ElaboratedInstallPlan, BuildStatusMap)-rebuildTargetsDryRun distDirLayout@DistDirLayout{..} = \installPlan -> do--    -- Do the various checks to work out the 'BuildStatus' of each package-    pkgsBuildStatus <- foldMInstallPlanDepOrder installPlan dryRunPkg--    -- For 'BuildStatusUpToDate' packages, improve the plan by marking them as-    -- 'InstallPlan.Installed'.-    let installPlan' = improveInstallPlanWithUpToDatePackages-                         installPlan pkgsBuildStatus--    return (installPlan', pkgsBuildStatus)-  where-    dryRunPkg :: ElaboratedPlanPackage-              -> ComponentDeps [BuildStatus]-              -> IO BuildStatus-    dryRunPkg (InstallPlan.PreExisting _pkg) _depsBuildStatus =-      return BuildStatusPreExisting--    dryRunPkg (InstallPlan.Configured pkg) depsBuildStatus = do-      mloc <- checkFetched (pkgSourceLocation pkg)-      case mloc of-        Nothing -> return BuildStatusDownload--        Just (LocalUnpackedPackage srcdir) ->-          -- For the case of a user-managed local dir, irrespective of the-          -- build style, we build from that directory and put build-          -- artifacts under the shared dist directory.-          dryRunLocalPkg pkg depsBuildStatus srcdir--        -- The three tarball cases are handled the same as each other,-        -- though depending on the build style.-        Just (LocalTarballPackage    tarball) ->-          dryRunTarballPkg pkg depsBuildStatus tarball--        Just (RemoteTarballPackage _ tarball) ->-          dryRunTarballPkg pkg depsBuildStatus tarball--        Just (RepoTarballPackage _ _ tarball) ->-          dryRunTarballPkg pkg depsBuildStatus tarball--    dryRunPkg (InstallPlan.Processing {}) _ = unexpectedState-    dryRunPkg (InstallPlan.Installed  {}) _ = unexpectedState-    dryRunPkg (InstallPlan.Failed     {}) _ = unexpectedState--    unexpectedState = error "rebuildTargetsDryRun: unexpected package state"--    dryRunTarballPkg :: ElaboratedConfiguredPackage-                     -> ComponentDeps [BuildStatus]-                     -> FilePath-                     -> IO BuildStatus-    dryRunTarballPkg pkg depsBuildStatus tarball =-      case pkgBuildStyle pkg of-        BuildAndInstall  -> return (BuildStatusUnpack tarball)-        BuildInplaceOnly -> do-          -- TODO: [nice to have] use a proper file monitor rather than this dir exists test-          exists <- doesDirectoryExist srcdir-          if exists-            then dryRunLocalPkg pkg depsBuildStatus srcdir-            else return (BuildStatusUnpack tarball)-      where-        srcdir = distUnpackedSrcDirectory (packageId pkg)--    dryRunLocalPkg :: ElaboratedConfiguredPackage-                   -> ComponentDeps [BuildStatus]-                   -> FilePath-                   -> IO BuildStatus-    dryRunLocalPkg pkg depsBuildStatus srcdir = do-        -- Go and do lots of I/O, reading caches and probing files to work out-        -- if anything has changed-        change <- checkPackageFileMonitorChanged-                    packageFileMonitor pkg srcdir depsBuildStatus-        case change of-          -- It did change, giving us 'BuildStatusRebuild' info on why-          Left rebuild ->-            return (BuildStatusRebuild srcdir rebuild)--          -- No changes, the package is up to date. Use the saved build results.-          Right (mipkg, buildSuccess) ->-            return (BuildStatusUpToDate mipkg buildSuccess)-      where-        packageFileMonitor =-          newPackageFileMonitor distDirLayout (packageId pkg)----- | A specialised traversal over the packages in an install plan.------ The packages are visited in dependency order, starting with packages with no--- depencencies. The result for each package is accumulated into a 'Map' and--- returned as the final result. In addition, when visting a package, the--- visiting function is passed the results for all the immediate package--- depencencies. This can be used to propagate information from depencencies.----foldMInstallPlanDepOrder-  :: forall m ipkg srcpkg iresult ifailure b.-     (Monad m,-      HasUnitId ipkg,   PackageFixedDeps ipkg,-      HasUnitId srcpkg, PackageFixedDeps srcpkg)-  => GenericInstallPlan ipkg srcpkg iresult ifailure-  -> (GenericPlanPackage ipkg srcpkg iresult ifailure ->-      ComponentDeps [b] -> m b)-  -> m (Map InstalledPackageId b)-foldMInstallPlanDepOrder plan0 visit =-    go Map.empty (InstallPlan.reverseTopologicalOrder plan0)-  where-    go :: Map InstalledPackageId b-       -> [GenericPlanPackage ipkg srcpkg iresult ifailure]-       -> m (Map InstalledPackageId b)-    go !results [] = return results--    go !results (pkg : pkgs) = do-      -- we go in the right order so the results map has entries for all deps-      let depresults :: ComponentDeps [b]-          depresults =-            fmap (map (\ipkgid -> let Just result = Map.lookup ipkgid results-                                   in result))-                 (depends pkg)-      result <- visit pkg depresults-      let results' = Map.insert (installedPackageId pkg) result results-      go results' pkgs--improveInstallPlanWithUpToDatePackages :: ElaboratedInstallPlan-                                       -> BuildStatusMap-                                       -> ElaboratedInstallPlan-improveInstallPlanWithUpToDatePackages installPlan pkgsBuildStatus =-    replaceWithPreInstalled installPlan-      [ (installedPackageId pkg, mipkg, buildSuccess)-      | InstallPlan.Configured pkg-          <- InstallPlan.reverseTopologicalOrder installPlan-      , let ipkgid = installedPackageId pkg-            Just pkgBuildStatus = Map.lookup ipkgid pkgsBuildStatus-      , BuildStatusUpToDate mipkg buildSuccess <- [pkgBuildStatus]-      ]-  where-    replaceWithPreInstalled =-      foldl' (\plan (ipkgid, mipkg, buildSuccess) ->-                InstallPlan.preinstalled ipkgid mipkg buildSuccess plan)----------------------------------- Package change detection------- | As part of the dry run for local unpacked packages we have to check if the--- package config or files have changed. That is the purpose of--- 'PackageFileMonitor' and 'checkPackageFileMonitorChanged'.------ When a package is (re)built, the monitor must be updated to reflect the new--- state of the package. Because we sometimes build without reconfiguring the--- state updates are split into two, one for package config changes and one--- for other changes. This is the purpose of 'updatePackageConfigFileMonitor'--- and 'updatePackageBuildFileMonitor'.----data PackageFileMonitor = PackageFileMonitor {-       pkgFileMonitorConfig :: FileMonitor ElaboratedConfiguredPackage (),-       pkgFileMonitorBuild  :: FileMonitor (Set ComponentName) BuildSuccess,-       pkgFileMonitorReg    :: FileMonitor () (Maybe InstalledPackageInfo)-     }--newPackageFileMonitor :: DistDirLayout -> PackageId -> PackageFileMonitor-newPackageFileMonitor DistDirLayout{distPackageCacheFile} pkgid =-    PackageFileMonitor {-      pkgFileMonitorConfig =-        newFileMonitor (distPackageCacheFile pkgid "config"),--      pkgFileMonitorBuild =-        FileMonitor {-          fileMonitorCacheFile = distPackageCacheFile pkgid "build",-          fileMonitorKeyValid  = \componentsToBuild componentsAlreadyBuilt ->-            componentsToBuild `Set.isSubsetOf` componentsAlreadyBuilt,-          fileMonitorCheckIfOnlyValueChanged = True-        },--      pkgFileMonitorReg =-        newFileMonitor (distPackageCacheFile pkgid "registration")-    }---- | Helper function for 'checkPackageFileMonitorChanged',--- 'updatePackageConfigFileMonitor' and 'updatePackageBuildFileMonitor'.------ It selects the info from a 'ElaboratedConfiguredPackage' that are used by--- the 'FileMonitor's (in the 'PackageFileMonitor') to detect value changes.----packageFileMonitorKeyValues :: ElaboratedConfiguredPackage-                            -> (ElaboratedConfiguredPackage, Set ComponentName)-packageFileMonitorKeyValues pkg =-    (pkgconfig, buildComponents)-  where-    -- The first part is the value used to guard (re)configuring the package.-    -- That is, if this value changes then we will reconfigure.-    -- The ElaboratedConfiguredPackage consists mostly (but not entirely) of-    -- information that affects the (re)configure step. But those parts that-    -- do not affect the configure step need to be nulled out. Those parts are-    -- the specific targets that we're going to build.-    ---    pkgconfig = pkg {-      pkgBuildTargets  = [],-      pkgReplTarget    = Nothing,-      pkgBuildHaddocks = False-    }--    -- The second part is the value used to guard the build step. So this is-    -- more or less the opposite of the first part, as it's just the info about-    -- what targets we're going to build.-    ---    buildComponents = pkgBuildTargetWholeComponents pkg---- | Do all the checks on whether a package has changed and thus needs either--- rebuilding or reconfiguring and rebuilding.----checkPackageFileMonitorChanged :: PackageFileMonitor-                               -> ElaboratedConfiguredPackage-                               -> FilePath-                               -> ComponentDeps [BuildStatus]-                               -> IO (Either BuildStatusRebuild-                                            (Maybe InstalledPackageInfo,-                                             BuildSuccess))-checkPackageFileMonitorChanged PackageFileMonitor{..}-                               pkg srcdir depsBuildStatus = do-    --TODO: [nice to have] some debug-level message about file changes, like rerunIfChanged-    configChanged <- checkFileMonitorChanged-                       pkgFileMonitorConfig srcdir pkgconfig-    case configChanged of-      MonitorChanged monitorReason ->-          return (Left (BuildStatusConfigure monitorReason'))-        where-          monitorReason' = fmap (const ()) monitorReason--      MonitorUnchanged () _-          -- The configChanged here includes the identity of the dependencies,-          -- so depsBuildStatus is just needed for the changes in the content-          -- of depencencies.-        | any buildStatusRequiresBuild (CD.flatDeps depsBuildStatus) -> do-            regChanged <- checkFileMonitorChanged pkgFileMonitorReg srcdir ()-            let mreg = changedToMaybe regChanged-            return (Left (BuildStatusBuild mreg BuildReasonDepsRebuilt))--        | otherwise -> do-            buildChanged  <- checkFileMonitorChanged-                               pkgFileMonitorBuild srcdir buildComponents-            regChanged    <- checkFileMonitorChanged-                               pkgFileMonitorReg srcdir ()-            let mreg = changedToMaybe regChanged-            case (buildChanged, regChanged) of-              (MonitorChanged (MonitoredValueChanged prevBuildComponents), _) ->-                  return (Left (BuildStatusBuild mreg buildReason))-                where-                  buildReason = BuildReasonExtraTargets prevBuildComponents--              (MonitorChanged monitorReason, _) ->-                  return (Left (BuildStatusBuild mreg buildReason))-                where-                  buildReason    = BuildReasonFilesChanged monitorReason'-                  monitorReason' = fmap (const ()) monitorReason--              (MonitorUnchanged _ _, MonitorChanged monitorReason) ->-                -- this should only happen if the file is corrupt or been-                -- manually deleted. We don't want to bother with another-                -- phase just for this, so we'll reregister by doing a build.-                  return (Left (BuildStatusBuild Nothing buildReason))-                where-                  buildReason    = BuildReasonFilesChanged monitorReason'-                  monitorReason' = fmap (const ()) monitorReason--              (MonitorUnchanged _ _, MonitorUnchanged _ _)-                | pkgHasEphemeralBuildTargets pkg ->-                  return (Left (BuildStatusBuild mreg buildReason))-                where-                  buildReason = BuildReasonEphemeralTargets--              (MonitorUnchanged buildSuccess _, MonitorUnchanged mipkg _) ->-                return (Right (mipkg, buildSuccess))-  where-    (pkgconfig, buildComponents) = packageFileMonitorKeyValues pkg-    changedToMaybe (MonitorChanged     _) = Nothing-    changedToMaybe (MonitorUnchanged x _) = Just x---updatePackageConfigFileMonitor :: PackageFileMonitor-                               -> FilePath-                               -> ElaboratedConfiguredPackage-                               -> IO ()-updatePackageConfigFileMonitor PackageFileMonitor{pkgFileMonitorConfig}-                               srcdir pkg =-    updateFileMonitor pkgFileMonitorConfig srcdir Nothing-                      [] pkgconfig ()-  where-    (pkgconfig, _buildComponents) = packageFileMonitorKeyValues pkg--updatePackageBuildFileMonitor :: PackageFileMonitor-                              -> FilePath-                              -> MonitorTimestamp-                              -> ElaboratedConfiguredPackage-                              -> BuildStatusRebuild-                              -> [FilePath]-                              -> BuildSuccess-                              -> IO ()-updatePackageBuildFileMonitor PackageFileMonitor{pkgFileMonitorBuild}-                              srcdir timestamp pkg pkgBuildStatus-                              allSrcFiles buildSuccess =-    updateFileMonitor pkgFileMonitorBuild srcdir (Just timestamp)-                      (map monitorFileHashed allSrcFiles)-                      buildComponents' buildSuccess-  where-    (_pkgconfig, buildComponents) = packageFileMonitorKeyValues pkg--    -- If the only thing that's changed is that we're now building extra-    -- components, then we can avoid later unnecessary rebuilds by saving the-    -- total set of components that have been built, namely the union of the-    -- existing ones plus the new ones. If files also changed this would be-    -- the wrong thing to do. Note that we rely on the-    -- fileMonitorCheckIfOnlyValueChanged = True mode to get this guarantee-    -- that it's /only/ the value that changed not any files that changed.-    buildComponents' =-      case pkgBuildStatus of-        BuildStatusBuild _ (BuildReasonExtraTargets prevBuildComponents)-          -> buildComponents `Set.union` prevBuildComponents-        _ -> buildComponents--updatePackageRegFileMonitor :: PackageFileMonitor-                            -> FilePath-                            -> Maybe InstalledPackageInfo-                            -> IO ()-updatePackageRegFileMonitor PackageFileMonitor{pkgFileMonitorReg}-                            srcdir mipkg =-    updateFileMonitor pkgFileMonitorReg srcdir Nothing-                      [] () mipkg--invalidatePackageRegFileMonitor :: PackageFileMonitor -> IO ()-invalidatePackageRegFileMonitor PackageFileMonitor{pkgFileMonitorReg} =-    removeExistingFile (fileMonitorCacheFile pkgFileMonitorReg)------------------------------------------------------------------------------------ * Doing it: executing an 'ElaboratedInstallPlan'------------------------------------------------------------------------------------ | Build things for real.------ It requires the 'BuildStatusMap' gatthered by 'rebuildTargetsDryRun'.----rebuildTargets :: Verbosity-               -> DistDirLayout-               -> ElaboratedInstallPlan-               -> ElaboratedSharedConfig-               -> BuildStatusMap-               -> BuildTimeSettings-               -> IO ElaboratedInstallPlan-rebuildTargets verbosity-               distDirLayout@DistDirLayout{..}-               installPlan-               sharedPackageConfig-               pkgsBuildStatus-               buildSettings@BuildTimeSettings{buildSettingNumJobs} = do--    -- Concurrency control: create the job controller and concurrency limits-    -- for downloading, building and installing.-    jobControl    <- if isParallelBuild then newParallelJobControl-                                        else newSerialJobControl-    buildLimit    <- newJobLimit buildSettingNumJobs-    installLock   <- newLock -- serialise installation-    cacheLock     <- newLock -- serialise access to setup exe cache-                             --TODO: [code cleanup] eliminate setup exe cache--    createDirectoryIfMissingVerbose verbosity False distBuildRootDirectory-    createDirectoryIfMissingVerbose verbosity False distTempDirectory--    -- Before traversing the install plan, pre-emptively find all packages that-    -- will need to be downloaded and start downloading them.-    asyncDownloadPackages verbosity withRepoCtx-                          installPlan pkgsBuildStatus $ \downloadMap ->--      -- For each package in the plan, in dependency order, but in parallel...-      executeInstallPlan verbosity jobControl installPlan $ \pkg ->-        handle (return . BuildFailure) $ --TODO: review exception handling--        let ipkgid = installedPackageId pkg-            Just pkgBuildStatus = Map.lookup ipkgid pkgsBuildStatus in--        rebuildTarget-          verbosity-          distDirLayout-          buildSettings downloadMap-          buildLimit installLock cacheLock -          sharedPackageConfig-          pkg-          pkgBuildStatus-  where-    isParallelBuild = buildSettingNumJobs >= 2-    withRepoCtx     = projectConfigWithBuilderRepoContext verbosity -                        buildSettings---- | Given all the context and resources, (re)build an individual package.----rebuildTarget :: Verbosity-              -> DistDirLayout-              -> BuildTimeSettings-              -> AsyncDownloadMap-              -> JobLimit -> Lock -> Lock-              -> ElaboratedSharedConfig-              -> ElaboratedReadyPackage-              -> BuildStatus-              -> IO BuildResult-rebuildTarget verbosity-              distDirLayout@DistDirLayout{distBuildDirectory}-              buildSettings downloadMap-              buildLimit installLock cacheLock-              sharedPackageConfig-              rpkg@(ReadyPackage pkg _)-              pkgBuildStatus =--    -- We rely on the 'BuildStatus' to decide which phase to start from:-    case pkgBuildStatus of-      BuildStatusDownload              -> downloadPhase-      BuildStatusUnpack tarball        -> unpackTarballPhase tarball-      BuildStatusRebuild srcdir status -> rebuildPhase status srcdir--      -- TODO: perhaps re-nest the types to make these impossible-      BuildStatusPreExisting {} -> unexpectedState-      BuildStatusUpToDate    {} -> unexpectedState-  where-    unexpectedState = error "rebuildTarget: unexpected package status"--    downloadPhase = do-        downsrcloc <- waitAsyncPackageDownload verbosity downloadMap pkg-        case downsrcloc of-          DownloadedTarball tarball -> unpackTarballPhase tarball-          --TODO: [nice to have] git/darcs repos etc---    unpackTarballPhase tarball =-      withJobLimit buildLimit $-        withTarballLocalDirectory-          verbosity distDirLayout tarball-          (packageId pkg) (pkgBuildStyle pkg)-          (pkgDescriptionOverride pkg) $--          case pkgBuildStyle pkg of-            BuildAndInstall  -> buildAndInstall-            BuildInplaceOnly -> buildInplace buildStatus-              where-                buildStatus = BuildStatusConfigure MonitorFirstRun--    -- Note that this really is rebuild, not build. It can only happen for-    -- 'BuildInplaceOnly' style packages. 'BuildAndInstall' style packages-    -- would only start from download or unpack phases.-    ---    rebuildPhase buildStatus srcdir =-        assert (pkgBuildStyle pkg == BuildInplaceOnly) $--        withJobLimit buildLimit $-          buildInplace buildStatus srcdir builddir-      where-        builddir = distBuildDirectory (packageId pkg)--    buildAndInstall srcdir builddir =-        buildAndInstallUnpackedPackage-          verbosity distDirLayout-          buildSettings installLock cacheLock-          sharedPackageConfig-          rpkg-          srcdir builddir'-      where-        builddir' = makeRelative srcdir builddir-        --TODO: [nice to have] ^^ do this relative stuff better--    buildInplace buildStatus srcdir builddir =-        --TODO: [nice to have] use a relative build dir rather than absolute-        buildInplaceUnpackedPackage-          verbosity distDirLayout-          buildSettings cacheLock-          sharedPackageConfig-          rpkg-          buildStatus-          srcdir builddir----TODO: [nice to have] do we need to use a with-style for the temp files for downloading http--- packages, or are we going to cache them persistently?--type AsyncDownloadMap = Map (PackageLocation (Maybe FilePath))-                            (MVar DownloadedSourceLocation)--data DownloadedSourceLocation = DownloadedTarball FilePath-                              --TODO: [nice to have] git/darcs repos etc--downloadedSourceLocation :: PackageLocation FilePath-                         -> Maybe DownloadedSourceLocation-downloadedSourceLocation pkgloc =-    case pkgloc of-      RemoteTarballPackage _ tarball -> Just (DownloadedTarball tarball)-      RepoTarballPackage _ _ tarball -> Just (DownloadedTarball tarball)-      _                              -> Nothing---- | Given the current 'InstallPlan' and 'BuildStatusMap', select all the--- packages we have to download and fork off an async action to download them.--- We download them in dependency order so that the one's we'll need--- first are the ones we will start downloading first.------ The body action is passed a map from those packages (identified by their--- location) to a completion var for that package. So the body action should--- lookup the location and use 'waitAsyncPackageDownload' to get the result.----asyncDownloadPackages :: Verbosity-                      -> ((RepoContext -> IO ()) -> IO ())-                      -> ElaboratedInstallPlan-                      -> BuildStatusMap-                      -> (AsyncDownloadMap -> IO a)-                      -> IO a-asyncDownloadPackages verbosity withRepoCtx installPlan pkgsBuildStatus body-  | null pkgsToDownload = body Map.empty-  | otherwise = do-    --TODO: [research required] use parallel downloads? if so, use the fetchLimit--    asyncDownloadVars <- mapM (\loc -> (,) loc <$> newEmptyMVar) pkgsToDownload--    let downloadAction :: IO ()-        downloadAction =-          withRepoCtx $ \repoctx ->-            forM_ asyncDownloadVars $ \(pkgloc, var) -> do-              Just scrloc <- downloadedSourceLocation <$>-                             fetchPackage verbosity repoctx pkgloc-              putMVar var scrloc--    withAsync downloadAction $ \_ ->-      body (Map.fromList asyncDownloadVars)-  where-    pkgsToDownload = -      [ pkgSourceLocation pkg-      | InstallPlan.Configured pkg-         <- InstallPlan.reverseTopologicalOrder installPlan-      , let ipkgid = installedPackageId pkg-            Just pkgBuildStatus = Map.lookup ipkgid pkgsBuildStatus-      , BuildStatusDownload <- [pkgBuildStatus]-      ]----- | Check if a package needs downloading, and if so expect to find a download--- in progress in the given 'AsyncDownloadMap' and wait on it to finish.----waitAsyncPackageDownload :: Verbosity-                         -> AsyncDownloadMap-                         -> ElaboratedConfiguredPackage-                         -> IO DownloadedSourceLocation-waitAsyncPackageDownload verbosity downloadMap pkg =-    case Map.lookup (pkgSourceLocation pkg) downloadMap of-      Just hnd -> do-        debug verbosity $-          "Waiting for download of " ++ display (packageId pkg) ++ " to finish"-        --TODO: [required eventually] do the exception handling on download stuff-        takeMVar hnd-      Nothing ->-        fail "waitAsyncPackageDownload: package not being download"---executeInstallPlan-  :: forall ipkg srcpkg iresult.-     (HasUnitId ipkg,   PackageFixedDeps ipkg,-      HasUnitId srcpkg, PackageFixedDeps srcpkg)-  => Verbosity-  -> JobControl IO ( GenericReadyPackage srcpkg ipkg-                   , GenericBuildResult ipkg iresult BuildFailure )-  -> GenericInstallPlan ipkg srcpkg iresult BuildFailure-  -> (    GenericReadyPackage srcpkg ipkg-       -> IO (GenericBuildResult ipkg iresult BuildFailure))-  -> IO (GenericInstallPlan ipkg srcpkg iresult BuildFailure)-executeInstallPlan verbosity jobCtl plan0 installPkg =-    tryNewTasks 0 plan0-  where-    tryNewTasks taskCount plan = do-      case InstallPlan.ready plan of-        [] | taskCount == 0 -> return plan-           | otherwise      -> waitForTasks taskCount plan-        pkgs                -> do-          sequence_-            [ do debug verbosity $ "Ready to install " ++ display pkgid-                 spawnJob jobCtl $ do-                   buildResult <- installPkg pkg-                   return (pkg, buildResult)-            | pkg <- pkgs-            , let pkgid = packageId pkg-            ]--          let taskCount' = taskCount + length pkgs-              plan'      = InstallPlan.processing pkgs plan-          waitForTasks taskCount' plan'--    waitForTasks taskCount plan = do-      debug verbosity $ "Waiting for install task to finish..."-      (pkg, buildResult) <- collectJob jobCtl-      let taskCount' = taskCount-1-          plan'      = updatePlan pkg buildResult plan-      tryNewTasks taskCount' plan'--    updatePlan :: GenericReadyPackage srcpkg ipkg-               -> GenericBuildResult ipkg iresult BuildFailure-               -> GenericInstallPlan ipkg srcpkg iresult BuildFailure-               -> GenericInstallPlan ipkg srcpkg iresult BuildFailure-    updatePlan pkg (BuildSuccess mipkg buildSuccess) =-        InstallPlan.completed (installedPackageId pkg) mipkg buildSuccess--    updatePlan pkg (BuildFailure buildFailure) =-        InstallPlan.failed (installedPackageId pkg) buildFailure depsFailure-      where-        depsFailure = DependentFailed (packageId pkg)-        -- So this first pkgid failed for whatever reason (buildFailure).-        -- All the other packages that depended on this pkgid, which we-        -- now cannot build, we mark as failing due to 'DependentFailed'-        -- which kind of means it was not their fault.----- | Ensure that the package is unpacked in an appropriate directory, either--- a temporary one or a persistent one under the shared dist directory. ----withTarballLocalDirectory-  :: Verbosity-  -> DistDirLayout-  -> FilePath-  -> PackageId-  -> BuildStyle-  -> Maybe CabalFileText-  -> (FilePath -> FilePath -> IO a)-  -> IO a-withTarballLocalDirectory verbosity distDirLayout@DistDirLayout{..}-                          tarball pkgid buildstyle pkgTextOverride-                          buildPkg  =-      case buildstyle of-        -- In this case we make a temp dir, unpack the tarball to there and-        -- build and install it from that temp dir.-        BuildAndInstall ->-          withTempDirectory verbosity distTempDirectory-                            (display (packageName pkgid)) $ \tmpdir -> do-            unpackPackageTarball verbosity tarball tmpdir-                                 pkgid pkgTextOverride-            let srcdir   = tmpdir </> display pkgid-                builddir = srcdir </> "dist"-            buildPkg srcdir builddir--        -- In this case we make sure the tarball has been unpacked to the-        -- appropriate location under the shared dist dir, and then build it-        -- inplace there-        BuildInplaceOnly -> do-          let srcrootdir = distUnpackedSrcRootDirectory-              srcdir     = distUnpackedSrcDirectory pkgid-              builddir   = distBuildDirectory pkgid-          -- TODO: [nice to have] use a proper file monitor rather than this dir exists test-          exists <- doesDirectoryExist srcdir-          unless exists $ do-            createDirectoryIfMissingVerbose verbosity False srcrootdir-            unpackPackageTarball verbosity tarball srcrootdir-                                 pkgid pkgTextOverride-            moveTarballShippedDistDirectory verbosity distDirLayout-                                            srcrootdir pkgid-          buildPkg srcdir builddir---unpackPackageTarball :: Verbosity -> FilePath -> FilePath-                     -> PackageId -> Maybe CabalFileText-                     -> IO ()-unpackPackageTarball verbosity tarball parentdir pkgid pkgTextOverride =-    --TODO: [nice to have] switch to tar package and catch tar exceptions-    annotateFailure UnpackFailed $ do--      -- Unpack the tarball-      ---      info verbosity $ "Extracting " ++ tarball ++ " to " ++ parentdir ++ "..."-      Tar.extractTarGzFile parentdir pkgsubdir tarball--      -- Sanity check-      ---      exists <- doesFileExist cabalFile-      when (not exists) $-        die $ "Package .cabal file not found in the tarball: " ++ cabalFile--      -- Overwrite the .cabal with the one from the index, when appropriate-      ---      case pkgTextOverride of-        Nothing     -> return ()-        Just pkgtxt -> do-          info verbosity $ "Updating " ++ display pkgname <.> "cabal"-                        ++ " with the latest revision from the index."-          writeFileAtomic cabalFile pkgtxt--  where-    cabalFile = parentdir </> pkgsubdir-                          </> display pkgname <.> "cabal"-    pkgsubdir = display pkgid-    pkgname   = packageName pkgid----- | This is a bit of a hacky workaround. A number of packages ship--- pre-processed .hs files in a dist directory inside the tarball. We don't--- use the standard 'dist' location so unless we move this dist dir to the--- right place then we'll miss the shipped pre-procssed files. This hacky--- approach to shipped pre-procssed files ought to be replaced by a proper--- system, though we'll still need to keep this hack for older packages.----moveTarballShippedDistDirectory :: Verbosity -> DistDirLayout-                                -> FilePath -> PackageId -> IO ()-moveTarballShippedDistDirectory verbosity DistDirLayout{distBuildDirectory}-                                parentdir pkgid = do-    distDirExists <- doesDirectoryExist tarballDistDir-    when distDirExists $ do-      debug verbosity $ "Moving '" ++ tarballDistDir ++ "' to '"-                                   ++ targetDistDir ++ "'"-      --TODO: [nice to have] or perhaps better to copy, and use a file monitor-      renameDirectory tarballDistDir targetDistDir-  where-    tarballDistDir = parentdir </> display pkgid </> "dist"-    targetDistDir  = distBuildDirectory pkgid---buildAndInstallUnpackedPackage :: Verbosity-                               -> DistDirLayout-                               -> BuildTimeSettings -> Lock -> Lock-                               -> ElaboratedSharedConfig-                               -> ElaboratedReadyPackage-                               -> FilePath -> FilePath-                               -> IO BuildResult-buildAndInstallUnpackedPackage verbosity-                               DistDirLayout{distTempDirectory}-                               BuildTimeSettings {-                                 buildSettingNumJobs,-                                 buildSettingLogFile-                               }-                               installLock cacheLock-                               pkgshared@ElaboratedSharedConfig {-                                 pkgConfigPlatform      = platform,-                                 pkgConfigCompiler      = compiler,-                                 pkgConfigCompilerProgs = progdb-                               }-                               rpkg@(ReadyPackage pkg _deps)-                               srcdir builddir = do--    createDirectoryIfMissingVerbose verbosity False builddir-    initLogFile--    --TODO: [code cleanup] deal consistently with talking to older Setup.hs versions, much like-    --      we do for ghc, with a proper options type and rendering step-    --      which will also let us call directly into the lib, rather than always-    --      going via the lib's command line interface, which would also allow-    --      passing data like installed packages, compiler, and program db for a-    --      quicker configure.--    --TODO: [required feature] docs and tests-    --TODO: [required feature] sudo re-exec--    -- Configure phase-    when isParallelBuild $-      notice verbosity $ "Configuring " ++ display pkgid ++ "..."-    annotateFailure ConfigureFailed $-      setup configureCommand configureFlags--    -- Build phase-    when isParallelBuild $-      notice verbosity $ "Building " ++ display pkgid ++ "..."-    annotateFailure BuildFailed $-      setup buildCommand buildFlags--    -- Install phase-    mipkg <--      criticalSection installLock $ -      annotateFailure InstallFailed $ do-      --TODO: [research required] do we need the installLock for copying? can we not do that in-      -- parallel? Isn't it just registering that we have to lock for?--      --TODO: [required eventually] need to lock installing this ipkig so other processes don't-      -- stomp on our files, since we don't have ABI compat, not safe to replace--      -- TODO: [required eventually] note that for nix-style installations it is not necessary to do-      -- the 'withWin32SelfUpgrade' dance, but it would be necessary for a-      -- shared bin dir.--      -- Actual installation-      setup Cabal.copyCommand copyFlags-      -      LBS.writeFile-        (InstallDirs.prefix (pkgInstallDirs pkg) </> "cabal-hash.txt") $-        (renderPackageHashInputs (packageHashInputs pkgshared pkg))--      -- here's where we could keep track of the installed files ourselves if-      -- we wanted by calling copy to an image dir and then we would make a-      -- manifest and move it to its final location--      --TODO: [nice to have] we should actually have it make an image in store/incomming and-      -- then when it's done, move it to its final location, to reduce problems-      -- with installs failing half-way. Could also register and then move.--      -- For libraries, grab the package configuration file-      -- and register it ourselves-      if pkgRequiresRegistration pkg-        then do-          ipkg <- generateInstalledPackageInfo-          -- We register ourselves rather than via Setup.hs. We need to-          -- grab and modify the InstalledPackageInfo. We decide what-          -- the installed package id is, not the build system.-          let ipkg' = ipkg { Installed.installedUnitId = ipkgid }-          Cabal.registerPackage verbosity compiler progdb-                                True -- multi-instance, nix style-                                (pkgRegisterPackageDBStack pkg) ipkg'-          return (Just ipkg')-        else return Nothing--    --TODO: [required feature] docs and test phases-    let docsResult  = DocsNotTried-        testsResult = TestsNotTried--    return (BuildSuccess mipkg (BuildOk docsResult testsResult))--  where-    pkgid  = packageId rpkg-    ipkgid = installedPackageId rpkg--    isParallelBuild = buildSettingNumJobs >= 2--    configureCommand = Cabal.configureCommand defaultProgramConfiguration-    configureFlags v = flip filterConfigureFlags v $-                       setupHsConfigureFlags rpkg pkgshared-                                             verbosity builddir--    buildCommand     = Cabal.buildCommand defaultProgramConfiguration-    buildFlags   _   = setupHsBuildFlags pkg pkgshared verbosity builddir--    generateInstalledPackageInfo :: IO InstalledPackageInfo-    generateInstalledPackageInfo =-      withTempInstalledPackageInfoFile-        verbosity distTempDirectory $ \pkgConfFile -> do-        -- make absolute since setup changes dir-        pkgConfFile' <- canonicalizePath pkgConfFile-        let registerFlags _ = setupHsRegisterFlags-                                pkg pkgshared-                                verbosity builddir-                                pkgConfFile'-        setup Cabal.registerCommand registerFlags--    copyFlags _ = setupHsCopyFlags pkg pkgshared verbosity builddir--    scriptOptions = setupHsScriptOptions rpkg pkgshared srcdir builddir-                                         isParallelBuild cacheLock--    setup :: CommandUI flags -> (Version -> flags) -> IO ()-    setup cmd flags =-      withLogging $ \mLogFileHandle -> -        setupWrapper-          verbosity-          scriptOptions { useLoggingHandle = mLogFileHandle }-          (Just (pkgDescription pkg))-          cmd flags []--    mlogFile =-      case buildSettingLogFile of-        Nothing        -> Nothing-        Just mkLogFile -> Just (mkLogFile compiler platform pkgid ipkgid)--    initLogFile =-      case mlogFile of-        Nothing      -> return ()-        Just logFile -> do-          createDirectoryIfMissing True (takeDirectory logFile)-          exists <- doesFileExist logFile-          when exists $ removeFile logFile--    withLogging action =-      case mlogFile of-        Nothing      -> action Nothing-        Just logFile -> withFile logFile AppendMode (action . Just)---buildInplaceUnpackedPackage :: Verbosity-                            -> DistDirLayout-                            -> BuildTimeSettings -> Lock-                            -> ElaboratedSharedConfig-                            -> ElaboratedReadyPackage-                            -> BuildStatusRebuild-                            -> FilePath -> FilePath-                            -> IO BuildResult-buildInplaceUnpackedPackage verbosity-                            distDirLayout@DistDirLayout {-                              distTempDirectory,-                              distPackageCacheDirectory-                            }-                            BuildTimeSettings{buildSettingNumJobs}-                            cacheLock-                            pkgshared@ElaboratedSharedConfig {-                              pkgConfigCompiler      = compiler,-                              pkgConfigCompilerProgs = progdb-                            }-                            rpkg@(ReadyPackage pkg _deps)-                            buildStatus-                            srcdir builddir = do--        --TODO: [code cleanup] there is duplication between the distdirlayout and the builddir here-        --      builddir is not enough, we also need the per-package cachedir-        createDirectoryIfMissingVerbose verbosity False builddir-        createDirectoryIfMissingVerbose verbosity False (distPackageCacheDirectory pkgid)-        createPackageDBIfMissing verbosity compiler progdb (pkgBuildPackageDBStack pkg)--        -- Configure phase-        ---        whenReConfigure $ do-          annotateFailure ConfigureFailed $-            setup configureCommand configureFlags []-          invalidatePackageRegFileMonitor packageFileMonitor-          updatePackageConfigFileMonitor packageFileMonitor srcdir pkg--        -- Build phase-        ---        let docsResult  = DocsNotTried-            testsResult = TestsNotTried--            buildSuccess :: BuildSuccess-            buildSuccess = BuildOk docsResult testsResult--        whenRebuild $ do-          timestamp <- beginUpdateFileMonitor-          annotateFailure BuildFailed $-            setup buildCommand buildFlags buildArgs--          --TODO: [required eventually] this doesn't track file-          --non-existence, so we could fail to rebuild if someone-          --adds a new file which changes behavior.-          allSrcFiles <- allPackageSourceFiles verbosity srcdir--          updatePackageBuildFileMonitor packageFileMonitor srcdir timestamp-                                        pkg buildStatus-                                        allSrcFiles buildSuccess--        mipkg <- whenReRegister $ annotateFailure InstallFailed $ do-          -- Register locally-          mipkg <- if pkgRequiresRegistration pkg-            then do-                ipkg <- generateInstalledPackageInfo-                -- We register ourselves rather than via Setup.hs. We need to-                -- grab and modify the InstalledPackageInfo. We decide what-                -- the installed package id is, not the build system.-                let ipkg' = ipkg { Installed.installedUnitId = ipkgid }-                Cabal.registerPackage verbosity compiler progdb False-                                      (pkgRegisterPackageDBStack pkg)-                                      ipkg'-                return (Just ipkg')--           else return Nothing--          updatePackageRegFileMonitor packageFileMonitor srcdir mipkg-          return mipkg--        -- Repl phase-        ---        whenRepl $-          annotateFailure BuildFailed $-          setup replCommand replFlags replArgs--        -- Haddock phase-        whenHaddock $-          annotateFailure BuildFailed $-          setup haddockCommand haddockFlags []--        return (BuildSuccess mipkg buildSuccess)--  where-    pkgid  = packageId rpkg-    ipkgid = installedPackageId rpkg--    isParallelBuild = buildSettingNumJobs >= 2--    packageFileMonitor = newPackageFileMonitor distDirLayout pkgid--    whenReConfigure action = case buildStatus of-      BuildStatusConfigure _ -> action-      _                      -> return ()--    whenRebuild action-      | null (pkgBuildTargets pkg) = return ()-      | otherwise                  = action--    whenRepl action-      | isNothing (pkgReplTarget pkg) = return ()-      | otherwise                     = action--    whenHaddock action-      | pkgBuildHaddocks pkg = action-      | otherwise            = return ()--    whenReRegister  action = case buildStatus of-      BuildStatusConfigure          _ -> action-      BuildStatusBuild Nothing      _ -> action-      BuildStatusBuild (Just mipkg) _ -> return mipkg--    configureCommand = Cabal.configureCommand defaultProgramConfiguration-    configureFlags v = flip filterConfigureFlags v $-                       setupHsConfigureFlags rpkg pkgshared-                                             verbosity builddir--    buildCommand     = Cabal.buildCommand defaultProgramConfiguration-    buildFlags   _   = setupHsBuildFlags pkg pkgshared-                                         verbosity builddir-    buildArgs        = setupHsBuildArgs  pkg--    replCommand      = Cabal.replCommand defaultProgramConfiguration-    replFlags _      = setupHsReplFlags pkg pkgshared-                                        verbosity builddir-    replArgs         = setupHsReplArgs  pkg--    haddockCommand   = Cabal.haddockCommand-    haddockFlags _   = setupHsHaddockFlags pkg pkgshared-                                           verbosity builddir--    scriptOptions    = setupHsScriptOptions rpkg pkgshared-                                            srcdir builddir-                                            isParallelBuild cacheLock--    setup :: CommandUI flags -> (Version -> flags) -> [String] -> IO ()-    setup cmd flags args =-      setupWrapper verbosity-                   scriptOptions-                   (Just (pkgDescription pkg))-                   cmd flags args--    generateInstalledPackageInfo :: IO InstalledPackageInfo-    generateInstalledPackageInfo =-      withTempInstalledPackageInfoFile-        verbosity distTempDirectory $ \pkgConfFile -> do-        -- make absolute since setup changes dir-        pkgConfFile' <- canonicalizePath pkgConfFile-        let registerFlags _ = setupHsRegisterFlags-                                pkg pkgshared-                                verbosity builddir-                                pkgConfFile'-        setup Cabal.registerCommand registerFlags []----- helper-annotateFailure :: (String -> BuildFailure) -> IO a -> IO a-annotateFailure annotate action =-  action `catches`-    [ Handler $ \ioe  -> handler (ioe  :: IOException)-    , Handler $ \exit -> handler (exit :: ExitCode)-    ]-  where-    handler :: Exception e => e -> IO a-    handler = throwIO . annotate-#if MIN_VERSION_base(4,8,0)-            . displayException-#else-            . show-#endif---withTempInstalledPackageInfoFile :: Verbosity -> FilePath-                                 -> (FilePath -> IO ())-                                 -> IO InstalledPackageInfo-withTempInstalledPackageInfoFile verbosity tempdir action =-    withTempFile tempdir "package-registration-" $ \pkgConfFile hnd -> do-      hClose hnd-      action pkgConfFile--      (warns, ipkg) <- withUTF8FileContents pkgConfFile $ \pkgConfStr ->-        case Installed.parseInstalledPackageInfo pkgConfStr of-          Installed.ParseFailed perror -> pkgConfParseFailed perror-          Installed.ParseOk warns ipkg -> return (warns, ipkg)--      unless (null warns) $-        warn verbosity $ unlines (map (showPWarning pkgConfFile) warns)--      return ipkg-  where-    pkgConfParseFailed :: Installed.PError -> IO a-    pkgConfParseFailed perror =-      die $ "Couldn't parse the output of 'setup register --gen-pkg-config':"-            ++ show perror+             ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE NoMonoLocalBinds #-}+{-# LANGUAGE ConstraintKinds #-}++-- |+--+module Distribution.Client.ProjectBuilding (+    -- * Dry run phase+    -- | What bits of the plan will we execute? The dry run does not change+    -- anything but tells us what will need to be built.+    rebuildTargetsDryRun,+    improveInstallPlanWithUpToDatePackages,++    -- ** Build status+    -- | This is the detailed status information we get from the dry run.+    BuildStatusMap,+    BuildStatus(..),+    BuildStatusRebuild(..),+    BuildReason(..),+    MonitorChangedReason(..),+    buildStatusToString,++    -- * Build phase+    -- | Now we actually execute the plan.+    rebuildTargets,+    -- ** Build outcomes+    -- | This is the outcome for each package of executing the plan.+    -- For each package, did the build succeed or fail?+    BuildOutcomes,+    BuildOutcome,+    BuildResult(..),+    BuildFailure(..),+    BuildFailureReason(..),+  ) where++import           Distribution.Client.PackageHash (renderPackageHashInputs)+import           Distribution.Client.RebuildMonad+import           Distribution.Client.ProjectConfig+import           Distribution.Client.ProjectPlanning+import           Distribution.Client.ProjectPlanning.Types+import           Distribution.Client.ProjectBuilding.Types+import           Distribution.Client.Store++import           Distribution.Client.Types+                   hiding (BuildOutcomes, BuildOutcome,+                           BuildResult(..), BuildFailure(..))+import           Distribution.Client.InstallPlan+                   ( GenericInstallPlan, GenericPlanPackage, IsUnit )+import qualified Distribution.Client.InstallPlan as InstallPlan+import           Distribution.Client.DistDirLayout+import           Distribution.Client.FileMonitor+import           Distribution.Client.SetupWrapper+import           Distribution.Client.JobControl+import           Distribution.Client.FetchUtils+import           Distribution.Client.GlobalFlags (RepoContext)+import qualified Distribution.Client.Tar as Tar+import           Distribution.Client.Setup (filterConfigureFlags)+import           Distribution.Client.SourceFiles+import           Distribution.Client.SrcDist (allPackageSourceFiles)+import           Distribution.Client.Utils (removeExistingFile)++import           Distribution.Package hiding (InstalledPackageId, installedPackageId)+import qualified Distribution.PackageDescription as PD+import           Distribution.InstalledPackageInfo (InstalledPackageInfo)+import qualified Distribution.InstalledPackageInfo as Installed+import qualified Distribution.Simple.InstallDirs as InstallDirs+import           Distribution.Types.BuildType+import           Distribution.Simple.Program+import qualified Distribution.Simple.Setup as Cabal+import           Distribution.Simple.Command (CommandUI)+import qualified Distribution.Simple.Register as Cabal+import           Distribution.Simple.LocalBuildInfo (ComponentName)+import           Distribution.Simple.Compiler+                   ( Compiler, compilerId, PackageDB(..) )++import           Distribution.Simple.Utils hiding (matchFileGlob)+import           Distribution.Version+import           Distribution.Verbosity+import           Distribution.Text+import           Distribution.ParseUtils ( showPWarning )+import           Distribution.Compat.Graph (IsNode(..))++import           Data.Map (Map)+import qualified Data.Map as Map+import           Data.Set (Set)+import qualified Data.Set as Set+import qualified Data.ByteString.Lazy as LBS++import           Control.Monad+import           Control.Exception+import           Data.Maybe++import           System.FilePath+import           System.IO+import           System.Directory+++------------------------------------------------------------------------------+-- * Overall building strategy.+------------------------------------------------------------------------------+--+-- We start with an 'ElaboratedInstallPlan' that has already been improved by+-- reusing packages from the store, and pruned to include only the targets of+-- interest and their dependencies. So the remaining packages in the+-- 'InstallPlan.Configured' state are ones we either need to build or rebuild.+--+-- First, we do a preliminary dry run phase where we work out which packages+-- we really need to (re)build, and for the ones we do need to build which+-- build phase to start at.+--+-- We use this to improve the 'ElaboratedInstallPlan' again by changing+-- up-to-date 'InstallPlan.Configured' packages to 'InstallPlan.Installed'+-- so that the build phase will skip them.+--+-- Then we execute the plan, that is actually build packages. The outcomes of+-- trying to build all the packages are collected and returned.+--+-- We split things like this (dry run and execute) for a couple reasons.+-- Firstly we need to be able to do dry runs anyway, and these need to be+-- reasonably accurate in terms of letting users know what (and why) things+-- are going to be (re)built.+--+-- Given that we need to be able to do dry runs, it would not be great if+-- we had to repeat all the same work when we do it for real. Not only is+-- it duplicate work, but it's duplicate code which is likely to get out of+-- sync. So we do things only once. We preserve info we discover in the dry+-- run phase and rely on it later when we build things for real. This also+-- somewhat simplifies the build phase. So this way the dry run can't so+-- easily drift out of sync with the real thing since we're relying on the+-- info it produces.+--+-- An additional advantage is that it makes it easier to debug rebuild+-- errors (ie rebuilding too much or too little), since all the rebuild+-- decisions are made without making any state changes at the same time+-- (that would make it harder to reproduce the problem situation).+--+-- Finally, we can use the dry run build status and the build outcomes to+-- give us some information on the overall status of packages in the project.+-- This includes limited information about the status of things that were+-- not actually in the subset of the plan that was used for the dry run or+-- execution phases. In particular we may know that some packages are now+-- definitely out of date. See "Distribution.Client.ProjectPlanOutput" for+-- details.+++------------------------------------------------------------------------------+-- * Dry run: what bits of the 'ElaboratedInstallPlan' will we execute?+------------------------------------------------------------------------------++-- Refer to ProjectBuilding.Types for details of these important types:++-- type BuildStatusMap     = ...+-- data BuildStatus        = ...+-- data BuildStatusRebuild = ...+-- data BuildReason        = ...++-- | Do the dry run pass. This is a prerequisite of 'rebuildTargets'.+--+-- It gives us the 'BuildStatusMap'. This should be used with+-- 'improveInstallPlanWithUpToDatePackages' to give an improved version of+-- the 'ElaboratedInstallPlan' with packages switched to the+-- 'InstallPlan.Installed' state when we find that they're already up to date.+--+rebuildTargetsDryRun :: DistDirLayout+                     -> ElaboratedSharedConfig+                     -> ElaboratedInstallPlan+                     -> IO BuildStatusMap+rebuildTargetsDryRun distDirLayout@DistDirLayout{..} shared =+    -- Do the various checks to work out the 'BuildStatus' of each package+    foldMInstallPlanDepOrder dryRunPkg+  where+    dryRunPkg :: ElaboratedPlanPackage+              -> [BuildStatus]+              -> IO BuildStatus+    dryRunPkg (InstallPlan.PreExisting _pkg) _depsBuildStatus =+      return BuildStatusPreExisting++    dryRunPkg (InstallPlan.Installed _pkg) _depsBuildStatus =+      return BuildStatusInstalled++    dryRunPkg (InstallPlan.Configured pkg) depsBuildStatus = do+      mloc <- checkFetched (elabPkgSourceLocation pkg)+      case mloc of+        Nothing -> return BuildStatusDownload++        Just (LocalUnpackedPackage srcdir) ->+          -- For the case of a user-managed local dir, irrespective of the+          -- build style, we build from that directory and put build+          -- artifacts under the shared dist directory.+          dryRunLocalPkg pkg depsBuildStatus srcdir++        -- The three tarball cases are handled the same as each other,+        -- though depending on the build style.+        Just (LocalTarballPackage    tarball) ->+          dryRunTarballPkg pkg depsBuildStatus tarball++        Just (RemoteTarballPackage _ tarball) ->+          dryRunTarballPkg pkg depsBuildStatus tarball++        Just (RepoTarballPackage _ _ tarball) ->+          dryRunTarballPkg pkg depsBuildStatus tarball++    dryRunTarballPkg :: ElaboratedConfiguredPackage+                     -> [BuildStatus]+                     -> FilePath+                     -> IO BuildStatus+    dryRunTarballPkg pkg depsBuildStatus tarball =+      case elabBuildStyle pkg of+        BuildAndInstall  -> return (BuildStatusUnpack tarball)+        BuildInplaceOnly -> do+          -- TODO: [nice to have] use a proper file monitor rather than this dir exists test+          exists <- doesDirectoryExist srcdir+          if exists+            then dryRunLocalPkg pkg depsBuildStatus srcdir+            else return (BuildStatusUnpack tarball)+      where+        srcdir = distUnpackedSrcDirectory (packageId pkg)++    dryRunLocalPkg :: ElaboratedConfiguredPackage+                   -> [BuildStatus]+                   -> FilePath+                   -> IO BuildStatus+    dryRunLocalPkg pkg depsBuildStatus srcdir = do+        -- Go and do lots of I/O, reading caches and probing files to work out+        -- if anything has changed+        change <- checkPackageFileMonitorChanged+                    packageFileMonitor pkg srcdir depsBuildStatus+        case change of+          -- It did change, giving us 'BuildStatusRebuild' info on why+          Left rebuild ->+            return (BuildStatusRebuild srcdir rebuild)++          -- No changes, the package is up to date. Use the saved build results.+          Right buildResult ->+            return (BuildStatusUpToDate buildResult)+      where+        packageFileMonitor =+          newPackageFileMonitor distDirLayout (elabDistDirParams shared pkg)+++-- | A specialised traversal over the packages in an install plan.+--+-- The packages are visited in dependency order, starting with packages with no+-- dependencies. The result for each package is accumulated into a 'Map' and+-- returned as the final result. In addition, when visting a package, the+-- visiting function is passed the results for all the immediate package+-- dependencies. This can be used to propagate information from dependencies.+--+foldMInstallPlanDepOrder+  :: forall m ipkg srcpkg b.+     (Monad m, IsUnit ipkg, IsUnit srcpkg)+  => (GenericPlanPackage ipkg srcpkg ->+      [b] -> m b)+  -> GenericInstallPlan ipkg srcpkg+  -> m (Map UnitId b)+foldMInstallPlanDepOrder visit =+    go Map.empty . InstallPlan.reverseTopologicalOrder+  where+    go :: Map UnitId b+       -> [GenericPlanPackage ipkg srcpkg]+       -> m (Map UnitId b)+    go !results [] = return results++    go !results (pkg : pkgs) = do+      -- we go in the right order so the results map has entries for all deps+      let depresults :: [b]+          depresults =+            map (\ipkgid -> let Just result = Map.lookup ipkgid results+                              in result)+                (InstallPlan.depends pkg)+      result <- visit pkg depresults+      let results' = Map.insert (nodeKey pkg) result results+      go results' pkgs++improveInstallPlanWithUpToDatePackages :: BuildStatusMap+                                       -> ElaboratedInstallPlan+                                       -> ElaboratedInstallPlan+improveInstallPlanWithUpToDatePackages pkgsBuildStatus =+    InstallPlan.installed canPackageBeImproved+  where+    canPackageBeImproved pkg =+      case Map.lookup (installedUnitId pkg) pkgsBuildStatus of+        Just BuildStatusUpToDate {} -> True+        Just _                      -> False+        Nothing -> error $ "improveInstallPlanWithUpToDatePackages: "+                        ++ display (packageId pkg) ++ " not in status map"+++-----------------------------+-- Package change detection+--++-- | As part of the dry run for local unpacked packages we have to check if the+-- package config or files have changed. That is the purpose of+-- 'PackageFileMonitor' and 'checkPackageFileMonitorChanged'.+--+-- When a package is (re)built, the monitor must be updated to reflect the new+-- state of the package. Because we sometimes build without reconfiguring the+-- state updates are split into two, one for package config changes and one+-- for other changes. This is the purpose of 'updatePackageConfigFileMonitor'+-- and 'updatePackageBuildFileMonitor'.+--+data PackageFileMonitor = PackageFileMonitor {+       pkgFileMonitorConfig :: FileMonitor ElaboratedConfiguredPackage (),+       pkgFileMonitorBuild  :: FileMonitor (Set ComponentName) BuildResultMisc,+       pkgFileMonitorReg    :: FileMonitor () (Maybe InstalledPackageInfo)+     }++-- | This is all the components of the 'BuildResult' other than the+-- @['InstalledPackageInfo']@.+--+-- We have to split up the 'BuildResult' components since they get produced+-- at different times (or rather, when different things change).+--+type BuildResultMisc = (DocsResult, TestsResult)++newPackageFileMonitor :: DistDirLayout -> DistDirParams -> PackageFileMonitor+newPackageFileMonitor DistDirLayout{distPackageCacheFile} dparams =+    PackageFileMonitor {+      pkgFileMonitorConfig =+        newFileMonitor (distPackageCacheFile dparams "config"),++      pkgFileMonitorBuild =+        FileMonitor {+          fileMonitorCacheFile = distPackageCacheFile dparams "build",+          fileMonitorKeyValid  = \componentsToBuild componentsAlreadyBuilt ->+            componentsToBuild `Set.isSubsetOf` componentsAlreadyBuilt,+          fileMonitorCheckIfOnlyValueChanged = True+        },++      pkgFileMonitorReg =+        newFileMonitor (distPackageCacheFile dparams "registration")+    }++-- | Helper function for 'checkPackageFileMonitorChanged',+-- 'updatePackageConfigFileMonitor' and 'updatePackageBuildFileMonitor'.+--+-- It selects the info from a 'ElaboratedConfiguredPackage' that are used by+-- the 'FileMonitor's (in the 'PackageFileMonitor') to detect value changes.+--+packageFileMonitorKeyValues :: ElaboratedConfiguredPackage+                            -> (ElaboratedConfiguredPackage, Set ComponentName)+packageFileMonitorKeyValues elab =+    (elab_config, buildComponents)+  where+    -- The first part is the value used to guard (re)configuring the package.+    -- That is, if this value changes then we will reconfigure.+    -- The ElaboratedConfiguredPackage consists mostly (but not entirely) of+    -- information that affects the (re)configure step. But those parts that+    -- do not affect the configure step need to be nulled out. Those parts are+    -- the specific targets that we're going to build.+    --+    elab_config =+        elab {+            elabBuildTargets  = [],+            elabTestTargets   = [],+            elabReplTarget    = Nothing,+            elabBuildHaddocks = False+        }++    -- The second part is the value used to guard the build step. So this is+    -- more or less the opposite of the first part, as it's just the info about+    -- what targets we're going to build.+    --+    buildComponents = elabBuildTargetWholeComponents elab++-- | Do all the checks on whether a package has changed and thus needs either+-- rebuilding or reconfiguring and rebuilding.+--+checkPackageFileMonitorChanged :: PackageFileMonitor+                               -> ElaboratedConfiguredPackage+                               -> FilePath+                               -> [BuildStatus]+                               -> IO (Either BuildStatusRebuild BuildResult)+checkPackageFileMonitorChanged PackageFileMonitor{..}+                               pkg srcdir depsBuildStatus = do+    --TODO: [nice to have] some debug-level message about file changes, like rerunIfChanged+    configChanged <- checkFileMonitorChanged+                       pkgFileMonitorConfig srcdir pkgconfig+    case configChanged of+      MonitorChanged monitorReason ->+          return (Left (BuildStatusConfigure monitorReason'))+        where+          monitorReason' = fmap (const ()) monitorReason++      MonitorUnchanged () _+          -- The configChanged here includes the identity of the dependencies,+          -- so depsBuildStatus is just needed for the changes in the content+          -- of dependencies.+        | any buildStatusRequiresBuild depsBuildStatus -> do+            regChanged <- checkFileMonitorChanged pkgFileMonitorReg srcdir ()+            let mreg = changedToMaybe regChanged+            return (Left (BuildStatusBuild mreg BuildReasonDepsRebuilt))++        | otherwise -> do+            buildChanged  <- checkFileMonitorChanged+                               pkgFileMonitorBuild srcdir buildComponents+            regChanged    <- checkFileMonitorChanged+                               pkgFileMonitorReg srcdir ()+            let mreg = changedToMaybe regChanged+            case (buildChanged, regChanged) of+              (MonitorChanged (MonitoredValueChanged prevBuildComponents), _) ->+                  return (Left (BuildStatusBuild mreg buildReason))+                where+                  buildReason = BuildReasonExtraTargets prevBuildComponents++              (MonitorChanged monitorReason, _) ->+                  return (Left (BuildStatusBuild mreg buildReason))+                where+                  buildReason    = BuildReasonFilesChanged monitorReason'+                  monitorReason' = fmap (const ()) monitorReason++              (MonitorUnchanged _ _, MonitorChanged monitorReason) ->+                -- this should only happen if the file is corrupt or been+                -- manually deleted. We don't want to bother with another+                -- phase just for this, so we'll reregister by doing a build.+                  return (Left (BuildStatusBuild Nothing buildReason))+                where+                  buildReason    = BuildReasonFilesChanged monitorReason'+                  monitorReason' = fmap (const ()) monitorReason++              (MonitorUnchanged _ _, MonitorUnchanged _ _)+                | pkgHasEphemeralBuildTargets pkg ->+                  return (Left (BuildStatusBuild mreg buildReason))+                where+                  buildReason = BuildReasonEphemeralTargets++              (MonitorUnchanged buildResult _, MonitorUnchanged _ _) ->+                  return $ Right BuildResult {+                    buildResultDocs    = docsResult,+                    buildResultTests   = testsResult,+                    buildResultLogFile = Nothing+                  }+                where+                  (docsResult, testsResult) = buildResult+  where+    (pkgconfig, buildComponents) = packageFileMonitorKeyValues pkg+    changedToMaybe (MonitorChanged     _) = Nothing+    changedToMaybe (MonitorUnchanged x _) = Just x+++updatePackageConfigFileMonitor :: PackageFileMonitor+                               -> FilePath+                               -> ElaboratedConfiguredPackage+                               -> IO ()+updatePackageConfigFileMonitor PackageFileMonitor{pkgFileMonitorConfig}+                               srcdir pkg =+    updateFileMonitor pkgFileMonitorConfig srcdir Nothing+                      [] pkgconfig ()+  where+    (pkgconfig, _buildComponents) = packageFileMonitorKeyValues pkg++updatePackageBuildFileMonitor :: PackageFileMonitor+                              -> FilePath+                              -> MonitorTimestamp+                              -> ElaboratedConfiguredPackage+                              -> BuildStatusRebuild+                              -> [MonitorFilePath]+                              -> BuildResultMisc+                              -> IO ()+updatePackageBuildFileMonitor PackageFileMonitor{pkgFileMonitorBuild}+                              srcdir timestamp pkg pkgBuildStatus+                              monitors buildResult =+    updateFileMonitor pkgFileMonitorBuild srcdir (Just timestamp)+                      monitors buildComponents' buildResult+  where+    (_pkgconfig, buildComponents) = packageFileMonitorKeyValues pkg++    -- If the only thing that's changed is that we're now building extra+    -- components, then we can avoid later unnecessary rebuilds by saving the+    -- total set of components that have been built, namely the union of the+    -- existing ones plus the new ones. If files also changed this would be+    -- the wrong thing to do. Note that we rely on the+    -- fileMonitorCheckIfOnlyValueChanged = True mode to get this guarantee+    -- that it's /only/ the value that changed not any files that changed.+    buildComponents' =+      case pkgBuildStatus of+        BuildStatusBuild _ (BuildReasonExtraTargets prevBuildComponents)+          -> buildComponents `Set.union` prevBuildComponents+        _ -> buildComponents++updatePackageRegFileMonitor :: PackageFileMonitor+                            -> FilePath+                            -> Maybe InstalledPackageInfo+                            -> IO ()+updatePackageRegFileMonitor PackageFileMonitor{pkgFileMonitorReg}+                            srcdir mipkg =+    updateFileMonitor pkgFileMonitorReg srcdir Nothing+                      [] () mipkg++invalidatePackageRegFileMonitor :: PackageFileMonitor -> IO ()+invalidatePackageRegFileMonitor PackageFileMonitor{pkgFileMonitorReg} =+    removeExistingFile (fileMonitorCacheFile pkgFileMonitorReg)+++------------------------------------------------------------------------------+-- * Doing it: executing an 'ElaboratedInstallPlan'+------------------------------------------------------------------------------++-- Refer to ProjectBuilding.Types for details of these important types:++-- type BuildOutcomes = ...+-- type BuildOutcome  = ...+-- data BuildResult   = ...+-- data BuildFailure  = ...+-- data BuildFailureReason = ...++-- | Build things for real.+--+-- It requires the 'BuildStatusMap' gathered by 'rebuildTargetsDryRun'.+--+rebuildTargets :: Verbosity+               -> DistDirLayout+               -> StoreDirLayout+               -> ElaboratedInstallPlan+               -> ElaboratedSharedConfig+               -> BuildStatusMap+               -> BuildTimeSettings+               -> IO BuildOutcomes+rebuildTargets verbosity+               distDirLayout@DistDirLayout{..}+               storeDirLayout+               installPlan+               sharedPackageConfig@ElaboratedSharedConfig {+                 pkgConfigCompiler      = compiler,+                 pkgConfigCompilerProgs = progdb+               }+               pkgsBuildStatus+               buildSettings@BuildTimeSettings{+                 buildSettingNumJobs,+                 buildSettingKeepGoing+               } = do++    -- Concurrency control: create the job controller and concurrency limits+    -- for downloading, building and installing.+    jobControl    <- if isParallelBuild+                       then newParallelJobControl buildSettingNumJobs+                       else newSerialJobControl+    registerLock  <- newLock -- serialise registration+    cacheLock     <- newLock -- serialise access to setup exe cache+                             --TODO: [code cleanup] eliminate setup exe cache++    debug verbosity $+        "Executing install plan "+     ++ if isParallelBuild+          then " in parallel using " ++ show buildSettingNumJobs ++ " threads."+          else " serially."++    createDirectoryIfMissingVerbose verbosity True distBuildRootDirectory+    createDirectoryIfMissingVerbose verbosity True distTempDirectory+    mapM_ (createPackageDBIfMissing verbosity compiler progdb) packageDBsToUse++    -- Before traversing the install plan, pre-emptively find all packages that+    -- will need to be downloaded and start downloading them.+    asyncDownloadPackages verbosity withRepoCtx+                          installPlan pkgsBuildStatus $ \downloadMap ->++      -- For each package in the plan, in dependency order, but in parallel...+      InstallPlan.execute jobControl keepGoing+                          (BuildFailure Nothing . DependentFailed . packageId)+                          installPlan $ \pkg ->+        --TODO: review exception handling+        handle (\(e :: BuildFailure) -> return (Left e)) $ fmap Right $++        let uid = installedUnitId pkg+            Just pkgBuildStatus = Map.lookup uid pkgsBuildStatus in++        rebuildTarget+          verbosity+          distDirLayout+          storeDirLayout+          buildSettings downloadMap+          registerLock cacheLock+          sharedPackageConfig+          installPlan pkg+          pkgBuildStatus+  where+    isParallelBuild = buildSettingNumJobs >= 2+    keepGoing       = buildSettingKeepGoing+    withRepoCtx     = projectConfigWithBuilderRepoContext verbosity+                        buildSettings+    packageDBsToUse = -- all the package dbs we may need to create+      (Set.toList . Set.fromList)+        [ pkgdb+        | InstallPlan.Configured elab <- InstallPlan.toList installPlan+        , pkgdb <- concat [ elabBuildPackageDBStack elab+                          , elabRegisterPackageDBStack elab+                          , elabSetupPackageDBStack elab ]+        ]+++-- | Create a package DB if it does not currently exist. Note that this action+-- is /not/ safe to run concurrently.+--+createPackageDBIfMissing :: Verbosity -> Compiler -> ProgramDb+                         -> PackageDB -> IO ()+createPackageDBIfMissing verbosity compiler progdb+                         (SpecificPackageDB dbPath) = do+    exists <- Cabal.doesPackageDBExist dbPath+    unless exists $ do+      createDirectoryIfMissingVerbose verbosity True (takeDirectory dbPath)+      Cabal.createPackageDB verbosity compiler progdb False dbPath+createPackageDBIfMissing _ _ _ _ = return ()+++-- | Given all the context and resources, (re)build an individual package.+--+rebuildTarget :: Verbosity+              -> DistDirLayout+              -> StoreDirLayout+              -> BuildTimeSettings+              -> AsyncFetchMap+              -> Lock -> Lock+              -> ElaboratedSharedConfig+              -> ElaboratedInstallPlan+              -> ElaboratedReadyPackage+              -> BuildStatus+              -> IO BuildResult+rebuildTarget verbosity+              distDirLayout@DistDirLayout{distBuildDirectory}+              storeDirLayout+              buildSettings downloadMap+              registerLock cacheLock+              sharedPackageConfig+              plan rpkg@(ReadyPackage pkg)+              pkgBuildStatus =++    -- We rely on the 'BuildStatus' to decide which phase to start from:+    case pkgBuildStatus of+      BuildStatusDownload              -> downloadPhase+      BuildStatusUnpack tarball        -> unpackTarballPhase tarball+      BuildStatusRebuild srcdir status -> rebuildPhase status srcdir++      -- TODO: perhaps re-nest the types to make these impossible+      BuildStatusPreExisting {} -> unexpectedState+      BuildStatusInstalled   {} -> unexpectedState+      BuildStatusUpToDate    {} -> unexpectedState+  where+    unexpectedState = error "rebuildTarget: unexpected package status"++    downloadPhase = do+        downsrcloc <- annotateFailureNoLog DownloadFailed $+                        waitAsyncPackageDownload verbosity downloadMap pkg+        case downsrcloc of+          DownloadedTarball tarball -> unpackTarballPhase tarball+          --TODO: [nice to have] git/darcs repos etc+++    unpackTarballPhase tarball =+        withTarballLocalDirectory+          verbosity distDirLayout tarball+          (packageId pkg) (elabDistDirParams sharedPackageConfig pkg) (elabBuildStyle pkg)+          (elabPkgDescriptionOverride pkg) $++          case elabBuildStyle pkg of+            BuildAndInstall  -> buildAndInstall+            BuildInplaceOnly -> buildInplace buildStatus+              where+                buildStatus = BuildStatusConfigure MonitorFirstRun++    -- Note that this really is rebuild, not build. It can only happen for+    -- 'BuildInplaceOnly' style packages. 'BuildAndInstall' style packages+    -- would only start from download or unpack phases.+    --+    rebuildPhase buildStatus srcdir =+        assert (elabBuildStyle pkg == BuildInplaceOnly) $++          buildInplace buildStatus srcdir builddir+      where+        builddir = distBuildDirectory (elabDistDirParams sharedPackageConfig pkg)++    buildAndInstall srcdir builddir =+        buildAndInstallUnpackedPackage+          verbosity distDirLayout storeDirLayout+          buildSettings registerLock cacheLock+          sharedPackageConfig+          rpkg+          srcdir builddir'+      where+        builddir' = makeRelative srcdir builddir+        --TODO: [nice to have] ^^ do this relative stuff better++    buildInplace buildStatus srcdir builddir =+        --TODO: [nice to have] use a relative build dir rather than absolute+        buildInplaceUnpackedPackage+          verbosity distDirLayout+          buildSettings registerLock cacheLock+          sharedPackageConfig+          plan rpkg+          buildStatus+          srcdir builddir++--TODO: [nice to have] do we need to use a with-style for the temp files for downloading http+-- packages, or are we going to cache them persistently?++-- | Given the current 'InstallPlan' and 'BuildStatusMap', select all the+-- packages we have to download and fork off an async action to download them.+-- We download them in dependency order so that the one's we'll need+-- first are the ones we will start downloading first.+--+-- The body action is passed a map from those packages (identified by their+-- location) to a completion var for that package. So the body action should+-- lookup the location and use 'waitAsyncPackageDownload' to get the result.+--+asyncDownloadPackages :: Verbosity+                      -> ((RepoContext -> IO a) -> IO a)+                      -> ElaboratedInstallPlan+                      -> BuildStatusMap+                      -> (AsyncFetchMap -> IO a)+                      -> IO a+asyncDownloadPackages verbosity withRepoCtx installPlan pkgsBuildStatus body+  | null pkgsToDownload = body Map.empty+  | otherwise           = withRepoCtx $ \repoctx ->+                            asyncFetchPackages verbosity repoctx+                                               pkgsToDownload body+  where+    pkgsToDownload =+      ordNub $+      [ elabPkgSourceLocation elab+      | InstallPlan.Configured elab+         <- InstallPlan.reverseTopologicalOrder installPlan+      , let uid = installedUnitId elab+            Just pkgBuildStatus = Map.lookup uid pkgsBuildStatus+      , BuildStatusDownload <- [pkgBuildStatus]+      ]+++-- | Check if a package needs downloading, and if so expect to find a download+-- in progress in the given 'AsyncFetchMap' and wait on it to finish.+--+waitAsyncPackageDownload :: Verbosity+                         -> AsyncFetchMap+                         -> ElaboratedConfiguredPackage+                         -> IO DownloadedSourceLocation+waitAsyncPackageDownload verbosity downloadMap elab = do+    pkgloc <- waitAsyncFetchPackage verbosity downloadMap+                                    (elabPkgSourceLocation elab)+    case downloadedSourceLocation pkgloc of+      Just loc -> return loc+      Nothing  -> fail "waitAsyncPackageDownload: unexpected source location"++data DownloadedSourceLocation = DownloadedTarball FilePath+                              --TODO: [nice to have] git/darcs repos etc++downloadedSourceLocation :: PackageLocation FilePath+                         -> Maybe DownloadedSourceLocation+downloadedSourceLocation pkgloc =+    case pkgloc of+      RemoteTarballPackage _ tarball -> Just (DownloadedTarball tarball)+      RepoTarballPackage _ _ tarball -> Just (DownloadedTarball tarball)+      _                              -> Nothing+++++-- | Ensure that the package is unpacked in an appropriate directory, either+-- a temporary one or a persistent one under the shared dist directory.+--+withTarballLocalDirectory+  :: Verbosity+  -> DistDirLayout+  -> FilePath+  -> PackageId+  -> DistDirParams+  -> BuildStyle+  -> Maybe CabalFileText+  -> (FilePath -> -- Source directory+      FilePath -> -- Build directory+      IO a)+  -> IO a+withTarballLocalDirectory verbosity distDirLayout@DistDirLayout{..}+                          tarball pkgid dparams buildstyle pkgTextOverride+                          buildPkg  =+      case buildstyle of+        -- In this case we make a temp dir (e.g. tmp/src2345/), unpack+        -- the tarball to it (e.g. tmp/src2345/foo-1.0/), and for+        -- compatibility we put the dist dir within it+        -- (i.e. tmp/src2345/foo-1.0/dist/).+        --+        -- Unfortunately, a few custom Setup.hs scripts do not respect+        -- the --builddir flag and always look for it at ./dist/ so+        -- this way we avoid breaking those packages+        BuildAndInstall ->+          let tmpdir = distTempDirectory in+          withTempDirectory verbosity tmpdir "src"   $ \unpackdir -> do+            unpackPackageTarball verbosity tarball unpackdir+                                 pkgid pkgTextOverride+            let srcdir   = unpackdir </> display pkgid+                builddir = srcdir </> "dist"+            buildPkg srcdir builddir++        -- In this case we make sure the tarball has been unpacked to the+        -- appropriate location under the shared dist dir, and then build it+        -- inplace there+        BuildInplaceOnly -> do+          let srcrootdir = distUnpackedSrcRootDirectory+              srcdir     = distUnpackedSrcDirectory pkgid+              builddir   = distBuildDirectory dparams+          -- TODO: [nice to have] use a proper file monitor rather than this dir exists test+          exists <- doesDirectoryExist srcdir+          unless exists $ do+            createDirectoryIfMissingVerbose verbosity True srcrootdir+            unpackPackageTarball verbosity tarball srcrootdir+                                 pkgid pkgTextOverride+            moveTarballShippedDistDirectory verbosity distDirLayout+                                            srcrootdir pkgid dparams+          buildPkg srcdir builddir+++unpackPackageTarball :: Verbosity -> FilePath -> FilePath+                     -> PackageId -> Maybe CabalFileText+                     -> IO ()+unpackPackageTarball verbosity tarball parentdir pkgid pkgTextOverride =+    --TODO: [nice to have] switch to tar package and catch tar exceptions+    annotateFailureNoLog UnpackFailed $ do++      -- Unpack the tarball+      --+      info verbosity $ "Extracting " ++ tarball ++ " to " ++ parentdir ++ "..."+      Tar.extractTarGzFile parentdir pkgsubdir tarball++      -- Sanity check+      --+      exists <- doesFileExist cabalFile+      when (not exists) $+        die' verbosity $ "Package .cabal file not found in the tarball: " ++ cabalFile++      -- Overwrite the .cabal with the one from the index, when appropriate+      --+      case pkgTextOverride of+        Nothing     -> return ()+        Just pkgtxt -> do+          info verbosity $ "Updating " ++ display pkgname <.> "cabal"+                        ++ " with the latest revision from the index."+          writeFileAtomic cabalFile pkgtxt++  where+    cabalFile = parentdir </> pkgsubdir+                          </> display pkgname <.> "cabal"+    pkgsubdir = display pkgid+    pkgname   = packageName pkgid+++-- | This is a bit of a hacky workaround. A number of packages ship+-- pre-processed .hs files in a dist directory inside the tarball. We don't+-- use the standard 'dist' location so unless we move this dist dir to the+-- right place then we'll miss the shipped pre-procssed files. This hacky+-- approach to shipped pre-procssed files ought to be replaced by a proper+-- system, though we'll still need to keep this hack for older packages.+--+moveTarballShippedDistDirectory :: Verbosity -> DistDirLayout+                                -> FilePath -> PackageId -> DistDirParams -> IO ()+moveTarballShippedDistDirectory verbosity DistDirLayout{distBuildDirectory}+                                parentdir pkgid dparams = do+    distDirExists <- doesDirectoryExist tarballDistDir+    when distDirExists $ do+      debug verbosity $ "Moving '" ++ tarballDistDir ++ "' to '"+                                   ++ targetDistDir ++ "'"+      --TODO: [nice to have] or perhaps better to copy, and use a file monitor+      renameDirectory tarballDistDir targetDistDir+  where+    tarballDistDir = parentdir </> display pkgid </> "dist"+    targetDistDir  = distBuildDirectory dparams+++buildAndInstallUnpackedPackage :: Verbosity+                               -> DistDirLayout+                               -> StoreDirLayout+                               -> BuildTimeSettings -> Lock -> Lock+                               -> ElaboratedSharedConfig+                               -> ElaboratedReadyPackage+                               -> FilePath -> FilePath+                               -> IO BuildResult+buildAndInstallUnpackedPackage verbosity+                               DistDirLayout{distTempDirectory}+                               storeDirLayout@StoreDirLayout {+                                 storePackageDBStack+                               }+                               BuildTimeSettings {+                                 buildSettingNumJobs,+                                 buildSettingLogFile+                               }+                               registerLock cacheLock+                               pkgshared@ElaboratedSharedConfig {+                                 pkgConfigPlatform      = platform,+                                 pkgConfigCompiler      = compiler,+                                 pkgConfigCompilerProgs = progdb+                               }+                               rpkg@(ReadyPackage pkg)+                               srcdir builddir = do++    createDirectoryIfMissingVerbose verbosity True builddir+    initLogFile++    --TODO: [code cleanup] deal consistently with talking to older Setup.hs versions, much like+    --      we do for ghc, with a proper options type and rendering step+    --      which will also let us call directly into the lib, rather than always+    --      going via the lib's command line interface, which would also allow+    --      passing data like installed packages, compiler, and program db for a+    --      quicker configure.++    --TODO: [required feature] docs and tests+    --TODO: [required feature] sudo re-exec++    let dispname = case elabPkgOrComp pkg of+            ElabPackage _ -> display pkgid+                ++ " (all, legacy fallback)"+            ElabComponent comp -> display pkgid+                ++ " (" ++ maybe "custom" display (compComponentName comp) ++ ")"++    -- Configure phase+    when isParallelBuild $+      notice verbosity $ "Configuring " ++ dispname ++ "..."+    annotateFailure mlogFile ConfigureFailed $+      setup' configureCommand configureFlags configureArgs++    -- Build phase+    when isParallelBuild $+      notice verbosity $ "Building " ++ dispname ++ "..."+    annotateFailure mlogFile BuildFailed $+      setup buildCommand buildFlags++    -- Install phase+    annotateFailure mlogFile InstallFailed $ do++      let copyPkgFiles tmpDir = do+            setup Cabal.copyCommand (copyFlags tmpDir)+            -- Note that the copy command has put the files into+            -- @$tmpDir/$prefix@ so we need to return this dir so+            -- the store knows which dir will be the final store entry.+            let prefix   = dropDrive (InstallDirs.prefix (elabInstallDirs pkg))+                entryDir = tmpDir </> prefix+            LBS.writeFile+              (entryDir </> "cabal-hash.txt")+              (renderPackageHashInputs (packageHashInputs pkgshared pkg))+            -- here's where we could keep track of the installed files ourselves+            -- if we wanted to by making a manifest of the files in the tmp dir+            return entryDir++          registerPkg+            | not (elabRequiresRegistration pkg) =+              debug verbosity $+                "registerPkg: elab does NOT require registration for " ++ display uid+            | otherwise = do+            -- We register ourselves rather than via Setup.hs. We need to+            -- grab and modify the InstalledPackageInfo. We decide what+            -- the installed package id is, not the build system.+            ipkg0 <- generateInstalledPackageInfo+            let ipkg = ipkg0 { Installed.installedUnitId = uid }+            assert (   elabRegisterPackageDBStack pkg+                    == storePackageDBStack compid) (return ())+            criticalSection registerLock $+              Cabal.registerPackage+                verbosity compiler progdb+                (storePackageDBStack compid) ipkg+                Cabal.defaultRegisterOptions {+                  Cabal.registerMultiInstance      = True,+                  Cabal.registerSuppressFilesCheck = True+                }+++      -- Actual installation+      void $ newStoreEntry verbosity storeDirLayout+                           compid uid+                           copyPkgFiles registerPkg++    --TODO: [nice to have] we currently rely on Setup.hs copy to do the right+    -- thing. Although we do copy into an image dir and do the move into the+    -- final location ourselves, perhaps we ought to do some sanity checks on+    -- the image dir first.++    -- TODO: [required eventually] note that for nix-style installations it is not necessary to do+    -- the 'withWin32SelfUpgrade' dance, but it would be necessary for a+    -- shared bin dir.++    --TODO: [required feature] docs and test phases+    let docsResult  = DocsNotTried+        testsResult = TestsNotTried++    return BuildResult {+       buildResultDocs    = docsResult,+       buildResultTests   = testsResult,+       buildResultLogFile = mlogFile+    }++  where+    pkgid  = packageId rpkg+    uid    = installedUnitId rpkg+    compid = compilerId compiler++    isParallelBuild = buildSettingNumJobs >= 2++    configureCommand = Cabal.configureCommand defaultProgramDb+    configureFlags v = flip filterConfigureFlags v $+                       setupHsConfigureFlags rpkg pkgshared+                                             verbosity builddir+    configureArgs    = setupHsConfigureArgs pkg++    buildCommand     = Cabal.buildCommand defaultProgramDb+    buildFlags   _   = setupHsBuildFlags pkg pkgshared verbosity builddir++    generateInstalledPackageInfo :: IO InstalledPackageInfo+    generateInstalledPackageInfo =+      withTempInstalledPackageInfoFile+        verbosity distTempDirectory $ \pkgConfDest -> do+        let registerFlags _ = setupHsRegisterFlags+                                pkg pkgshared+                                verbosity builddir+                                pkgConfDest+        setup Cabal.registerCommand registerFlags++    copyFlags destdir _ = setupHsCopyFlags pkg pkgshared verbosity+                                           builddir destdir++    scriptOptions = setupHsScriptOptions rpkg pkgshared srcdir builddir+                                         isParallelBuild cacheLock++    setup :: CommandUI flags -> (Version -> flags) -> IO ()+    setup cmd flags = setup' cmd flags []++    setup' :: CommandUI flags -> (Version -> flags) -> [String] -> IO ()+    setup' cmd flags args =+      withLogging $ \mLogFileHandle ->+        setupWrapper+          verbosity+          scriptOptions { useLoggingHandle = mLogFileHandle }+          (Just (elabPkgDescription pkg))+          cmd flags args++    mlogFile :: Maybe FilePath+    mlogFile =+      case buildSettingLogFile of+        Nothing        -> Nothing+        Just mkLogFile -> Just (mkLogFile compiler platform pkgid uid)++    initLogFile =+      case mlogFile of+        Nothing      -> return ()+        Just logFile -> do+          createDirectoryIfMissing True (takeDirectory logFile)+          exists <- doesFileExist logFile+          when exists $ removeFile logFile++    withLogging action =+      case mlogFile of+        Nothing      -> action Nothing+        Just logFile -> withFile logFile AppendMode (action . Just)+++buildInplaceUnpackedPackage :: Verbosity+                            -> DistDirLayout+                            -> BuildTimeSettings -> Lock -> Lock+                            -> ElaboratedSharedConfig+                            -> ElaboratedInstallPlan+                            -> ElaboratedReadyPackage+                            -> BuildStatusRebuild+                            -> FilePath -> FilePath+                            -> IO BuildResult+buildInplaceUnpackedPackage verbosity+                            distDirLayout@DistDirLayout {+                              distTempDirectory,+                              distPackageCacheDirectory+                            }+                            BuildTimeSettings{buildSettingNumJobs}+                            registerLock cacheLock+                            pkgshared@ElaboratedSharedConfig {+                              pkgConfigCompiler      = compiler,+                              pkgConfigCompilerProgs = progdb+                            }+                            plan+                            rpkg@(ReadyPackage pkg)+                            buildStatus+                            srcdir builddir = do++        --TODO: [code cleanup] there is duplication between the distdirlayout and the builddir here+        --      builddir is not enough, we also need the per-package cachedir+        createDirectoryIfMissingVerbose verbosity True builddir+        createDirectoryIfMissingVerbose verbosity True (distPackageCacheDirectory dparams)++        -- Configure phase+        --+        whenReConfigure $ do+          annotateFailureNoLog ConfigureFailed $+            setup configureCommand configureFlags configureArgs+          invalidatePackageRegFileMonitor packageFileMonitor+          updatePackageConfigFileMonitor packageFileMonitor srcdir pkg++        -- Build phase+        --+        let docsResult  = DocsNotTried+            testsResult = TestsNotTried++            buildResult :: BuildResultMisc+            buildResult = (docsResult, testsResult)++        whenRebuild $ do+          timestamp <- beginUpdateFileMonitor+          annotateFailureNoLog BuildFailed $+            setup buildCommand buildFlags buildArgs++          let listSimple =+                execRebuild srcdir (needElaboratedConfiguredPackage pkg)+              listSdist =+                fmap (map monitorFileHashed) $+                    allPackageSourceFiles verbosity scriptOptions srcdir+              ifNullThen m m' = do xs <- m+                                   if null xs then m' else return xs+          monitors <- case PD.buildType (elabPkgDescription pkg) of+            Just Simple -> listSimple+            -- If a Custom setup was used, AND the Cabal is recent+            -- enough to have sdist --list-sources, use that to+            -- determine the files that we need to track.  This can+            -- cause unnecessary rebuilding (for example, if README+            -- is edited, we will try to rebuild) but there isn't+            -- a more accurate Custom interface we can use to get+            -- this info.  We prefer not to use listSimple here+            -- as it can miss extra source files that are considered+            -- by the Custom setup.+            _ | elabSetupScriptCliVersion pkg >= mkVersion [1,17]+              -- However, sometimes sdist --list-sources will fail+              -- and return an empty list.  In that case, fall+              -- back on the (inaccurate) simple tracking.+              -> listSdist `ifNullThen` listSimple+              | otherwise+              -> listSimple++          let dep_monitors = map monitorFileHashed+                           $ elabInplaceDependencyBuildCacheFiles+                                distDirLayout pkgshared plan pkg+          updatePackageBuildFileMonitor packageFileMonitor srcdir timestamp+                                        pkg buildStatus+                                        (monitors ++ dep_monitors) buildResult++        -- PURPOSELY omitted: no copy!++        whenReRegister $ annotateFailureNoLog InstallFailed $ do+          -- Register locally+          mipkg <- if elabRequiresRegistration pkg+            then do+                ipkg0 <- generateInstalledPackageInfo+                -- We register ourselves rather than via Setup.hs. We need to+                -- grab and modify the InstalledPackageInfo. We decide what+                -- the installed package id is, not the build system.+                let ipkg = ipkg0 { Installed.installedUnitId = ipkgid }+                criticalSection registerLock $+                    Cabal.registerPackage verbosity compiler progdb+                                          (elabRegisterPackageDBStack pkg)+                                          ipkg Cabal.defaultRegisterOptions+                return (Just ipkg)++           else return Nothing++          updatePackageRegFileMonitor packageFileMonitor srcdir mipkg++        whenTest $ do+          annotateFailureNoLog TestsFailed $+            setup testCommand testFlags testArgs++        -- Repl phase+        --+        whenRepl $+          annotateFailureNoLog ReplFailed $+          setupInteractive replCommand replFlags replArgs++        -- Haddock phase+        whenHaddock $+          annotateFailureNoLog HaddocksFailed $+          setup haddockCommand haddockFlags []++        return BuildResult {+          buildResultDocs    = docsResult,+          buildResultTests   = testsResult,+          buildResultLogFile = Nothing+        }++  where+    ipkgid  = installedUnitId pkg+    dparams = elabDistDirParams pkgshared pkg++    isParallelBuild = buildSettingNumJobs >= 2++    packageFileMonitor = newPackageFileMonitor distDirLayout dparams++    whenReConfigure action = case buildStatus of+      BuildStatusConfigure _ -> action+      _                      -> return ()++    whenRebuild action+      | null (elabBuildTargets pkg)+      -- NB: we have to build the test suite!+      , null (elabTestTargets pkg) = return ()+      | otherwise                   = action++    whenTest action+      | null (elabTestTargets pkg) = return ()+      | otherwise                  = action++    whenRepl action+      | isNothing (elabReplTarget pkg) = return ()+      | otherwise                     = action++    whenHaddock action+      | elabBuildHaddocks pkg = action+      | otherwise            = return ()++    whenReRegister  action+      = case buildStatus of+          -- We registered the package already+          BuildStatusBuild (Just _) _     -> info verbosity "whenReRegister: previously registered"+          -- There is nothing to register+          _ | null (elabBuildTargets pkg) -> info verbosity "whenReRegister: nothing to register"+            | otherwise                   -> action++    configureCommand = Cabal.configureCommand defaultProgramDb+    configureFlags v = flip filterConfigureFlags v $+                       setupHsConfigureFlags rpkg pkgshared+                                             verbosity builddir+    configureArgs    = setupHsConfigureArgs pkg++    buildCommand     = Cabal.buildCommand defaultProgramDb+    buildFlags   _   = setupHsBuildFlags pkg pkgshared+                                         verbosity builddir+    buildArgs        = setupHsBuildArgs  pkg++    testCommand      = Cabal.testCommand -- defaultProgramDb+    testFlags    _   = setupHsTestFlags pkg pkgshared+                                         verbosity builddir+    testArgs         = setupHsTestArgs  pkg++    replCommand      = Cabal.replCommand defaultProgramDb+    replFlags _      = setupHsReplFlags pkg pkgshared+                                        verbosity builddir+    replArgs         = setupHsReplArgs  pkg++    haddockCommand   = Cabal.haddockCommand+    haddockFlags _   = setupHsHaddockFlags pkg pkgshared+                                           verbosity builddir++    scriptOptions    = setupHsScriptOptions rpkg pkgshared+                                            srcdir builddir+                                            isParallelBuild cacheLock++    setupInteractive :: CommandUI flags+                     -> (Version -> flags) -> [String] -> IO ()+    setupInteractive cmd flags args =+      setupWrapper verbosity+                   scriptOptions { isInteractive = True }+                   (Just (elabPkgDescription pkg))+                   cmd flags args++    setup :: CommandUI flags -> (Version -> flags) -> [String] -> IO ()+    setup cmd flags args =+      setupWrapper verbosity+                   scriptOptions+                   (Just (elabPkgDescription pkg))+                   cmd flags args++    generateInstalledPackageInfo :: IO InstalledPackageInfo+    generateInstalledPackageInfo =+      withTempInstalledPackageInfoFile+        verbosity distTempDirectory $ \pkgConfDest -> do+        let registerFlags _ = setupHsRegisterFlags+                                pkg pkgshared+                                verbosity builddir+                                pkgConfDest+        setup Cabal.registerCommand registerFlags []++withTempInstalledPackageInfoFile :: Verbosity -> FilePath+                                  -> (FilePath -> IO ())+                                  -> IO InstalledPackageInfo+withTempInstalledPackageInfoFile verbosity tempdir action =+    withTempDirectory verbosity tempdir "package-registration-" $ \dir -> do+      -- make absolute since @action@ will often change directory+      abs_dir <- canonicalizePath dir++      let pkgConfDest = abs_dir </> "pkgConf"+      action pkgConfDest++      readPkgConf "." pkgConfDest+  where+    pkgConfParseFailed :: Installed.PError -> IO a+    pkgConfParseFailed perror =+      die' verbosity $ "Couldn't parse the output of 'setup register --gen-pkg-config':"+            ++ show perror++    readPkgConf pkgConfDir pkgConfFile = do+      (warns, ipkg) <- withUTF8FileContents (pkgConfDir </> pkgConfFile) $ \pkgConfStr ->+        case Installed.parseInstalledPackageInfo pkgConfStr of+          Installed.ParseFailed perror -> pkgConfParseFailed perror+          Installed.ParseOk warns ipkg -> return (warns, ipkg)++      unless (null warns) $+        warn verbosity $ unlines (map (showPWarning pkgConfFile) warns)++      return ipkg+++------------------------------------------------------------------------------+-- * Utilities+------------------------------------------------------------------------------++annotateFailureNoLog :: (SomeException -> BuildFailureReason)+                     -> IO a -> IO a+annotateFailureNoLog annotate action =+  annotateFailure Nothing annotate action++annotateFailure :: Maybe FilePath+                -> (SomeException -> BuildFailureReason)+                -> IO a -> IO a+annotateFailure mlogFile annotate action =+  action `catches`+    -- It's not just IOException and ExitCode we have to deal with, there's+    -- lots, including exceptions from the hackage-security and tar packages.+    -- So we take the strategy of catching everything except async exceptions.+    [+#if MIN_VERSION_base(4,7,0)+      Handler $ \async -> throwIO (async :: SomeAsyncException)+#else+      Handler $ \async -> throwIO (async :: AsyncException)+#endif+    , Handler $ \other -> handler (other :: SomeException)+    ]+  where+    handler :: Exception e => e -> IO a+    handler = throwIO . BuildFailure mlogFile . annotate . toException 
+ Distribution/Client/ProjectBuilding/Types.hs view
@@ -0,0 +1,206 @@+{-# LANGUAGE DeriveDataTypeable #-}++-- | Types for the "Distribution.Client.ProjectBuilding"+--+-- Moved out to avoid module cycles.+--+module Distribution.Client.ProjectBuilding.Types (+    -- * Pre-build status+    BuildStatusMap,+    BuildStatus(..),+    buildStatusRequiresBuild,+    buildStatusToString,+    BuildStatusRebuild(..),+    BuildReason(..),+    MonitorChangedReason(..),++    -- * Build outcomes+    BuildOutcomes,+    BuildOutcome,+    BuildResult(..),+    BuildFailure(..),+    BuildFailureReason(..),+  ) where++import Distribution.Client.Types          (DocsResult, TestsResult)+import Distribution.Client.FileMonitor    (MonitorChangedReason(..))++import Distribution.Package               (UnitId, PackageId)+import Distribution.InstalledPackageInfo  (InstalledPackageInfo)+import Distribution.Simple.LocalBuildInfo (ComponentName)++import Data.Map          (Map)+import Data.Set          (Set)+import Data.Typeable     (Typeable)+import Control.Exception (Exception, SomeException)+++------------------------------------------------------------------------------+-- Pre-build status: result of the dry run+--++-- | The 'BuildStatus' of every package in the 'ElaboratedInstallPlan'.+--+-- This is used as the result of the dry-run of building an install plan.+--+type BuildStatusMap = Map UnitId BuildStatus++-- | The build status for an individual package is the state that the+-- package is in /prior/ to initiating a (re)build.+--+-- This should not be confused with a 'BuildResult' which is the result+-- /after/ successfully building a package.+--+-- It serves two purposes:+--+--  * For dry-run output, it lets us explain to the user if and why a package+--    is going to be (re)built.+--+--  * It tell us what step to start or resume building from, and carries+--    enough information for us to be able to do so.+--+data BuildStatus =++     -- | The package is in the 'InstallPlan.PreExisting' state, so does not+     --   need building.+     BuildStatusPreExisting++     -- | The package is in the 'InstallPlan.Installed' state, so does not+     --   need building.+   | BuildStatusInstalled++     -- | The package has not been downloaded yet, so it will have to be+     --   downloaded, unpacked and built.+   | BuildStatusDownload++     -- | The package has not been unpacked yet, so it will have to be+     --   unpacked and built.+   | BuildStatusUnpack FilePath++     -- | The package exists in a local dir already, and just needs building+     --   or rebuilding. So this can only happen for 'BuildInplaceOnly' style+     --   packages.+   | BuildStatusRebuild FilePath BuildStatusRebuild++     -- | The package exists in a local dir already, and is fully up to date.+     --   So this package can be put into the 'InstallPlan.Installed' state+     --   and it does not need to be built.+   | BuildStatusUpToDate BuildResult+++-- | Which 'BuildStatus' values indicate we'll have to do some build work of+-- some sort. In particular we use this as part of checking if any of a+-- package's deps have changed.+--+buildStatusRequiresBuild :: BuildStatus -> Bool+buildStatusRequiresBuild BuildStatusPreExisting = False+buildStatusRequiresBuild BuildStatusInstalled   = False+buildStatusRequiresBuild BuildStatusUpToDate {} = False+buildStatusRequiresBuild _                      = True++-- | This is primarily here for debugging. It's not actually used anywhere.+--+buildStatusToString :: BuildStatus -> String+buildStatusToString BuildStatusPreExisting    = "BuildStatusPreExisting"+buildStatusToString BuildStatusInstalled      = "BuildStatusInstalled"+buildStatusToString BuildStatusDownload       = "BuildStatusDownload"+buildStatusToString (BuildStatusUnpack fp)    = "BuildStatusUnpack " ++ show fp+buildStatusToString (BuildStatusRebuild fp _) = "BuildStatusRebuild " ++ show fp+buildStatusToString (BuildStatusUpToDate _)   = "BuildStatusUpToDate"+++-- | For a package that is going to be built or rebuilt, the state it's in now.+--+-- So again, this tells us why a package needs to be rebuilt and what build+-- phases need to be run. The 'MonitorChangedReason' gives us details like+-- which file changed, which is mainly for high verbosity debug output.+--+data BuildStatusRebuild =++     -- | The package configuration changed, so the configure and build phases+     --   needs to be (re)run.+     BuildStatusConfigure (MonitorChangedReason ())++     -- | The configuration has not changed but the build phase needs to be+     -- rerun. We record the reason the (re)build is needed.+     --+     -- The optional registration info here tells us if we've registered the+     -- package already, or if we still need to do that after building.+     -- @Just Nothing@ indicates that we know that no registration is+     -- necessary (e.g., executable.)+     --+   | BuildStatusBuild (Maybe (Maybe InstalledPackageInfo)) BuildReason++data BuildReason =+     -- | The dependencies of this package have been (re)built so the build+     -- phase needs to be rerun.+     --+     BuildReasonDepsRebuilt++     -- | Changes in files within the package (or first run or corrupt cache)+   | BuildReasonFilesChanged (MonitorChangedReason ())++     -- | An important special case is that no files have changed but the+     -- set of components the /user asked to build/ has changed. We track the+     -- set of components /we have built/, which of course only grows (until+     -- some other change resets it).+     --+     -- The @Set 'ComponentName'@ is the set of components we have built+     -- previously. When we update the monitor we take the union of the ones+     -- we have built previously with the ones the user has asked for this+     -- time and save those. See 'updatePackageBuildFileMonitor'.+     --+   | BuildReasonExtraTargets (Set ComponentName)++     -- | Although we're not going to build any additional targets as a whole,+     -- we're going to build some part of a component or run a repl or any+     -- other action that does not result in additional persistent artifacts.+     --+   | BuildReasonEphemeralTargets+++------------------------------------------------------------------------------+-- Build outcomes: result of the build+--++-- | A summary of the outcome for building a whole set of packages.+--+type BuildOutcomes = Map UnitId BuildOutcome++-- | A summary of the outcome for building a single package: either success+-- or failure.+--+type BuildOutcome  = Either BuildFailure BuildResult++-- | Information arising from successfully building a single package.+--+data BuildResult = BuildResult {+       buildResultDocs    :: DocsResult,+       buildResultTests   :: TestsResult,+       buildResultLogFile :: Maybe FilePath+     }+  deriving Show++-- | Information arising from the failure to build a single package.+--+data BuildFailure = BuildFailure {+       buildFailureLogFile :: Maybe FilePath,+       buildFailureReason  :: BuildFailureReason+     }+  deriving (Show, Typeable)++instance Exception BuildFailure++-- | Detail on the reason that a package failed to build.+--+data BuildFailureReason = DependentFailed PackageId+                        | DownloadFailed  SomeException+                        | UnpackFailed    SomeException+                        | ConfigureFailed SomeException+                        | BuildFailed     SomeException+                        | ReplFailed      SomeException+                        | HaddocksFailed  SomeException+                        | TestsFailed     SomeException+                        | InstallFailed   SomeException+  deriving Show+
Distribution/Client/ProjectConfig.hs view
@@ -8,14 +8,21 @@     ProjectConfig(..),     ProjectConfigBuildOnly(..),     ProjectConfigShared(..),+    ProjectConfigProvenance(..),     PackageConfig(..),     MapLast(..),     MapMappend(..), -    -- * Project config files+    -- * Project root     findProjectRoot,+    ProjectRoot(..),+    BadProjectRoot(..),++    -- * Project config files     readProjectConfig,+    readProjectLocalFreezeConfig,     writeProjectLocalExtraConfig,+    writeProjectLocalFreezeConfig,     writeProjectConfigFile,     commandLineFlagsToProjectConfig, @@ -41,6 +48,9 @@     BadPerPackageCompilerPaths(..)   ) where +import Prelude ()+import Distribution.Client.Compat.Prelude+ import Distribution.Client.ProjectConfig.Types import Distribution.Client.ProjectConfig.Legacy import Distribution.Client.RebuildMonad@@ -49,36 +59,47 @@  import Distribution.Client.Types import Distribution.Client.DistDirLayout-         ( CabalDirLayout(..) )+         ( DistDirLayout(..), CabalDirLayout(..), ProjectRoot(..) ) import Distribution.Client.GlobalFlags          ( RepoContext(..), withRepoContext' ) import Distribution.Client.BuildReports.Types          ( ReportLevel(..) ) import Distribution.Client.Config          ( loadConfig, defaultConfigFile )+import Distribution.Client.IndexUtils.Timestamp+         ( IndexState(..) ) +import Distribution.Solver.Types.SourcePackage+import Distribution.Solver.Types.Settings+ import Distribution.Package-         ( PackageName, PackageId, packageId, UnitId, Dependency )+         ( PackageName, PackageId, packageId, UnitId )+import Distribution.Types.Dependency import Distribution.System          ( Platform ) import Distribution.PackageDescription          ( SourceRepo(..) )+#if CABAL_PARSEC+import Distribution.PackageDescription.Parsec+         ( readGenericPackageDescription )+#else import Distribution.PackageDescription.Parse-         ( readPackageDescription )+         ( readGenericPackageDescription )+#endif import Distribution.Simple.Compiler          ( Compiler, compilerInfo ) import Distribution.Simple.Program          ( ConfiguredProgram(..) ) import Distribution.Simple.Setup          ( Flag(Flag), toFlag, flagToMaybe, flagToList-         , fromFlag, AllowNewer(..) )+         , fromFlag, fromFlagOrDefault, AllowNewer(..), AllowOlder(..), RelaxDeps(..) ) import Distribution.Client.Setup-         ( defaultSolver, defaultMaxBackjumps, )+         ( defaultSolver, defaultMaxBackjumps ) import Distribution.Simple.InstallDirs          ( PathTemplate, fromPathTemplate          , toPathTemplate, substPathTemplate, initialPathTemplateEnv ) import Distribution.Simple.Utils-         ( die, warn )+         ( die', warn ) import Distribution.Client.Utils          ( determineNumJobs ) import Distribution.Utils.NubList@@ -89,19 +110,14 @@ import Distribution.ParseUtils          ( ParseResult(..), locatedErrorMsg, showPWarning ) -#if !MIN_VERSION_base(4,8,0)-import Control.Applicative-#endif import Control.Monad import Control.Monad.Trans (liftIO) import Control.Exception-import Data.Typeable import Data.Maybe import Data.Either import qualified Data.Map as Map-import Data.Map (Map)+import Data.Set (Set) import qualified Data.Set as Set-import Distribution.Compat.Semigroup import System.FilePath hiding (combine) import System.Directory import Network.URI (URI(..), URIAuth(..), parseAbsoluteURI)@@ -149,18 +165,18 @@ -- to the 'BuildTimeSettings' -- projectConfigWithSolverRepoContext :: Verbosity-                                   -> FilePath                                    -> ProjectConfigShared                                    -> ProjectConfigBuildOnly                                    -> (RepoContext -> IO a) -> IO a-projectConfigWithSolverRepoContext verbosity downloadCacheRootDir+projectConfigWithSolverRepoContext verbosity                                    ProjectConfigShared{..}                                    ProjectConfigBuildOnly{..} =     withRepoContext'       verbosity       (fromNubList projectConfigRemoteRepos)       (fromNubList projectConfigLocalRepos)-      downloadCacheRootDir+      (fromFlagOrDefault (error "projectConfigWithSolverRepoContext: projectConfigCacheDir")+                         projectConfigCacheDir)       (flagToMaybe projectConfigHttpTransport)       (flagToMaybe projectConfigIgnoreExpiry) @@ -187,12 +203,16 @@                                           (getMapMappend projectConfigSpecificPackage)     solverSettingCabalVersion      = flagToMaybe projectConfigCabalVersion     solverSettingSolver            = fromFlag projectConfigSolver+    solverSettingAllowOlder        = fromJust projectConfigAllowOlder     solverSettingAllowNewer        = fromJust projectConfigAllowNewer     solverSettingMaxBackjumps      = case fromFlag projectConfigMaxBackjumps of                                        n | n < 0     -> Nothing                                          | otherwise -> Just n     solverSettingReorderGoals      = fromFlag projectConfigReorderGoals+    solverSettingCountConflicts    = fromFlag projectConfigCountConflicts     solverSettingStrongFlags       = fromFlag projectConfigStrongFlags+    solverSettingAllowBootLibInstalls = fromFlag projectConfigAllowBootLibInstalls+    solverSettingIndexState        = fromFlagOrDefault IndexStateHead projectConfigIndexState   --solverSettingIndependentGoals  = fromFlag projectConfigIndependentGoals   --solverSettingShadowPkgs        = fromFlag projectConfigShadowPkgs   --solverSettingReinstall         = fromFlag projectConfigReinstall@@ -204,11 +224,14 @@      defaults = mempty {        projectConfigSolver            = Flag defaultSolver,-       projectConfigAllowNewer        = Just AllowNewerNone,+       projectConfigAllowOlder        = Just (AllowOlder RelaxDepsNone),+       projectConfigAllowNewer        = Just (AllowNewer RelaxDepsNone),        projectConfigMaxBackjumps      = Flag defaultMaxBackjumps,-       projectConfigReorderGoals      = Flag False,-       projectConfigStrongFlags       = Flag False-     --projectConfigIndependentGoals  = Flag False,+       projectConfigReorderGoals      = Flag (ReorderGoals False),+       projectConfigCountConflicts    = Flag (CountConflicts True),+       projectConfigStrongFlags       = Flag (StrongFlags False),+       projectConfigAllowBootLibInstalls = Flag (AllowBootLibInstalls False)+     --projectConfigIndependentGoals  = Flag (IndependentGoals False),      --projectConfigShadowPkgs        = Flag False,      --projectConfigReinstall         = Flag False,      --projectConfigAvoidReinstalls   = Flag False,@@ -222,52 +245,50 @@ -- resolveBuildTimeSettings :: Verbosity                          -> CabalDirLayout-                         -> ProjectConfigShared-                         -> ProjectConfigBuildOnly-                         -> ProjectConfigBuildOnly+                         -> ProjectConfig                          -> BuildTimeSettings resolveBuildTimeSettings verbosity                          CabalDirLayout {-                           cabalLogsDirectory,-                           cabalPackageCacheDirectory-                         }-                         ProjectConfigShared {-                           projectConfigRemoteRepos,-                           projectConfigLocalRepos+                           cabalLogsDirectory                          }-                         fromProjectFile-                         fromCommandLine =+                         ProjectConfig {+                           projectConfigShared = ProjectConfigShared {+                             projectConfigRemoteRepos,+                             projectConfigLocalRepos+                           },+                           projectConfigBuildOnly+                         } =     BuildTimeSettings {..}   where     buildSettingDryRun        = fromFlag    projectConfigDryRun     buildSettingOnlyDeps      = fromFlag    projectConfigOnlyDeps     buildSettingSummaryFile   = fromNubList projectConfigSummaryFile-    --buildSettingLogFile       -- defined below, more complicated +    --buildSettingLogFile       -- defined below, more complicated     --buildSettingLogVerbosity  -- defined below, more complicated     buildSettingBuildReports  = fromFlag    projectConfigBuildReports     buildSettingSymlinkBinDir = flagToList  projectConfigSymlinkBinDir     buildSettingOneShot       = fromFlag    projectConfigOneShot     buildSettingNumJobs       = determineNumJobs projectConfigNumJobs+    buildSettingKeepGoing     = fromFlag    projectConfigKeepGoing     buildSettingOfflineMode   = fromFlag    projectConfigOfflineMode     buildSettingKeepTempFiles = fromFlag    projectConfigKeepTempFiles     buildSettingRemoteRepos   = fromNubList projectConfigRemoteRepos     buildSettingLocalRepos    = fromNubList projectConfigLocalRepos-    buildSettingCacheDir      = cabalPackageCacheDirectory+    buildSettingCacheDir      = fromFlag    projectConfigCacheDir     buildSettingHttpTransport = flagToMaybe projectConfigHttpTransport     buildSettingIgnoreExpiry  = fromFlag    projectConfigIgnoreExpiry     buildSettingReportPlanningFailure                               = fromFlag projectConfigReportPlanningFailure-    buildSettingRootCmd       = flagToMaybe projectConfigRootCmd      ProjectConfigBuildOnly{..} = defaults-                              <> fromProjectFile-                              <> fromCommandLine+                              <> projectConfigBuildOnly      defaults = mempty {       projectConfigDryRun                = toFlag False,       projectConfigOnlyDeps              = toFlag False,       projectConfigBuildReports          = toFlag NoReports,       projectConfigReportPlanningFailure = toFlag False,+      projectConfigKeepGoing             = toFlag False,       projectConfigOneShot               = toFlag False,       projectConfigOfflineMode           = toFlag False,       projectConfigKeepTempFiles         = toFlag False,@@ -288,7 +309,8 @@       | otherwise          = fmap  substLogFileName givenTemplate      defaultTemplate = toPathTemplate $-                        cabalLogsDirectory </> "$pkgid" <.> "log"+                        cabalLogsDirectory </>+                        "$compiler" </> "$libname" <.> "log"     givenTemplate   = flagToMaybe projectConfigLogFile      useDefaultTemplate@@ -332,58 +354,104 @@ -- parent directories. If no project file is found then the current dir is the -- project root (and the project will use an implicit config). ---findProjectRoot :: IO FilePath-findProjectRoot = do+findProjectRoot :: Maybe FilePath -- ^ starting directory, or current directory+                -> Maybe FilePath -- ^ @cabal.project@ file name override+                -> IO (Either BadProjectRoot ProjectRoot)+findProjectRoot _ (Just projectFile)+  | isAbsolute projectFile = do+    exists <- doesFileExist projectFile+    if exists+      then do projectFile' <- canonicalizePath projectFile+              let projectRoot = ProjectRootExplicit (takeDirectory projectFile')+                                                    (takeFileName projectFile')+              return (Right projectRoot)+      else return (Left (BadProjectRootExplicitFile projectFile)) -    curdir  <- getCurrentDirectory-    homedir <- getHomeDirectory+findProjectRoot mstartdir mprojectFile = do+    startdir <- maybe getCurrentDirectory canonicalizePath mstartdir+    homedir  <- getHomeDirectory+    probe startdir homedir+  where+    projectFileName = fromMaybe "cabal.project" mprojectFile      -- Search upwards. If we get to the users home dir or the filesystem root,     -- then use the current dir-    let probe dir | isDrive dir || dir == homedir-                  = return curdir -- implicit project root-        probe dir = do-          exists <- doesFileExist (dir </> "cabal.project")+    probe startdir homedir = go startdir+      where+        go dir | isDrive dir || dir == homedir =+          case mprojectFile of+            Nothing   -> return (Right (ProjectRootImplicit startdir))+            Just file -> return (Left (BadProjectRootExplicitFile file))+        go dir = do+          exists <- doesFileExist (dir </> projectFileName)           if exists-            then return dir       -- explicit project root-            else probe (takeDirectory dir)+            then return (Right (ProjectRootExplicit dir projectFileName))+            else go (takeDirectory dir) -    probe curdir    --TODO: [nice to have] add compat support for old style sandboxes  +-- | Errors returned by 'findProjectRoot'.+--+data BadProjectRoot = BadProjectRootExplicitFile FilePath+#if MIN_VERSION_base(4,8,0)+  deriving (Show, Typeable)+#else+  deriving (Typeable)++instance Show BadProjectRoot where+  show = renderBadProjectRoot+#endif++instance Exception BadProjectRoot where+#if MIN_VERSION_base(4,8,0)+  displayException = renderBadProjectRoot+#endif++renderBadProjectRoot :: BadProjectRoot -> String+renderBadProjectRoot (BadProjectRootExplicitFile projectFile) =+    "The given project file '" ++ projectFile ++ "' does not exist."++ -- | Read all the config relevant for a project. This includes the project -- file if any, plus other global config. ---readProjectConfig :: Verbosity -> FilePath -> Rebuild ProjectConfig-readProjectConfig verbosity projectRootDir = do-    global <- readGlobalConfig verbosity-    local  <- readProjectLocalConfig      verbosity projectRootDir-    extra  <- readProjectLocalExtraConfig verbosity projectRootDir-    return (global <> local <> extra)+readProjectConfig :: Verbosity -> DistDirLayout -> Rebuild ProjectConfig+readProjectConfig verbosity distDirLayout = do+    global <- readGlobalConfig             verbosity+    local  <- readProjectLocalConfig       verbosity distDirLayout+    freeze <- readProjectLocalFreezeConfig verbosity distDirLayout+    extra  <- readProjectLocalExtraConfig  verbosity distDirLayout+    return (global <> local <> freeze <> extra)   -- | Reads an explicit @cabal.project@ file in the given project root dir, -- or returns the default project config for an implicitly defined project. ---readProjectLocalConfig :: Verbosity -> FilePath -> Rebuild ProjectConfig-readProjectLocalConfig verbosity projectRootDir = do+readProjectLocalConfig :: Verbosity -> DistDirLayout -> Rebuild ProjectConfig+readProjectLocalConfig verbosity DistDirLayout{distProjectFile} = do   usesExplicitProjectRoot <- liftIO $ doesFileExist projectFile   if usesExplicitProjectRoot     then do       monitorFiles [monitorFileHashed projectFile]-      liftIO readProjectFile+      addProjectFileProvenance <$> liftIO readProjectFile     else do       monitorFiles [monitorNonExistentFile projectFile]       return defaultImplicitProjectConfig    where-    projectFile = projectRootDir </> "cabal.project"+    projectFile = distProjectFile ""     readProjectFile =           reportParseResult verbosity "project file" projectFile         . parseProjectConfig       =<< readFile projectFile +    addProjectFileProvenance config =+      config {+        projectConfigProvenance =+          Set.insert (Explicit projectFile) (projectConfigProvenance config)+      }+     defaultImplicitProjectConfig :: ProjectConfig     defaultImplicitProjectConfig =       mempty {@@ -391,30 +459,50 @@         projectPackages         = [ "./*.cabal" ],          -- This is to automatically pick up deps that we unpack locally.-        projectPackagesOptional = [ "./*/*.cabal" ]-      }+        projectPackagesOptional = [ "./*/*.cabal" ], +        projectConfigProvenance = Set.singleton Implicit+      } --- | Reads a @cabal.project.extra@ file in the given project root dir,+-- | Reads a @cabal.project.local@ file in the given project root dir, -- or returns empty. This file gets written by @cabal configure@, or in -- principle can be edited manually or by other tools. ---readProjectLocalExtraConfig :: Verbosity -> FilePath -> Rebuild ProjectConfig-readProjectLocalExtraConfig verbosity projectRootDir = do-    hasExtraConfig <- liftIO $ doesFileExist projectExtraConfigFile-    if hasExtraConfig-      then do monitorFiles [monitorFileHashed projectExtraConfigFile]-              liftIO readProjectExtraConfigFile-      else do monitorFiles [monitorNonExistentFile projectExtraConfigFile]+readProjectLocalExtraConfig :: Verbosity -> DistDirLayout+                            -> Rebuild ProjectConfig+readProjectLocalExtraConfig verbosity distDirLayout =+    readProjectExtensionFile verbosity distDirLayout "local"+                             "project local configuration file"++-- | Reads a @cabal.project.freeze@ file in the given project root dir,+-- or returns empty. This file gets written by @cabal freeze@, or in+-- principle can be edited manually or by other tools.+--+readProjectLocalFreezeConfig :: Verbosity -> DistDirLayout+                             -> Rebuild ProjectConfig+readProjectLocalFreezeConfig verbosity distDirLayout =+    readProjectExtensionFile verbosity distDirLayout "freeze"+                             "project freeze file"++-- | Reads a named config file in the given project root dir, or returns empty.+--+readProjectExtensionFile :: Verbosity -> DistDirLayout -> String -> FilePath+                         -> Rebuild ProjectConfig+readProjectExtensionFile verbosity DistDirLayout{distProjectFile}+                         extensionName extensionDescription = do+    exists <- liftIO $ doesFileExist extensionFile+    if exists+      then do monitorFiles [monitorFileHashed extensionFile]+              liftIO readExtensionFile+      else do monitorFiles [monitorNonExistentFile extensionFile]               return mempty   where-    projectExtraConfigFile = projectRootDir </> "cabal.project.local"+    extensionFile = distProjectFile extensionName -    readProjectExtraConfigFile =-          reportParseResult verbosity "project local configuration file"-                            projectExtraConfigFile+    readExtensionFile =+          reportParseResult verbosity extensionDescription extensionFile         . parseProjectConfig-      =<< readFile projectExtraConfigFile+      =<< readFile extensionFile   -- | Parse the 'ProjectConfig' format.@@ -438,15 +526,20 @@     showLegacyProjectConfig . convertToLegacyProjectConfig  --- | Write a @cabal.project.extra@ file in the given project root dir.+-- | Write a @cabal.project.local@ file in the given project root dir. ---writeProjectLocalExtraConfig :: FilePath -> ProjectConfig -> IO ()-writeProjectLocalExtraConfig projectRootDir =-    writeProjectConfigFile projectExtraConfigFile-  where-    projectExtraConfigFile = projectRootDir </> "cabal.project.local"+writeProjectLocalExtraConfig :: DistDirLayout -> ProjectConfig -> IO ()+writeProjectLocalExtraConfig DistDirLayout{distProjectFile} =+    writeProjectConfigFile (distProjectFile "local")  +-- | Write a @cabal.project.freeze@ file in the given project root dir.+--+writeProjectLocalFreezeConfig :: DistDirLayout -> ProjectConfig -> IO ()+writeProjectLocalFreezeConfig DistDirLayout{distProjectFile} =+    writeProjectConfigFile (distProjectFile "freeze")++ -- | Write in the @cabal.project@ format to the given file. -- writeProjectConfigFile :: FilePath -> ProjectConfig -> IO ()@@ -472,9 +565,9 @@       let msg = unlines (map (showPWarning filename) warnings)        in warn verbosity msg     return x-reportParseResult _verbosity filetype filename (ParseFailed err) =+reportParseResult verbosity filetype filename (ParseFailed err) =     let (line, msg) = locatedErrorMsg err-     in die $ "Error parsing " ++ filetype ++ " " ++ filename+     in die' verbosity $ "Error parsing " ++ filetype ++ " " ++ filename            ++ maybe "" (\n -> ':' : show n) line ++ ":\n" ++ msg  @@ -498,11 +591,21 @@  -- | Exception thrown by 'findProjectPackages'. ---newtype BadPackageLocations = BadPackageLocations [BadPackageLocation]+data BadPackageLocations+   = BadPackageLocations (Set ProjectConfigProvenance) [BadPackageLocation]+#if MIN_VERSION_base(4,8,0)   deriving (Show, Typeable)+#else+  deriving (Typeable) -instance Exception BadPackageLocations---TODO: [required eventually] displayException for nice rendering+instance Show BadPackageLocations where+  show = renderBadPackageLocations+#endif++instance Exception BadPackageLocations where+#if MIN_VERSION_base(4,8,0)+  displayException = renderBadPackageLocations+#endif --TODO: [nice to have] custom exception subclass for Doc rendering, colour etc  data BadPackageLocation@@ -521,14 +624,104 @@    | BadLocDirManyCabalFiles   String   deriving Show +renderBadPackageLocations :: BadPackageLocations -> String+renderBadPackageLocations (BadPackageLocations provenance bpls)+      -- There is no provenance information,+      -- render standard bad package error information.+    | Set.null provenance = renderErrors renderBadPackageLocation --- | Given the project config, +      -- The configuration is implicit, render bad package locations+      -- using possibly specialized error messages.+    | Set.singleton Implicit == provenance =+        renderErrors renderImplicitBadPackageLocation++      -- The configuration contains both implicit and explicit provenance.+      -- This should not occur, and a message is output to assist debugging.+    | Implicit `Set.member` provenance =+           "Warning: both implicit and explicit configuration is present."+        ++ renderExplicit++      -- The configuration was read from one or more explicit path(s),+      -- list the locations and render the bad package error information.+      -- The intent is to supersede this with the relevant location information+      -- per package error.+    | otherwise = renderExplicit+  where+    renderErrors f = unlines (map f bpls)++    renderExplicit =+           "When using configuration(s) from "+        ++ intercalate ", " (mapMaybe getExplicit (Set.toList provenance))+        ++ ", the following errors occurred:\n"+        ++ renderErrors renderBadPackageLocation++    getExplicit (Explicit path) = Just path+    getExplicit Implicit        = Nothing++--TODO: [nice to have] keep track of the config file (and src loc) packages+-- were listed, to use in error messages++-- | Render bad package location error information for the implicit+-- @cabal.project@ configuration. --+-- TODO: This is currently not fully realized, with only one of the implicit+-- cases handled. More cases should be added with informative help text+-- about the issues related specifically when having no project configuration+-- is present.+renderImplicitBadPackageLocation :: BadPackageLocation -> String+renderImplicitBadPackageLocation bpl = case bpl of+    BadLocGlobEmptyMatch pkglocstr ->+        "No cabal.project file or cabal file matching the default glob '"+     ++ pkglocstr ++ "' was found.\n"+     ++ "Please create a package description file <pkgname>.cabal "+     ++ "or a cabal.project file referencing the packages you "+     ++ "want to build."+    _ -> renderBadPackageLocation bpl++renderBadPackageLocation :: BadPackageLocation -> String+renderBadPackageLocation bpl = case bpl of+    BadPackageLocationFile badmatch ->+        renderBadPackageLocationMatch badmatch+    BadLocGlobEmptyMatch pkglocstr ->+        "The package location glob '" ++ pkglocstr+     ++ "' does not match any files or directories."+    BadLocGlobBadMatches pkglocstr failures ->+        "The package location glob '" ++ pkglocstr ++ "' does not match any "+     ++ "recognised forms of package. "+     ++ concatMap ((' ':) . renderBadPackageLocationMatch) failures+    BadLocUnexpectedUriScheme pkglocstr ->+        "The package location URI '" ++ pkglocstr ++ "' does not use a "+     ++ "supported URI scheme. The supported URI schemes are http, https and "+     ++ "file."+    BadLocUnrecognisedUri pkglocstr ->+        "The package location URI '" ++ pkglocstr ++ "' does not appear to "+     ++ "be a valid absolute URI."+    BadLocUnrecognised pkglocstr ->+        "The package location syntax '" ++ pkglocstr ++ "' is not recognised."++renderBadPackageLocationMatch :: BadPackageLocationMatch -> String+renderBadPackageLocationMatch bplm = case bplm of+    BadLocUnexpectedFile pkglocstr ->+        "The package location '" ++ pkglocstr ++ "' is not recognised. The "+     ++ "supported file targets are .cabal files, .tar.gz tarballs or package "+     ++ "directories (i.e. directories containing a .cabal file)."+    BadLocNonexistantFile pkglocstr ->+        "The package location '" ++ pkglocstr ++ "' does not exist."+    BadLocDirNoCabalFile pkglocstr ->+        "The package directory '" ++ pkglocstr ++ "' does not contain any "+     ++ ".cabal file."+    BadLocDirManyCabalFiles pkglocstr ->+        "The package directory '" ++ pkglocstr ++ "' contains multiple "+     ++ ".cabal files (which is not currently supported)."++-- | Given the project config,+-- -- Throws 'BadPackageLocations'. ---findProjectPackages :: FilePath -> ProjectConfig+findProjectPackages :: DistDirLayout -> ProjectConfig                     -> Rebuild [ProjectPackageLocation]-findProjectPackages projectRootDir ProjectConfig{..} = do+findProjectPackages DistDirLayout{distProjectRootDirectory}+                    ProjectConfig{..} = do      requiredPkgs <- findPackageLocations True    projectPackages     optionalPkgs <- findPackageLocations False   projectPackagesOptional@@ -541,7 +734,7 @@       (problems, pkglocs) <-         partitionEithers <$> mapM (findPackageLocation required) pkglocstr       unless (null problems) $-        liftIO $ throwIO $ BadPackageLocations problems+        liftIO $ throwIO $ BadPackageLocations projectConfigProvenance problems       return (concat pkglocs)  @@ -580,6 +773,10 @@           | recognisedScheme && not (null host) ->             Just (Right [ProjectPackageRemoteTarball uri]) +          --TODO: [required eventually] handle file: urls which do have a null+          -- host. translate URI into filepath and use ProjectPackageLocalTarball+          -- or keep as file url and use ProjectPackageRemoteTarball?+           | not recognisedScheme && not (null host) ->             Just (Left (BadLocUnexpectedUriScheme pkglocstr)) @@ -599,7 +796,7 @@           matches <- matchFileGlob glob           case matches of             [] | isJust (isTrivialFilePathGlob glob)-               -> return (Left (BadPackageLocationFile +               -> return (Left (BadPackageLocationFile                                   (BadLocNonexistantFile pkglocstr)))              [] -> return (Left (BadLocGlobEmptyMatch pkglocstr))@@ -607,13 +804,15 @@             _  -> do               (failures, pkglocs) <- partitionEithers <$>                                      mapM checkFilePackageMatch matches-              if null pkglocs-                then return (Left (BadLocGlobBadMatches pkglocstr failures))-                else return (Right pkglocs)+              return $! case (failures, pkglocs) of+                ([failure], []) | isJust (isTrivialFilePathGlob glob)+                        -> Left (BadPackageLocationFile failure)+                (_, []) -> Left (BadLocGlobBadMatches pkglocstr failures)+                _       -> Right pkglocs       checkIsSingleFilePackage pkglocstr = do-      let filename = projectRootDir </> pkglocstr+      let filename = distProjectRootDirectory </> pkglocstr       isFile <- liftIO $ doesFileExist filename       isDir  <- liftIO $ doesDirectoryExist filename       if isFile || isDir@@ -629,7 +828,8 @@       -- The pkglocstr may be absolute or may be relative to the project root.       -- Either way, </> does the right thing here. We return relative paths if       -- they were relative in the first place.-      let abspath = projectRootDir </> pkglocstr+      let abspath = distProjectRootDirectory </> pkglocstr+      isFile <- liftIO $ doesFileExist abspath       isDir  <- liftIO $ doesDirectoryExist abspath       parentDirExists <- case takeDirectory abspath of                            []  -> return False@@ -650,6 +850,9 @@           | takeExtension pkglocstr == ".cabal"          -> return (Right (ProjectPackageLocalCabalFile pkglocstr)) +          | isFile+         -> return (Left (BadLocUnexpectedFile pkglocstr))+           | parentDirExists          -> return (Left (BadLocNonexistantFile pkglocstr)) @@ -691,7 +894,7 @@ -- paths. -- readSourcePackage :: Verbosity -> ProjectPackageLocation-                  -> Rebuild SourcePackage+                  -> Rebuild UnresolvedSourcePackage readSourcePackage verbosity (ProjectPackageLocalCabalFile cabalFile) =     readSourcePackage verbosity (ProjectPackageLocalDirectory dir cabalFile)   where@@ -700,7 +903,7 @@ readSourcePackage verbosity (ProjectPackageLocalDirectory dir cabalFile) = do     monitorFiles [monitorFileHashed cabalFile]     root <- askRoot-    pkgdesc <- liftIO $ readPackageDescription verbosity (root </> cabalFile)+    pkgdesc <- liftIO $ readGenericPackageDescription verbosity (root </> cabalFile)     return SourcePackage {       packageInfoId        = packageId pkgdesc,       packageDescription   = pkgdesc,@@ -718,12 +921,34 @@  data BadPerPackageCompilerPaths    = BadPerPackageCompilerPaths [(PackageName, String)]+#if MIN_VERSION_base(4,8,0)   deriving (Show, Typeable)+#else+  deriving (Typeable) -instance Exception BadPerPackageCompilerPaths---TODO: [required eventually] displayException for nice rendering+instance Show BadPerPackageCompilerPaths where+  show = renderBadPerPackageCompilerPaths+#endif++instance Exception BadPerPackageCompilerPaths where+#if MIN_VERSION_base(4,8,0)+  displayException = renderBadPerPackageCompilerPaths+#endif --TODO: [nice to have] custom exception subclass for Doc rendering, colour etc +renderBadPerPackageCompilerPaths :: BadPerPackageCompilerPaths -> String+renderBadPerPackageCompilerPaths+  (BadPerPackageCompilerPaths ((pkgname, progname) : _)) =+    "The path to the compiler program (or programs used by the compiler) "+ ++ "cannot be specified on a per-package basis in the cabal.project file "+ ++ "(i.e. setting the '" ++ progname ++ "-location' for package '"+ ++ display pkgname ++ "'). All packages have to use the same compiler, so "+ ++ "specify the path in a global 'program-locations' section."+ --TODO: [nice to have] better format control so we can pretty-print the+ -- offending part of the project file. Currently the line wrapping breaks any+ -- formatting.+renderBadPerPackageCompilerPaths _ = error "renderBadPerPackageCompilerPaths"+ -- | The project configuration is not allowed to specify program locations for -- programs used by the compiler as these have to be the same for each set of -- packages.@@ -744,4 +969,3 @@          , progname `Set.member` compProgNames ] of       [] -> return ()       ps -> throwIO (BadPerPackageCompilerPaths ps)-
Distribution/Client/ProjectConfig/Legacy.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE CPP, RecordWildCards, NamedFieldPuns, DeriveGeneric #-}+{-# LANGUAGE RecordWildCards, NamedFieldPuns, DeriveGeneric #-}  -- | Project configuration, implementation in terms of legacy types. --@@ -20,17 +20,21 @@     renderPackageLocationToken,   ) where +import Prelude ()+import Distribution.Client.Compat.Prelude+ import Distribution.Client.ProjectConfig.Types import Distribution.Client.Types          ( RemoteRepo(..), emptyRemoteRepo )-import Distribution.Client.Dependency.Types-         ( ConstraintSource(..) ) import Distribution.Client.Config          ( SavedConfig(..), remoteRepoFields ) +import Distribution.Solver.Types.ConstraintSource+ import Distribution.Package import Distribution.PackageDescription-         ( SourceRepo(..), RepoKind(..) )+         ( SourceRepo(..), RepoKind(..) +         , dispFlagAssignment, parseFlagAssignment ) import Distribution.PackageDescription.Parse          ( sourceRepoFieldDescrs ) import Distribution.Simple.Compiler@@ -39,8 +43,8 @@          ( Flag(Flag), toFlag, fromFlagOrDefault          , ConfigFlags(..), configureOptions          , HaddockFlags(..), haddockOptions, defaultHaddockFlags-         , programConfigurationPaths', splitArgs-         , AllowNewer(..) )+         , programDbPaths', splitArgs+         , AllowNewer(..), AllowOlder(..), RelaxDeps(..) ) import Distribution.Client.Setup          ( GlobalFlags(..), globalCommand          , ConfigExFlags(..), configureExOptions, defaultConfigExFlags@@ -49,8 +53,6 @@          ( programName, knownPrograms ) import Distribution.Simple.Program.Db          ( ProgramDb, defaultProgramDb )-import Distribution.Client.Targets-         ( dispFlagAssignment, parseFlagAssignment ) import Distribution.Simple.Utils          ( lowercase ) import Distribution.Utils.NubList@@ -76,14 +78,7 @@          ( CommandUI(commandOptions), ShowOrParseArgs(..)          , OptionField, option, reqArg' ) -#if !MIN_VERSION_base(4,8,0)-import Control.Applicative-#endif-import Control.Monad import qualified Data.Map as Map-import Data.Char (isSpace)-import Distribution.Compat.Semigroup-import GHC.Generics (Generic)  ------------------------------------------------------------------ -- Representing the project config file in terms of legacy types@@ -242,6 +237,7 @@        projectConfigBuildOnly       = configBuildOnly,       projectConfigShared          = configAllPackages,+      projectConfigProvenance      = mempty,       projectConfigLocalPackages   = configLocalPackages,       projectConfigSpecificPackage = fmap perPackage legacySpecificConfig     }@@ -279,12 +275,14 @@     } = globalFlags      ConfigFlags {+      configDistPref            = projectConfigDistDir,       configHcFlavor            = projectConfigHcFlavor,       configHcPath              = projectConfigHcPath,       configHcPkg               = projectConfigHcPkg,     --configInstallDirs         = projectConfigInstallDirs,     --configUserInstall         = projectConfigUserInstall,     --configPackageDBs          = projectConfigPackageDBs,+      configAllowOlder          = projectConfigAllowOlder,       configAllowNewer          = projectConfigAllowNewer     } = configFlags @@ -296,16 +294,21 @@     } = configExFlags      InstallFlags {+      installProjectFileName    = projectConfigProjectFile,       installHaddockIndex       = projectConfigHaddockIndex,     --installReinstall          = projectConfigReinstall,     --installAvoidReinstalls    = projectConfigAvoidReinstalls,     --installOverrideReinstall  = projectConfigOverrideReinstall,+      installIndexState         = projectConfigIndexState,       installMaxBackjumps       = projectConfigMaxBackjumps,     --installUpgradeDeps        = projectConfigUpgradeDeps,       installReorderGoals       = projectConfigReorderGoals,+      installCountConflicts     = projectConfigCountConflicts,+      installPerComponent       = projectConfigPerComponent,     --installIndependentGoals   = projectConfigIndependentGoals,     --installShadowPkgs         = projectConfigShadowPkgs,-      installStrongFlags        = projectConfigStrongFlags+      installStrongFlags        = projectConfigStrongFlags,+      installAllowBootLibInstalls = projectConfigAllowBootLibInstalls     } = installFlags  @@ -364,6 +367,7 @@       haddockHoogle             = packageConfigHaddockHoogle,       haddockHtml               = packageConfigHaddockHtml,       haddockHtmlLocation       = packageConfigHaddockHtmlLocation,+      haddockForeignLibs        = packageConfigHaddockForeignLibs,       haddockExecutables        = packageConfigHaddockExecutables,       haddockTestSuites         = packageConfigHaddockTestSuites,       haddockBenchmarks         = packageConfigHaddockBenchmarks,@@ -389,7 +393,7 @@     GlobalFlags {       globalCacheDir          = projectConfigCacheDir,       globalLogsDir           = projectConfigLogsDir,-      globalWorldFile         = projectConfigWorldFile,+      globalWorldFile         = _,       globalHttpTransport     = projectConfigHttpTransport,       globalIgnoreExpiry      = projectConfigIgnoreExpiry     } = globalFlags@@ -402,7 +406,7 @@       installDryRun             = projectConfigDryRun,       installOnly               = _,       installOnlyDeps           = projectConfigOnlyDeps,-      installRootCmd            = projectConfigRootCmd,+      installRootCmd            = _,       installSummaryFile        = projectConfigSummaryFile,       installLogFile            = projectConfigLogFile,       installBuildReports       = projectConfigBuildReports,@@ -410,6 +414,7 @@       installSymlinkBinDir      = projectConfigSymlinkBinDir,       installOneShot            = projectConfigOneShot,       installNumJobs            = projectConfigNumJobs,+      installKeepGoing          = projectConfigKeepGoing,       installOfflineMode        = projectConfigOfflineMode     } = installFlags @@ -465,15 +470,18 @@       globalCacheDir          = projectConfigCacheDir,       globalLocalRepos        = projectConfigLocalRepos,       globalLogsDir           = projectConfigLogsDir,-      globalWorldFile         = projectConfigWorldFile,+      globalWorldFile         = mempty,       globalRequireSandbox    = mempty,       globalIgnoreSandbox     = mempty,       globalIgnoreExpiry      = projectConfigIgnoreExpiry,-      globalHttpTransport     = projectConfigHttpTransport+      globalHttpTransport     = projectConfigHttpTransport,+      globalNix               = mempty     }      configFlags = mempty {       configVerbosity     = projectConfigVerbosity,+      configDistPref      = projectConfigDistDir,+      configAllowOlder    = projectConfigAllowOlder,       configAllowNewer    = projectConfigAllowNewer     } @@ -494,21 +502,27 @@       installMaxBackjumps      = projectConfigMaxBackjumps,       installUpgradeDeps       = mempty, --projectConfigUpgradeDeps,       installReorderGoals      = projectConfigReorderGoals,+      installCountConflicts    = projectConfigCountConflicts,       installIndependentGoals  = mempty, --projectConfigIndependentGoals,       installShadowPkgs        = mempty, --projectConfigShadowPkgs,       installStrongFlags       = projectConfigStrongFlags,+      installAllowBootLibInstalls = projectConfigAllowBootLibInstalls,       installOnly              = mempty,       installOnlyDeps          = projectConfigOnlyDeps,-      installRootCmd           = projectConfigRootCmd,+      installIndexState        = projectConfigIndexState,+      installRootCmd           = mempty, --no longer supported       installSummaryFile       = projectConfigSummaryFile,       installLogFile           = projectConfigLogFile,       installBuildReports      = projectConfigBuildReports,       installReportPlanningFailure = projectConfigReportPlanningFailure,       installSymlinkBinDir     = projectConfigSymlinkBinDir,+      installPerComponent      = projectConfigPerComponent,       installOneShot           = projectConfigOneShot,       installNumJobs           = projectConfigNumJobs,+      installKeepGoing         = projectConfigKeepGoing,       installRunTests          = mempty,-      installOfflineMode       = projectConfigOfflineMode+      installOfflineMode       = projectConfigOfflineMode,+      installProjectFileName   = projectConfigProjectFile     }  @@ -526,6 +540,7 @@     }   where     configFlags = ConfigFlags {+      configArgs                = mempty,       configPrograms_           = mempty,       configProgramPaths        = mempty,       configProgramArgs         = mempty,@@ -533,6 +548,7 @@       configHcFlavor            = projectConfigHcFlavor,       configHcPath              = projectConfigHcPath,       configHcPkg               = projectConfigHcPkg,+      configInstantiateWith     = mempty,       configVanillaLib          = mempty,       configProfLib             = mempty,       configSharedLib           = mempty,@@ -548,6 +564,7 @@       configInstallDirs         = mempty,       configScratchDir          = mempty,       configDistPref            = mempty,+      configCabalFilePath       = mempty,       configVerbosity           = mempty,       configUserInstall         = mempty, --projectConfigUserInstall,       configPackageDBs          = mempty, --projectConfigPackageDBs,@@ -560,7 +577,9 @@       configConstraints         = mempty,       configDependencies        = mempty,       configExtraIncludeDirs    = mempty,+      configDeterministic       = mempty,       configIPID                = mempty,+      configCID                 = mempty,       configConfigurationsFlags = mempty,       configTests               = mempty,       configCoverage            = mempty, --TODO: don't merge@@ -570,6 +589,7 @@       configFlagError           = mempty,                --TODO: ???       configRelocatable         = mempty,       configDebugInfo           = mempty,+      configAllowOlder          = mempty,       configAllowNewer          = mempty     } @@ -587,6 +607,7 @@     }   where     configFlags = ConfigFlags {+      configArgs                = mempty,       configPrograms_           = configPrograms_ mempty,       configProgramPaths        = Map.toList (getMapLast packageConfigProgramPaths),       configProgramArgs         = Map.toList (getMapMappend packageConfigProgramArgs),@@ -594,6 +615,7 @@       configHcFlavor            = mempty,       configHcPath              = mempty,       configHcPkg               = mempty,+      configInstantiateWith     = mempty,       configVanillaLib          = packageConfigVanillaLib,       configProfLib             = packageConfigProfLib,       configSharedLib           = packageConfigSharedLib,@@ -609,6 +631,7 @@       configInstallDirs         = mempty,       configScratchDir          = mempty,       configDistPref            = mempty,+      configCabalFilePath       = mempty,       configVerbosity           = mempty,       configUserInstall         = mempty,       configPackageDBs          = mempty,@@ -622,6 +645,8 @@       configDependencies        = mempty,       configExtraIncludeDirs    = packageConfigExtraIncludeDirs,       configIPID                = mempty,+      configCID                 = mempty,+      configDeterministic       = mempty,       configConfigurationsFlags = packageConfigFlagAssignment,       configTests               = packageConfigTests,       configCoverage            = packageConfigCoverage, --TODO: don't merge@@ -631,6 +656,7 @@       configFlagError           = mempty,                --TODO: ???       configRelocatable         = packageConfigRelocatable,       configDebugInfo           = packageConfigDebugInfo,+      configAllowOlder          = mempty,       configAllowNewer          = mempty     } @@ -646,6 +672,7 @@       haddockHtml          = packageConfigHaddockHtml,       haddockHtmlLocation  = packageConfigHaddockHtmlLocation,       haddockForHackage    = mempty, --TODO: added recently+      haddockForeignLibs   = packageConfigHaddockForeignLibs,       haddockExecutables   = packageConfigHaddockExecutables,       haddockTestSuites    = packageConfigHaddockTestSuites,       haddockBenchmarks    = packageConfigHaddockBenchmarks,@@ -773,7 +800,7 @@       ]   . filterFields       [ "remote-repo-cache"-      , "logs-dir", "world-file", "ignore-expiry", "http-transport"+      , "logs-dir", "ignore-expiry", "http-transport"       ]   . commandOptionsToFields   ) (commandOptions (globalCommand []) ParseArgs)@@ -782,11 +809,18 @@       legacyConfigureShFlags       (\flags conf -> conf { legacyConfigureShFlags = flags })   . addFields+      [ simpleField "allow-older"+        (maybe mempty dispRelaxDeps) (fmap Just parseRelaxDeps)+        (fmap unAllowOlder . configAllowOlder)+        (\v conf -> conf { configAllowOlder = fmap AllowOlder v })+      ]+  . addFields       [ simpleField "allow-newer"-        (maybe mempty dispAllowNewer) (fmap Just parseAllowNewer)-        configAllowNewer (\v conf -> conf { configAllowNewer = v })+        (maybe mempty dispRelaxDeps) (fmap Just parseRelaxDeps)+        (fmap unAllowNewer . configAllowNewer)+        (\v conf -> conf { configAllowNewer = fmap AllowNewer v })       ]-  . filterFields ["verbose"]+  . filterFields ["verbose", "builddir" ]   . commandOptionsToFields   ) (configureOptions ParseArgs)  ++@@ -823,26 +857,27 @@       , "root-cmd", "symlink-bindir"       , "build-log"       , "remote-build-reporting", "report-planning-failure"-      , "one-shot", "jobs", "offline"+      , "one-shot", "jobs", "keep-going", "offline", "per-component"         -- solver flags:-      , "max-backjumps", "reorder-goals", "strong-flags"+      , "max-backjumps", "reorder-goals", "count-conflicts", "strong-flags"+      , "allow-boot-library-installs", "index-state"       ]   . commandOptionsToFields   ) (installOptions ParseArgs)   where     constraintSrc = ConstraintSourceProjectConfig "TODO" -parseAllowNewer :: ReadP r AllowNewer-parseAllowNewer =-     ((const AllowNewerNone <$> (Parse.string "none" +++ Parse.string "None"))-  +++ (const AllowNewerAll  <$> (Parse.string "all"  +++ Parse.string "All")))-  <++ (      AllowNewerSome <$> parseOptCommaList parse)+parseRelaxDeps :: ReadP r RelaxDeps+parseRelaxDeps =+     ((const RelaxDepsNone <$> (Parse.string "none" +++ Parse.string "None"))+  +++ (const RelaxDepsAll  <$> (Parse.string "all"  +++ Parse.string "All")))+  <++ (      RelaxDepsSome <$> parseOptCommaList parse) -dispAllowNewer :: AllowNewer -> Doc-dispAllowNewer  AllowNewerNone       = Disp.text "None"-dispAllowNewer (AllowNewerSome pkgs) = Disp.fsep . Disp.punctuate Disp.comma-                                                 . map disp $ pkgs-dispAllowNewer  AllowNewerAll        = Disp.text "All"+dispRelaxDeps :: RelaxDeps -> Doc+dispRelaxDeps  RelaxDepsNone       = Disp.text "None"+dispRelaxDeps (RelaxDepsSome pkgs) = Disp.fsep . Disp.punctuate Disp.comma+                                               . map disp $ pkgs+dispRelaxDeps  RelaxDepsAll        = Disp.text "All"   legacyPackageConfigFieldDescrs :: [FieldDescr LegacyPackageConfig]@@ -877,13 +912,13 @@           (\v conf -> conf { configConfigurationsFlags = v })       ]   . filterFields-      [ "compiler", "with-compiler", "with-hc-pkg"+      [ "with-compiler", "with-hc-pkg"       , "program-prefix", "program-suffix"       , "library-vanilla", "library-profiling"       , "shared", "executable-dynamic"       , "profiling", "executable-profiling"       , "profiling-detail", "library-profiling-detail"-      , "optimization", "debug-info", "library-for-ghci", "split-objs"+      , "library-for-ghci", "split-objs"       , "executable-stripping", "library-stripping"       , "tests", "benchmarks"       , "coverage", "library-coverage"@@ -919,6 +954,7 @@       ("haddock-"++)   . filterFields       [ "hoogle", "html", "html-location"+      , "foreign-libraries"       , "executables", "tests", "benchmarks", "all", "internal", "css"       , "hyperlink-source", "hscolour-css"       , "contents-location", "keep-temp-files"@@ -1081,11 +1117,11 @@ programOptionsFieldDescrs :: (a -> [(String, [String])])                           -> ([(String, [String])] -> a -> a)                           -> [FieldDescr a]-programOptionsFieldDescrs get set =+programOptionsFieldDescrs get' set =     commandOptionsToFields-  $ programConfigurationOptions+  $ programDbOptions       defaultProgramDb-      ParseArgs get set+      ParseArgs get' set  programOptionsSectionDescr :: SectionDescr LegacyPackageConfig programOptionsSectionDescr =@@ -1110,7 +1146,7 @@ programLocationsFieldDescrs :: [FieldDescr ConfigFlags] programLocationsFieldDescrs =      commandOptionsToFields-   $ programConfigurationPaths'+   $ programDbPaths'        (++ "-location")        defaultProgramDb        ParseArgs@@ -1136,25 +1172,25 @@     }  --- | For each known program @PROG@ in 'progConf', produce a @PROG-options@+-- | For each known program @PROG@ in 'progDb', produce a @PROG-options@ -- 'OptionField'.-programConfigurationOptions+programDbOptions   :: ProgramDb   -> ShowOrParseArgs   -> (flags -> [(String, [String])])   -> ([(String, [String])] -> (flags -> flags))   -> [OptionField flags]-programConfigurationOptions progConf showOrParseArgs get set =+programDbOptions progDb showOrParseArgs get' set =   case showOrParseArgs of     -- we don't want a verbose help text list so we just show a generic one:     ShowArgs  -> [programOptions  "PROG"]     ParseArgs -> map (programOptions . programName . fst)-                 (knownPrograms progConf)+                 (knownPrograms progDb)   where     programOptions prog =       option "" [prog ++ "-options"]         ("give extra options to " ++ prog)-        get set+        get' set         (reqArg' "OPTS" (\args -> [(prog, splitArgs args)])            (\progArgs -> [ joinsArgs args                          | (prog', args) <- progArgs, prog==prog' ]))@@ -1213,11 +1249,11 @@ -- of parseOptCommaList below listFieldWithSep :: ([Doc] -> Doc) -> String -> (a -> Doc) -> ReadP [a] a                  -> (b -> [a]) -> ([a] -> b -> b) -> FieldDescr b-listFieldWithSep separator name showF readF get set =-  liftField get set' $+listFieldWithSep separator name showF readF get' set =+  liftField get' set' $     ParseUtils.field name showF' (parseOptCommaList readF)   where-    set' xs b = set (get b ++ xs) b+    set' xs b = set (get' b ++ xs) b     showF'    = separator . map showF  --TODO: [code cleanup] local redefinition that should replace the version in
Distribution/Client/ProjectConfig/Types.hs view
@@ -8,6 +8,7 @@     ProjectConfig(..),     ProjectConfigBuildOnly(..),     ProjectConfigShared(..),+    ProjectConfigProvenance(..),     PackageConfig(..),      -- * Resolving configuration@@ -22,14 +23,21 @@ import Distribution.Client.Types          ( RemoteRepo ) import Distribution.Client.Dependency.Types-         ( PreSolver, ConstraintSource )+         ( PreSolver ) import Distribution.Client.Targets          ( UserConstraint )-import Distribution.Client.BuildReports.Types +import Distribution.Client.BuildReports.Types          ( ReportLevel(..) ) +import Distribution.Client.IndexUtils.Timestamp+         ( IndexState )++import Distribution.Solver.Types.Settings+import Distribution.Solver.Types.ConstraintSource+ import Distribution.Package-         ( PackageName, PackageId, UnitId, Dependency )+         ( PackageName, PackageId, UnitId )+import Distribution.Types.Dependency import Distribution.Version          ( Version ) import Distribution.System@@ -40,7 +48,7 @@          ( Compiler, CompilerFlavor          , OptimisationLevel(..), ProfDetailLevel, DebugInfoLevel(..) ) import Distribution.Simple.Setup-         ( Flag, AllowNewer(..) )+         ( Flag, AllowNewer(..), AllowOlder(..) ) import Distribution.Simple.InstallDirs          ( PathTemplate ) import Distribution.Utils.NubList@@ -50,9 +58,11 @@  import Data.Map (Map) import qualified Data.Map as Map+import Data.Set (Set) import Distribution.Compat.Binary (Binary) import Distribution.Compat.Semigroup import GHC.Generics (Generic)+import Data.Typeable   -------------------------------@@ -80,14 +90,15 @@    = ProjectConfig {         -- | Packages in this project, including local dirs, local .cabal files-       -- local and remote tarballs. Where these are file globs, they must-       -- match something.+       -- local and remote tarballs. When these are file globs, they must+       -- match at least one package.        projectPackages              :: [String],         -- | Like 'projectConfigPackageGlobs' but /optional/ in the sense that        -- file globs are allowed to match nothing. The primary use case for        -- this is to be able to say @optional-packages: */@ to automagically-       -- pick up deps that we unpack locally.+       -- pick up deps that we unpack locally without erroring when+       -- there aren't any.        projectPackagesOptional      :: [String],         -- | Packages in this project from remote source repositories.@@ -96,12 +107,18 @@        -- | Packages in this project from hackage repositories.        projectPackagesNamed         :: [Dependency], +       -- See respective types for an explanation of what these+       -- values are about:        projectConfigBuildOnly       :: ProjectConfigBuildOnly,        projectConfigShared          :: ProjectConfigShared,+       projectConfigProvenance      :: Set ProjectConfigProvenance,++       -- | Configuration to be applied to *local* packages; i.e.,+       -- any packages which are explicitly named in `cabal.project`.        projectConfigLocalPackages   :: PackageConfig,        projectConfigSpecificPackage :: MapMappend PackageName PackageConfig      }-  deriving (Eq, Show, Generic)+  deriving (Eq, Show, Generic, Typeable)  -- | That part of the project configuration that only affects /how/ we build -- and not the /value/ of the things we build. This means this information@@ -120,14 +137,13 @@        projectConfigSymlinkBinDir         :: Flag FilePath,        projectConfigOneShot               :: Flag Bool,        projectConfigNumJobs               :: Flag (Maybe Int),+       projectConfigKeepGoing             :: Flag Bool,        projectConfigOfflineMode           :: Flag Bool,        projectConfigKeepTempFiles         :: Flag Bool,        projectConfigHttpTransport         :: Flag String,        projectConfigIgnoreExpiry          :: Flag Bool,        projectConfigCacheDir              :: Flag FilePath,-       projectConfigLogsDir               :: Flag FilePath,-       projectConfigWorldFile             :: Flag FilePath,-       projectConfigRootCmd               :: Flag String+       projectConfigLogsDir               :: Flag FilePath      }   deriving (Eq, Show, Generic) @@ -137,6 +153,8 @@ -- data ProjectConfigShared    = ProjectConfigShared {+       projectConfigDistDir           :: Flag FilePath,+       projectConfigProjectFile       :: Flag FilePath,        projectConfigHcFlavor          :: Flag CompilerFlavor,        projectConfigHcPath            :: Flag FilePath,        projectConfigHcPkg             :: Flag FilePath,@@ -153,20 +171,25 @@        -- configuration used both by the solver and other phases        projectConfigRemoteRepos       :: NubList RemoteRepo,     -- ^ Available Hackage servers.        projectConfigLocalRepos        :: NubList FilePath,+       projectConfigIndexState        :: Flag IndexState,         -- solver configuration        projectConfigConstraints       :: [(UserConstraint, ConstraintSource)],        projectConfigPreferences       :: [Dependency],        projectConfigCabalVersion      :: Flag Version,  --TODO: [required eventually] unused        projectConfigSolver            :: Flag PreSolver,+       projectConfigAllowOlder        :: Maybe AllowOlder,        projectConfigAllowNewer        :: Maybe AllowNewer,        projectConfigMaxBackjumps      :: Flag Int,-       projectConfigReorderGoals      :: Flag Bool,-       projectConfigStrongFlags       :: Flag Bool+       projectConfigReorderGoals      :: Flag ReorderGoals,+       projectConfigCountConflicts    :: Flag CountConflicts,+       projectConfigStrongFlags       :: Flag StrongFlags,+       projectConfigAllowBootLibInstalls :: Flag AllowBootLibInstalls,+       projectConfigPerComponent      :: Flag Bool         -- More things that only make sense for manual mode, not --local mode        -- too much control!-     --projectConfigIndependentGoals  :: Flag Bool,+     --projectConfigIndependentGoals  :: Flag IndependentGoals,      --projectConfigShadowPkgs        :: Flag Bool,      --projectConfigReinstall         :: Flag Bool,      --projectConfigAvoidReinstalls   :: Flag Bool,@@ -176,6 +199,21 @@   deriving (Eq, Show, Generic)  +-- | Specifies the provenance of project configuration, whether defaults were+-- used or if the configuration was read from an explicit file path.+data ProjectConfigProvenance++     -- | The configuration is implicit due to no explicit configuration+     -- being found. See 'Distribution.Client.ProjectConfig.readProjectConfig'+     -- for how implicit configuration is determined.+   = Implicit++     -- | The path the project configuration was explicitly read from.+     -- | The configuration was explicitly read from the specified 'FilePath'.+   | Explicit FilePath+  deriving (Eq, Ord, Show, Generic)++ -- | Project configuration that is specific to each package, that is where we -- can in principle have different values for different packages in the same -- project.@@ -215,6 +253,7 @@        packageConfigHaddockHoogle       :: Flag Bool, --TODO: [required eventually] use this        packageConfigHaddockHtml         :: Flag Bool, --TODO: [required eventually] use this        packageConfigHaddockHtmlLocation :: Flag String, --TODO: [required eventually] use this+       packageConfigHaddockForeignLibs  :: Flag Bool, --TODO: [required eventually] use this        packageConfigHaddockExecutables  :: Flag Bool, --TODO: [required eventually] use this        packageConfigHaddockTestSuites   :: Flag Bool, --TODO: [required eventually] use this        packageConfigHaddockBenchmarks   :: Flag Bool, --TODO: [required eventually] use this@@ -229,13 +268,14 @@ instance Binary ProjectConfig instance Binary ProjectConfigBuildOnly instance Binary ProjectConfigShared+instance Binary ProjectConfigProvenance instance Binary PackageConfig   -- | Newtype wrapper for 'Map' that provides a 'Monoid' instance that takes -- the last value rather than the first value for overlapping keys. newtype MapLast k v = MapLast { getMapLast :: Map k v }-  deriving (Eq, Show, Functor, Generic, Binary)+  deriving (Eq, Show, Functor, Generic, Binary, Typeable)  instance Ord k => Monoid (MapLast k v) where   mempty  = MapLast Map.empty@@ -249,7 +289,7 @@ -- | Newtype wrapper for 'Map' that provides a 'Monoid' instance that -- 'mappend's values of overlapping keys rather than taking the first. newtype MapMappend k v = MapMappend { getMapMappend :: Map k v }-  deriving (Eq, Show, Functor, Generic, Binary)+  deriving (Eq, Show, Functor, Generic, Binary, Typeable)  instance (Semigroup v, Ord k) => Monoid (MapMappend k v) where   mempty  = MapMappend Map.empty@@ -313,20 +353,24 @@        solverSettingFlagAssignments   :: Map PackageName FlagAssignment,        solverSettingCabalVersion      :: Maybe Version,  --TODO: [required eventually] unused        solverSettingSolver            :: PreSolver,+       solverSettingAllowOlder        :: AllowOlder,        solverSettingAllowNewer        :: AllowNewer,        solverSettingMaxBackjumps      :: Maybe Int,-       solverSettingReorderGoals      :: Bool,-       solverSettingStrongFlags       :: Bool+       solverSettingReorderGoals      :: ReorderGoals,+       solverSettingCountConflicts    :: CountConflicts,+       solverSettingStrongFlags       :: StrongFlags,+       solverSettingAllowBootLibInstalls :: AllowBootLibInstalls,+       solverSettingIndexState        :: IndexState        -- Things that only make sense for manual mode, not --local mode        -- too much control!-     --solverSettingIndependentGoals  :: Bool,+     --solverSettingIndependentGoals  :: IndependentGoals,      --solverSettingShadowPkgs        :: Bool,      --solverSettingReinstall         :: Bool,      --solverSettingAvoidReinstalls   :: Bool,      --solverSettingOverrideReinstall :: Bool,      --solverSettingUpgradeDeps       :: Bool      }-  deriving (Eq, Show, Generic)+  deriving (Eq, Show, Generic, Typeable)  instance Binary SolverSettings @@ -354,13 +398,13 @@        buildSettingSymlinkBinDir         :: [FilePath],        buildSettingOneShot               :: Bool,        buildSettingNumJobs               :: Int,+       buildSettingKeepGoing             :: Bool,        buildSettingOfflineMode           :: Bool,        buildSettingKeepTempFiles         :: Bool,        buildSettingRemoteRepos           :: [RemoteRepo],        buildSettingLocalRepos            :: [FilePath],        buildSettingCacheDir              :: FilePath,        buildSettingHttpTransport         :: Maybe String,-       buildSettingIgnoreExpiry          :: Bool,-       buildSettingRootCmd               :: Maybe String+       buildSettingIgnoreExpiry          :: Bool      } 
Distribution/Client/ProjectOrchestration.hs view
@@ -1,4 +1,6 @@+{-# LANGUAGE CPP #-} {-# LANGUAGE RecordWildCards, NamedFieldPuns #-}+{-# LANGUAGE RankNTypes, ScopedTypeVariables #-}  -- | This module deals with building and incrementally rebuilding a collection -- of packages. It is what backs the @cabal build@ and @configure@ commands,@@ -38,152 +40,235 @@ -- 'ElaboratedInstallPlan'. -- module Distribution.Client.ProjectOrchestration (+    -- * Discovery phase: what is in the project?+    establishProjectBaseContext,+    ProjectBaseContext(..),+    BuildTimeSettings(..),+    commandLineFlagsToProjectConfig,+     -- * Pre-build phase: decide what to do.     runProjectPreBuildPhase,-    CliConfigFlags,-    PreBuildHooks(..),     ProjectBuildContext(..), +    -- ** Selecting what targets we mean+    readTargetSelectors,+    reportTargetSelectorProblems,+    resolveTargets,+    TargetsMap,+    TargetSelector(..),+    PackageId,+    AvailableTarget(..),+    AvailableTargetStatus(..),+    TargetRequested(..),+    ComponentName(..),+    ComponentKind(..),+    ComponentTarget(..),+    SubComponentTarget(..),+    TargetProblemCommon(..),+    selectComponentTargetBasic,+    distinctTargetComponents,+    -- ** Utils for selecting targets+    filterTargetsKind,+    filterTargetsKindWith,+    selectBuildableTargets,+    selectBuildableTargetsWith,+    selectBuildableTargets',+    selectBuildableTargetsWith',+    forgetTargetsDetail,+     -- ** Adjusting the plan-    selectTargets,+    pruneInstallPlanToTargets,+    TargetAction(..),+    pruneInstallPlanToDependencies,+    CannotPruneDependencies(..),     printPlan,      -- * Build phase: now do it.     runProjectBuildPhase,      -- * Post build actions-    reportBuildFailures,+    runProjectPostBuildPhase,+    dieOnBuildFailures,++    -- * Shared CLI utils+    cmdCommonHelpTextNewBuildBeta,   ) where  import           Distribution.Client.ProjectConfig import           Distribution.Client.ProjectPlanning+                   hiding ( pruneInstallPlanToTargets )+import qualified Distribution.Client.ProjectPlanning as ProjectPlanning+                   ( pruneInstallPlanToTargets )+import           Distribution.Client.ProjectPlanning.Types import           Distribution.Client.ProjectBuilding+import           Distribution.Client.ProjectPlanOutput  import           Distribution.Client.Types-                   hiding ( BuildResult, BuildSuccess(..), BuildFailure(..)-                          , DocsResult(..), TestsResult(..) )+                   ( GenericReadyPackage(..), UnresolvedSourcePackage ) import qualified Distribution.Client.InstallPlan as InstallPlan-import           Distribution.Client.BuildTarget-                   ( UserBuildTarget, resolveUserBuildTargets-                   , BuildTarget(..), buildTargetPackage )+import           Distribution.Client.TargetSelector+                   ( TargetSelector(..)+                   , ComponentKind(..), componentKind+                   , readTargetSelectors, reportTargetSelectorProblems ) import           Distribution.Client.DistDirLayout import           Distribution.Client.Config (defaultCabalDir) import           Distribution.Client.Setup hiding (packageName) +import           Distribution.Solver.Types.OptionalStanza+ import           Distribution.Package                    hiding (InstalledPackageId, installedPackageId)-import qualified Distribution.PackageDescription as PD-import           Distribution.PackageDescription (FlagAssignment)-import qualified Distribution.InstalledPackageInfo as Installed-import           Distribution.Simple.Setup (HaddockFlags)+import           Distribution.PackageDescription (FlagAssignment, showFlagValue)+import           Distribution.Simple.LocalBuildInfo+                   ( ComponentName(..), pkgComponents )+import qualified Distribution.Simple.Setup as Setup+import           Distribution.Simple.Command (commandShowOptions) -import           Distribution.Simple.Utils (die, notice)+import           Distribution.Simple.Utils+                   ( die'+                   , notice, noticeNoWrap, debugNoWrap ) import           Distribution.Verbosity import           Distribution.Text +import qualified Data.Monoid as Mon import qualified Data.Set as Set import qualified Data.Map as Map import           Data.Map (Map) import           Data.List+import           Data.Maybe import           Data.Either-import           System.Exit (exitFailure)+import           Control.Exception (Exception(..), throwIO, assert)+import           System.Exit (ExitCode(..), exitFailure)+#ifdef MIN_VERSION_unix+import           System.Posix.Signals (sigKILL, sigSEGV)+#endif  --- | Command line configuration flags. These are used to extend\/override the--- project configuration.+-- | This holds the context of a project prior to solving: the content of the+-- @cabal.project@ and all the local package @.cabal@ files. ---type CliConfigFlags = ( GlobalFlags-                      , ConfigFlags, ConfigExFlags-                      , InstallFlags, HaddockFlags )+data ProjectBaseContext = ProjectBaseContext {+       distDirLayout  :: DistDirLayout,+       cabalDirLayout :: CabalDirLayout,+       projectConfig  :: ProjectConfig,+       localPackages  :: [UnresolvedSourcePackage],+       buildSettings  :: BuildTimeSettings+     } --- | Hooks to alter the behaviour of 'runProjectPreBuildPhase'.------ For example the @configure@, @build@ and @repl@ commands use this to get--- their different behaviour.----data PreBuildHooks = PreBuildHooks {-       hookPrePlanning      :: FilePath-                            -> DistDirLayout+establishProjectBaseContext :: Verbosity                             -> ProjectConfig-                            -> IO (),-       hookSelectPlanSubset :: ElaboratedInstallPlan-                            -> IO ElaboratedInstallPlan-     }+                            -> IO ProjectBaseContext+establishProjectBaseContext verbosity cliConfig = do --- | This holds the context between the pre-build and build phases.+    cabalDir <- defaultCabalDir+    projectRoot <- either throwIO return =<<+                   findProjectRoot Nothing mprojectFile++    let cabalDirLayout = defaultCabalDirLayout cabalDir+        distDirLayout  = defaultDistDirLayout projectRoot+                                              mdistDirectory++    (projectConfig, localPackages) <-+      rebuildProjectConfig verbosity+                           distDirLayout+                           cliConfig++    let buildSettings = resolveBuildTimeSettings+                          verbosity cabalDirLayout+                          projectConfig++    return ProjectBaseContext {+      distDirLayout,+      cabalDirLayout,+      projectConfig,+      localPackages,+      buildSettings+    }+  where+    mdistDirectory = Setup.flagToMaybe projectConfigDistDir+    mprojectFile   = Setup.flagToMaybe projectConfigProjectFile+    ProjectConfigShared {+      projectConfigDistDir,+      projectConfigProjectFile+    } = projectConfigShared cliConfig+++-- | This holds the context between the pre-build, build and post-build phases. -- data ProjectBuildContext = ProjectBuildContext {-      distDirLayout    :: DistDirLayout,-      elaboratedPlan   :: ElaboratedInstallPlan,-      elaboratedShared :: ElaboratedSharedConfig,-      pkgsBuildStatus  :: BuildStatusMap,-      buildSettings    :: BuildTimeSettings+      -- | This is the improved plan, before we select a plan subset based on+      -- the build targets, and before we do the dry-run. So this contains+      -- all packages in the project.+      elaboratedPlanOriginal :: ElaboratedInstallPlan,++      -- | This is the 'elaboratedPlanOriginal' after we select a plan subset+      -- and do the dry-run phase to find out what is up-to or out-of date.+      -- This is the plan that will be executed during the build phase. So+      -- this contains only a subset of packages in the project.+      elaboratedPlanToExecute:: ElaboratedInstallPlan,++      -- | The part of the install plan that's shared between all packages in+      -- the plan. This does not change between the two plan variants above,+      -- so there is just the one copy.+      elaboratedShared       :: ElaboratedSharedConfig,++      -- | The result of the dry-run phase. This tells us about each member of+      -- the 'elaboratedPlanToExecute'.+      pkgsBuildStatus        :: BuildStatusMap     }   -- | Pre-build phase: decide what to do. ---runProjectPreBuildPhase :: Verbosity-                        -> CliConfigFlags-                        -> PreBuildHooks-                        -> IO ProjectBuildContext runProjectPreBuildPhase+    :: Verbosity+    -> ProjectBaseContext+    -> (ElaboratedInstallPlan -> IO ElaboratedInstallPlan)+    -> IO ProjectBuildContext+runProjectPreBuildPhase     verbosity-    ( globalFlags-    , configFlags, configExFlags-    , installFlags, haddockFlags )-    PreBuildHooks{..} = do--    cabalDir <- defaultCabalDir-    let cabalDirLayout = defaultCabalDirLayout cabalDir--    projectRootDir <- findProjectRoot-    let distDirLayout = defaultDistDirLayout projectRootDir--    let cliConfig = commandLineFlagsToProjectConfig-                      globalFlags configFlags configExFlags-                      installFlags haddockFlags--    hookPrePlanning-      projectRootDir-      distDirLayout-      cliConfig+    ProjectBaseContext {+      distDirLayout,+      cabalDirLayout,+      projectConfig,+      localPackages+    }+    selectPlanSubset = do      -- Take the project configuration and make a plan for how to build     -- everything in the project. This is independent of any specific targets     -- the user has asked for.     ---    (elaboratedPlan, elaboratedShared, projectConfig) <-+    (elaboratedPlan, _, elaboratedShared) <-       rebuildInstallPlan verbosity-                         projectRootDir distDirLayout cabalDirLayout-                         cliConfig--    let buildSettings = resolveBuildTimeSettings-                          verbosity cabalDirLayout-                          (projectConfigShared    projectConfig)-                          (projectConfigBuildOnly projectConfig)-                          (projectConfigBuildOnly cliConfig)+                         distDirLayout cabalDirLayout+                         projectConfig+                         localPackages      -- The plan for what to do is represented by an 'ElaboratedInstallPlan'      -- Now given the specific targets the user has asked for, decide     -- which bits of the plan we will want to execute.     ---    elaboratedPlan' <- hookSelectPlanSubset elaboratedPlan+    elaboratedPlan' <- selectPlanSubset elaboratedPlan -    -- Check if any packages don't need rebuilding, and improve the plan.+    -- Check which packages need rebuilding.     -- This also gives us more accurate reasons for the --dry-run output.     ---    (elaboratedPlan'', pkgsBuildStatus) <--      rebuildTargetsDryRun distDirLayout-                           elaboratedPlan'+    pkgsBuildStatus <- rebuildTargetsDryRun distDirLayout elaboratedShared+                                            elaboratedPlan' +    -- Improve the plan by marking up-to-date packages as installed.+    --+    let elaboratedPlan'' = improveInstallPlanWithUpToDatePackages+                             pkgsBuildStatus elaboratedPlan'+    debugNoWrap verbosity (InstallPlan.showInstallPlan elaboratedPlan'')+     return ProjectBuildContext {-      distDirLayout,-      elaboratedPlan = elaboratedPlan'',+      elaboratedPlanOriginal = elaboratedPlan,+      elaboratedPlanToExecute = elaboratedPlan'',       elaboratedShared,-      pkgsBuildStatus,-      buildSettings+      pkgsBuildStatus     }  @@ -193,16 +278,70 @@ -- rebuild the various packages needed. -- runProjectBuildPhase :: Verbosity+                     -> ProjectBaseContext                      -> ProjectBuildContext-                     -> IO ElaboratedInstallPlan-runProjectBuildPhase verbosity ProjectBuildContext {..} =+                     -> IO BuildOutcomes+runProjectBuildPhase _ ProjectBaseContext{buildSettings} _+  | buildSettingDryRun buildSettings+  = return Map.empty++runProjectBuildPhase verbosity+                     ProjectBaseContext{..} ProjectBuildContext {..} =+    fmap (Map.union (previousBuildOutcomes pkgsBuildStatus)) $     rebuildTargets verbosity                    distDirLayout-                   elaboratedPlan+                   (cabalStoreDirLayout cabalDirLayout)+                   elaboratedPlanToExecute                    elaboratedShared                    pkgsBuildStatus                    buildSettings+  where+    previousBuildOutcomes :: BuildStatusMap -> BuildOutcomes+    previousBuildOutcomes =+      Map.mapMaybe $ \status -> case status of+        BuildStatusUpToDate buildSuccess -> Just (Right buildSuccess)+        --TODO: [nice to have] record build failures persistently+        _                                  -> Nothing +-- | Post-build phase: various administrative tasks+--+-- Update bits of state based on the build outcomes and report any failures.+--+runProjectPostBuildPhase :: Verbosity+                         -> ProjectBaseContext+                         -> ProjectBuildContext+                         -> BuildOutcomes+                         -> IO ()+runProjectPostBuildPhase _ ProjectBaseContext{buildSettings} _ _+  | buildSettingDryRun buildSettings+  = return ()++runProjectPostBuildPhase verbosity+                         ProjectBaseContext {..} ProjectBuildContext {..}+                         buildOutcomes = do+    -- Update other build artefacts+    -- TODO: currently none, but could include:+    --        - bin symlinks/wrappers+    --        - haddock/hoogle/ctags indexes+    --        - delete stale lib registrations+    --        - delete stale package dirs++    postBuildStatus <- updatePostBuildProjectStatus+                         verbosity+                         distDirLayout+                         elaboratedPlanOriginal+                         pkgsBuildStatus+                         buildOutcomes++    writePlanGhcEnvironment distDirLayout+                            elaboratedPlanOriginal+                            elaboratedShared+                            postBuildStatus++    -- Finally if there were any build failures then report them and throw+    -- an exception to terminate the program+    dieOnBuildFailures verbosity elaboratedPlanToExecute buildOutcomes+     -- Note that it is a deliberate design choice that the 'buildTargets' is     -- not passed to phase 1, and the various bits of input config is not     -- passed to phase 2.@@ -224,25 +363,62 @@ -- Taking targets into account, selecting what to build -- --- | Adjust an 'ElaboratedInstallPlan' by selecting just those parts of it--- required to build the given user targets.+-- | The set of components to build, represented as a mapping from 'UnitId's+-- to the 'ComponentTarget's within the unit that will be selected+-- (e.g. selected to build, test or repl). ----- How to get the 'PackageTarget's from the 'UserBuildTarget' is customisable.+-- Associated with each 'ComponentTarget' is the set of 'TargetSelector's that+-- matched this target. Typically this is exactly one, but in general it is+-- possible to for different selectors to match the same target. This extra+-- information is primarily to help make helpful error messages. ---selectTargets :: PackageTarget-              -> (ComponentTarget -> PackageTarget)-              -> [UserBuildTarget]-              -> ElaboratedInstallPlan-              -> IO ElaboratedInstallPlan-selectTargets targetDefaultComponents targetSpecificComponent-              userBuildTargets installPlan = do--    -- Match the user targets against the available targets. If no targets are-    -- given this uses the package in the current directory, if any.-    ---    buildTargets <- resolveUserBuildTargets localPackages userBuildTargets-    --TODO: [required eventually] report something if there are no targets+type TargetsMap = Map UnitId [(ComponentTarget, [TargetSelector PackageId])] +-- | Given a set of 'TargetSelector's, resolve which 'UnitId's and+-- 'ComponentTarget's they ought to refer to.+--+-- The idea is that every user target identifies one or more roots in the+-- 'ElaboratedInstallPlan', which we will use to determine the closure+-- of what packages need to be built, dropping everything from the plan+-- that is unnecessary. This closure and pruning is done by+-- 'pruneInstallPlanToTargets' and this needs to be told the roots in terms+-- of 'UnitId's and the 'ComponentTarget's within those.+--+-- This means we first need to translate the 'TargetSelector's into the+-- 'UnitId's and 'ComponentTarget's. This translation has to be different for+-- the different command line commands, like @build@, @repl@ etc. For example+-- the command @build pkgfoo@ could select a different set of components in+-- pkgfoo than @repl pkgfoo@. The @build@ command would select any library and+-- all executables, whereas @repl@ would select the library or a single+-- executable. Furthermore, both of these examples could fail, and fail in+-- different ways and each needs to be able to produce helpful error messages.+--+-- So 'resolveTargets' takes two helpers: one to select the targets to be used+-- by user targets that refer to a whole package ('TargetPackage'), and+-- another to check user targets that refer to a component (or a module or+-- file within a component). These helpers can fail, and use their own error+-- type. Both helpers get given the 'AvailableTarget' info about the+-- component(s).+--+-- While commands vary quite a bit in their behaviour about which components to+-- select for a whole-package target, most commands have the same behaviour for+-- checking a user target that refers to a specific component. To help with+-- this commands can use 'selectComponentTargetBasic', either directly or as+-- a basis for their own @selectComponentTarget@ implementation.+--+resolveTargets :: forall err.+                  (forall k. TargetSelector PackageId+                          -> [AvailableTarget k]+                          -> Either err [k])+               -> (forall k. PackageId -> ComponentName -> SubComponentTarget+                          -> AvailableTarget k+                          -> Either err  k )+               -> (TargetProblemCommon -> err)+               -> ElaboratedInstallPlan+               -> [TargetSelector PackageId]+               -> Either [err] TargetsMap+resolveTargets selectPackageTargets selectComponentTarget liftProblem+               installPlan targetSelectors =     --TODO: [required eventually]     -- we cannot resolve names of packages other than those that are     -- directly in the current plan. We ought to keep a set of the known@@ -250,124 +426,192 @@     -- really need that until we can do something sensible with packages     -- outside of the project. -    -- Now check if those targets belong to the current project or not.-    -- Ultimately we want to do something sensible for targets not in this-    -- project, but for now we just bail. This gives us back the ipkgid from-    -- the plan.-    ---    buildTargets' <- either reportBuildTargetProblems return-                   $ resolveAndCheckTargets-                       targetDefaultComponents-                       targetSpecificComponent-                       installPlan-                       buildTargets--    -- Finally, prune the install plan to cover just those target packages-    -- and their deps.-    ---    return (pruneInstallPlanToTargets buildTargets' installPlan)-  where-    localPackages =-      [ (pkgDescription pkg, pkgSourceLocation pkg)-      | InstallPlan.Configured pkg <- InstallPlan.toList installPlan ]-      --TODO: [code cleanup] is there a better way to identify local packages?--+    case partitionEithers+           [ fmap ((,) targetSelector) (checkTarget targetSelector)+           | targetSelector <- targetSelectors ] of+      ([], targets) -> Right+                     . Map.map nubComponentTargets+                     $ Map.fromListWith (++)+                         [ (uid, [(ct, ts)])+                         | (ts, cts) <- targets+                         , (uid, ct) <- cts ] -resolveAndCheckTargets :: PackageTarget-                       -> (ComponentTarget -> PackageTarget)-                       -> ElaboratedInstallPlan-                       -> [BuildTarget PackageName]-                       -> Either [BuildTargetProblem]-                                 (Map InstalledPackageId [PackageTarget])-resolveAndCheckTargets targetDefaultComponents-                       targetSpecificComponent-                       installPlan targets =-    case partitionEithers (map checkTarget targets) of-      ([], targets') -> Right $ Map.fromListWith (++)-                                  [ (ipkgid, [t]) | (ipkgid, t) <- targets' ]-      (problems, _)  -> Left problems+      (problems, _) -> Left problems   where     -- TODO [required eventually] currently all build targets refer to packages     -- inside the project. Ultimately this has to be generalised to allow     -- referring to other packages and targets.+    checkTarget :: TargetSelector PackageId+                -> Either err [(UnitId, ComponentTarget)]      -- We can ask to build any whole package, project-local or a dependency-    checkTarget (BuildTargetPackage pn)-      | Just ipkgid <- Map.lookup pn projAllPkgs-      = Right (ipkgid, targetDefaultComponents)--    -- But if we ask to build an individual component, then that component-    -- had better be in a package that is local to the project.-    -- TODO: and if it's an optional stanza, then that stanza must be available-    checkTarget t@(BuildTargetComponent pn cn)-      | Just ipkgid <- Map.lookup pn projLocalPkgs-      = Right (ipkgid, targetSpecificComponent-                         (ComponentTarget cn WholeComponent))--      | Map.member pn projAllPkgs-      = Left (BuildTargetComponentNotProjectLocal t)+    checkTarget bt@(TargetPackage _ pkgid mkfilter)+      | Just ats <- fmap (maybe id filterTargetsKind mkfilter)+                  $ Map.lookup pkgid availableTargetsByPackage+      = case selectPackageTargets bt ats of+          Left  e  -> Left e+          Right ts -> Right [ (unitid, ComponentTarget cname WholeComponent)+                            | (unitid, cname) <- ts ] -    checkTarget t@(BuildTargetModule pn cn mn)-      | Just ipkgid <- Map.lookup pn projLocalPkgs-      = Right (ipkgid, BuildSpecificComponent (ComponentTarget cn (ModuleTarget mn)))+      | otherwise+      = Left (liftProblem (TargetProblemNoSuchPackage pkgid)) -      | Map.member pn projAllPkgs-      = Left (BuildTargetComponentNotProjectLocal t)+    checkTarget bt@(TargetAllPackages mkfilter) =+      let ats = maybe id filterTargetsKind mkfilter+              $ filter availableTargetLocalToProject+              $ concat (Map.elems availableTargetsByPackage)+       in case selectPackageTargets bt ats of+            Left  e  -> Left e+            Right ts -> Right [ (unitid, ComponentTarget cname WholeComponent)+                              | (unitid, cname) <- ts ] -    checkTarget t@(BuildTargetFile pn cn fn)-      | Just ipkgid <- Map.lookup pn projLocalPkgs-      = Right (ipkgid, BuildSpecificComponent (ComponentTarget cn (FileTarget fn)))+    checkTarget (TargetComponent pkgid cname subtarget)+      | Just ats <- Map.lookup (pkgid, cname) availableTargetsByComponent+      = case partitionEithers+               (map (selectComponentTarget pkgid cname subtarget) ats) of+          (e:_,_) -> Left e+          ([],ts) -> Right [ (unitid, ctarget)+                           | let ctarget = ComponentTarget cname subtarget+                           , (unitid, _) <- ts ] -      | Map.member pn projAllPkgs-      = Left (BuildTargetComponentNotProjectLocal t)+      | Map.member pkgid availableTargetsByPackage+      = Left (liftProblem (TargetProblemNoSuchComponent pkgid cname)) -    checkTarget t-      = Left (BuildTargetNotInProject (buildTargetPackage t))+      | otherwise+      = Left (liftProblem (TargetProblemNoSuchPackage pkgid)) +    --TODO: check if the package is in the plan, even if it's not local+    --TODO: check if the package is in hackage and return different+    -- error cases here so the commands can handle things appropriately -    projAllPkgs, projLocalPkgs :: Map PackageName InstalledPackageId-    projAllPkgs =-      Map.fromList-        [ (packageName pkg, installedPackageId pkg)-        | pkg <- InstallPlan.toList installPlan ]+    availableTargetsByPackage   :: Map PackageId                  [AvailableTarget (UnitId, ComponentName)]+    availableTargetsByComponent :: Map (PackageId, ComponentName) [AvailableTarget (UnitId, ComponentName)]+    availableTargetsByComponent = availableTargets installPlan+    availableTargetsByPackage   = Map.mapKeysWith+                                    (++) (\(pkgid, _cname) -> pkgid)+                                    availableTargetsByComponent+                      `Map.union` availableTargetsEmptyPackages -    projLocalPkgs =+    -- Add in all the empty packages. These do not appear in the+    -- availableTargetsByComponent map, since that only contains components+    -- so packages with no components are invisible from that perspective.+    -- The empty packages need to be there for proper error reporting, so users+    -- can select the empty package and then we can report that it is empty,+    -- otherwise we falsely report there is no such package at all.+    availableTargetsEmptyPackages =       Map.fromList-        [ (packageName pkg, installedPackageId pkg)+        [ (packageId pkg, [])         | InstallPlan.Configured pkg <- InstallPlan.toList installPlan-        , case pkgSourceLocation pkg of-            LocalUnpackedPackage _ -> True; _ -> False-          --TODO: [code cleanup] is there a better way to identify local packages?+        , case elabPkgOrComp pkg of+            ElabComponent _ -> False+            ElabPackage   _ -> null (pkgComponents (elabPkgDescription pkg))         ]      --TODO: [research required] what if the solution has multiple versions of this package?     --      e.g. due to setup deps or due to multiple independent sets of     --      packages being built (e.g. ghc + ghcjs in a project) -data BuildTargetProblem-   = BuildTargetNotInProject PackageName-   | BuildTargetComponentNotProjectLocal (BuildTarget PackageName)-   | BuildTargetOptionalStanzaDisabled Bool-      -- ^ @True@: explicitly disabled by user-      -- @False@: disabled by solver+filterTargetsKind :: ComponentKind -> [AvailableTarget k] -> [AvailableTarget k]+filterTargetsKind ckind = filterTargetsKindWith (== ckind) -reportBuildTargetProblems :: [BuildTargetProblem] -> IO a-reportBuildTargetProblems = die . unlines . map reportBuildTargetProblem+filterTargetsKindWith :: (ComponentKind -> Bool)+                     -> [AvailableTarget k] -> [AvailableTarget k]+filterTargetsKindWith p ts =+    [ t | t@(AvailableTarget _ cname _ _) <- ts+        , p (componentKind cname) ] -reportBuildTargetProblem :: BuildTargetProblem -> String-reportBuildTargetProblem (BuildTargetNotInProject pn) =-        "Cannot build the package " ++ display pn ++ ", it is not in this project."-     ++ "(either directly or indirectly). If you want to add it to the "-     ++ "project then edit the cabal.project file."+selectBuildableTargets :: [AvailableTarget k] -> [k]+selectBuildableTargets ts =+    [ k | AvailableTarget _ _ (TargetBuildable k _) _ <- ts ] -reportBuildTargetProblem (BuildTargetComponentNotProjectLocal t) =-        "The package " ++ display (buildTargetPackage t) ++ " is in the "-     ++ "project but it is not a locally unpacked package, so  "+selectBuildableTargetsWith :: (TargetRequested -> Bool)+                          -> [AvailableTarget k] -> [k]+selectBuildableTargetsWith p ts =+    [ k | AvailableTarget _ _ (TargetBuildable k req) _ <- ts, p req ] -reportBuildTargetProblem (BuildTargetOptionalStanzaDisabled _) = undefined+selectBuildableTargets' :: [AvailableTarget k] -> ([k], [AvailableTarget ()])+selectBuildableTargets' ts =+    (,) [ k | AvailableTarget _ _ (TargetBuildable k _) _ <- ts ]+        [ forgetTargetDetail t+        | t@(AvailableTarget _ _ (TargetBuildable _ _) _) <- ts ] +selectBuildableTargetsWith' :: (TargetRequested -> Bool)+                           -> [AvailableTarget k] -> ([k], [AvailableTarget ()])+selectBuildableTargetsWith' p ts =+    (,) [ k | AvailableTarget _ _ (TargetBuildable k req) _ <- ts, p req ]+        [ forgetTargetDetail t+        | t@(AvailableTarget _ _ (TargetBuildable _ req) _) <- ts, p req ] ++forgetTargetDetail :: AvailableTarget k -> AvailableTarget ()+forgetTargetDetail = fmap (const ())++forgetTargetsDetail :: [AvailableTarget k] -> [AvailableTarget ()]+forgetTargetsDetail = map forgetTargetDetail++-- | A basic @selectComponentTarget@ implementation to use or pass to+-- 'resolveTargets', that does the basic checks that the component is+-- buildable and isn't a test suite or benchmark that is disabled. This+-- can also be used to do these basic checks as part of a custom impl that+--+selectComponentTargetBasic :: PackageId+                           -> ComponentName+                           -> SubComponentTarget+                           -> AvailableTarget k+                           -> Either TargetProblemCommon k+selectComponentTargetBasic pkgid cname subtarget AvailableTarget {..} =+    case availableTargetStatus of+      TargetDisabledByUser ->+        Left (TargetOptionalStanzaDisabledByUser pkgid cname subtarget)++      TargetDisabledBySolver ->+        Left (TargetOptionalStanzaDisabledBySolver pkgid cname subtarget)++      TargetNotLocal ->+        Left (TargetComponentNotProjectLocal pkgid cname subtarget)++      TargetNotBuildable ->+        Left (TargetComponentNotBuildable pkgid cname subtarget)++      TargetBuildable targetKey _ ->+        Right targetKey++data TargetProblemCommon+   = TargetNotInProject                   PackageName+   | TargetComponentNotProjectLocal       PackageId ComponentName SubComponentTarget+   | TargetComponentNotBuildable          PackageId ComponentName SubComponentTarget+   | TargetOptionalStanzaDisabledByUser   PackageId ComponentName SubComponentTarget+   | TargetOptionalStanzaDisabledBySolver PackageId ComponentName SubComponentTarget++    -- The target matching stuff only returns packages local to the project,+    -- so these lookups should never fail, but if 'resolveTargets' is called+    -- directly then of course it can.+   | TargetProblemNoSuchPackage           PackageId+   | TargetProblemNoSuchComponent         PackageId ComponentName+  deriving (Eq, Show)++-- | Wrapper around 'ProjectPlanning.pruneInstallPlanToTargets' that adjusts+-- for the extra unneeded info in the 'TargetsMap'.+--+pruneInstallPlanToTargets :: TargetAction -> TargetsMap+                          -> ElaboratedInstallPlan -> ElaboratedInstallPlan+pruneInstallPlanToTargets targetActionType targetsMap elaboratedPlan =+    assert (Map.size targetsMap > 0) $+    ProjectPlanning.pruneInstallPlanToTargets+      targetActionType+      (Map.map (map fst) targetsMap)+      elaboratedPlan++-- | Utility used by repl and run to check if the targets spans multiple+-- components, since those commands do not support multiple components.+--+distinctTargetComponents :: TargetsMap -> Set.Set (UnitId, ComponentName)+distinctTargetComponents targetsMap =+    Set.fromList [ (uid, cname)+                 | (uid, cts) <- Map.toList targetsMap+                 , (ComponentTarget cname _, _) <- cts ]++ ------------------------------------------------------------------------------ -- Displaying what we plan to do --@@ -375,46 +619,70 @@ -- | Print a user-oriented presentation of the install plan, indicating what -- will be built. ---printPlan :: Verbosity -> ProjectBuildContext -> IO ()+printPlan :: Verbosity+          -> ProjectBaseContext+          -> ProjectBuildContext+          -> IO () printPlan verbosity-          ProjectBuildContext {-            elaboratedPlan,-            pkgsBuildStatus,+          ProjectBaseContext {             buildSettings = BuildTimeSettings{buildSettingDryRun}           }+          ProjectBuildContext {+            elaboratedPlanToExecute = elaboratedPlan,+            elaboratedShared,+            pkgsBuildStatus+          }    | null pkgs   = notice verbosity "Up to date" -  | verbosity >= verbose-  = notice verbosity $ unlines $-      ("In order, the following " ++ wouldWill ++ " be built:")+  | otherwise+  = noticeNoWrap verbosity $ unlines $+      ("In order, the following " ++ wouldWill ++ " be built" +++      ifNormal " (use -v for more details)" ++ ":")     : map showPkgAndReason pkgs -  | otherwise-  = notice verbosity $ unlines $-      ("In order, the following " ++ wouldWill-       ++ " be built (use -v for more details):")-    : map showPkg pkgs   where-    pkgs = linearizeInstallPlan elaboratedPlan+    pkgs = InstallPlan.executionOrder elaboratedPlan +    ifVerbose s | verbosity >= verbose = s+                | otherwise            = ""++    ifNormal s | verbosity >= verbose = ""+               | otherwise            = s+     wouldWill | buildSettingDryRun = "would"               | otherwise          = "will" -    showPkg pkg = display (packageId pkg)-     showPkgAndReason :: ElaboratedReadyPackage -> String-    showPkgAndReason (ReadyPackage pkg _) =-      display (packageId pkg) ++-      showTargets pkg ++-      showFlagAssignment (nonDefaultFlags pkg) ++-      showStanzas pkg ++-      let buildStatus = pkgsBuildStatus Map.! installedPackageId pkg in+    showPkgAndReason (ReadyPackage elab) =+      " - " +++      (if verbosity >= deafening+        then display (installedUnitId elab)+        else display (packageId elab)+        ) +++      (case elabPkgOrComp elab of+          ElabPackage pkg -> showTargets elab ++ ifVerbose (showStanzas pkg)+          ElabComponent comp ->+            " (" ++ showComp elab comp ++ ")"+            ) +++      showFlagAssignment (nonDefaultFlags elab) +++      showConfigureFlags elab +++      let buildStatus = pkgsBuildStatus Map.! installedUnitId elab in       " (" ++ showBuildStatus buildStatus ++ ")" +    showComp elab comp =+        maybe "custom" display (compComponentName comp) +++        if Map.null (elabInstantiatedWith elab)+            then ""+            else " with " +++                intercalate ", "+                    -- TODO: Abbreviate the UnitIds+                    [ display k ++ "=" ++ display v+                    | (k,v) <- Map.toList (elabInstantiatedWith elab) ]+     nonDefaultFlags :: ElaboratedConfiguredPackage -> FlagAssignment-    nonDefaultFlags pkg = pkgFlagAssignment pkg \\ pkgFlagDefaults pkg+    nonDefaultFlags elab = elabFlagAssignment elab \\ elabFlagDefaults elab      showStanzas pkg = concat                     $ [ " *test"@@ -422,21 +690,53 @@                    ++ [ " *bench"                       | BenchStanzas `Set.member` pkgStanzasEnabled pkg ] -    showTargets pkg-      | null (pkgBuildTargets pkg) = ""+    showTargets elab+      | null (elabBuildTargets elab) = ""       | otherwise-      = " (" ++ unwords [ showComponentTarget pkg t | t <- pkgBuildTargets pkg ]+      = " (" ++ intercalate ", " [ showComponentTarget (packageId elab) t | t <- elabBuildTargets elab ]              ++ ")" -    -- TODO: [code cleanup] this should be a proper function in a proper place     showFlagAssignment :: FlagAssignment -> String     showFlagAssignment = concatMap ((' ' :) . showFlagValue)-    showFlagValue (f, True)   = '+' : showFlagName f-    showFlagValue (f, False)  = '-' : showFlagName f-    showFlagName (PD.FlagName f) = f +    showConfigureFlags elab =+        let fullConfigureFlags+              = setupHsConfigureFlags+                    (ReadyPackage elab)+                    elaboratedShared+                    verbosity+                    "$builddir"+            -- | Given a default value @x@ for a flag, nub @Flag x@+            -- into @NoFlag@.  This gives us a tidier command line+            -- rendering.+            nubFlag :: Eq a => a -> Setup.Flag a -> Setup.Flag a+            nubFlag x (Setup.Flag x') | x == x' = Setup.NoFlag+            nubFlag _ f = f+            -- TODO: Closely logic from 'configureProfiling'.+            tryExeProfiling = Setup.fromFlagOrDefault False+                                (configProf fullConfigureFlags)+            tryLibProfiling = Setup.fromFlagOrDefault False+                                (Mon.mappend (configProf    fullConfigureFlags)+                                             (configProfExe fullConfigureFlags))+            partialConfigureFlags+              = Mon.mempty {+                configProf    =+                    nubFlag False (configProf fullConfigureFlags),+                configProfExe =+                    nubFlag tryExeProfiling (configProfExe fullConfigureFlags),+                configProfLib =+                    nubFlag tryLibProfiling (configProfLib fullConfigureFlags)+                -- Maybe there are more we can add+              }+        -- Not necessary to "escape" it, it's just for user output+        in unwords . ("":) $+            commandShowOptions+            (Setup.configureCommand (pkgConfigCompilerProgs elaboratedShared))+            partialConfigureFlags+     showBuildStatus status = case status of-      BuildStatusPreExisting -> "already installed"+      BuildStatusPreExisting -> "existing package"+      BuildStatusInstalled   -> "already installed"       BuildStatusDownload {} -> "requires download & build"       BuildStatusUnpack   {} -> "requires build"       BuildStatusRebuild _ rebuild -> case rebuild of@@ -446,48 +746,237 @@         BuildStatusBuild _ buildreason -> case buildreason of           BuildReasonDepsRebuilt      -> "dependency rebuilt"           BuildReasonFilesChanged-            (MonitoredFileChanged _)  -> "files changed"-          BuildReasonFilesChanged             mreason                   -> showMonitorChangedReason mreason           BuildReasonExtraTargets _   -> "additional components to build"           BuildReasonEphemeralTargets -> "ephemeral targets"       BuildStatusUpToDate {} -> "up to date" -- doesn't happen -    showMonitorChangedReason (MonitoredFileChanged file) = "file " ++ file+    showMonitorChangedReason (MonitoredFileChanged file) = "file " ++ file ++ " changed"     showMonitorChangedReason (MonitoredValueChanged _)   = "value changed"     showMonitorChangedReason  MonitorFirstRun     = "first run"     showMonitorChangedReason  MonitorCorruptCache = "cannot read state cache" -linearizeInstallPlan :: ElaboratedInstallPlan -> [ElaboratedReadyPackage]-linearizeInstallPlan =-    unfoldr next++-- | If there are build failures then report them and throw an exception.+--+dieOnBuildFailures :: Verbosity+                   -> ElaboratedInstallPlan -> BuildOutcomes -> IO ()+dieOnBuildFailures verbosity plan buildOutcomes+  | null failures = return ()++  | isSimpleCase  = exitFailure++  | otherwise = do+      -- For failures where we have a build log, print the log plus a header+       sequence_+         [ do notice verbosity $+                '\n' : renderFailureDetail False pkg reason+                    ++ "\nBuild log ( " ++ logfile ++ " ):"+              readFile logfile >>= noticeNoWrap verbosity+         | (pkg, ShowBuildSummaryAndLog reason logfile)+             <- failuresClassification+         ]++       -- For all failures, print either a short summary (if we showed the+       -- build log) or all details+       die' verbosity $ unlines+         [ case failureClassification of+             ShowBuildSummaryAndLog reason _+               | verbosity > normal+              -> renderFailureDetail mentionDepOf pkg reason++               | otherwise+              -> renderFailureSummary mentionDepOf pkg reason+              ++ ". See the build log above for details."++             ShowBuildSummaryOnly reason ->+               renderFailureDetail mentionDepOf pkg reason++         | let mentionDepOf = verbosity <= normal+         , (pkg, failureClassification) <- failuresClassification ]   where-    next plan = case InstallPlan.ready plan of-      []      -> Nothing-      (pkg:_) -> Just (pkg, plan')+    failures =  [ (pkgid, failure)+                | (pkgid, Left failure) <- Map.toList buildOutcomes ]++    failuresClassification =+      [ (pkg, classifyBuildFailure failure)+      | (pkgid, failure) <- failures+      , case buildFailureReason failure of+          DependentFailed {} -> verbosity > normal+          _                  -> True+      , InstallPlan.Configured pkg <-+           maybeToList (InstallPlan.lookup plan pkgid)+      ]++    classifyBuildFailure :: BuildFailure -> BuildFailurePresentation+    classifyBuildFailure BuildFailure {+                           buildFailureReason  = reason,+                           buildFailureLogFile = mlogfile+                         } =+      maybe (ShowBuildSummaryOnly   reason)+            (ShowBuildSummaryAndLog reason) $ do+        logfile <- mlogfile+        e       <- buildFailureException reason+        ExitFailure 1 <- fromException e+        return logfile++    -- Special case: we don't want to report anything complicated in the case+    -- of just doing build on the current package, since it's clear from+    -- context which package failed.+    --+    -- We generalise this rule as follows:+    --  - if only one failure occurs, and it is in a single root package (ie a+    --    package with nothing else depending on it)+    --  - and that failure is of a kind that always reports enough detail+    --    itself (e.g. ghc reporting errors on stdout)+    --  - then we do not report additional error detail or context.+    --+    isSimpleCase+      | [(pkgid, failure)] <- failures+      , [pkg]              <- rootpkgs+      , installedUnitId pkg == pkgid+      , isFailureSelfExplanatory (buildFailureReason failure)+      = True+      | otherwise+      = False++    -- NB: if the Setup script segfaulted or was interrupted,+    -- we should give more detailed information.  So only+    -- assume that exit code 1 is "pedestrian failure."+    isFailureSelfExplanatory (BuildFailed e)+      | Just (ExitFailure 1) <- fromException e = True++    isFailureSelfExplanatory (ConfigureFailed e)+      | Just (ExitFailure 1) <- fromException e = True++    isFailureSelfExplanatory _                  = False++    rootpkgs =+      [ pkg+      | InstallPlan.Configured pkg <- InstallPlan.toList plan+      , hasNoDependents pkg ]++    ultimateDeps pkgid =+        filter (\pkg -> hasNoDependents pkg && installedUnitId pkg /= pkgid)+               (InstallPlan.reverseDependencyClosure plan [pkgid])++    hasNoDependents :: HasUnitId pkg => pkg -> Bool+    hasNoDependents = null . InstallPlan.revDirectDeps plan . installedUnitId++    renderFailureDetail mentionDepOf pkg reason =+        renderFailureSummary mentionDepOf pkg reason ++ "."+     ++ renderFailureExtraDetail reason+     ++ maybe "" showException (buildFailureException reason)++    renderFailureSummary mentionDepOf pkg reason =+        case reason of+          DownloadFailed  _ -> "Failed to download " ++ pkgstr+          UnpackFailed    _ -> "Failed to unpack "   ++ pkgstr+          ConfigureFailed _ -> "Failed to build "    ++ pkgstr+          BuildFailed     _ -> "Failed to build "    ++ pkgstr+          ReplFailed      _ -> "repl failed for "    ++ pkgstr+          HaddocksFailed  _ -> "Failed to build documentation for " ++ pkgstr+          TestsFailed     _ -> "Tests failed for " ++ pkgstr+          InstallFailed   _ -> "Failed to build "  ++ pkgstr+          DependentFailed depid+                            -> "Failed to build " ++ display (packageId pkg)+                            ++ " because it depends on " ++ display depid+                            ++ " which itself failed to build"+      where+        pkgstr = elabConfiguredName verbosity pkg+              ++ if mentionDepOf+                   then renderDependencyOf (installedUnitId pkg)+                   else ""++    renderFailureExtraDetail reason =+      case reason of+        ConfigureFailed _ -> " The failure occurred during the configure step."+        InstallFailed   _ -> " The failure occurred during the final install step."+        _                 -> ""++    renderDependencyOf pkgid =+      case ultimateDeps pkgid of+        []         -> ""+        (p1:[])    -> " (which is required by " ++ elabPlanPackageName verbosity p1 ++ ")"+        (p1:p2:[]) -> " (which is required by " ++ elabPlanPackageName verbosity p1+                                     ++ " and " ++ elabPlanPackageName verbosity p2 ++ ")"+        (p1:p2:_)  -> " (which is required by " ++ elabPlanPackageName verbosity p1+                                        ++ ", " ++ elabPlanPackageName verbosity p2+                                        ++ " and others)"++    showException e = case fromException e of+      Just (ExitFailure 1) -> ""++#ifdef MIN_VERSION_unix+      -- Note [Positive "signal" exit code]+      -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+      -- What's the business with the test for negative and positive+      -- signal values?  The API for process specifies that if the+      -- process died due to a signal, it returns a *negative* exit+      -- code.  So that's the negative test.+      --+      -- What about the positive test?  Well, when we find out that+      -- a process died due to a signal, we ourselves exit with that+      -- exit code.  However, we don't "kill ourselves" with the+      -- signal; we just exit with the same code as the signal: thus+      -- the caller sees a *positive* exit code.  So that's what+      -- happens when we get a positive exit code.+      Just (ExitFailure n)+        | -n == fromIntegral sigSEGV ->+            " The build process segfaulted (i.e. SIGSEGV)."++        |  n == fromIntegral sigSEGV ->+            " The build process terminated with exit code " ++ show n+         ++ " which may be because some part of it segfaulted. (i.e. SIGSEGV)."++        | -n == fromIntegral sigKILL ->+            " The build process was killed (i.e. SIGKILL). " ++ explanation++        |  n == fromIntegral sigKILL ->+            " The build process terminated with exit code " ++ show n+         ++ " which may be because some part of it was killed "+         ++ "(i.e. SIGKILL). " ++ explanation         where-          ipkgid = installedPackageId pkg-          ipkg   = Installed.emptyInstalledPackageInfo {-                     Installed.sourcePackageId    = packageId pkg,-                     Installed.installedUnitId = ipkgid-                   }-          plan'  = InstallPlan.completed ipkgid (Just ipkg)-                     (BuildOk DocsNotTried TestsNotTried)-                     (InstallPlan.processing [pkg] plan)-    --TODO: [code cleanup] This is a bit of a hack, pretending that each package is installed-    -- could we use InstallPlan.topologicalOrder?+          explanation = "The typical reason for this is that there is not "+                     ++ "enough memory available (e.g. the OS killed a process "+                     ++ "using lots of memory)."+#endif+      Just (ExitFailure n) ->+        " The build process terminated with exit code " ++ show n +      _ -> " The exception was:\n  "+#if MIN_VERSION_base(4,8,0)+             ++ displayException e+#else+             ++ show e+#endif -reportBuildFailures :: ElaboratedInstallPlan -> IO ()-reportBuildFailures plan =+    buildFailureException reason =+      case reason of+        DownloadFailed  e -> Just e+        UnpackFailed    e -> Just e+        ConfigureFailed e -> Just e+        BuildFailed     e -> Just e+        ReplFailed      e -> Just e+        HaddocksFailed  e -> Just e+        TestsFailed     e -> Just e+        InstallFailed   e -> Just e+        DependentFailed _ -> Nothing -  case [ (pkg, reason)-       | InstallPlan.Failed pkg reason <- InstallPlan.toList plan ] of-    []      -> return ()-    _failed -> exitFailure-    --TODO: [required eventually] see the old printBuildFailures for an example-    -- of the kind of things we could report, but we want to handle the special-    -- case of the current package better, since if you do "cabal build" then-    -- you don't need a lot of context to explain where the ghc error message-    -- comes from, and indeed extra noise would just be annoying.+data BuildFailurePresentation =+       ShowBuildSummaryOnly   BuildFailureReason+     | ShowBuildSummaryAndLog BuildFailureReason FilePath+++cmdCommonHelpTextNewBuildBeta :: String+cmdCommonHelpTextNewBuildBeta =+    "Note: this command is part of the new project-based system (aka "+ ++ "nix-style\nlocal builds). These features are currently in beta. "+ ++ "Please see\n"+ ++ "http://cabal.readthedocs.io/en/latest/nix-local-build-overview.html "+ ++ "for\ndetails and advice on what you can expect to work. If you "+ ++ "encounter problems\nplease file issues at "+ ++ "https://github.com/haskell/cabal/issues and if you\nhave any time "+ ++ "to get involved and help with testing, fixing bugs etc then\nthat "+ ++ "is very much appreciated.\n" 
Distribution/Client/ProjectPlanOutput.hs view
@@ -2,31 +2,65 @@              DeriveGeneric, DeriveDataTypeable, GeneralizedNewtypeDeriving,              ScopedTypeVariables #-} --- | An experimental new UI for cabal for working with multiple packages------------------------------------------------------------------------------ module Distribution.Client.ProjectPlanOutput (+    -- * Plan output     writePlanExternalRepresentation,++    -- * Project status+    -- | Several outputs rely on having a general overview of+    PostBuildProjectStatus(..),+    updatePostBuildProjectStatus,+    writePlanGhcEnvironment,   ) where  import           Distribution.Client.ProjectPlanning.Types-                   ( ElaboratedInstallPlan, ElaboratedConfiguredPackage(..)-                   , ElaboratedSharedConfig(..) )+import           Distribution.Client.ProjectBuilding.Types import           Distribution.Client.DistDirLayout+import           Distribution.Client.Types (confInstId)+import           Distribution.Client.PackageHash (showHashValue)  import qualified Distribution.Client.InstallPlan as InstallPlan import qualified Distribution.Client.Utils.Json as J-import qualified Distribution.Client.ComponentDeps as ComponentDeps+import qualified Distribution.Simple.InstallDirs as InstallDirs +import qualified Distribution.Solver.Types.ComponentDeps as ComponentDeps+ import           Distribution.Package+import           Distribution.System+import           Distribution.InstalledPackageInfo (InstalledPackageInfo) import qualified Distribution.PackageDescription as PD+import           Distribution.Compiler (CompilerFlavor(GHC))+import           Distribution.Simple.Compiler+                   ( PackageDBStack, PackageDB(..)+                   , compilerVersion, compilerFlavor, showCompilerId )+import           Distribution.Simple.GHC+                   ( getImplInfo, GhcImplInfo(supportsPkgEnvFiles)+                   , GhcEnvironmentFileEntry(..), simpleGhcEnvironmentFile+                   , writeGhcEnvironmentFile ) import           Distribution.Text+import qualified Distribution.Compat.Graph as Graph+import           Distribution.Compat.Graph (Graph, Node)+import qualified Distribution.Compat.Binary as Binary import           Distribution.Simple.Utils+import           Distribution.Verbosity import qualified Paths_cabal_install as Our (version) +import           Data.Maybe (maybeToList, fromMaybe) import           Data.Monoid+import qualified Data.Map as Map+import           Data.Set (Set)+import qualified Data.Set as Set+import qualified Data.ByteString.Lazy as BS import qualified Data.ByteString.Builder as BB +import           System.FilePath+import           System.IO ++-----------------------------------------------------------------------------+-- Writing plan.json files+--+ -- | Write out a representation of the elaborated install plan. -- -- This is for the benefit of debugging and external tools like editors.@@ -40,61 +74,736 @@     writeFileAtomic (distProjectCacheFile distDirLayout "plan.json") $         BB.toLazyByteString       . J.encodeToBuilder-      $ encodePlanAsJson elaboratedInstallPlan elaboratedSharedConfig+      $ encodePlanAsJson distDirLayout elaboratedInstallPlan elaboratedSharedConfig  -- | Renders a subset of the elaborated install plan in a semi-stable JSON -- format. ---encodePlanAsJson :: ElaboratedInstallPlan -> ElaboratedSharedConfig -> J.Value-encodePlanAsJson elaboratedInstallPlan _elaboratedSharedConfig =+encodePlanAsJson :: DistDirLayout -> ElaboratedInstallPlan -> ElaboratedSharedConfig -> J.Value+encodePlanAsJson distDirLayout elaboratedInstallPlan elaboratedSharedConfig =     --TODO: [nice to have] include all of the sharedPackageConfig and all of     --      the parts of the elaboratedInstallPlan     J.object [ "cabal-version"     J..= jdisplay Our.version              , "cabal-lib-version" J..= jdisplay cabalVersion-             , "install-plan"      J..= jsonIPlan+             , "compiler-id"       J..= (J.String . showCompilerId . pkgConfigCompiler)+                                        elaboratedSharedConfig+             , "os"                J..= jdisplay os+             , "arch"              J..= jdisplay arch+             , "install-plan"      J..= installPlanToJ elaboratedInstallPlan              ]   where-    jsonIPlan = map toJ (InstallPlan.toList elaboratedInstallPlan)+    Platform arch os = pkgConfigPlatform elaboratedSharedConfig -    -- ipi :: InstalledPackageInfo-    toJ (InstallPlan.PreExisting ipi) =-      -- installed packages currently lack configuration information-      -- such as their flag settings or non-lib components.+    installPlanToJ :: ElaboratedInstallPlan -> [J.Value]+    installPlanToJ = map planPackageToJ . InstallPlan.toList++    planPackageToJ :: ElaboratedPlanPackage -> J.Value+    planPackageToJ pkg =+      case pkg of+        InstallPlan.PreExisting ipi -> installedPackageInfoToJ ipi+        InstallPlan.Configured elab -> elaboratedPackageToJ False elab+        InstallPlan.Installed  elab -> elaboratedPackageToJ True  elab+        -- Note that the plan.json currently only uses the elaborated plan,+        -- not the improved plan. So we will not get the Installed state for+        -- that case, but the code supports it in case we want to use this+        -- later in some use case where we want the status of the build.++    installedPackageInfoToJ :: InstalledPackageInfo -> J.Value+    installedPackageInfoToJ ipi =+      -- Pre-existing packages lack configuration information such as their flag+      -- settings or non-lib components. We only get pre-existing packages for+      -- the global/core packages however, so this isn't generally a problem.+      -- So these packages are never local to the project.       ---      -- TODO: how to find out whether package is "local"?       J.object         [ "type"       J..= J.String "pre-existing"-        , "id"         J..= jdisplay (installedUnitId ipi)-        , "components" J..= J.object-          [ "lib" J..= J.object [ "depends" J..= map jdisplay (installedDepends ipi) ] ]+        , "id"         J..= (jdisplay . installedUnitId) ipi+        , "pkg-name"   J..= (jdisplay . pkgName . packageId) ipi+        , "pkg-version" J..= (jdisplay . pkgVersion . packageId) ipi+        , "depends"    J..= map jdisplay (installedDepends ipi)         ] -    -- ecp :: ElaboratedConfiguredPackage-    toJ (InstallPlan.Configured ecp) =-      J.object-        [ "type"       J..= J.String "configured"-        , "id"         J..= (jdisplay . installedUnitId) ecp-        , "components" J..= components-        , "flags"      J..= J.object [ fn J..= v-                                     | (PD.FlagName fn,v) <- pkgFlagAssignment ecp ]-        ]-      where-        components = J.object-          [ comp2str c J..= J.object-            [ "depends" J..= map (jdisplay . installedUnitId) v ]-          | (c,v) <- ComponentDeps.toList (pkgDependencies ecp) ]+    elaboratedPackageToJ :: Bool -> ElaboratedConfiguredPackage -> J.Value+    elaboratedPackageToJ isInstalled elab =+      J.object $+        [ "type"       J..= J.String (if isInstalled then "installed"+                                                     else "configured")+        , "id"         J..= (jdisplay . installedUnitId) elab+        , "pkg-name"   J..= (jdisplay . pkgName . packageId) elab+        , "pkg-version" J..= (jdisplay . pkgVersion . packageId) elab+        , "flags"      J..= J.object [ PD.unFlagName fn J..= v+                                     | (fn,v) <- elabFlagAssignment elab ]+        , "style"      J..= J.String (style2str (elabLocalToProject elab) (elabBuildStyle elab))+        ] +++        [ "pkg-src-sha256" J..= J.String (showHashValue hash)+        | Just hash <- [elabPkgSourceHash elab] ] +++        (case elabBuildStyle elab of+            BuildInplaceOnly ->+                ["dist-dir"   J..= J.String dist_dir]+            BuildAndInstall ->+                -- TODO: install dirs?+                []+            ) +++        case elabPkgOrComp elab of+          ElabPackage pkg ->+            let components = J.object $+                  [ comp2str c J..= (J.object $+                    [ "depends"     J..= map (jdisplay . confInstId) ldeps+                    , "exe-depends" J..= map (jdisplay . confInstId) edeps ] +++                    bin_file c)+                  | (c,(ldeps,edeps))+                      <- ComponentDeps.toList $+                         ComponentDeps.zip (pkgLibDependencies pkg)+                                           (pkgExeDependencies pkg) ]+            in ["components" J..= components]+          ElabComponent comp ->+            ["depends"     J..= map (jdisplay . confInstId) (elabLibDependencies elab)+            ,"exe-depends" J..= map jdisplay (elabExeDependencies elab)+            ,"component-name" J..= J.String (comp2str (compSolverName comp))+            ] +++            bin_file (compSolverName comp)+     where+      dist_dir = distBuildDirectory distDirLayout+                    (elabDistDirParams elaboratedSharedConfig elab) -    toJ _ = error "encodePlanToJson: only expecting PreExisting and Configured"+      bin_file c = case c of+        ComponentDeps.ComponentExe s   -> bin_file' s+        ComponentDeps.ComponentTest s  -> bin_file' s+        ComponentDeps.ComponentBench s -> bin_file' s+        _ -> []+      bin_file' s =+        ["bin-file" J..= J.String bin]+       where+        bin = if elabBuildStyle elab == BuildInplaceOnly+               then dist_dir </> "build" </> display s </> display s+               else InstallDirs.bindir (elabInstallDirs elab) </> display s      -- TODO: maybe move this helper to "ComponentDeps" module?     --       Or maybe define a 'Text' instance?+    comp2str :: ComponentDeps.Component -> String     comp2str c = case c of         ComponentDeps.ComponentLib     -> "lib"-        ComponentDeps.ComponentExe s   -> "exe:"   <> s-        ComponentDeps.ComponentTest s  -> "test:"  <> s-        ComponentDeps.ComponentBench s -> "bench:" <> s+        ComponentDeps.ComponentSubLib s -> "lib:"   <> display s+        ComponentDeps.ComponentFLib s  -> "flib:"  <> display s+        ComponentDeps.ComponentExe s   -> "exe:"   <> display s+        ComponentDeps.ComponentTest s  -> "test:"  <> display s+        ComponentDeps.ComponentBench s -> "bench:" <> display s         ComponentDeps.ComponentSetup   -> "setup" +    style2str :: Bool -> BuildStyle -> String+    style2str True  _                = "local"+    style2str False BuildInplaceOnly = "inplace"+    style2str False BuildAndInstall  = "global"+     jdisplay :: Text a => a -> J.Value     jdisplay = J.String . display+++-----------------------------------------------------------------------------+-- Project status+--++-- So, what is the status of a project after a build? That is, how do the+-- inputs (package source files etc) compare to the output artefacts (build+-- libs, exes etc)? Do the outputs reflect the current values of the inputs+-- or are outputs out of date or invalid?+--+-- First of all, what do we mean by out-of-date and what do we mean by+-- invalid? We think of the build system as a morally pure function that+-- computes the output artefacts given input values. We say an output artefact+-- is out of date when its value is not the value that would be computed by a+-- build given the current values of the inputs. An output artefact can be+-- out-of-date but still be perfectly usable; it simply correspond to a+-- previous state of the inputs.+--+-- On the other hand there are cases where output artefacts cannot safely be+-- used. For example libraries and dynamically linked executables cannot be+-- used when the libs they depend on change without them being recompiled+-- themselves. Whether an artefact is still usable depends on what it is, e.g.+-- dynamically linked vs statically linked and on how it gets updated (e.g.+-- only atomically on success or if failure can leave invalid states). We need+-- a definition (or two) that is independent of the kind of artefact and can+-- be computed just in terms of changes in package graphs, but are still+-- useful for determining when particular kinds of artefacts are invalid.+--+-- Note that when we talk about packages in this context we just mean nodes+-- in the elaborated install plan, which can be components or packages.+--+-- There's obviously a close connection between packages being out of date and+-- their output artefacts being unusable: most of the time if a package+-- remains out of date at the end of a build then some of its output artefacts+-- will be unusable. That is true most of the time because a build will have+-- attempted to build one of the out-of-date package's dependencies. If the+-- build of the dependency succeeded then it changed output artefacts (like+-- libs) and if it failed then it may have failed after already changing+-- things (think failure after updating some but not all .hi files).+--+-- There are a few reasons we may end up with still-usable output artefacts+-- for a package even when it remains out of date at the end of a build.+-- Firstly if executing a plan fails then packages can be skipped, and thus we+-- may have packages where all their dependencies were skipped. Secondly we+-- have artefacts like statically linked executables which are not affected by+-- libs they depend on being recompiled. Furthermore, packages can be out of+-- date due to changes in build tools or Setup.hs scripts they depend on, but+-- again libraries or executables in those out-of-date packages remain usable.+--+-- So we have two useful definitions of invalid. Both are useful, for+-- different purposes, so we will compute both. The first corresponds to the+-- invalid libraries and dynamic executables. We say a package is invalid by+-- changed deps if any of the packages it depends on (via library dep edges)+-- were rebuilt (successfully or unsuccessfully). The second definition+-- corresponds to invalid static executables. We say a package is invalid by+-- a failed build simply if the package was built but unsuccessfully.+--+-- So how do we find out what packages are out of date or invalid?+--+-- Obviously we know something for all the packages that were part of the plan+-- that was executed, but that is just a subset since we prune the plan down+-- to the targets and their dependencies.+--+-- Recall the steps we go though:+--+-- + starting with the initial improved plan (this is the full project);+--+-- + prune the plan to the user's build targets;+--+-- + rebuildTargetsDryRun on the pruned plan giving us a BuildStatusMap+--   covering the pruned subset of the original plan;+--+-- + execute the plan giving us BuildOutcomes which tell us success/failure+--   for each package.+--+-- So given that the BuildStatusMap and BuildOutcomes do not cover everything+-- in the original plan, what can they tell us about the original plan?+--+-- The BuildStatusMap tells us directly that some packages are up to date and+-- others out of date (but only for the pruned subset). But we know that+-- everything that is a reverse dependency of an out-of-date package is itself+-- out-of-date (whether or not it is in the pruned subset). Of course after+-- a build the BuildOutcomes may tell us that some of those out-of-date+-- packages are now up to date (ie a successful build outcome).+--+-- The difference is packages that are reverse dependencies of out-of-date+-- packages but are not brought up-to-date by the build (i.e. did not have+-- successful outcomes, either because they failed or were not in the pruned+-- subset to be built). We also know which packages were rebuilt, so we can+-- use this to find the now-invalid packages.+--+-- Note that there are still packages for which we cannot discover full status+-- information. There may be packages outside of the pruned plan that do not+-- depend on packages within the pruned plan that were discovered to be+-- out-of-date. For these packages we do not know if their build artefacts+-- are out-of-date or not. We do know however that they are not invalid, as+-- that's not possible given our definition of invalid. Intuitively it is+-- because we have not disturbed anything that these packages depend on, e.g.+-- we've not rebuilt any libs they depend on. Recall that our widest+-- definition of invalid was only concerned about dependencies on libraries+-- (to cover problems like shared libs or GHC seeing inconsistent .hi files).+--+-- So our algorithm for out-of-date packages is relatively simple: take the+-- reverse dependency closure in the original improved plan (pre-pruning) of+-- the out-of-date packages (as determined by the BuildStatusMap from the dry+-- run). That gives a set of packages that were definitely out of date after+-- the dry run. Now we remove from this set the packages that the+-- BuildOutcomes tells us are now up-to-date after the build. The remaining+-- set is the out-of-date packages.+--+-- As for packages that are invalid by changed deps, we start with the plan+-- dependency graph but keep only those edges that point to libraries (so+-- ignoring deps on exes and setup scripts). We take the packages for which a+-- build was attempted (successfully or unsuccessfully, but not counting+-- knock-on failures) and take the reverse dependency closure. We delete from+-- this set all the packages that were built successfully. Note that we do not+-- need to intersect with the out-of-date packages since this follows+-- automatically: all rev deps of packages we attempted to build must have+-- been out of date at the start of the build, and if they were not built+-- successfully then they're still out of date -- meeting our definition of+-- invalid.+++type PackageIdSet     = Set UnitId+type PackagesUpToDate = PackageIdSet++data PostBuildProjectStatus = PostBuildProjectStatus {++       -- | Packages that are known to be up to date. These were found to be+       -- up to date before the build, or they have a successful build outcome+       -- afterwards.+       --+       -- This does not include any packages outside of the subset of the plan+       -- that was executed because we did not check those and so don't know+       -- for sure that they're still up to date.+       --+       packagesDefinitelyUpToDate :: PackageIdSet,++       -- | Packages that are probably still up to date (and at least not+       -- known to be out of date, and certainly not invalid). This includes+       -- 'packagesDefinitelyUpToDate' plus packages that were up to date+       -- previously and are outside of the subset of the plan that was+       -- executed. It excludes 'packagesOutOfDate'.+       --+       packagesProbablyUpToDate :: PackageIdSet,++       -- | Packages that are known to be out of date. These are packages+       -- that were determined to be out of date before the build, and they+       -- do not have a successful build outcome afterwards.+       --+       -- Note that this can sometimes include packages outside of the subset+       -- of the plan that was executed. For example suppose package A and B+       -- depend on C, and A is the target so only A and C are in the subset+       -- to be built. Now suppose C is found to have changed, then both A+       -- and B are out-of-date before the build and since B is outside the+       -- subset to be built then it will remain out of date.+       --+       -- Note also that this is /not/ the inverse of+       -- 'packagesDefinitelyUpToDate' or 'packagesProbablyUpToDate'.+       -- There are packages where we have no information (ones that were not+       -- in the subset of the plan that was executed).+       --+       packagesOutOfDate :: PackageIdSet,++       -- | Packages that depend on libraries that have changed during the+       -- build (either build success or failure).+       --+       -- This corresponds to the fact that libraries and dynamic executables+       -- are invalid once any of the libs they depend on change.+       --+       -- This does include packages that themselves failed (i.e. it is a+       -- superset of 'packagesInvalidByFailedBuild'). It does not include+       -- changes in dependencies on executables (i.e. build tools).+       --+       packagesInvalidByChangedLibDeps :: PackageIdSet,++       -- | Packages that themselves failed during the build (i.e. them+       -- directly not a dep).+       --+       -- This corresponds to the fact that static executables are invalid+       -- in unlucky circumstances such as linking failing half way though,+       -- or data file generation failing.+       --+       -- This is a subset of 'packagesInvalidByChangedLibDeps'.+       --+       packagesInvalidByFailedBuild :: PackageIdSet,++       -- | A subset of the plan graph, including only dependency-on-library+       -- edges. That is, dependencies /on/ libraries, not dependencies /of/+       -- libraries. This tells us all the libraries that packages link to.+       --+       -- This is here as a convenience, as strictly speaking it's not status+       -- as it's just a function of the original 'ElaboratedInstallPlan'.+       --+       packagesLibDepGraph :: Graph (Node UnitId ElaboratedPlanPackage),++       -- | As a convenience for 'Set.intersection' with any of the other+       -- 'PackageIdSet's to select only packages that are part of the+       -- project locally (i.e. with a local source dir).+       --+       packagesBuildLocal     :: PackageIdSet,++       -- | As a convenience for 'Set.intersection' with any of the other+       -- 'PackageIdSet's to select only packages that are being built+       -- in-place within the project (i.e. not destined for the store).+       --+       packagesBuildInplace   :: PackageIdSet,++       -- | As a convenience for 'Set.intersection' or 'Set.difference' with+       -- any of the other 'PackageIdSet's to select only packages that were+       -- pre-installed or already in the store prior to the build.+       --+       packagesAlreadyInStore :: PackageIdSet+     }++-- | Work out which packages are out of date or invalid after a build.+--+postBuildProjectStatus :: ElaboratedInstallPlan+                       -> PackagesUpToDate+                       -> BuildStatusMap+                       -> BuildOutcomes+                       -> PostBuildProjectStatus+postBuildProjectStatus plan previousPackagesUpToDate+                       pkgBuildStatus buildOutcomes =+    PostBuildProjectStatus {+      packagesDefinitelyUpToDate,+      packagesProbablyUpToDate,+      packagesOutOfDate,+      packagesInvalidByChangedLibDeps,+      packagesInvalidByFailedBuild,+      -- convenience stuff+      packagesLibDepGraph,+      packagesBuildLocal,+      packagesBuildInplace,+      packagesAlreadyInStore+    }+  where+    packagesDefinitelyUpToDate =+       packagesUpToDatePreBuild+        `Set.union`+       packagesSuccessfulPostBuild++    packagesProbablyUpToDate =+      packagesDefinitelyUpToDate+        `Set.union`+      (previousPackagesUpToDate' `Set.difference` packagesOutOfDatePreBuild)++    packagesOutOfDate =+      packagesOutOfDatePreBuild `Set.difference` packagesSuccessfulPostBuild++    packagesInvalidByChangedLibDeps =+      packagesDepOnChangedLib `Set.difference` packagesSuccessfulPostBuild++    packagesInvalidByFailedBuild =+      packagesFailurePostBuild++    -- Note: if any of the intermediate values below turn out to be useful in+    -- their own right then we can simply promote them to the result record++    -- The previous set of up-to-date packages will contain bogus package ids+    -- when the solver plan or config contributing to the hash changes.+    -- So keep only the ones where the package id (i.e. hash) is the same.+    previousPackagesUpToDate' =+      Set.intersection+        previousPackagesUpToDate+        (InstallPlan.keysSet plan)++    packagesUpToDatePreBuild =+      Set.filter+        (\ipkgid -> not (lookupBuildStatusRequiresBuild True ipkgid))+        -- For packages not in the plan subset we did the dry-run on we don't+        -- know anything about their status, so not known to be /up to date/.+        (InstallPlan.keysSet plan)++    packagesOutOfDatePreBuild =+      Set.fromList . map installedUnitId $+      InstallPlan.reverseDependencyClosure plan+        [ ipkgid+        | pkg <- InstallPlan.toList plan+        , let ipkgid = installedUnitId pkg+        , lookupBuildStatusRequiresBuild False ipkgid+        -- For packages not in the plan subset we did the dry-run on we don't+        -- know anything about their status, so not known to be /out of date/.+        ]++    packagesSuccessfulPostBuild =+      Set.fromList+        [ ikgid | (ikgid, Right _) <- Map.toList buildOutcomes ]++    -- direct failures, not failures due to deps+    packagesFailurePostBuild =+      Set.fromList+        [ ikgid+        | (ikgid, Left failure) <- Map.toList buildOutcomes+        , case buildFailureReason failure of+            DependentFailed _ -> False+            _                 -> True+        ]++    -- Packages that have a library dependency on a package for which a build+    -- was attempted+    packagesDepOnChangedLib =+      Set.fromList . map Graph.nodeKey $+      fromMaybe (error "packagesBuildStatusAfterBuild: broken dep closure") $+      Graph.revClosure packagesLibDepGraph+        ( Map.keys+        . Map.filter (uncurry buildAttempted)+        $ Map.intersectionWith (,) pkgBuildStatus buildOutcomes +        )++    -- The plan graph but only counting dependency-on-library edges+    packagesLibDepGraph :: Graph (Node UnitId ElaboratedPlanPackage)+    packagesLibDepGraph =+      Graph.fromDistinctList+        [ Graph.N pkg (installedUnitId pkg) libdeps+        | pkg <- InstallPlan.toList plan+        , let libdeps = case pkg of+                InstallPlan.PreExisting ipkg  -> installedDepends ipkg+                InstallPlan.Configured srcpkg -> elabLibDeps srcpkg+                InstallPlan.Installed  srcpkg -> elabLibDeps srcpkg+        ]+    elabLibDeps = map (newSimpleUnitId . confInstId) . elabLibDependencies++    -- Was a build was attempted for this package?+    -- If it doesn't have both a build status and outcome then the answer is no.+    buildAttempted :: BuildStatus -> BuildOutcome -> Bool+    -- And not if it didn't need rebuilding in the first place.+    buildAttempted buildStatus _buildOutcome+      | not (buildStatusRequiresBuild buildStatus)+      = False++    -- And not if it was skipped due to a dep failing first.+    buildAttempted _ (Left BuildFailure {buildFailureReason})+      | DependentFailed _ <- buildFailureReason+      = False++    -- Otherwise, succeeded or failed, yes the build was tried.+    buildAttempted _ (Left BuildFailure {}) = True+    buildAttempted _ (Right _)              = True++    lookupBuildStatusRequiresBuild def ipkgid =+      case Map.lookup ipkgid pkgBuildStatus of+        Nothing          -> def -- Not in the plan subset we did the dry-run on+        Just buildStatus -> buildStatusRequiresBuild buildStatus++    packagesBuildLocal =+      selectPlanPackageIdSet $ \pkg ->+        case pkg of+          InstallPlan.PreExisting _     -> False+          InstallPlan.Installed   _     -> False+          InstallPlan.Configured srcpkg -> elabLocalToProject srcpkg++    packagesBuildInplace =+      selectPlanPackageIdSet $ \pkg ->+        case pkg of+          InstallPlan.PreExisting _     -> False+          InstallPlan.Installed   _     -> False+          InstallPlan.Configured srcpkg -> elabBuildStyle srcpkg+                                        == BuildInplaceOnly++    packagesAlreadyInStore =+      selectPlanPackageIdSet $ \pkg ->+        case pkg of+          InstallPlan.PreExisting _ -> True+          InstallPlan.Installed   _ -> True+          InstallPlan.Configured  _ -> False++    selectPlanPackageIdSet p = Map.keysSet+                             . Map.filter p+                             $ InstallPlan.toMap plan++++updatePostBuildProjectStatus :: Verbosity+                             -> DistDirLayout+                             -> ElaboratedInstallPlan+                             -> BuildStatusMap+                             -> BuildOutcomes+                             -> IO PostBuildProjectStatus+updatePostBuildProjectStatus verbosity distDirLayout+                             elaboratedInstallPlan+                             pkgsBuildStatus buildOutcomes = do++    -- Read the previous up-to-date set, update it and write it back+    previousUpToDate   <- readPackagesUpToDateCacheFile distDirLayout+    let currentBuildStatus@PostBuildProjectStatus{..}+                        = postBuildProjectStatus+                            elaboratedInstallPlan+                            previousUpToDate+                            pkgsBuildStatus+                            buildOutcomes+    let currentUpToDate = packagesProbablyUpToDate+    writePackagesUpToDateCacheFile distDirLayout currentUpToDate++    -- Report various possibly interesting things+    -- We additionally intersect with the packagesBuildInplace so that+    -- we don't show huge numbers of boring packages from the store.+    debugNoWrap verbosity $+        "packages definitely up to date: "+     ++ displayPackageIdSet (packagesDefinitelyUpToDate+          `Set.intersection` packagesBuildInplace)++    debugNoWrap verbosity $+        "packages previously probably up to date: "+     ++ displayPackageIdSet (previousUpToDate+          `Set.intersection` packagesBuildInplace)++    debugNoWrap verbosity $+        "packages now probably up to date: "+     ++ displayPackageIdSet (packagesProbablyUpToDate+          `Set.intersection` packagesBuildInplace)++    debugNoWrap verbosity $+        "packages newly up to date: "+     ++ displayPackageIdSet (packagesDefinitelyUpToDate+            `Set.difference` previousUpToDate+          `Set.intersection` packagesBuildInplace)++    debugNoWrap verbosity $+        "packages out to date: "+     ++ displayPackageIdSet (packagesOutOfDate+          `Set.intersection` packagesBuildInplace)++    debugNoWrap verbosity $+        "packages invalid due to dep change: "+     ++ displayPackageIdSet packagesInvalidByChangedLibDeps++    debugNoWrap verbosity $+        "packages invalid due to build failure: "+     ++ displayPackageIdSet packagesInvalidByFailedBuild++    return currentBuildStatus+  where+    displayPackageIdSet = intercalate ", " . map display . Set.toList++-- | Helper for reading the cache file.+--+-- This determines the type and format of the binary cache file.+--+readPackagesUpToDateCacheFile :: DistDirLayout -> IO PackagesUpToDate+readPackagesUpToDateCacheFile DistDirLayout{distProjectCacheFile} =+    handleDoesNotExist Set.empty $+    handleDecodeFailure $+      withBinaryFile (distProjectCacheFile "up-to-date") ReadMode $ \hnd ->+        Binary.decodeOrFailIO =<< BS.hGetContents hnd+  where+    handleDecodeFailure = fmap (either (const Set.empty) id)++-- | Helper for writing the package up-to-date cache file.+--+-- This determines the type and format of the binary cache file.+--+writePackagesUpToDateCacheFile :: DistDirLayout -> PackagesUpToDate -> IO ()+writePackagesUpToDateCacheFile DistDirLayout{distProjectCacheFile} upToDate =+    writeFileAtomic (distProjectCacheFile "up-to-date") $+      Binary.encode upToDate++-- Writing .ghc.environment files+--++writePlanGhcEnvironment :: DistDirLayout+                        -> ElaboratedInstallPlan+                        -> ElaboratedSharedConfig+                        -> PostBuildProjectStatus+                        -> IO ()+writePlanGhcEnvironment DistDirLayout{distProjectRootDirectory}+                        elaboratedInstallPlan+                        ElaboratedSharedConfig {+                          pkgConfigCompiler = compiler,+                          pkgConfigPlatform = platform+                        }+                        postBuildStatus+  | compilerFlavor compiler == GHC+  , supportsPkgEnvFiles (getImplInfo compiler)+  --TODO: check ghcjs compat+  --TODO: This feature is temporarily disabled due to #4010+  , False+  = writeGhcEnvironmentFile+      distProjectRootDirectory+      platform (compilerVersion compiler)+      (renderGhcEnviromentFile distProjectRootDirectory+                               elaboratedInstallPlan+                               postBuildStatus)+    --TODO: [required eventually] support for writing user-wide package+    -- environments, e.g. like a global project, but we would not put the+    -- env file in the home dir, rather it lives under ~/.ghc/++writePlanGhcEnvironment _ _ _ _ = return ()++renderGhcEnviromentFile :: FilePath+                        -> ElaboratedInstallPlan+                        -> PostBuildProjectStatus+                        -> [GhcEnvironmentFileEntry]+renderGhcEnviromentFile projectRootDir elaboratedInstallPlan+                        postBuildStatus =+    headerComment+  : simpleGhcEnvironmentFile packageDBs unitIds+  where+    headerComment =+        GhcEnvFileComment+      $ "This is a GHC environment file written by cabal. This means you can\n"+     ++ "run ghc or ghci and get the environment of the project as a whole.\n"+     ++ "But you still need to use cabal repl $target to get the environment\n"+     ++ "of specific components (libs, exes, tests etc) because each one can\n"+     ++ "have its own source dirs, cpp flags etc.\n\n"+    unitIds    = selectGhcEnviromentFileLibraries postBuildStatus+    packageDBs = relativePackageDBPaths projectRootDir $+                 selectGhcEnviromentFilePackageDbs elaboratedInstallPlan+++-- We're producing an environment for users to use in ghci, so of course+-- that means libraries only (can't put exes into the ghc package env!).+-- The library environment should be /consistent/ with the environment+-- that each of the packages in the project use (ie same lib versions).+-- So that means all the normal library dependencies of all the things+-- in the project (including deps of exes that are local to the project).+-- We do not however want to include the dependencies of Setup.hs scripts,+-- since these are generally uninteresting but also they need not in+-- general be consistent with the library versions that packages local to+-- the project use (recall that Setup.hs script's deps can be picked+-- independently of other packages in the project).+--+-- So, our strategy is as follows:+--+-- produce a dependency graph of all the packages in the install plan,+-- but only consider normal library deps as edges in the graph. Thus we+-- exclude the dependencies on Setup.hs scripts (in the case of+-- per-component granularity) or of Setup.hs scripts (in the case of+-- per-package granularity). Then take a dependency closure, using as+-- roots all the packages/components local to the project. This will+-- exclude Setup scripts and their dependencies.+--+-- Note: this algorithm will have to be adapted if/when the install plan+-- is extended to cover multiple compilers at once, and may also have to+-- change if we start to treat unshared deps of test suites in a similar+-- way to how we treat Setup.hs script deps (ie being able to pick them+-- independently).+--+-- Since we had to use all the local packages, including exes, (as roots+-- to find the libs) then those exes still end up in our list so we have+-- to filter them out at the end.+--+selectGhcEnviromentFileLibraries :: PostBuildProjectStatus -> [UnitId]+selectGhcEnviromentFileLibraries PostBuildProjectStatus{..} =+    case Graph.closure packagesLibDepGraph (Set.toList packagesBuildLocal) of+      Nothing    -> error "renderGhcEnviromentFile: broken dep closure"+      Just nodes -> [ pkgid | Graph.N pkg pkgid _ <- nodes+                            , hasUpToDateLib pkg ]+  where+    hasUpToDateLib planpkg = case planpkg of+      -- A pre-existing global lib+      InstallPlan.PreExisting  _ -> True++      -- A package in the store. Check it's a lib.+      InstallPlan.Installed  pkg -> elabRequiresRegistration pkg++      -- A package we were installing this time, either destined for the store+      -- or just locally. Check it's a lib and that it is probably up to date.+      InstallPlan.Configured pkg ->+          elabRequiresRegistration pkg+       && installedUnitId pkg `Set.member` packagesProbablyUpToDate+++selectGhcEnviromentFilePackageDbs :: ElaboratedInstallPlan -> PackageDBStack+selectGhcEnviromentFilePackageDbs elaboratedInstallPlan =+    -- If we have any inplace packages then their package db stack is the+    -- one we should use since it'll include the store + the local db but+    -- it's certainly possible to have no local inplace packages+    -- e.g. just "extra" packages coming from the store.+    case (inplacePackages, sourcePackages) of+      ([], pkgs) -> checkSamePackageDBs pkgs+      (pkgs, _)  -> checkSamePackageDBs pkgs+  where+    checkSamePackageDBs pkgs =+      case ordNub (map elabBuildPackageDBStack pkgs) of+        [packageDbs] -> packageDbs+        []           -> []+        _            -> error $ "renderGhcEnviromentFile: packages with "+                             ++ "different package db stacks"+        -- This should not happen at the moment but will happen as soon+        -- as we support projects where we build packages with different+        -- compilers, at which point we have to consider how to adapt+        -- this feature, e.g. write out multiple env files, one for each+        -- compiler / project profile.++    inplacePackages =+      [ srcpkg+      | srcpkg <- sourcePackages+      , elabBuildStyle srcpkg == BuildInplaceOnly ]+    sourcePackages =+      [ srcpkg+      | pkg <- InstallPlan.toList elaboratedInstallPlan+      , srcpkg <- maybeToList $ case pkg of+                    InstallPlan.Configured srcpkg -> Just srcpkg+                    InstallPlan.Installed  srcpkg -> Just srcpkg+                    InstallPlan.PreExisting _     -> Nothing+      ]++relativePackageDBPaths :: FilePath -> PackageDBStack -> PackageDBStack+relativePackageDBPaths relroot = map (relativePackageDBPath relroot)++relativePackageDBPath :: FilePath -> PackageDB -> PackageDB+relativePackageDBPath relroot pkgdb =+    case pkgdb of+      GlobalPackageDB        -> GlobalPackageDB+      UserPackageDB          -> UserPackageDB+      SpecificPackageDB path -> SpecificPackageDB relpath+        where relpath = makeRelative relroot path 
Distribution/Client/ProjectPlanning.hs view
@@ -1,2283 +1,3356 @@ {-# LANGUAGE CPP, RecordWildCards, NamedFieldPuns, RankNTypes #-}---- | Planning how to build everything in a project.----module Distribution.Client.ProjectPlanning (-    -- * elaborated install plan types-    ElaboratedInstallPlan,-    ElaboratedConfiguredPackage(..),-    ElaboratedPlanPackage,-    ElaboratedSharedConfig(..),-    ElaboratedReadyPackage,-    BuildStyle(..),-    CabalFileText,--    --TODO: [code cleanup] these types should live with execution, not with-    --      plan definition. Need to better separate InstallPlan definition.-    GenericBuildResult(..),-    BuildResult,-    BuildSuccess(..),-    BuildFailure(..),-    DocsResult(..),-    TestsResult(..),--    -- * Producing the elaborated install plan-    rebuildInstallPlan,--    -- * Build targets-    PackageTarget(..),-    ComponentTarget(..),-    SubComponentTarget(..),-    showComponentTarget,--    -- * Selecting a plan subset-    pruneInstallPlanToTargets,--    -- * Utils required for building-    pkgHasEphemeralBuildTargets,-    pkgBuildTargetWholeComponents,--    -- * Setup.hs CLI flags for building-    setupHsScriptOptions,-    setupHsConfigureFlags,-    setupHsBuildFlags,-    setupHsBuildArgs,-    setupHsReplFlags,-    setupHsReplArgs,-    setupHsCopyFlags,-    setupHsRegisterFlags,-    setupHsHaddockFlags,--    packageHashInputs,--    -- TODO: [code cleanup] utils that should live in some shared place?-    createPackageDBIfMissing-  ) where--import           Distribution.Client.ProjectPlanning.Types-import           Distribution.Client.PackageHash-import           Distribution.Client.RebuildMonad-import           Distribution.Client.ProjectConfig-import           Distribution.Client.ProjectPlanOutput--import           Distribution.Client.Types-                   hiding ( BuildResult, BuildSuccess(..), BuildFailure(..)-                          , DocsResult(..), TestsResult(..) )-import qualified Distribution.Client.InstallPlan as InstallPlan-import           Distribution.Client.Dependency-import           Distribution.Client.Dependency.Types-import qualified Distribution.Client.ComponentDeps as CD-import           Distribution.Client.ComponentDeps (ComponentDeps)-import qualified Distribution.Client.IndexUtils as IndexUtils-import qualified Distribution.Client.PackageIndex as SourcePackageIndex-import           Distribution.Client.Targets (userToPackageConstraint)-import           Distribution.Client.DistDirLayout-import           Distribution.Client.SetupWrapper-import           Distribution.Client.JobControl-import           Distribution.Client.FetchUtils-import qualified Hackage.Security.Client as Sec-import           Distribution.Client.PkgConfigDb-import           Distribution.Client.Setup hiding (packageName, cabalVersion)-import           Distribution.Utils.NubList--import           Distribution.Package hiding-  (InstalledPackageId, installedPackageId)-import           Distribution.System-import qualified Distribution.PackageDescription as Cabal-import qualified Distribution.PackageDescription as PD-import qualified Distribution.PackageDescription.Configuration as PD-import qualified Distribution.InstalledPackageInfo as Installed-import           Distribution.Simple.PackageIndex (InstalledPackageIndex)-import qualified Distribution.Simple.PackageIndex as PackageIndex-import           Distribution.Simple.Compiler hiding (Flag)-import qualified Distribution.Simple.GHC   as GHC   --TODO: [code cleanup] eliminate-import qualified Distribution.Simple.GHCJS as GHCJS --TODO: [code cleanup] eliminate-import           Distribution.Simple.Program-import           Distribution.Simple.Program.Db-import           Distribution.Simple.Program.Find-import qualified Distribution.Simple.Setup as Cabal-import           Distribution.Simple.Setup-  (Flag, toFlag, flagToMaybe, flagToList, fromFlagOrDefault)-import qualified Distribution.Simple.Configure as Cabal-import qualified Distribution.Simple.LocalBuildInfo as Cabal-import           Distribution.Simple.LocalBuildInfo (ComponentName(..))-import qualified Distribution.Simple.Register as Cabal-import qualified Distribution.Simple.InstallDirs as InstallDirs-import qualified Distribution.Simple.BuildTarget as Cabal--import           Distribution.Simple.Utils hiding (matchFileGlob)-import           Distribution.Version-import           Distribution.Verbosity-import           Distribution.Text--import           Data.Map (Map)-import qualified Data.Map as Map-import           Data.Set (Set)-import qualified Data.Set as Set-import qualified Data.Graph as Graph-import qualified Data.Tree  as Tree-#if !MIN_VERSION_base(4,8,0)-import           Control.Applicative-#endif-import           Control.Monad-import           Control.Monad.State as State-import           Control.Exception-import           Data.List-import           Data.Maybe-import           Data.Either-import           Data.Monoid-import           Data.Function-import           System.FilePath-import           System.Directory (doesDirectoryExist)------------------------------------------------------------------------------------ * Elaborated install plan----------------------------------------------------------------------------------- "Elaborated" -- worked out with great care and nicety of detail;---                 executed with great minuteness: elaborate preparations;---                 elaborate care.------ So here's the idea:------ Rather than a miscellaneous collection of 'ConfigFlags', 'InstallFlags' etc--- all passed in as separate args and which are then further selected,--- transformed etc during the execution of the build. Instead we construct--- an elaborated install plan that includes everything we will need, and then--- during the execution of the plan we do as little transformation of this--- info as possible.------ So we're trying to split the work into two phases: construction of the--- elaborated install plan (which as far as possible should be pure) and--- then simple execution of that plan without any smarts, just doing what the--- plan says to do.------ So that means we need a representation of this fully elaborated install--- plan. The representation consists of two parts:------ * A 'ElaboratedInstallPlan'. This is a 'GenericInstallPlan' with a---   representation of source packages that includes a lot more detail about---   that package's individual configuration------ * A 'ElaboratedSharedConfig'. Some package configuration is the same for---   every package in a plan. Rather than duplicate that info every entry in---   the 'GenericInstallPlan' we keep that separately.------ The division between the shared and per-package config is /not set in stone--- for all time/. For example if we wanted to generalise the install plan to--- describe a situation where we want to build some packages with GHC and some--- with GHCJS then the platform and compiler would no longer be shared between--- all packages but would have to be per-package (probably with some sanity--- condition on the graph structure).------- Refer to ProjectPlanning.Types for details of these important types:---- type ElaboratedInstallPlan = ...--- type ElaboratedPlanPackage = ...--- data ElaboratedSharedConfig = ...--- data ElaboratedConfiguredPackage = ...--- data BuildStyle =---sanityCheckElaboratedConfiguredPackage :: ElaboratedSharedConfig-                                       -> ElaboratedConfiguredPackage-                                       -> Bool-sanityCheckElaboratedConfiguredPackage sharedConfig-                                       pkg@ElaboratedConfiguredPackage{..} =--    pkgStanzasEnabled `Set.isSubsetOf` pkgStanzasAvailable--    -- the stanzas explicitly enabled should be available and enabled- && Map.keysSet (Map.filter id pkgStanzasRequested)-      `Set.isSubsetOf` pkgStanzasEnabled--    -- the stanzas explicitly disabled should not be available- && Set.null (Map.keysSet (Map.filter not pkgStanzasRequested)-                `Set.intersection` pkgStanzasAvailable)-- && (pkgBuildStyle == BuildInplaceOnly ||-     installedPackageId pkg == hashedInstalledPackageId-                                 (packageHashInputs sharedConfig pkg))-- && (pkgBuildStyle == BuildInplaceOnly ||-     Set.null pkgStanzasAvailable)------------------------------------------------------------------------------------ * Deciding what to do: making an 'ElaboratedInstallPlan'---------------------------------------------------------------------------------rebuildInstallPlan :: Verbosity-                   -> FilePath -> DistDirLayout -> CabalDirLayout-                   -> ProjectConfig-                   -> IO ( ElaboratedInstallPlan-                         , ElaboratedSharedConfig-                         , ProjectConfig )-rebuildInstallPlan verbosity-                   projectRootDir-                   distDirLayout@DistDirLayout {-                     distDirectory,-                     distProjectCacheFile,-                     distProjectCacheDirectory-                   }-                   cabalDirLayout@CabalDirLayout {-                     cabalPackageCacheDirectory,-                     cabalStoreDirectory,-                     cabalStorePackageDB-                   }-                   cliConfig =-    runRebuild projectRootDir $ do-    progsearchpath <- liftIO $ getSystemSearchPath-    let cliConfigPersistent = cliConfig { projectConfigBuildOnly = mempty }--    -- The overall improved plan is cached-    rerunIfChanged verbosity fileMonitorImprovedPlan-                   -- react to changes in command line args and the path-                   (cliConfigPersistent, progsearchpath) $ do--      -- And so is the elaborated plan that the improved plan based on-      (elaboratedPlan, elaboratedShared,-       projectConfig) <--        rerunIfChanged verbosity fileMonitorElaboratedPlan-                       (cliConfigPersistent, progsearchpath) $ do--          (projectConfig, projectConfigTransient) <- phaseReadProjectConfig-          localPackages <- phaseReadLocalPackages projectConfig-          compilerEtc   <- phaseConfigureCompiler projectConfig-          _             <- phaseConfigurePrograms projectConfig compilerEtc-          solverPlan    <- phaseRunSolver         projectConfigTransient-                                                  compilerEtc localPackages-          (elaboratedPlan,-           elaboratedShared) <- phaseElaboratePlan projectConfigTransient-                                                   compilerEtc-                                                   solverPlan localPackages-          phaseMaintainPlanOutputs elaboratedPlan elaboratedShared--          return (elaboratedPlan, elaboratedShared,-                  projectConfig)--      -- The improved plan changes each time we install something, whereas-      -- the underlying elaborated plan only changes when input config-      -- changes, so it's worth caching them separately.-      improvedPlan <- phaseImprovePlan elaboratedPlan elaboratedShared-      return (improvedPlan, elaboratedShared, projectConfig)--  where-    fileMonitorCompiler       = newFileMonitorInCacheDir "compiler"-    fileMonitorSolverPlan     = newFileMonitorInCacheDir "solver-plan"-    fileMonitorSourceHashes   = newFileMonitorInCacheDir "source-hashes"-    fileMonitorElaboratedPlan = newFileMonitorInCacheDir "elaborated-plan"-    fileMonitorImprovedPlan   = newFileMonitorInCacheDir "improved-plan"--    newFileMonitorInCacheDir :: Eq a => FilePath -> FileMonitor a b-    newFileMonitorInCacheDir  = newFileMonitor . distProjectCacheFile--    -- Read the cabal.project (or implicit config) and combine it with-    -- arguments from the command line-    ---    phaseReadProjectConfig :: Rebuild (ProjectConfig, ProjectConfig)-    phaseReadProjectConfig = do-      liftIO $ do-        info verbosity "Project settings changed, reconfiguring..."-        createDirectoryIfMissingVerbose verbosity False distDirectory-        createDirectoryIfMissingVerbose verbosity False distProjectCacheDirectory--      projectConfig <- readProjectConfig verbosity projectRootDir--      -- The project config comming from the command line includes "build only"-      -- flags that we don't cache persistently (because like all "build only"-      -- flags they do not affect the value of the outcome) but that we do-      -- sometimes using during planning (in particular the http transport)-      let projectConfigTransient  = projectConfig <> cliConfig-          projectConfigPersistent = projectConfig-                                 <> cliConfig {-                                      projectConfigBuildOnly = mempty-                                    }-      liftIO $ writeProjectConfigFile (distProjectCacheFile "config")-                                      projectConfigPersistent-      return (projectConfigPersistent, projectConfigTransient)--    -- Look for all the cabal packages in the project-    -- some of which may be local src dirs, tarballs etc-    ---    phaseReadLocalPackages :: ProjectConfig-                           -> Rebuild [SourcePackage]-    phaseReadLocalPackages projectConfig = do--      localCabalFiles <- findProjectPackages projectRootDir projectConfig-      mapM (readSourcePackage verbosity) localCabalFiles---    -- Configure the compiler we're using.-    ---    -- This is moderately expensive and doesn't change that often so we cache-    -- it independently.-    ---    phaseConfigureCompiler :: ProjectConfig-                           -> Rebuild (Compiler, Platform, ProgramDb)-    phaseConfigureCompiler ProjectConfig {-                             projectConfigShared = ProjectConfigShared {-                               projectConfigHcFlavor,-                               projectConfigHcPath,-                               projectConfigHcPkg-                             },-                             projectConfigLocalPackages = PackageConfig {-                               packageConfigProgramPaths,-                               packageConfigProgramArgs,-                               packageConfigProgramPathExtra-                             }-                           } = do-        progsearchpath <- liftIO $ getSystemSearchPath-        rerunIfChanged verbosity fileMonitorCompiler-                       (hcFlavor, hcPath, hcPkg, progsearchpath,-                        packageConfigProgramPaths,-                        packageConfigProgramArgs,-                        packageConfigProgramPathExtra) $ do--          liftIO $ info verbosity "Compiler settings changed, reconfiguring..."-          result@(_, _, progdb') <- liftIO $-            Cabal.configCompilerEx-              hcFlavor hcPath hcPkg-              progdb verbosity--        -- Note that we added the user-supplied program locations and args-        -- for /all/ programs, not just those for the compiler prog and-        -- compiler-related utils. In principle we don't know which programs-        -- the compiler will configure (and it does vary between compilers).-        -- We do know however that the compiler will only configure the-        -- programs it cares about, and those are the ones we monitor here.-          monitorFiles (programsMonitorFiles progdb')--          return result-      where-        hcFlavor = flagToMaybe projectConfigHcFlavor-        hcPath   = flagToMaybe projectConfigHcPath-        hcPkg    = flagToMaybe projectConfigHcPkg-        progdb   =-            userSpecifyPaths (Map.toList (getMapLast packageConfigProgramPaths))-          . userSpecifyArgss (Map.toList (getMapMappend packageConfigProgramArgs))-          . modifyProgramSearchPath-              (++ [ ProgramSearchPathDir dir-                  | dir <- fromNubList packageConfigProgramPathExtra ])-          $ defaultProgramDb---    -- Configuring other programs.-    ---    -- Having configred the compiler, now we configure all the remaining-    -- programs. This is to check we can find them, and to monitor them for-    -- changes.-    ---    -- TODO: [required eventually] we don't actually do this yet.-    ---    -- We rely on the fact that the previous phase added the program config for-    -- all local packages, but that all the programs configured so far are the-    -- compiler program or related util programs.-    ---    phaseConfigurePrograms :: ProjectConfig-                           -> (Compiler, Platform, ProgramDb)-                           -> Rebuild ()-    phaseConfigurePrograms projectConfig (_, _, compilerprogdb) = do-        -- Users are allowed to specify program locations independently for-        -- each package (e.g. to use a particular version of a pre-processor-        -- for some packages). However they cannot do this for the compiler-        -- itself as that's just not going to work. So we check for this.-        liftIO $ checkBadPerPackageCompilerPaths-          (configuredPrograms compilerprogdb)-          (getMapMappend (projectConfigSpecificPackage projectConfig))--        --TODO: [required eventually] find/configure other programs that the-        -- user specifies.--        --TODO: [required eventually] find/configure all build-tools-        -- but note that some of them may be built as part of the plan.---    -- Run the solver to get the initial install plan.-    -- This is expensive so we cache it independently.-    ---    phaseRunSolver :: ProjectConfig-                   -> (Compiler, Platform, ProgramDb)-                   -> [SourcePackage]-                   -> Rebuild (SolverInstallPlan, PackagesImplicitSetupDeps)-    phaseRunSolver projectConfig@ProjectConfig {-                     projectConfigShared,-                     projectConfigBuildOnly-                   }-                   (compiler, platform, progdb)-                   localPackages =-        rerunIfChanged verbosity fileMonitorSolverPlan-                       (solverSettings, cabalPackageCacheDirectory,-                        localPackages, localPackagesEnabledStanzas,-                        compiler, platform, programsDbSignature progdb) $ do--          installedPkgIndex <- getInstalledPackages verbosity-                                                    compiler progdb platform-                                                    corePackageDbs-          sourcePkgDb       <- getSourcePackages    verbosity withRepoCtx-          pkgConfigDB       <- getPkgConfigDb      verbosity progdb--          --TODO: [code cleanup] it'd be better if the Compiler contained the-          -- ConfiguredPrograms that it needs, rather than relying on the progdb-          -- since we don't need to depend on all the programs here, just the-          -- ones relevant for the compiler.--          liftIO $ do-            solver <- chooseSolver verbosity-                                   (solverSettingSolver solverSettings)-                                   (compilerInfo compiler)--            notice verbosity "Resolving dependencies..."-            foldProgress logMsg die return $-              planPackages compiler platform solver solverSettings-                           installedPkgIndex sourcePkgDb pkgConfigDB-                           localPackages localPackagesEnabledStanzas-      where-        corePackageDbs = [GlobalPackageDB]-        withRepoCtx    = projectConfigWithSolverRepoContext verbosity-                           cabalPackageCacheDirectory-                           projectConfigShared-                           projectConfigBuildOnly-        solverSettings = resolveSolverSettings projectConfig-        logMsg message rest = debugNoWrap verbosity message >> rest--        localPackagesEnabledStanzas =-          Map.fromList-            [ (pkgname, stanzas)-            | pkg <- localPackages-            , let pkgname            = packageName pkg-                  testsEnabled       = lookupLocalPackageConfig-                                         packageConfigTests-                                         projectConfig pkgname-                  benchmarksEnabled  = lookupLocalPackageConfig-                                         packageConfigBenchmarks-                                         projectConfig pkgname-                  stanzas =-                    Map.fromList $-                      [ (TestStanzas, enabled)-                      | enabled <- flagToList testsEnabled ]-                   ++ [ (BenchStanzas , enabled)-                      | enabled <- flagToList benchmarksEnabled ]-            ]--    -- Elaborate the solver's install plan to get a fully detailed plan. This-    -- version of the plan has the final nix-style hashed ids.-    ---    phaseElaboratePlan :: ProjectConfig-                       -> (Compiler, Platform, ProgramDb)-                       -> (SolverInstallPlan, PackagesImplicitSetupDeps)-                       -> [SourcePackage]-                       -> Rebuild ( ElaboratedInstallPlan-                                  , ElaboratedSharedConfig )-    phaseElaboratePlan ProjectConfig {-                         projectConfigShared,-                         projectConfigLocalPackages,-                         projectConfigSpecificPackage,-                         projectConfigBuildOnly-                       }-                       (compiler, platform, progdb)-                       (solverPlan, pkgsImplicitSetupDeps)-                       localPackages = do--        liftIO $ debug verbosity "Elaborating the install plan..."--        sourcePackageHashes <--          rerunIfChanged verbosity fileMonitorSourceHashes-                         (packageLocationsSignature solverPlan) $-            getPackageSourceHashes verbosity withRepoCtx solverPlan--        defaultInstallDirs <- liftIO $ userInstallDirTemplates compiler-        return $-          elaborateInstallPlan-            platform compiler progdb-            distDirLayout-            cabalDirLayout-            solverPlan-            pkgsImplicitSetupDeps-            localPackages-            sourcePackageHashes-            defaultInstallDirs-            projectConfigShared-            projectConfigLocalPackages-            (getMapMappend projectConfigSpecificPackage)-      where-        withRepoCtx = projectConfigWithSolverRepoContext verbosity-                        cabalPackageCacheDirectory-                        projectConfigShared-                        projectConfigBuildOnly---    -- Update the files we maintain that reflect our current build environment.-    -- In particular we maintain a JSON representation of the elaborated-    -- install plan.-    ---    -- TODO: [required eventually] maintain the ghc environment file reflecting-    -- the libs available. This will need to be after plan improvement phase.-    ---    phaseMaintainPlanOutputs :: ElaboratedInstallPlan-                             -> ElaboratedSharedConfig-                             -> Rebuild ()-    phaseMaintainPlanOutputs elaboratedPlan elaboratedShared = do-        liftIO $ debug verbosity "Updating plan.json"-        liftIO $ writePlanExternalRepresentation-                   distDirLayout-                   elaboratedPlan-                   elaboratedShared---    -- Improve the elaborated install plan. The elaborated plan consists-    -- mostly of source packages (with full nix-style hashed ids). Where-    -- corresponding installed packages already exist in the store, replace-    -- them in the plan.-    ---    -- Note that we do monitor the store's package db here, so we will redo-    -- this improvement phase when the db changes -- including as a result of-    -- executing a plan and installing things.-    ---    phaseImprovePlan :: ElaboratedInstallPlan-                     -> ElaboratedSharedConfig-                     -> Rebuild ElaboratedInstallPlan-    phaseImprovePlan elaboratedPlan elaboratedShared = do--        liftIO $ debug verbosity "Improving the install plan..."-        recreateDirectory verbosity True storeDirectory-        storePkgIndex <- getPackageDBContents verbosity-                                              compiler progdb platform-                                              storePackageDb-        let improvedPlan = improveInstallPlanWithPreExistingPackages-                             storePkgIndex-                             elaboratedPlan-        return improvedPlan--      where-        storeDirectory  = cabalStoreDirectory (compilerId compiler)-        storePackageDb  = cabalStorePackageDB (compilerId compiler)-        ElaboratedSharedConfig {-          pkgConfigPlatform      = platform,-          pkgConfigCompiler      = compiler,-          pkgConfigCompilerProgs = progdb-        } = elaboratedShared---programsMonitorFiles :: ProgramDb -> [MonitorFilePath]-programsMonitorFiles progdb =-    [ monitor-    | prog    <- configuredPrograms progdb-    , monitor <- monitorFileSearchPath (programMonitorFiles prog)-                                       (programPath prog)-    ]---- | Select the bits of a 'ProgramDb' to monitor for value changes.--- Use 'programsMonitorFiles' for the files to monitor.----programsDbSignature :: ProgramDb -> [ConfiguredProgram]-programsDbSignature progdb =-    [ prog { programMonitorFiles = []-           , programOverrideEnv  = filter ((/="PATH") . fst)-                                          (programOverrideEnv prog) }-    | prog <- configuredPrograms progdb ]--getInstalledPackages :: Verbosity-                     -> Compiler -> ProgramDb -> Platform-                     -> PackageDBStack-                     -> Rebuild InstalledPackageIndex-getInstalledPackages verbosity compiler progdb platform packagedbs = do-    monitorFiles . map monitorFileOrDirectory-      =<< liftIO (IndexUtils.getInstalledPackagesMonitorFiles-                    verbosity compiler-                    packagedbs progdb platform)-    liftIO $ IndexUtils.getInstalledPackages-               verbosity compiler-               packagedbs progdb--getPackageDBContents :: Verbosity-                     -> Compiler -> ProgramDb -> Platform-                     -> PackageDB-                     -> Rebuild InstalledPackageIndex-getPackageDBContents verbosity compiler progdb platform packagedb = do-    monitorFiles . map monitorFileOrDirectory-      =<< liftIO (IndexUtils.getInstalledPackagesMonitorFiles-                    verbosity compiler-                    [packagedb] progdb platform)-    liftIO $ do-      createPackageDBIfMissing verbosity compiler-                               progdb [packagedb]-      Cabal.getPackageDBContents verbosity compiler-                                 packagedb progdb--getSourcePackages :: Verbosity -> (forall a. (RepoContext -> IO a) -> IO a)-                  -> Rebuild SourcePackageDb-getSourcePackages verbosity withRepoCtx = do-    (sourcePkgDb, repos) <--      liftIO $-        withRepoCtx $ \repoctx -> do-          sourcePkgDb <- IndexUtils.getSourcePackages verbosity repoctx-          return (sourcePkgDb, repoContextRepos repoctx)--    monitorFiles . map monitorFile-                 . IndexUtils.getSourcePackagesMonitorFiles-                 $ repos-    return sourcePkgDb--createPackageDBIfMissing :: Verbosity -> Compiler -> ProgramDb-                         -> PackageDBStack -> IO ()-createPackageDBIfMissing verbosity compiler progdb packageDbs =-  case reverse packageDbs of-    SpecificPackageDB dbPath : _ -> do-      exists <- liftIO $ Cabal.doesPackageDBExist dbPath-      unless exists $ do-        createDirectoryIfMissingVerbose verbosity False (takeDirectory dbPath)-        Cabal.createPackageDB verbosity compiler progdb False dbPath-    _ -> return ()---getPkgConfigDb :: Verbosity -> ProgramDb -> Rebuild PkgConfigDb-getPkgConfigDb verbosity progdb = do-    dirs <- liftIO $ getPkgConfigDbDirs verbosity progdb-    -- Just monitor the dirs so we'll notice new .pc files.-    -- Alternatively we could monitor all the .pc files too.-    forM_ dirs $ \dir -> do-        dirExists <- liftIO $ doesDirectoryExist dir-        -- TODO: turn this into a utility function-        monitorFiles [if dirExists-                        then monitorDirectory dir-                        else monitorNonExistentDirectory dir]--    liftIO $ readPkgConfigDb verbosity progdb---recreateDirectory :: Verbosity -> Bool -> FilePath -> Rebuild ()-recreateDirectory verbosity createParents dir = do-    liftIO $ createDirectoryIfMissingVerbose verbosity createParents dir-    monitorFiles [monitorDirectoryExistence dir]----- | Select the config values to monitor for changes package source hashes.-packageLocationsSignature :: SolverInstallPlan-                          -> [(PackageId, PackageLocation (Maybe FilePath))]-packageLocationsSignature solverPlan =-    [ (packageId pkg, packageSource pkg)-    | InstallPlan.Configured-        (ConfiguredPackage pkg _ _ _) <- InstallPlan.toList solverPlan-    ]----- | Get the 'HashValue' for all the source packages where we use hashes,--- and download any packages required to do so.------ Note that we don't get hashes for local unpacked packages.----getPackageSourceHashes :: Verbosity-                       -> (forall a. (RepoContext -> IO a) -> IO a)-                       -> SolverInstallPlan-                       -> Rebuild (Map PackageId PackageSourceHash)-getPackageSourceHashes verbosity withRepoCtx solverPlan = do--    -- Determine if and where to get the package's source hash from.-    ---    let allPkgLocations :: [(PackageId, PackageLocation (Maybe FilePath))]-        allPkgLocations =-          [ (packageId pkg, packageSource pkg)-          | InstallPlan.Configured-              (ConfiguredPackage pkg _ _ _) <- InstallPlan.toList solverPlan ]--        -- Tarballs that were local in the first place.-        -- We'll hash these tarball files directly.-        localTarballPkgs :: [(PackageId, FilePath)]-        localTarballPkgs =-          [ (pkgid, tarball)-          | (pkgid, LocalTarballPackage tarball) <- allPkgLocations ]--        -- Tarballs from remote URLs. We must have downloaded these already-        -- (since we extracted the .cabal file earlier)-        --TODO: [required eventually] finish remote tarball functionality---        allRemoteTarballPkgs =---          [ (pkgid, )---          | (pkgid, RemoteTarballPackage ) <- allPkgLocations ]--        -- Tarballs from repositories, either where the repository provides-        -- hashes as part of the repo metadata, or where we will have to-        -- download and hash the tarball.-        repoTarballPkgsWithMetadata    :: [(PackageId, Repo)]-        repoTarballPkgsWithoutMetadata :: [(PackageId, Repo)]-        (repoTarballPkgsWithMetadata,-         repoTarballPkgsWithoutMetadata) =-          partitionEithers-          [ case repo of-              RepoSecure{} -> Left  (pkgid, repo)-              _            -> Right (pkgid, repo)-          | (pkgid, RepoTarballPackage repo _ _) <- allPkgLocations ]--    -- For tarballs from repos that do not have hashes available we now have-    -- to check if the packages were downloaded already.-    ---    (repoTarballPkgsToDownload,-     repoTarballPkgsDownloaded)-      <- fmap partitionEithers $-         liftIO $ sequence-           [ do mtarball <- checkRepoTarballFetched repo pkgid-                case mtarball of-                  Nothing      -> return (Left  (pkgid, repo))-                  Just tarball -> return (Right (pkgid, tarball))-           | (pkgid, repo) <- repoTarballPkgsWithoutMetadata ]--    (hashesFromRepoMetadata,-     repoTarballPkgsNewlyDownloaded) <--      -- Avoid having to initialise the repository (ie 'withRepoCtx') if we-      -- don't have to. (The main cost is configuring the http client.)-      if null repoTarballPkgsToDownload && null repoTarballPkgsWithMetadata-      then return (Map.empty, [])-      else liftIO $ withRepoCtx $ \repoctx -> do--      -- For tarballs from repos that do have hashes available as part of the-      -- repo metadata we now load up the index for each repo and retrieve-      -- the hashes for the packages-      ---      hashesFromRepoMetadata <--        Sec.uncheckClientErrors $ --TODO: [code cleanup] wrap in our own exceptions-        fmap (Map.fromList . concat) $-        sequence-          -- Reading the repo index is expensive so we group the packages by repo-          [ repoContextWithSecureRepo repoctx repo $ \secureRepo ->-              Sec.withIndex secureRepo $ \repoIndex ->-                sequence-                  [ do hash <- Sec.trusted <$> -- strip off Trusted tag-                               Sec.indexLookupHash repoIndex pkgid-                       -- Note that hackage-security currently uses SHA256-                       -- but this API could in principle give us some other-                       -- choice in future.-                       return (pkgid, hashFromTUF hash)-                  | pkgid <- pkgids ]-          | (repo, pkgids) <--                map (\grp@((_,repo):_) -> (repo, map fst grp))-              . groupBy ((==)    `on` (remoteRepoName . repoRemote . snd))-              . sortBy  (compare `on` (remoteRepoName . repoRemote . snd))-              $ repoTarballPkgsWithMetadata-          ]--      -- For tarballs from repos that do not have hashes available, download-      -- the ones we previously determined we need.-      ---      repoTarballPkgsNewlyDownloaded <--        sequence-          [ do tarball <- fetchRepoTarball verbosity repoctx repo pkgid-               return (pkgid, tarball)-          | (pkgid, repo) <- repoTarballPkgsToDownload ]--      return (hashesFromRepoMetadata,-              repoTarballPkgsNewlyDownloaded)--    -- Hash tarball files for packages where we have to do that. This includes-    -- tarballs that were local in the first place, plus tarballs from repos,-    -- either previously cached or freshly downloaded.-    ---    let allTarballFilePkgs :: [(PackageId, FilePath)]-        allTarballFilePkgs = localTarballPkgs-                          ++ repoTarballPkgsDownloaded-                          ++ repoTarballPkgsNewlyDownloaded-    hashesFromTarballFiles <- liftIO $-      fmap Map.fromList $-      sequence-        [ do srchash <- readFileHashValue tarball-             return (pkgid, srchash)-        | (pkgid, tarball) <- allTarballFilePkgs-        ]-    monitorFiles [ monitorFile tarball-                 | (_pkgid, tarball) <- allTarballFilePkgs ]--    -- Return the combination-    return $! hashesFromRepoMetadata-           <> hashesFromTarballFiles----- --------------------------------------------------------------- * Installation planning--- --------------------------------------------------------------planPackages :: Compiler-             -> Platform-             -> Solver -> SolverSettings-             -> InstalledPackageIndex-             -> SourcePackageDb-             -> PkgConfigDb-             -> [SourcePackage]-             -> Map PackageName (Map OptionalStanza Bool)-             -> Progress String String-                         (SolverInstallPlan, PackagesImplicitSetupDeps)-planPackages comp platform solver SolverSettings{..}-             installedPkgIndex sourcePkgDb pkgConfigDB-             localPackages pkgStanzasEnable =--    rememberImplicitSetupDeps (depResolverSourcePkgIndex stdResolverParams) <$>--    resolveDependencies-      platform (compilerInfo comp)-      pkgConfigDB solver-      resolverParams--  where--    --TODO: [nice to have] disable multiple instances restriction in the solver, but then-    -- make sure we can cope with that in the output.-    resolverParams =--        setMaxBackjumps solverSettingMaxBackjumps--        --TODO: [required eventually] should only be configurable for custom installs-   -- . setIndependentGoals solverSettingIndependentGoals--      . setReorderGoals solverSettingReorderGoals--        --TODO: [required eventually] should only be configurable for custom installs-   -- . setAvoidReinstalls solverSettingAvoidReinstalls--        --TODO: [required eventually] should only be configurable for custom installs-   -- . setShadowPkgs solverSettingShadowPkgs--      . setStrongFlags solverSettingStrongFlags--        --TODO: [required eventually] decide if we need to prefer installed for-        -- global packages, or prefer latest even for global packages. Perhaps-        -- should be configurable but with a different name than "upgrade-dependencies".-      . setPreferenceDefault PreferLatestForSelected-                           {-(if solverSettingUpgradeDeps-                                then PreferAllLatest-                                else PreferLatestForSelected)-}--      . removeUpperBounds solverSettingAllowNewer--      . addDefaultSetupDependencies (defaultSetupDeps comp platform-                                   . PD.packageDescription-                                   . packageDescription)--      . addPreferences-          -- preferences from the config file or command line-          [ PackageVersionPreference name ver-          | Dependency name ver <- solverSettingPreferences ]--      . addConstraints-          -- version constraints from the config file or command line-            [ LabeledPackageConstraint (userToPackageConstraint pc) src-            | (pc, src) <- solverSettingConstraints ]--      . addPreferences-          -- enable stanza preference where the user did not specify-          [ PackageStanzasPreference pkgname stanzas-          | pkg <- localPackages-          , let pkgname = packageName pkg-                stanzaM = Map.findWithDefault Map.empty pkgname pkgStanzasEnable-                stanzas = [ stanza | stanza <- [minBound..maxBound]-                          , Map.lookup stanza stanzaM == Nothing ]-          , not (null stanzas)-          ]--      . addConstraints-          -- enable stanza constraints where the user asked to enable-          [ LabeledPackageConstraint-              (PackageConstraintStanzas pkgname stanzas)-              ConstraintSourceConfigFlagOrTarget-          | pkg <- localPackages-          , let pkgname = packageName pkg-                stanzaM = Map.findWithDefault Map.empty pkgname pkgStanzasEnable-                stanzas = [ stanza | stanza <- [minBound..maxBound]-                          , Map.lookup stanza stanzaM == Just True ]-          , not (null stanzas)-          ]--      . addConstraints-          --TODO: [nice to have] should have checked at some point that the-          -- package in question actually has these flags.-          [ LabeledPackageConstraint-              (PackageConstraintFlags pkgname flags)-              ConstraintSourceConfigFlagOrTarget-          | (pkgname, flags) <- Map.toList solverSettingFlagAssignments ]--      . addConstraints-          --TODO: [nice to have] we have user-supplied flags for unspecified-          -- local packages (as well as specific per-package flags). For the-          -- former we just apply all these flags to all local targets which-          -- is silly. We should check if the flags are appropriate.-          [ LabeledPackageConstraint-              (PackageConstraintFlags pkgname flags)-              ConstraintSourceConfigFlagOrTarget-          | let flags = solverSettingFlagAssignment-          , not (null flags)-          , pkg <- localPackages-          , let pkgname = packageName pkg ]--      $ stdResolverParams--    stdResolverParams =-      standardInstallPolicy-        installedPkgIndex sourcePkgDb-        (map SpecificSourcePackage localPackages)------------------------------------------------------------------------------------ * Install plan post-processing----------------------------------------------------------------------------------- This phase goes from the InstallPlan we get from the solver and has to--- make an elaborated install plan.------ We go in two steps:------  1. elaborate all the source packages that the solver has chosen.---  2. swap source packages for pre-existing installed packages wherever---     possible.------ We do it in this order, elaborating and then replacing, because the easiest--- way to calculate the installed package ids used for the replacement step is--- from the elaborated configuration for each package.-------------------------------------------------------------------------------------- * Install plan elaboration----------------------------------------------------------------------------------- | Produce an elaborated install plan using the policy for local builds with--- a nix-style shared store.------ In theory should be able to make an elaborated install plan with a policy--- matching that of the classic @cabal install --user@ or @--global@----elaborateInstallPlan-  :: Platform -> Compiler -> ProgramDb-  -> DistDirLayout-  -> CabalDirLayout-  -> SolverInstallPlan-  -> PackagesImplicitSetupDeps-  -> [SourcePackage]-  -> Map PackageId PackageSourceHash-  -> InstallDirs.InstallDirTemplates-  -> ProjectConfigShared-  -> PackageConfig-  -> Map PackageName PackageConfig-  -> (ElaboratedInstallPlan, ElaboratedSharedConfig)-elaborateInstallPlan platform compiler compilerprogdb-                     DistDirLayout{..}-                     cabalDirLayout@CabalDirLayout{cabalStorePackageDB}-                     solverPlan pkgsImplicitSetupDeps localPackages-                     sourcePackageHashes-                     defaultInstallDirs-                     _sharedPackageConfig-                     localPackagesConfig-                     perPackageConfig =-    (elaboratedInstallPlan, elaboratedSharedConfig)-  where-    elaboratedSharedConfig =-      ElaboratedSharedConfig {-        pkgConfigPlatform      = platform,-        pkgConfigCompiler      = compiler,-        pkgConfigCompilerProgs = compilerprogdb-      }--    elaboratedInstallPlan =-      flip InstallPlan.mapPreservingGraph solverPlan $ \mapDep planpkg ->-        case planpkg of-          InstallPlan.PreExisting pkg ->-            InstallPlan.PreExisting pkg--          InstallPlan.Configured  pkg ->-            InstallPlan.Configured-              (elaborateConfiguredPackage (fixupDependencies mapDep pkg))--          _ -> error "elaborateInstallPlan: unexpected package state"--    -- remap the installed package ids of the direct deps, since we're-    -- changing the installed package ids of all the packages to use the-    -- final nix-style hashed ids.-    fixupDependencies mapDep-       (ConfiguredPackage pkg flags stanzas deps) =-        ConfiguredPackage pkg flags stanzas deps'-      where-        deps' = fmap (map (\d -> d { confInstId = mapDep (confInstId d) })) deps--    elaborateConfiguredPackage :: ConfiguredPackage-                               -> ElaboratedConfiguredPackage-    elaborateConfiguredPackage-        pkg@(ConfiguredPackage (SourcePackage pkgid gdesc srcloc descOverride)-                               flags stanzas deps) =-        elaboratedPackage-      where-        -- Knot tying: the final elaboratedPackage includes the-        -- pkgInstalledId, which is calculated by hashing many-        -- of the other fields of the elaboratedPackage.-        ---        elaboratedPackage = ElaboratedConfiguredPackage {..}--        pkgInstalledId-          | shouldBuildInplaceOnly pkg-          = mkUnitId (display pkgid ++ "-inplace")--          | otherwise-          = assert (isJust pkgSourceHash) $-            hashedInstalledPackageId-              (packageHashInputs-                elaboratedSharedConfig-                elaboratedPackage)  -- recursive use of elaboratedPackage--          | otherwise-          = error $ "elaborateInstallPlan: non-inplace package "-                 ++ " is missing a source hash: " ++ display pkgid--        -- All the other fields of the ElaboratedConfiguredPackage-        ---        pkgSourceId         = pkgid-        pkgDescription      = let Right (desc, _) =-                                    PD.finalizePackageDescription-                                    flags (const True)-                                    platform (compilerInfo compiler)-                                    [] gdesc-                               in desc-        pkgFlagAssignment   = flags-        pkgFlagDefaults     = [ (Cabal.flagName flag, Cabal.flagDefault flag)-                              | flag <- PD.genPackageFlags gdesc ]-        pkgDependencies     = deps-        pkgStanzasAvailable = Set.fromList stanzas-        pkgStanzasRequested =-            Map.fromList $ [ (TestStanzas,  v) | v <- maybeToList tests ]-                        ++ [ (BenchStanzas, v) | v <- maybeToList benchmarks ]-          where-            tests, benchmarks :: Maybe Bool-            tests      = perPkgOptionMaybe pkgid packageConfigTests-            benchmarks = perPkgOptionMaybe pkgid packageConfigBenchmarks--        -- These sometimes get adjusted later-        pkgStanzasEnabled   = Set.empty-        pkgBuildTargets     = []-        pkgReplTarget       = Nothing-        pkgBuildHaddocks    = False--        pkgSourceLocation   = srcloc-        pkgSourceHash       = Map.lookup pkgid sourcePackageHashes-        pkgBuildStyle       = if shouldBuildInplaceOnly pkg-                                then BuildInplaceOnly else BuildAndInstall-        pkgBuildPackageDBStack    = buildAndRegisterDbs-        pkgRegisterPackageDBStack = buildAndRegisterDbs-        pkgRequiresRegistration   = isJust (Cabal.condLibrary gdesc)--        pkgSetupScriptStyle       = packageSetupScriptStylePostSolver-                                      pkgsImplicitSetupDeps pkg pkgDescription-        pkgSetupScriptCliVersion  = packageSetupScriptSpecVersion-                                      pkgSetupScriptStyle pkgDescription deps-        pkgSetupPackageDBStack    = buildAndRegisterDbs--        buildAndRegisterDbs-          | shouldBuildInplaceOnly pkg = inplacePackageDbs-          | otherwise                  = storePackageDbs--        pkgDescriptionOverride    = descOverride--        pkgVanillaLib    = perPkgOptionFlag pkgid True packageConfigVanillaLib --TODO: [required feature]: also needs to be handled recursively-        pkgSharedLib     = pkgid `Set.member` pkgsUseSharedLibrary-        pkgDynExe        = perPkgOptionFlag pkgid False packageConfigDynExe-        pkgGHCiLib       = perPkgOptionFlag pkgid False packageConfigGHCiLib --TODO: [required feature] needs to default to enabled on windows still--        pkgProfExe       = perPkgOptionFlag pkgid False packageConfigProf-        pkgProfLib       = pkgid `Set.member` pkgsUseProfilingLibrary--        (pkgProfExeDetail,-         pkgProfLibDetail) = perPkgOptionLibExeFlag pkgid ProfDetailDefault-                               packageConfigProfDetail-                               packageConfigProfLibDetail-        pkgCoverage      = perPkgOptionFlag pkgid False packageConfigCoverage--        pkgOptimization  = perPkgOptionFlag pkgid NormalOptimisation packageConfigOptimization-        pkgSplitObjs     = perPkgOptionFlag pkgid False packageConfigSplitObjs-        pkgStripLibs     = perPkgOptionFlag pkgid False packageConfigStripLibs-        pkgStripExes     = perPkgOptionFlag pkgid False packageConfigStripExes-        pkgDebugInfo     = perPkgOptionFlag pkgid NoDebugInfo packageConfigDebugInfo--        -- Combine the configured compiler prog settings with the user-supplied-        -- config. For the compiler progs any user-supplied config was taken-        -- into account earlier when configuring the compiler so its ok that-        -- our configured settings for the compiler override the user-supplied-        -- config here.-        pkgProgramPaths  = Map.fromList-                             [ (programId prog, programPath prog)-                             | prog <- configuredPrograms compilerprogdb ]-                        <> perPkgOptionMapLast pkgid packageConfigProgramPaths-        pkgProgramArgs   = Map.fromList-                             [ (programId prog, args)-                             | prog <- configuredPrograms compilerprogdb-                             , let args = programOverrideArgs prog-                             , not (null args)-                             ]-                        <> perPkgOptionMapMappend pkgid packageConfigProgramArgs-        pkgProgramPathExtra    = perPkgOptionNubList pkgid packageConfigProgramPathExtra-        pkgConfigureScriptArgs = perPkgOptionList pkgid packageConfigConfigureArgs-        pkgExtraLibDirs        = perPkgOptionList pkgid packageConfigExtraLibDirs-        pkgExtraFrameworkDirs  = perPkgOptionList pkgid packageConfigExtraFrameworkDirs-        pkgExtraIncludeDirs    = perPkgOptionList pkgid packageConfigExtraIncludeDirs-        pkgProgPrefix          = perPkgOptionMaybe pkgid packageConfigProgPrefix-        pkgProgSuffix          = perPkgOptionMaybe pkgid packageConfigProgSuffix--        pkgInstallDirs-          | shouldBuildInplaceOnly pkg-          -- use the ordinary default install dirs-          = (InstallDirs.absoluteInstallDirs-               pkgid-               (installedUnitId pkg)-               (compilerInfo compiler)-               InstallDirs.NoCopyDest-               platform-               defaultInstallDirs) {--              InstallDirs.libsubdir  = "", -- absoluteInstallDirs sets these as-              InstallDirs.datasubdir = ""  -- 'undefined' but we have to use-            }                              -- them as "Setup.hs configure" args--          | otherwise-          -- use special simplified install dirs-          = storePackageInstallDirs-              cabalDirLayout-              (compilerId compiler)-              pkgInstalledId--        pkgHaddockHoogle       = perPkgOptionFlag pkgid False packageConfigHaddockHoogle-        pkgHaddockHtml         = perPkgOptionFlag pkgid False packageConfigHaddockHtml-        pkgHaddockHtmlLocation = perPkgOptionMaybe pkgid packageConfigHaddockHtmlLocation-        pkgHaddockExecutables  = perPkgOptionFlag pkgid False packageConfigHaddockExecutables-        pkgHaddockTestSuites   = perPkgOptionFlag pkgid False packageConfigHaddockTestSuites-        pkgHaddockBenchmarks   = perPkgOptionFlag pkgid False packageConfigHaddockBenchmarks-        pkgHaddockInternal     = perPkgOptionFlag pkgid False packageConfigHaddockInternal-        pkgHaddockCss          = perPkgOptionMaybe pkgid packageConfigHaddockCss-        pkgHaddockHscolour     = perPkgOptionFlag pkgid False packageConfigHaddockHscolour-        pkgHaddockHscolourCss  = perPkgOptionMaybe pkgid packageConfigHaddockHscolourCss-        pkgHaddockContents     = perPkgOptionMaybe pkgid packageConfigHaddockContents--    perPkgOptionFlag  :: PackageId -> a ->  (PackageConfig -> Flag a) -> a-    perPkgOptionMaybe :: PackageId ->       (PackageConfig -> Flag a) -> Maybe a-    perPkgOptionList  :: PackageId ->       (PackageConfig -> [a])    -> [a]--    perPkgOptionFlag  pkgid def f = fromFlagOrDefault def (lookupPerPkgOption pkgid f)-    perPkgOptionMaybe pkgid     f = flagToMaybe (lookupPerPkgOption pkgid f)-    perPkgOptionList  pkgid     f = lookupPerPkgOption pkgid f-    perPkgOptionNubList    pkgid f = fromNubList   (lookupPerPkgOption pkgid f)-    perPkgOptionMapLast    pkgid f = getMapLast    (lookupPerPkgOption pkgid f)-    perPkgOptionMapMappend pkgid f = getMapMappend (lookupPerPkgOption pkgid f)--    perPkgOptionLibExeFlag pkgid def fboth flib = (exe, lib)-      where-        exe = fromFlagOrDefault def bothflag-        lib = fromFlagOrDefault def (bothflag <> libflag)--        bothflag = lookupPerPkgOption pkgid fboth-        libflag  = lookupPerPkgOption pkgid flib--    lookupPerPkgOption :: (Package pkg, Monoid m)-                       => pkg -> (PackageConfig -> m) -> m-    lookupPerPkgOption pkg f-      -- the project config specifies values that apply to packages local to-      -- but by default non-local packages get all default config values-      -- the project, and can specify per-package values for any package,-      | isLocalToProject pkg = local <> perpkg-      | otherwise            =          perpkg-      where-        local  = f localPackagesConfig-        perpkg = maybe mempty f (Map.lookup (packageName pkg) perPackageConfig)--    inplacePackageDbs = storePackageDbs-                     ++ [ distPackageDB (compilerId compiler) ]--    storePackageDbs   = [ GlobalPackageDB-                        , cabalStorePackageDB (compilerId compiler) ]--    -- For this local build policy, every package that lives in a local source-    -- dir (as opposed to a tarball), or depends on such a package, will be-    -- built inplace into a shared dist dir. Tarball packages that depend on-    -- source dir packages will also get unpacked locally.-    shouldBuildInplaceOnly :: HasUnitId pkg => pkg -> Bool-    shouldBuildInplaceOnly pkg = Set.member (installedPackageId pkg)-                                            pkgsToBuildInplaceOnly--    pkgsToBuildInplaceOnly :: Set InstalledPackageId-    pkgsToBuildInplaceOnly =-        Set.fromList-      $ map installedPackageId-      $ InstallPlan.reverseDependencyClosure-          solverPlan-          [ fakeUnitId (packageId pkg)-          | pkg <- localPackages ]--    isLocalToProject :: Package pkg => pkg -> Bool-    isLocalToProject pkg = Set.member (packageId pkg)-                                      pkgsLocalToProject--    pkgsLocalToProject :: Set PackageId-    pkgsLocalToProject = Set.fromList [ packageId pkg | pkg <- localPackages ]--    pkgsUseSharedLibrary :: Set PackageId-    pkgsUseSharedLibrary =-        packagesWithDownwardClosedProperty needsSharedLib-      where-        needsSharedLib pkg =-            fromMaybe compilerShouldUseSharedLibByDefault-                      (liftM2 (||) pkgSharedLib pkgDynExe)-          where-            pkgid        = packageId pkg-            pkgSharedLib = perPkgOptionMaybe pkgid packageConfigSharedLib-            pkgDynExe    = perPkgOptionMaybe pkgid packageConfigDynExe--    --TODO: [code cleanup] move this into the Cabal lib. It's currently open-    -- coded in Distribution.Simple.Configure, but should be made a proper-    -- function of the Compiler or CompilerInfo.-    compilerShouldUseSharedLibByDefault =-      case compilerFlavor compiler of-        GHC   -> GHC.isDynamic compiler-        GHCJS -> GHCJS.isDynamic compiler-        _     -> False--    pkgsUseProfilingLibrary :: Set PackageId-    pkgsUseProfilingLibrary =-        packagesWithDownwardClosedProperty needsProfilingLib-      where-        needsProfilingLib pkg =-            fromFlagOrDefault False (profBothFlag <> profLibFlag)-          where-            pkgid        = packageId pkg-            profBothFlag = lookupPerPkgOption pkgid packageConfigProf-            profLibFlag  = lookupPerPkgOption pkgid packageConfigProfLib-            --TODO: [code cleanup] unused: the old deprecated packageConfigProfExe--    packagesWithDownwardClosedProperty property =-        Set.fromList-      $ map packageId-      $ InstallPlan.dependencyClosure-          solverPlan-          [ installedPackageId pkg-          | pkg <- InstallPlan.toList solverPlan-          , property pkg ] -- just the packages that satisfy the propety-      --TODO: [nice to have] this does not check the config consistency,-      -- e.g. a package explicitly turning off profiling, but something-      -- depending on it that needs profiling. This really needs a separate-      -- package config validation/resolution pass.--      --TODO: [nice to have] config consistency checking:-      -- * profiling libs & exes, exe needs lib, recursive-      -- * shared libs & exes, exe needs lib, recursive-      -- * vanilla libs & exes, exe needs lib, recursive-      -- * ghci or shared lib needed by TH, recursive, ghc version dependent--------------------------------- Build targets------- Refer to ProjectPlanning.Types for details of these important types:---- data PackageTarget = ...--- data ComponentTarget = ...--- data SubComponentTarget = ...-----TODO: this needs to report some user target/config errors-elaboratePackageTargets :: ElaboratedConfiguredPackage -> [PackageTarget]-                        -> ([ComponentTarget], Maybe ComponentTarget, Bool)-elaboratePackageTargets ElaboratedConfiguredPackage{..} targets =-    let buildTargets  = nubComponentTargets-                      . map compatSubComponentTargets-                      . concatMap elaborateBuildTarget-                      $ targets-        --TODO: instead of listToMaybe we should be reporting an error here-        replTargets   = listToMaybe-                      . nubComponentTargets-                      . map compatSubComponentTargets-                      . concatMap elaborateReplTarget-                      $ targets-        buildHaddocks = HaddockDefaultComponents `elem` targets--     in (buildTargets, replTargets, buildHaddocks)-  where-    --TODO: need to report an error here if defaultComponents is empty-    elaborateBuildTarget  BuildDefaultComponents    = pkgDefaultComponents-    elaborateBuildTarget (BuildSpecificComponent t) = [t]-    elaborateBuildTarget  _                         = []--    --TODO: need to report an error here if defaultComponents is empty-    elaborateReplTarget  ReplDefaultComponent     = take 1 pkgDefaultComponents-    elaborateReplTarget (ReplSpecificComponent t) = [t]-    elaborateReplTarget  _                        = []--    pkgDefaultComponents =-        [ ComponentTarget cname WholeComponent-        | c <- Cabal.pkgComponents pkgDescription-        , PD.buildable (Cabal.componentBuildInfo c)-        , let cname = Cabal.componentName c-        , enabledOptionalStanza cname-        ]-      where-        enabledOptionalStanza cname =-          case componentOptionalStanza cname of-            Nothing     -> True-            Just stanza -> Map.lookup stanza pkgStanzasRequested-                        == Just True--    -- Not all Cabal Setup.hs versions support sub-component targets, so switch-    -- them over to the whole component-    compatSubComponentTargets :: ComponentTarget -> ComponentTarget-    compatSubComponentTargets target@(ComponentTarget cname _subtarget)-      | not setupHsSupportsSubComponentTargets-                  = ComponentTarget cname WholeComponent-      | otherwise = target--    -- Actually the reality is that no current version of Cabal's Setup.hs-    -- build command actually support building specific files or modules.-    setupHsSupportsSubComponentTargets = False-    -- TODO: when that changes, adjust this test, e.g.-    -- | pkgSetupScriptCliVersion >= Version [x,y] []--    nubComponentTargets :: [ComponentTarget] -> [ComponentTarget]-    nubComponentTargets =-        concatMap (wholeComponentOverrides . map snd)-      . groupBy ((==)    `on` fst)-      . sortBy  (compare `on` fst)-      . map (\t@(ComponentTarget cname _) -> (cname, t))--    -- If we're building the whole component then that the only target all we-    -- need, otherwise we can have several targets within the component.-    wholeComponentOverrides :: [ComponentTarget] -> [ComponentTarget]-    wholeComponentOverrides ts =-      case [ t | t@(ComponentTarget _ WholeComponent) <- ts ] of-        (t:_) -> [t]-        []    -> ts---pkgHasEphemeralBuildTargets :: ElaboratedConfiguredPackage -> Bool-pkgHasEphemeralBuildTargets pkg =-    isJust (pkgReplTarget pkg)- || (not . null) [ () | ComponentTarget _ subtarget <- pkgBuildTargets pkg-                      , subtarget /= WholeComponent ]---- | The components that we'll build all of, meaning that after they're built--- we can skip building them again (unlike with building just some modules or--- other files within a component).----pkgBuildTargetWholeComponents :: ElaboratedConfiguredPackage-                              -> Set ComponentName-pkgBuildTargetWholeComponents pkg =-    Set.fromList-      [ cname | ComponentTarget cname WholeComponent <- pkgBuildTargets pkg ]------------------------------------------------------------------------------------ * Install plan pruning----------------------------------------------------------------------------------- | Given a set of package targets (and optionally component targets within--- those packages), take the subset of the install plan needed to build those--- targets. Also, update the package config to specify which optional stanzas--- to enable, and which targets within each package to build.----pruneInstallPlanToTargets :: Map InstalledPackageId [PackageTarget]-                          -> ElaboratedInstallPlan -> ElaboratedInstallPlan-pruneInstallPlanToTargets perPkgTargetsMap =-    either (\_ -> assert False undefined) id-  . InstallPlan.new False-  . PackageIndex.fromList-    -- We have to do this in two passes-  . pruneInstallPlanPass2-  . pruneInstallPlanPass1 perPkgTargetsMap-  . InstallPlan.toList---- The first pass does three things:------ * Set the build targets based on the user targets (but not rev deps yet).--- * A first go at determining which optional stanzas (testsuites, benchmarks)---   are needed. We have a second go in the next pass.--- * Take the dependency closure using pruned dependencies. We prune deps that---   are used only by unneeded optional stanzas. These pruned deps are only---   used for the dependency closure and are not persisted in this pass.----pruneInstallPlanPass1 :: Map InstalledPackageId [PackageTarget]-                      -> [ElaboratedPlanPackage]-                      -> [ElaboratedPlanPackage]-pruneInstallPlanPass1 perPkgTargetsMap pkgs =-    map fst $-    dependencyClosure-      (installedPackageId . fst)  -- the pkg id-      snd                         -- the pruned deps-      [ (pkg', pruneOptionalDependencies pkg')-      | pkg <- pkgs-      , let pkg' = mapConfiguredPackage-                     (pruneOptionalStanzas . setBuildTargets) pkg-      ]-      (Map.keys perPkgTargetsMap)-  where-    -- Elaborate and set the targets we'll build for this package. This is just-    -- based on the targets from the user, not targets implied by reverse-    -- depencencies. Those comes in the second pass once we know the rev deps.-    ---    setBuildTargets pkg =-        pkg {-          pkgBuildTargets   = buildTargets,-          pkgReplTarget     = replTarget,-          pkgBuildHaddocks  = buildHaddocks-        }-      where-        (buildTargets, replTarget, buildHaddocks)-                = elaboratePackageTargets pkg targets-        targets = fromMaybe []-                $ Map.lookup (installedPackageId pkg) perPkgTargetsMap--    -- Decide whether or not to enable testsuites and benchmarks-    ---    -- The testsuite and benchmark targets are somewhat special in that we need-    -- to configure the packages with them enabled, and we need to do that even-    -- if we only want to build one of several testsuites.-    ---    -- There are two cases in which we will enable the testsuites (or-    -- benchmarks): if one of the targets is a testsuite, or if all of the-    -- testsuite depencencies are already cached in the store. The rationale-    -- for the latter is to minimise how often we have to reconfigure due to-    -- the particular targets we choose to build. Otherwise choosing to build-    -- a testsuite target, and then later choosing to build an exe target-    -- would involve unnecessarily reconfiguring the package with testsuites-    -- disabled. Technically this introduces a little bit of stateful-    -- behaviour to make this "sticky", but it should be benign.-    ---    pruneOptionalStanzas pkg = pkg { pkgStanzasEnabled = stanzas }-      where-        stanzas :: Set OptionalStanza-        stanzas = optionalStanzasRequiredByTargets  pkg-               <> optionalStanzasRequestedByDefault pkg-               <> optionalStanzasWithDepsAvailable availablePkgs pkg--    -- Calculate package depencencies but cut out those needed only by-    -- optional stanzas that we've determined we will not enable.-    -- These pruned deps are not persisted in this pass since they're based on-    -- the optional stanzas and we'll make further tweaks to the optional-    -- stanzas in the next pass.-    ---    pruneOptionalDependencies :: ElaboratedPlanPackage -> [InstalledPackageId]-    pruneOptionalDependencies (InstallPlan.Configured pkg) =-        (CD.flatDeps . CD.filterDeps keepNeeded) (depends pkg)-      where-        keepNeeded (CD.ComponentTest  _) _ = TestStanzas  `Set.member` stanzas-        keepNeeded (CD.ComponentBench _) _ = BenchStanzas `Set.member` stanzas-        keepNeeded _                     _ = True-        stanzas = pkgStanzasEnabled pkg-    pruneOptionalDependencies pkg =-        CD.flatDeps (depends pkg)--    optionalStanzasRequiredByTargets :: ElaboratedConfiguredPackage-                                     -> Set OptionalStanza-    optionalStanzasRequiredByTargets pkg =-      Set.fromList-        [ stanza-        | ComponentTarget cname _ <- pkgBuildTargets pkg-                                  ++ maybeToList (pkgReplTarget pkg)-        , stanza <- maybeToList (componentOptionalStanza cname)-        ]--    optionalStanzasRequestedByDefault :: ElaboratedConfiguredPackage-                                      -> Set OptionalStanza-    optionalStanzasRequestedByDefault =-        Map.keysSet-      . Map.filter (id :: Bool -> Bool)-      . pkgStanzasRequested--    availablePkgs =-      Set.fromList-        [ installedPackageId pkg-        | InstallPlan.PreExisting pkg <- pkgs ]--optionalStanzasWithDepsAvailable :: Set InstalledPackageId-                                 -> ElaboratedConfiguredPackage-                                 -> Set OptionalStanza-optionalStanzasWithDepsAvailable availablePkgs pkg =-    Set.fromList-      [ stanza-      | stanza <- Set.toList (pkgStanzasAvailable pkg)-      , let deps :: [InstalledPackageId]-            deps = map installedPackageId-                 $ CD.select (optionalStanzaDeps stanza)-                             (pkgDependencies pkg)-      , all (`Set.member` availablePkgs) deps-      ]-  where-    optionalStanzaDeps TestStanzas  (CD.ComponentTest  _) = True-    optionalStanzaDeps BenchStanzas (CD.ComponentBench _) = True-    optionalStanzaDeps _            _                     = False----- The second pass does three things:------ * A second go at deciding which optional stanzas to enable.--- * Prune the depencencies based on the final choice of optional stanzas.--- * Extend the targets within each package to build, now we know the reverse---   depencencies, ie we know which libs are needed as deps by other packages.------ Achieving sticky behaviour with enabling\/disabling optional stanzas is--- tricky. The first approximation was handled by the first pass above, but--- it's not quite enough. That pass will enable stanzas if all of the deps--- of the optional stanza are already instaled /in the store/. That's important--- but it does not account for depencencies that get built inplace as part of--- the project. We cannot take those inplace build deps into account in the--- pruning pass however because we don't yet know which ones we're going to--- build. Once we do know, we can have another go and enable stanzas that have--- all their deps available. Now we can consider all packages in the pruned--- plan to be available, including ones we already decided to build from--- source.------ Deciding which targets to build depends on knowing which packages have--- reverse dependencies (ie are needed). This requires the result of first--- pass, which is another reason we have to split it into two passes.------ Note that just because we might enable testsuites or benchmarks (in the--- first or second pass) doesn't mean that we build all (or even any) of them.--- That depends on which targets we picked in the first pass.----pruneInstallPlanPass2 :: [ElaboratedPlanPackage]-                      -> [ElaboratedPlanPackage]-pruneInstallPlanPass2 pkgs =-    map (mapConfiguredPackage setStanzasDepsAndTargets) pkgs-  where-    setStanzasDepsAndTargets pkg =-        pkg {-          pkgStanzasEnabled = stanzas,-          pkgDependencies   = CD.filterDeps keepNeeded (pkgDependencies pkg),-          pkgBuildTargets   = pkgBuildTargets pkg ++ targetsRequiredForRevDeps-        }-      where-        stanzas :: Set OptionalStanza-        stanzas = pkgStanzasEnabled pkg-               <> optionalStanzasWithDepsAvailable availablePkgs pkg--        keepNeeded (CD.ComponentTest  _) _ = TestStanzas  `Set.member` stanzas-        keepNeeded (CD.ComponentBench _) _ = BenchStanzas `Set.member` stanzas-        keepNeeded _                     _ = True--        targetsRequiredForRevDeps =-          [ ComponentTarget CLibName WholeComponent-          -- if anything needs this pkg, build the library component-          | installedPackageId pkg `Set.member` hasReverseLibDeps-          ]-        --TODO: also need to track build-tool rev-deps for exes--    availablePkgs :: Set InstalledPackageId-    availablePkgs = Set.fromList (map installedPackageId pkgs)--    hasReverseLibDeps :: Set InstalledPackageId-    hasReverseLibDeps =-      Set.fromList [ depid | pkg <- pkgs-                           , depid <- CD.flatDeps (depends pkg) ]---mapConfiguredPackage :: (ElaboratedConfiguredPackage -> ElaboratedConfiguredPackage)-                     -> ElaboratedPlanPackage-                     -> ElaboratedPlanPackage-mapConfiguredPackage f (InstallPlan.Configured pkg) =-  InstallPlan.Configured (f pkg)-mapConfiguredPackage _ pkg = pkg--componentOptionalStanza :: Cabal.ComponentName -> Maybe OptionalStanza-componentOptionalStanza (Cabal.CTestName  _) = Just TestStanzas-componentOptionalStanza (Cabal.CBenchName _) = Just BenchStanzas-componentOptionalStanza _                    = Nothing---dependencyClosure :: (pkg -> InstalledPackageId)-                  -> (pkg -> [InstalledPackageId])-                  -> [pkg]-                  -> [InstalledPackageId]-                  -> [pkg]-dependencyClosure pkgid deps allpkgs =-    map vertexToPkg-  . concatMap Tree.flatten-  . Graph.dfs graph-  . map pkgidToVertex-  where-    (graph, vertexToPkg, pkgidToVertex) = dependencyGraph pkgid deps allpkgs--dependencyGraph :: (pkg -> InstalledPackageId)-                -> (pkg -> [InstalledPackageId])-                -> [pkg]-                -> (Graph.Graph,-                    Graph.Vertex -> pkg,-                    InstalledPackageId -> Graph.Vertex)-dependencyGraph pkgid deps pkgs =-    (graph, vertexToPkg', pkgidToVertex')-  where-    (graph, vertexToPkg, pkgidToVertex) =-      Graph.graphFromEdges [ ( pkg, pkgid pkg, deps pkg )-                           | pkg <- pkgs ]-    vertexToPkg'   = (\(pkg,_,_) -> pkg)-                   . vertexToPkg-    pkgidToVertex' = fromMaybe (error "dependencyGraph: lookup failure")-                   . pkgidToVertex--------------------------------- Setup.hs script policy------- Handling for Setup.hs scripts is a bit tricky, part of it lives in the--- solver phase, and part in the elaboration phase. We keep the helper--- functions for both phases together here so at least you can see all of it--- in one place.------ There are four major cases for Setup.hs handling:------  1. @build-type@ Custom with a @custom-setup@ section---  2. @build-type@ Custom without a @custom-setup@ section---  3. @build-type@ not Custom with @cabal-version >  $our-cabal-version@---  4. @build-type@ not Custom with @cabal-version <= $our-cabal-version@------ It's also worth noting that packages specifying @cabal-version: >= 1.23@--- or later that have @build-type@ Custom will always have a @custom-setup@--- section. Therefore in case 2, the specified @cabal-version@ will always be--- less than 1.23.------ In cases 1 and 2 we obviously have to build an external Setup.hs script,--- while in case 4 we can use the internal library API. In case 3 we also have--- to build an external Setup.hs script because the package needs a later--- Cabal lib version than we can support internally.------ data SetupScriptStyle = ...  -- see ProjectPlanning.Types---- | Work out the 'SetupScriptStyle' given the package description.------ This only works on original packages before we give them to the solver,--- since after the solver some implicit setup deps are made explicit.------ See 'rememberImplicitSetupDeps' and 'packageSetupScriptStylePostSolver'.----packageSetupScriptStylePreSolver :: PD.PackageDescription -> SetupScriptStyle-packageSetupScriptStylePreSolver pkg-  | buildType == PD.Custom-  , isJust (PD.setupBuildInfo pkg)-  = SetupCustomExplicitDeps--  | buildType == PD.Custom-  = SetupCustomImplicitDeps--  | PD.specVersion pkg > cabalVersion -- one cabal-install is built against-  = SetupNonCustomExternalLib--  | otherwise-  = SetupNonCustomInternalLib-  where-    buildType = fromMaybe PD.Custom (PD.buildType pkg)----- | Part of our Setup.hs handling policy is implemented by getting the solver--- to work out setup dependencies for packages. The solver already handles--- packages that explicitly specify setup dependencies, but we can also tell--- the solver to treat other packages as if they had setup dependencies.--- That's what this function does, it gets called by the solver for all--- packages that don't already have setup dependencies.------ The dependencies we want to add is different for each 'SetupScriptStyle'.------ Note that adding default deps means these deps are actually /added/ to the--- packages that we get out of the solver in the 'SolverInstallPlan'. Making--- implicit setup deps explicit is a problem in the post-solver stages because--- we still need to distinguish the case of explicit and implict setup deps.--- See 'rememberImplicitSetupDeps'.----defaultSetupDeps :: Compiler -> Platform-                 -> PD.PackageDescription-                 -> Maybe [Dependency]-defaultSetupDeps compiler platform pkg =-    case packageSetupScriptStylePreSolver pkg of--      -- For packages with build type custom that do not specify explicit-      -- setup dependencies, we add a dependency on Cabal and a number-      -- of other packages.-      SetupCustomImplicitDeps ->-        Just $-        [ Dependency depPkgname anyVersion-        | depPkgname <- legacyCustomSetupPkgs compiler platform ] ++-        [ Dependency cabalPkgname cabalConstraint-        | packageName pkg /= cabalPkgname ]-        where-          -- The Cabal dep is slightly special:-          -- * We omit the dep for the Cabal lib itself, since it bootstraps.-          -- * We constrain it to be >= 1.18 < 2-          ---          cabalConstraint   = orLaterVersion cabalCompatMinVer-                                `intersectVersionRanges`-                              orLaterVersion (PD.specVersion pkg)-                                `intersectVersionRanges`-                              earlierVersion cabalCompatMaxVer-          -- The idea here is that at some point we will make significant-          -- breaking changes to the Cabal API that Setup.hs scripts use.-          -- So for old custom Setup scripts that do not specify explicit-          -- constraints, we constrain them to use a compatible Cabal version.-          cabalCompatMaxVer = Version [1,25] []-          -- In principle we can talk to any old Cabal version, and we need to-          -- be able to do that for custom Setup scripts that require older-          -- Cabal lib versions. However in practice we have currently have-          -- problems with Cabal-1.16. (1.16 does not know about build targets)-          -- If this is fixed we can relax this constraint.-          cabalCompatMinVer = Version [1,18] []--      -- For other build types (like Simple) if we still need to compile an-      -- external Setup.hs, it'll be one of the simple ones that only depends-      -- on Cabal and base.-      SetupNonCustomExternalLib ->-        Just [ Dependency cabalPkgname cabalConstraint-             , Dependency basePkgname  anyVersion ]-        where-          cabalConstraint = orLaterVersion (PD.specVersion pkg)--      -- The internal setup wrapper method has no deps at all.-      SetupNonCustomInternalLib -> Just []--      SetupCustomExplicitDeps ->-        error $ "defaultSetupDeps: called for a package with explicit "-             ++ "setup deps: " ++ display (packageId pkg)----- | See 'rememberImplicitSetupDeps' for details.-type PackagesImplicitSetupDeps = Set InstalledPackageId---- | A consequence of using 'defaultSetupDeps' in 'planPackages' is that by--- making implicit setup deps explicit we loose track of which packages--- originally had implicit setup deps. That's important because we do still--- have different behaviour based on the setup style (in particular whether to--- compile a Setup.hs script with version macros).------ So we remember the necessary information in an auxilliary set and use it--- in 'packageSetupScriptStylePreSolver' to recover the full info.----rememberImplicitSetupDeps :: SourcePackageIndex.PackageIndex SourcePackage-                          -> SolverInstallPlan-                          -> (SolverInstallPlan, PackagesImplicitSetupDeps)-rememberImplicitSetupDeps sourcePkgIndex plan =-    (plan, pkgsImplicitSetupDeps)-  where-    pkgsImplicitSetupDeps =-      Set.fromList-        [ installedPackageId pkg-        | InstallPlan.Configured-            pkg@(ConfiguredPackage newpkg _ _ _) <- InstallPlan.toList plan-          -- has explicit setup deps now-        , hasExplicitSetupDeps newpkg-          -- but originally had no setup deps-        , let Just origpkg = SourcePackageIndex.lookupPackageId-                               sourcePkgIndex (packageId pkg)-        , not (hasExplicitSetupDeps origpkg)-        ]--    hasExplicitSetupDeps =-        (SetupCustomExplicitDeps==)-      . packageSetupScriptStylePreSolver-      . PD.packageDescription . packageDescription----- | Use the extra info saved by 'rememberImplicitSetupDeps' to let us work--- out the correct 'SetupScriptStyle'. This should give the same result as--- 'packageSetupScriptStylePreSolver' gave prior to munging the package info--- through the solver.----packageSetupScriptStylePostSolver :: Set InstalledPackageId-                                  -> ConfiguredPackage-                                  -> PD.PackageDescription-                                  -> SetupScriptStyle-packageSetupScriptStylePostSolver pkgsImplicitSetupDeps pkg pkgDescription =-    case packageSetupScriptStylePreSolver pkgDescription of-      SetupCustomExplicitDeps-        | Set.member (installedPackageId pkg) pkgsImplicitSetupDeps-            -> SetupCustomImplicitDeps-      other -> other----- | Work out which version of the Cabal spec we will be using to talk to the--- Setup.hs interface for this package.------ This depends somewhat on the 'SetupScriptStyle' but most cases are a result--- of what the solver picked for us, based on the explicit setup deps or the--- ones added implicitly by 'defaultSetupDeps'.----packageSetupScriptSpecVersion :: Package pkg-                              => SetupScriptStyle-                              -> PD.PackageDescription-                              -> ComponentDeps [pkg]-                              -> Version---- We're going to be using the internal Cabal library, so the spec version of--- that is simply the version of the Cabal library that cabal-install has been--- built with.-packageSetupScriptSpecVersion SetupNonCustomInternalLib _ _ =-    cabalVersion---- If we happen to be building the Cabal lib itself then because that--- bootstraps itself then we use the version of the lib we're building.-packageSetupScriptSpecVersion SetupCustomImplicitDeps pkg _-  | packageName pkg == cabalPkgname-  = packageVersion pkg---- In all other cases we have a look at what version of the Cabal lib the--- solver picked. Or if it didn't depend on Cabal at all (which is very rare)--- then we look at the .cabal file to see what spec version it declares.-packageSetupScriptSpecVersion _ pkg deps =-    case find ((cabalPkgname ==) . packageName) (CD.setupDeps deps) of-      Just dep -> packageVersion dep-      Nothing  -> PD.specVersion pkg---cabalPkgname, basePkgname :: PackageName-cabalPkgname = PackageName "Cabal"-basePkgname  = PackageName "base"---legacyCustomSetupPkgs :: Compiler -> Platform -> [PackageName]-legacyCustomSetupPkgs compiler (Platform _ os) =-    map PackageName $-        [ "array", "base", "binary", "bytestring", "containers"-        , "deepseq", "directory", "filepath", "old-time", "pretty"-        , "process", "time", "transformers" ]-     ++ [ "Win32" | os == Windows ]-     ++ [ "unix"  | os /= Windows ]-     ++ [ "ghc-prim"         | isGHC ]-     ++ [ "template-haskell" | isGHC ]-  where-    isGHC = compilerCompatFlavor GHC compiler--    -- This util is copied here just in this branch to avoid requiring a new-    -- Cabal version. The master branch already does the right thing.-    compilerCompatFlavor :: CompilerFlavor -> Compiler -> Bool-    compilerCompatFlavor flavor comp =-        flavor == compilerFlavor comp-     || flavor `elem` [ flavor' | CompilerId flavor' _ <- compilerCompat comp ]---- The other aspects of our Setup.hs policy lives here where we decide on--- the 'SetupScriptOptions'.------ Our current policy for the 'SetupCustomImplicitDeps' case is that we--- try to make the implicit deps cover everything, and we don't allow the--- compiler to pick up other deps. This may or may not be sustainable, and--- we might have to allow the deps to be non-exclusive, but that itself would--- be tricky since we would have to allow the Setup access to all the packages--- in the store and local dbs.--setupHsScriptOptions :: ElaboratedReadyPackage-                     -> ElaboratedSharedConfig-                     -> FilePath-                     -> FilePath-                     -> Bool-                     -> Lock-                     -> SetupScriptOptions-setupHsScriptOptions (ReadyPackage ElaboratedConfiguredPackage{..} deps)-                     ElaboratedSharedConfig{..} srcdir builddir-                     isParallelBuild cacheLock =-    SetupScriptOptions {-      useCabalVersion          = thisVersion pkgSetupScriptCliVersion,-      useCabalSpecVersion      = Just pkgSetupScriptCliVersion,-      useCompiler              = Just pkgConfigCompiler,-      usePlatform              = Just pkgConfigPlatform,-      usePackageDB             = pkgSetupPackageDBStack,-      usePackageIndex          = Nothing,-      useDependencies          = [ (installedPackageId ipkg, packageId ipkg)-                                 | ipkg <- CD.setupDeps deps ],-      useDependenciesExclusive = True,-      useVersionMacros         = pkgSetupScriptStyle == SetupCustomExplicitDeps,-      useProgramConfig         = pkgConfigCompilerProgs,-      useDistPref              = builddir,-      useLoggingHandle         = Nothing, -- this gets set later-      useWorkingDir            = Just srcdir,-      useWin32CleanHack        = False,   --TODO: [required eventually]-      forceExternalSetupMethod = isParallelBuild,-      setupCacheLock           = Just cacheLock-    }----- | To be used for the input for elaborateInstallPlan.------ TODO: [code cleanup] make InstallDirs.defaultInstallDirs pure.----userInstallDirTemplates :: Compiler-                        -> IO InstallDirs.InstallDirTemplates-userInstallDirTemplates compiler = do-    InstallDirs.defaultInstallDirs-                  (compilerFlavor compiler)-                  True  -- user install-                  False -- unused--storePackageInstallDirs :: CabalDirLayout-                        -> CompilerId-                        -> InstalledPackageId-                        -> InstallDirs.InstallDirs FilePath-storePackageInstallDirs CabalDirLayout{cabalStorePackageDirectory}-                        compid ipkgid =-    InstallDirs.InstallDirs {..}-  where-    prefix       = cabalStorePackageDirectory compid ipkgid-    bindir       = prefix </> "bin"-    libdir       = prefix </> "lib"-    libsubdir    = ""-    dynlibdir    = libdir-    libexecdir   = prefix </> "libexec"-    includedir   = libdir </> "include"-    datadir      = prefix </> "share"-    datasubdir   = ""-    docdir       = datadir </> "doc"-    mandir       = datadir </> "man"-    htmldir      = docdir  </> "html"-    haddockdir   = htmldir-    sysconfdir   = prefix </> "etc"-----TODO: [code cleanup] perhaps reorder this code--- based on the ElaboratedInstallPlan + ElaboratedSharedConfig,--- make the various Setup.hs {configure,build,copy} flags---setupHsConfigureFlags :: ElaboratedReadyPackage-                      -> ElaboratedSharedConfig-                      -> Verbosity-                      -> FilePath-                      -> Cabal.ConfigFlags-setupHsConfigureFlags (ReadyPackage-                         pkg@ElaboratedConfiguredPackage{..}-                         pkgdeps)-                      sharedConfig@ElaboratedSharedConfig{..}-                      verbosity builddir =-    assert (sanityCheckElaboratedConfiguredPackage sharedConfig pkg)-    Cabal.ConfigFlags {..}-  where-    configDistPref            = toFlag builddir-    configVerbosity           = toFlag verbosity--    configIPID                = toFlag (display (installedUnitId pkg))--    configProgramPaths        = Map.toList pkgProgramPaths-    configProgramArgs         = Map.toList pkgProgramArgs-    configProgramPathExtra    = toNubList pkgProgramPathExtra-    configHcFlavor            = toFlag (compilerFlavor pkgConfigCompiler)-    configHcPath              = mempty -- we use configProgramPaths instead-    configHcPkg               = mempty -- we use configProgramPaths instead--    configVanillaLib          = toFlag pkgVanillaLib-    configSharedLib           = toFlag pkgSharedLib-    configDynExe              = toFlag pkgDynExe-    configGHCiLib             = toFlag pkgGHCiLib-    configProfExe             = mempty-    configProfLib             = toFlag pkgProfLib-    configProf                = toFlag pkgProfExe--    -- configProfDetail is for exe+lib, but overridden by configProfLibDetail-    -- so we specify both so we can specify independently-    configProfDetail          = toFlag pkgProfExeDetail-    configProfLibDetail       = toFlag pkgProfLibDetail--    configCoverage            = toFlag pkgCoverage-    configLibCoverage         = mempty--    configOptimization        = toFlag pkgOptimization-    configSplitObjs           = toFlag pkgSplitObjs-    configStripExes           = toFlag pkgStripExes-    configStripLibs           = toFlag pkgStripLibs-    configDebugInfo           = toFlag pkgDebugInfo-    configAllowNewer          = mempty -- we use configExactConfiguration True--    configConfigurationsFlags = pkgFlagAssignment-    configConfigureArgs       = pkgConfigureScriptArgs-    configExtraLibDirs        = pkgExtraLibDirs-    configExtraFrameworkDirs  = pkgExtraFrameworkDirs-    configExtraIncludeDirs    = pkgExtraIncludeDirs-    configProgPrefix          = maybe mempty toFlag pkgProgPrefix-    configProgSuffix          = maybe mempty toFlag pkgProgSuffix--    configInstallDirs         = fmap (toFlag . InstallDirs.toPathTemplate)-                                     pkgInstallDirs--    -- we only use configDependencies, unless we're talking to an old Cabal-    -- in which case we use configConstraints-    configDependencies        = [ (packageName (Installed.sourcePackageId deppkg),-                                  Installed.installedUnitId deppkg)-                                | deppkg <- CD.nonSetupDeps pkgdeps ]-    configConstraints         = [ thisPackageVersion (packageId deppkg)-                                | deppkg <- CD.nonSetupDeps pkgdeps ]--    -- explicitly clear, then our package db stack-    -- TODO: [required eventually] have to do this differently for older Cabal versions-    configPackageDBs          = Nothing : map Just pkgBuildPackageDBStack--    configTests               = toFlag (TestStanzas  `Set.member` pkgStanzasEnabled)-    configBenchmarks          = toFlag (BenchStanzas `Set.member` pkgStanzasEnabled)--    configExactConfiguration  = toFlag True-    configFlagError           = mempty --TODO: [research required] appears not to be implemented-    configRelocatable         = mempty --TODO: [research required] ???-    configScratchDir          = mempty -- never use-    configUserInstall         = mempty -- don't rely on defaults-    configPrograms_           = mempty -- never use, shouldn't exist---setupHsBuildFlags :: ElaboratedConfiguredPackage-                  -> ElaboratedSharedConfig-                  -> Verbosity-                  -> FilePath-                  -> Cabal.BuildFlags-setupHsBuildFlags ElaboratedConfiguredPackage{..} _ verbosity builddir =-    Cabal.BuildFlags {-      buildProgramPaths = mempty, --unused, set at configure time-      buildProgramArgs  = mempty, --unused, set at configure time-      buildVerbosity    = toFlag verbosity,-      buildDistPref     = toFlag builddir,-      buildNumJobs      = mempty, --TODO: [nice to have] sometimes want to use toFlag (Just numBuildJobs),-      buildArgs         = mempty  -- unused, passed via args not flags-    }---setupHsBuildArgs :: ElaboratedConfiguredPackage -> [String]-setupHsBuildArgs pkg =-    map (showComponentTarget pkg) (pkgBuildTargets pkg)---showComponentTarget :: ElaboratedConfiguredPackage -> ComponentTarget -> String-showComponentTarget pkg =-    showBuildTarget . toBuildTarget-  where-    showBuildTarget t =-      Cabal.showBuildTarget (qlBuildTarget t) (packageId pkg) t--    qlBuildTarget Cabal.BuildTargetComponent{} = Cabal.QL2-    qlBuildTarget _                            = Cabal.QL3--    toBuildTarget :: ComponentTarget -> Cabal.BuildTarget-    toBuildTarget (ComponentTarget cname subtarget) =-      case subtarget of-        WholeComponent     -> Cabal.BuildTargetComponent cname-        ModuleTarget mname -> Cabal.BuildTargetModule    cname mname-        FileTarget   fname -> Cabal.BuildTargetFile      cname fname---setupHsReplFlags :: ElaboratedConfiguredPackage-                 -> ElaboratedSharedConfig-                 -> Verbosity-                 -> FilePath-                 -> Cabal.ReplFlags-setupHsReplFlags ElaboratedConfiguredPackage{..} _ verbosity builddir =-    Cabal.ReplFlags {-      replProgramPaths = mempty, --unused, set at configure time-      replProgramArgs  = mempty, --unused, set at configure time-      replVerbosity    = toFlag verbosity,-      replDistPref     = toFlag builddir,-      replReload       = mempty  --only used as callback from repl-    }---setupHsReplArgs :: ElaboratedConfiguredPackage -> [String]-setupHsReplArgs pkg =-    maybe [] (\t -> [showComponentTarget pkg t]) (pkgReplTarget pkg)-    --TODO: should be able to give multiple modules in one component---setupHsCopyFlags :: ElaboratedConfiguredPackage-                 -> ElaboratedSharedConfig-                 -> Verbosity-                 -> FilePath-                 -> Cabal.CopyFlags-setupHsCopyFlags _ _ verbosity builddir =-    Cabal.CopyFlags {-      --TODO: [nice to have] we currently just rely on Setup.hs copy to always do the right-      -- thing, but perhaps we ought really to copy into an image dir and do-      -- some sanity checks and move into the final location ourselves-      copyDest      = toFlag InstallDirs.NoCopyDest,-      copyDistPref  = toFlag builddir,-      copyVerbosity = toFlag verbosity-    }--setupHsRegisterFlags :: ElaboratedConfiguredPackage-                     -> ElaboratedSharedConfig-                     -> Verbosity-                     -> FilePath-                     -> FilePath-                     -> Cabal.RegisterFlags-setupHsRegisterFlags ElaboratedConfiguredPackage {pkgBuildStyle} _-                     verbosity builddir pkgConfFile =-    Cabal.RegisterFlags {-      regPackageDB   = mempty,  -- misfeature-      regGenScript   = mempty,  -- never use-      regGenPkgConf  = toFlag (Just pkgConfFile),-      regInPlace     = case pkgBuildStyle of-                         BuildInplaceOnly -> toFlag True-                         _                -> toFlag False,-      regPrintId     = mempty,  -- never use-      regDistPref    = toFlag builddir,-      regVerbosity   = toFlag verbosity-    }--setupHsHaddockFlags :: ElaboratedConfiguredPackage-                    -> ElaboratedSharedConfig-                    -> Verbosity-                    -> FilePath-                    -> Cabal.HaddockFlags-setupHsHaddockFlags ElaboratedConfiguredPackage{..} _ verbosity builddir =-    Cabal.HaddockFlags {-      haddockProgramPaths  = mempty, --unused, set at configure time-      haddockProgramArgs   = mempty, --unused, set at configure time-      haddockHoogle        = toFlag pkgHaddockHoogle,-      haddockHtml          = toFlag pkgHaddockHtml,-      haddockHtmlLocation  = maybe mempty toFlag pkgHaddockHtmlLocation,-      haddockForHackage    = mempty, --TODO: new flag-      haddockExecutables   = toFlag pkgHaddockExecutables,-      haddockTestSuites    = toFlag pkgHaddockTestSuites,-      haddockBenchmarks    = toFlag pkgHaddockBenchmarks,-      haddockInternal      = toFlag pkgHaddockInternal,-      haddockCss           = maybe mempty toFlag pkgHaddockCss,-      haddockHscolour      = toFlag pkgHaddockHscolour,-      haddockHscolourCss   = maybe mempty toFlag pkgHaddockHscolourCss,-      haddockContents      = maybe mempty toFlag pkgHaddockContents,-      haddockDistPref      = toFlag builddir,-      haddockKeepTempFiles = mempty, --TODO: from build settings-      haddockVerbosity     = toFlag verbosity-    }--{--setupHsTestFlags :: ElaboratedConfiguredPackage-                 -> ElaboratedSharedConfig-                 -> Verbosity-                 -> FilePath-                 -> Cabal.TestFlags-setupHsTestFlags _ _ verbosity builddir =-    Cabal.TestFlags {-    }--}----------------------------------------------------------------------------------- * Sharing installed packages-------------------------------------------------------------------------------------- Nix style store management for tarball packages------ So here's our strategy:------ We use a per-user nix-style hashed store, but /only/ for tarball packages.--- So that includes packages from hackage repos (and other http and local--- tarballs). For packages in local directories we do not register them into--- the shared store by default, we just build them locally inplace.------ The reason we do it like this is that it's easy to make stable hashes for--- tarball packages, and these packages benefit most from sharing. By contrast--- unpacked dir packages are harder to hash and they tend to change more--- frequently so there's less benefit to sharing them.------ When using the nix store approach we have to run the solver *without*--- looking at the packages installed in the store, just at the source packages--- (plus core\/global installed packages). Then we do a post-processing pass--- to replace configured packages in the plan with pre-existing ones, where--- possible. Where possible of course means where the nix-style package hash--- equals one that's already in the store.------ One extra wrinkle is that unless we know package tarball hashes upfront, we--- will have to download the tarballs to find their hashes. So we have two--- options: delay replacing source with pre-existing installed packages until--- the point during the execution of the install plan where we have the--- tarball, or try to do as much up-front as possible and then check again--- during plan execution. The former isn't great because we would end up--- telling users we're going to re-install loads of packages when in fact we--- would just share them. It'd be better to give as accurate a prediction as--- we can. The latter is better for users, but we do still have to check--- during plan execution because it's important that we don't replace existing--- installed packages even if they have the same package hash, because we--- don't guarantee ABI stability.---- TODO: [required eventually] for safety of concurrent installs, we must make sure we register but--- not replace installed packages with ghc-pkg.--packageHashInputs :: ElaboratedSharedConfig-                  -> ElaboratedConfiguredPackage-                  -> PackageHashInputs-packageHashInputs-    pkgshared-    pkg@ElaboratedConfiguredPackage{-      pkgSourceId,-      pkgSourceHash = Just srchash,-      pkgDependencies-    } =-    PackageHashInputs {-      pkgHashPkgId       = pkgSourceId,-      pkgHashSourceHash  = srchash,-      pkgHashDirectDeps  = Set.fromList-                             [ installedPackageId dep-                             | dep <- CD.select relevantDeps pkgDependencies ],-      pkgHashOtherConfig = packageHashConfigInputs pkgshared pkg-    }-  where-    -- Obviously the main deps are relevant-    relevantDeps  CD.ComponentLib      = True-    relevantDeps (CD.ComponentExe _)   = True-    -- Setup deps can affect the Setup.hs behaviour and thus what is built-    relevantDeps  CD.ComponentSetup    = True-    -- However testsuites and benchmarks do not get installed and should not-    -- affect the result, so we do not include them.-    relevantDeps (CD.ComponentTest  _) = False-    relevantDeps (CD.ComponentBench _) = False--packageHashInputs _ pkg =-    error $ "packageHashInputs: only for packages with source hashes. "-         ++ display (packageId pkg)--packageHashConfigInputs :: ElaboratedSharedConfig-                        -> ElaboratedConfiguredPackage-                        -> PackageHashConfigInputs-packageHashConfigInputs-    ElaboratedSharedConfig{..}-    ElaboratedConfiguredPackage{..} =--    PackageHashConfigInputs {-      pkgHashCompilerId          = compilerId pkgConfigCompiler,-      pkgHashPlatform            = pkgConfigPlatform,-      pkgHashFlagAssignment      = pkgFlagAssignment,-      pkgHashConfigureScriptArgs = pkgConfigureScriptArgs,-      pkgHashVanillaLib          = pkgVanillaLib,-      pkgHashSharedLib           = pkgSharedLib,-      pkgHashDynExe              = pkgDynExe,-      pkgHashGHCiLib             = pkgGHCiLib,-      pkgHashProfLib             = pkgProfLib,-      pkgHashProfExe             = pkgProfExe,-      pkgHashProfLibDetail       = pkgProfLibDetail,-      pkgHashProfExeDetail       = pkgProfExeDetail,-      pkgHashCoverage            = pkgCoverage,-      pkgHashOptimization        = pkgOptimization,-      pkgHashSplitObjs           = pkgSplitObjs,-      pkgHashStripLibs           = pkgStripLibs,-      pkgHashStripExes           = pkgStripExes,-      pkgHashDebugInfo           = pkgDebugInfo,-      pkgHashExtraLibDirs        = pkgExtraLibDirs,-      pkgHashExtraFrameworkDirs  = pkgExtraFrameworkDirs,-      pkgHashExtraIncludeDirs    = pkgExtraIncludeDirs,-      pkgHashProgPrefix          = pkgProgPrefix,-      pkgHashProgSuffix          = pkgProgSuffix-    }----- | Given the 'InstalledPackageIndex' for a nix-style package store, and an--- 'ElaboratedInstallPlan', replace configured source packages by pre-existing--- installed packages whenever they exist.----improveInstallPlanWithPreExistingPackages :: InstalledPackageIndex-                                          -> ElaboratedInstallPlan-                                          -> ElaboratedInstallPlan-improveInstallPlanWithPreExistingPackages installedPkgIndex installPlan =-    replaceWithPreExisting installPlan-      [ ipkg-      | InstallPlan.Configured pkg-          <- InstallPlan.reverseTopologicalOrder installPlan-      , ipkg <- maybeToList (canPackageBeImproved pkg) ]-  where-    --TODO: sanity checks:-    -- * the installed package must have the expected deps etc-    -- * the installed package must not be broken, valid dep closure--    --TODO: decide what to do if we encounter broken installed packages,-    -- since overwriting is never safe.--    canPackageBeImproved pkg =-      PackageIndex.lookupUnitId-        installedPkgIndex (installedPackageId pkg)--    replaceWithPreExisting =-      foldl' (\plan ipkg -> InstallPlan.preexisting-                              (installedPackageId ipkg) ipkg plan)+{-# LANGUAGE ViewPatterns #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE NoMonoLocalBinds #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveFunctor #-}++-- | Planning how to build everything in a project.+--+module Distribution.Client.ProjectPlanning (+    -- * elaborated install plan types+    ElaboratedInstallPlan,+    ElaboratedConfiguredPackage(..),+    ElaboratedPlanPackage,+    ElaboratedSharedConfig(..),+    ElaboratedReadyPackage,+    BuildStyle(..),+    CabalFileText,++    -- * Producing the elaborated install plan+    rebuildProjectConfig,+    rebuildInstallPlan,++    -- * Build targets+    availableTargets,+    AvailableTarget(..),+    AvailableTargetStatus(..),+    TargetRequested(..),+    ComponentTarget(..),+    SubComponentTarget(..),+    showComponentTarget,+    nubComponentTargets,++    -- * Selecting a plan subset+    pruneInstallPlanToTargets,+    TargetAction(..),+    pruneInstallPlanToDependencies,+    CannotPruneDependencies(..),++    -- * Utils required for building+    pkgHasEphemeralBuildTargets,+    elabBuildTargetWholeComponents,++    -- * Setup.hs CLI flags for building+    setupHsScriptOptions,+    setupHsConfigureFlags,+    setupHsConfigureArgs,+    setupHsBuildFlags,+    setupHsBuildArgs,+    setupHsReplFlags,+    setupHsReplArgs,+    setupHsTestFlags,+    setupHsTestArgs,+    setupHsCopyFlags,+    setupHsRegisterFlags,+    setupHsHaddockFlags,++    packageHashInputs,+  ) where++import Prelude ()+import Distribution.Client.Compat.Prelude++import           Distribution.Client.ProjectPlanning.Types as Ty+import           Distribution.Client.PackageHash+import           Distribution.Client.RebuildMonad+import           Distribution.Client.Store+import           Distribution.Client.ProjectConfig+import           Distribution.Client.ProjectPlanOutput++import           Distribution.Client.Types+import qualified Distribution.Client.InstallPlan as InstallPlan+import qualified Distribution.Client.SolverInstallPlan as SolverInstallPlan+import           Distribution.Client.Dependency+import           Distribution.Client.Dependency.Types+import qualified Distribution.Client.IndexUtils as IndexUtils+import           Distribution.Client.Targets (userToPackageConstraint)+import           Distribution.Client.DistDirLayout+import           Distribution.Client.SetupWrapper+import           Distribution.Client.JobControl+import           Distribution.Client.FetchUtils+import qualified Hackage.Security.Client as Sec+import           Distribution.Client.Setup hiding (packageName, cabalVersion)+import           Distribution.Utils.NubList+import           Distribution.Utils.LogProgress+import           Distribution.Utils.MapAccum++import qualified Distribution.Solver.Types.ComponentDeps as CD+import           Distribution.Solver.Types.ComponentDeps (ComponentDeps)+import           Distribution.Solver.Types.ConstraintSource+import           Distribution.Solver.Types.LabeledPackageConstraint+import           Distribution.Solver.Types.OptionalStanza+import           Distribution.Solver.Types.PkgConfigDb+import           Distribution.Solver.Types.ResolverPackage+import           Distribution.Solver.Types.SolverId+import           Distribution.Solver.Types.SolverPackage+import           Distribution.Solver.Types.InstSolverPackage+import           Distribution.Solver.Types.SourcePackage+import           Distribution.Solver.Types.Settings++import           Distribution.ModuleName+import           Distribution.Package hiding+  (InstalledPackageId, installedPackageId)+import           Distribution.Types.AnnotatedId+import           Distribution.Types.ComponentName+import           Distribution.Types.PkgconfigDependency+import           Distribution.Types.UnqualComponentName+import           Distribution.System+import qualified Distribution.PackageDescription as Cabal+import qualified Distribution.PackageDescription as PD+import qualified Distribution.PackageDescription.Configuration as PD+import           Distribution.Simple.PackageIndex (InstalledPackageIndex)+import           Distribution.Simple.Compiler hiding (Flag)+import qualified Distribution.Simple.GHC   as GHC   --TODO: [code cleanup] eliminate+import qualified Distribution.Simple.GHCJS as GHCJS --TODO: [code cleanup] eliminate+import           Distribution.Simple.Program+import           Distribution.Simple.Program.Db+import           Distribution.Simple.Program.Find+import qualified Distribution.Simple.Setup as Cabal+import           Distribution.Simple.Setup+  (Flag, toFlag, flagToMaybe, flagToList, fromFlagOrDefault)+import qualified Distribution.Simple.Configure as Cabal+import qualified Distribution.Simple.LocalBuildInfo as Cabal+import           Distribution.Simple.LocalBuildInfo+                   ( Component(..), pkgComponents, componentBuildInfo+                   , componentName )+import qualified Distribution.Simple.InstallDirs as InstallDirs+import qualified Distribution.InstalledPackageInfo as IPI++import           Distribution.Backpack.ConfiguredComponent+import           Distribution.Backpack.LinkedComponent+import           Distribution.Backpack.ComponentsGraph+import           Distribution.Backpack.ModuleShape+import           Distribution.Backpack.FullUnitId+import           Distribution.Backpack+import           Distribution.Types.ComponentInclude++import           Distribution.Simple.Utils hiding (matchFileGlob)+import           Distribution.Version+import           Distribution.Verbosity+import           Distribution.Text++import qualified Distribution.Compat.Graph as Graph+import           Distribution.Compat.Graph(IsNode(..))++import           Text.PrettyPrint hiding ((<>))+import qualified Text.PrettyPrint as Disp+import qualified Data.Map as Map+import           Data.Set (Set)+import qualified Data.Set as Set+import           Control.Monad+import qualified Data.Traversable as T+import           Control.Monad.State as State+import           Control.Exception+import           Data.List (groupBy)+import           Data.Either+import           Data.Function+import           System.FilePath++------------------------------------------------------------------------------+-- * Elaborated install plan+------------------------------------------------------------------------------++-- "Elaborated" -- worked out with great care and nicety of detail;+--                 executed with great minuteness: elaborate preparations;+--                 elaborate care.+--+-- So here's the idea:+--+-- Rather than a miscellaneous collection of 'ConfigFlags', 'InstallFlags' etc+-- all passed in as separate args and which are then further selected,+-- transformed etc during the execution of the build. Instead we construct+-- an elaborated install plan that includes everything we will need, and then+-- during the execution of the plan we do as little transformation of this+-- info as possible.+--+-- So we're trying to split the work into two phases: construction of the+-- elaborated install plan (which as far as possible should be pure) and+-- then simple execution of that plan without any smarts, just doing what the+-- plan says to do.+--+-- So that means we need a representation of this fully elaborated install+-- plan. The representation consists of two parts:+--+-- * A 'ElaboratedInstallPlan'. This is a 'GenericInstallPlan' with a+--   representation of source packages that includes a lot more detail about+--   that package's individual configuration+--+-- * A 'ElaboratedSharedConfig'. Some package configuration is the same for+--   every package in a plan. Rather than duplicate that info every entry in+--   the 'GenericInstallPlan' we keep that separately.+--+-- The division between the shared and per-package config is /not set in stone+-- for all time/. For example if we wanted to generalise the install plan to+-- describe a situation where we want to build some packages with GHC and some+-- with GHCJS then the platform and compiler would no longer be shared between+-- all packages but would have to be per-package (probably with some sanity+-- condition on the graph structure).+--++-- Refer to ProjectPlanning.Types for details of these important types:++-- type ElaboratedInstallPlan = ...+-- type ElaboratedPlanPackage = ...+-- data ElaboratedSharedConfig = ...+-- data ElaboratedConfiguredPackage = ...+-- data BuildStyle =+++-- | Check that an 'ElaboratedConfiguredPackage' actually makes+-- sense under some 'ElaboratedSharedConfig'.+sanityCheckElaboratedConfiguredPackage+    :: ElaboratedSharedConfig+    -> ElaboratedConfiguredPackage+    -> a+    -> a+sanityCheckElaboratedConfiguredPackage sharedConfig+                             elab@ElaboratedConfiguredPackage{..} =+    (case elabPkgOrComp of+        ElabPackage pkg -> sanityCheckElaboratedPackage elab pkg+        ElabComponent comp -> sanityCheckElaboratedComponent elab comp)++    -- either a package is being built inplace, or the+    -- 'installedPackageId' we assigned is consistent with+    -- the 'hashedInstalledPackageId' we would compute from+    -- the elaborated configured package+  . assert (elabBuildStyle == BuildInplaceOnly ||+     elabComponentId == hashedInstalledPackageId+                            (packageHashInputs sharedConfig elab))++    -- the stanzas explicitly disabled should not be available+  . assert (Set.null (Map.keysSet (Map.filter not elabStanzasRequested)+                `Set.intersection` elabStanzasAvailable))++    -- either a package is built inplace, or we are not attempting to+    -- build any test suites or benchmarks (we never build these+    -- for remote packages!)+  . assert (elabBuildStyle == BuildInplaceOnly ||+     Set.null elabStanzasAvailable)++sanityCheckElaboratedComponent+    :: ElaboratedConfiguredPackage+    -> ElaboratedComponent+    -> a+    -> a+sanityCheckElaboratedComponent ElaboratedConfiguredPackage{..}+                               ElaboratedComponent{..} =++    -- Should not be building bench or test if not inplace.+    assert (elabBuildStyle == BuildInplaceOnly ||+     case compComponentName of+        Nothing              -> True+        Just CLibName        -> True+        Just (CSubLibName _) -> True+        Just (CExeName _)    -> True+        -- This is interesting: there's no way to declare a dependency+        -- on a foreign library at the moment, but you may still want+        -- to install these to the store+        Just (CFLibName _)   -> True+        Just (CBenchName _)  -> False+        Just (CTestName _)   -> False)+++sanityCheckElaboratedPackage+    :: ElaboratedConfiguredPackage+    -> ElaboratedPackage+    -> a+    -> a+sanityCheckElaboratedPackage ElaboratedConfiguredPackage{..}+                             ElaboratedPackage{..} =+    -- we should only have enabled stanzas that actually can be built+    -- (according to the solver)+    assert (pkgStanzasEnabled `Set.isSubsetOf` elabStanzasAvailable)++    -- the stanzas that the user explicitly requested should be+    -- enabled (by the previous test, they are also available)+  . assert (Map.keysSet (Map.filter id elabStanzasRequested)+                `Set.isSubsetOf` pkgStanzasEnabled)++------------------------------------------------------------------------------+-- * Deciding what to do: making an 'ElaboratedInstallPlan'+------------------------------------------------------------------------------++-- | Return the up-to-date project config and information about the local+-- packages within the project.+--+rebuildProjectConfig :: Verbosity+                     -> DistDirLayout+                     -> ProjectConfig+                     -> IO (ProjectConfig, [UnresolvedSourcePackage])+rebuildProjectConfig verbosity+                     distDirLayout@DistDirLayout {+                       distProjectRootDirectory,+                       distDirectory,+                       distProjectCacheFile,+                       distProjectCacheDirectory+                     }+                     cliConfig = do++    (projectConfig, localPackages) <-+      runRebuild distProjectRootDirectory $+      rerunIfChanged verbosity fileMonitorProjectConfig () $ do++        projectConfig <- phaseReadProjectConfig+        localPackages <- phaseReadLocalPackages projectConfig+        return (projectConfig, localPackages)++    return (projectConfig <> cliConfig, localPackages)++  where+    fileMonitorProjectConfig = newFileMonitor (distProjectCacheFile "config")++    -- Read the cabal.project (or implicit config) and combine it with+    -- arguments from the command line+    --+    phaseReadProjectConfig :: Rebuild ProjectConfig+    phaseReadProjectConfig = do+      liftIO $ do+        info verbosity "Project settings changed, reconfiguring..."+        createDirectoryIfMissingVerbose verbosity True distDirectory+        createDirectoryIfMissingVerbose verbosity True distProjectCacheDirectory++      readProjectConfig verbosity distDirLayout++    -- Look for all the cabal packages in the project+    -- some of which may be local src dirs, tarballs etc+    --+    phaseReadLocalPackages :: ProjectConfig -> Rebuild [UnresolvedSourcePackage]+    phaseReadLocalPackages projectConfig = do+      localCabalFiles <- findProjectPackages distDirLayout projectConfig+      mapM (readSourcePackage verbosity) localCabalFiles+++-- | Return an up-to-date elaborated install plan.+--+-- Two variants of the install plan are returned: with and without packages+-- from the store. That is, the \"improved\" plan where source packages are+-- replaced by pre-existing installed packages from the store (when their ids+-- match), and also the original elaborated plan which uses primarily source+-- packages.++-- The improved plan is what we use for building, but the original elaborated+-- plan is useful for reporting and configuration. For example the @freeze@+-- command needs the source package info to know about flag choices and+-- dependencies of executables and setup scripts.+--+rebuildInstallPlan :: Verbosity+                   -> DistDirLayout -> CabalDirLayout+                   -> ProjectConfig+                   -> [UnresolvedSourcePackage]+                   -> IO ( ElaboratedInstallPlan  -- with store packages+                         , ElaboratedInstallPlan  -- with source packages+                         , ElaboratedSharedConfig )+                      -- ^ @(improvedPlan, elaboratedPlan, _, _)@+rebuildInstallPlan verbosity+                   distDirLayout@DistDirLayout {+                     distProjectRootDirectory,+                     distProjectCacheFile+                   }+                   CabalDirLayout {+                     cabalStoreDirLayout+                   } = \projectConfig localPackages ->+    runRebuild distProjectRootDirectory $ do+    progsearchpath <- liftIO $ getSystemSearchPath+    let projectConfigMonitored = projectConfig { projectConfigBuildOnly = mempty }++    -- The overall improved plan is cached+    rerunIfChanged verbosity fileMonitorImprovedPlan+                   -- react to changes in the project config,+                   -- the package .cabal files and the path+                   (projectConfigMonitored, localPackages, progsearchpath) $ do++      -- And so is the elaborated plan that the improved plan based on+      (elaboratedPlan, elaboratedShared) <-+        rerunIfChanged verbosity fileMonitorElaboratedPlan+                       (projectConfigMonitored, localPackages,+                        progsearchpath) $ do++          compilerEtc   <- phaseConfigureCompiler projectConfig+          _             <- phaseConfigurePrograms projectConfig compilerEtc+          (solverPlan, pkgConfigDB)+                        <- phaseRunSolver         projectConfig+                                                  compilerEtc+                                                  localPackages+          (elaboratedPlan,+           elaboratedShared) <- phaseElaboratePlan projectConfig+                                                   compilerEtc pkgConfigDB+                                                   solverPlan+                                                   localPackages++          phaseMaintainPlanOutputs elaboratedPlan elaboratedShared+          return (elaboratedPlan, elaboratedShared)++      -- The improved plan changes each time we install something, whereas+      -- the underlying elaborated plan only changes when input config+      -- changes, so it's worth caching them separately.+      improvedPlan <- phaseImprovePlan elaboratedPlan elaboratedShared++      return (improvedPlan, elaboratedPlan, elaboratedShared)++  where+    fileMonitorCompiler       = newFileMonitorInCacheDir "compiler"+    fileMonitorSolverPlan     = newFileMonitorInCacheDir "solver-plan"+    fileMonitorSourceHashes   = newFileMonitorInCacheDir "source-hashes"+    fileMonitorElaboratedPlan = newFileMonitorInCacheDir "elaborated-plan"+    fileMonitorImprovedPlan   = newFileMonitorInCacheDir "improved-plan"++    newFileMonitorInCacheDir :: Eq a => FilePath -> FileMonitor a b+    newFileMonitorInCacheDir  = newFileMonitor . distProjectCacheFile+++    -- Configure the compiler we're using.+    --+    -- This is moderately expensive and doesn't change that often so we cache+    -- it independently.+    --+    phaseConfigureCompiler :: ProjectConfig+                           -> Rebuild (Compiler, Platform, ProgramDb)+    phaseConfigureCompiler ProjectConfig {+                             projectConfigShared = ProjectConfigShared {+                               projectConfigHcFlavor,+                               projectConfigHcPath,+                               projectConfigHcPkg+                             },+                             projectConfigLocalPackages = PackageConfig {+                               packageConfigProgramPaths,+                               packageConfigProgramArgs,+                               packageConfigProgramPathExtra+                             }+                           } = do+        progsearchpath <- liftIO $ getSystemSearchPath+        rerunIfChanged verbosity fileMonitorCompiler+                       (hcFlavor, hcPath, hcPkg, progsearchpath,+                        packageConfigProgramPaths,+                        packageConfigProgramArgs,+                        packageConfigProgramPathExtra) $ do++          liftIO $ info verbosity "Compiler settings changed, reconfiguring..."+          result@(_, _, progdb') <- liftIO $+            Cabal.configCompilerEx+              hcFlavor hcPath hcPkg+              progdb verbosity++        -- Note that we added the user-supplied program locations and args+        -- for /all/ programs, not just those for the compiler prog and+        -- compiler-related utils. In principle we don't know which programs+        -- the compiler will configure (and it does vary between compilers).+        -- We do know however that the compiler will only configure the+        -- programs it cares about, and those are the ones we monitor here.+          monitorFiles (programsMonitorFiles progdb')++          return result+      where+        hcFlavor = flagToMaybe projectConfigHcFlavor+        hcPath   = flagToMaybe projectConfigHcPath+        hcPkg    = flagToMaybe projectConfigHcPkg+        progdb   =+            userSpecifyPaths (Map.toList (getMapLast packageConfigProgramPaths))+          . userSpecifyArgss (Map.toList (getMapMappend packageConfigProgramArgs))+          . modifyProgramSearchPath+              (++ [ ProgramSearchPathDir dir+                  | dir <- fromNubList packageConfigProgramPathExtra ])+          $ defaultProgramDb+++    -- Configuring other programs.+    --+    -- Having configred the compiler, now we configure all the remaining+    -- programs. This is to check we can find them, and to monitor them for+    -- changes.+    --+    -- TODO: [required eventually] we don't actually do this yet.+    --+    -- We rely on the fact that the previous phase added the program config for+    -- all local packages, but that all the programs configured so far are the+    -- compiler program or related util programs.+    --+    phaseConfigurePrograms :: ProjectConfig+                           -> (Compiler, Platform, ProgramDb)+                           -> Rebuild ()+    phaseConfigurePrograms projectConfig (_, _, compilerprogdb) = do+        -- Users are allowed to specify program locations independently for+        -- each package (e.g. to use a particular version of a pre-processor+        -- for some packages). However they cannot do this for the compiler+        -- itself as that's just not going to work. So we check for this.+        liftIO $ checkBadPerPackageCompilerPaths+          (configuredPrograms compilerprogdb)+          (getMapMappend (projectConfigSpecificPackage projectConfig))++        --TODO: [required eventually] find/configure other programs that the+        -- user specifies.++        --TODO: [required eventually] find/configure all build-tools+        -- but note that some of them may be built as part of the plan.+++    -- Run the solver to get the initial install plan.+    -- This is expensive so we cache it independently.+    --+    phaseRunSolver :: ProjectConfig+                   -> (Compiler, Platform, ProgramDb)+                   -> [UnresolvedSourcePackage]+                   -> Rebuild (SolverInstallPlan, PkgConfigDb)+    phaseRunSolver projectConfig@ProjectConfig {+                     projectConfigShared,+                     projectConfigBuildOnly+                   }+                   (compiler, platform, progdb)+                   localPackages =+        rerunIfChanged verbosity fileMonitorSolverPlan+                       (solverSettings,+                        localPackages, localPackagesEnabledStanzas,+                        compiler, platform, programDbSignature progdb) $ do++          installedPkgIndex <- getInstalledPackages verbosity+                                                    compiler progdb platform+                                                    corePackageDbs+          sourcePkgDb       <- getSourcePackages verbosity withRepoCtx+                                 (solverSettingIndexState solverSettings)+          pkgConfigDB       <- getPkgConfigDb verbosity progdb++          --TODO: [code cleanup] it'd be better if the Compiler contained the+          -- ConfiguredPrograms that it needs, rather than relying on the progdb+          -- since we don't need to depend on all the programs here, just the+          -- ones relevant for the compiler.++          liftIO $ do+            solver <- chooseSolver verbosity+                                   (solverSettingSolver solverSettings)+                                   (compilerInfo compiler)++            notice verbosity "Resolving dependencies..."+            plan <- foldProgress logMsg (die' verbosity) return $+              planPackages verbosity compiler platform solver solverSettings+                           installedPkgIndex sourcePkgDb pkgConfigDB+                           localPackages localPackagesEnabledStanzas+            return (plan, pkgConfigDB)+      where+        corePackageDbs = [GlobalPackageDB]+        withRepoCtx    = projectConfigWithSolverRepoContext verbosity+                           projectConfigShared+                           projectConfigBuildOnly+        solverSettings = resolveSolverSettings projectConfig+        logMsg message rest = debugNoWrap verbosity message >> rest++        localPackagesEnabledStanzas =+          Map.fromList+            [ (pkgname, stanzas)+            | pkg <- localPackages+            , let pkgname            = packageName pkg+                  testsEnabled       = lookupLocalPackageConfig+                                         packageConfigTests+                                         projectConfig pkgname+                  benchmarksEnabled  = lookupLocalPackageConfig+                                         packageConfigBenchmarks+                                         projectConfig pkgname+                  stanzas =+                    Map.fromList $+                      [ (TestStanzas, enabled)+                      | enabled <- flagToList testsEnabled ]+                   ++ [ (BenchStanzas , enabled)+                      | enabled <- flagToList benchmarksEnabled ]+            ]++    -- Elaborate the solver's install plan to get a fully detailed plan. This+    -- version of the plan has the final nix-style hashed ids.+    --+    phaseElaboratePlan :: ProjectConfig+                       -> (Compiler, Platform, ProgramDb)+                       -> PkgConfigDb+                       -> SolverInstallPlan+                       -> [SourcePackage loc]+                       -> Rebuild ( ElaboratedInstallPlan+                                  , ElaboratedSharedConfig )+    phaseElaboratePlan ProjectConfig {+                         projectConfigShared,+                         projectConfigLocalPackages,+                         projectConfigSpecificPackage,+                         projectConfigBuildOnly+                       }+                       (compiler, platform, progdb) pkgConfigDB+                       solverPlan localPackages = do++        liftIO $ debug verbosity "Elaborating the install plan..."++        sourcePackageHashes <-+          rerunIfChanged verbosity fileMonitorSourceHashes+                         (packageLocationsSignature solverPlan) $+            getPackageSourceHashes verbosity withRepoCtx solverPlan++        defaultInstallDirs <- liftIO $ userInstallDirTemplates compiler+        (elaboratedPlan, elaboratedShared)+          <- liftIO . runLogProgress verbosity $+              elaborateInstallPlan+                verbosity+                platform compiler progdb pkgConfigDB+                distDirLayout+                cabalStoreDirLayout+                solverPlan+                localPackages+                sourcePackageHashes+                defaultInstallDirs+                projectConfigShared+                projectConfigLocalPackages+                (getMapMappend projectConfigSpecificPackage)+        let instantiatedPlan = instantiateInstallPlan elaboratedPlan+        liftIO $ debugNoWrap verbosity (InstallPlan.showInstallPlan instantiatedPlan)+        return (instantiatedPlan, elaboratedShared)+      where+        withRepoCtx = projectConfigWithSolverRepoContext verbosity+                        projectConfigShared+                        projectConfigBuildOnly++    -- Update the files we maintain that reflect our current build environment.+    -- In particular we maintain a JSON representation of the elaborated+    -- install plan (but not the improved plan since that reflects the state+    -- of the build rather than just the input environment).+    --+    phaseMaintainPlanOutputs :: ElaboratedInstallPlan+                             -> ElaboratedSharedConfig+                             -> Rebuild ()+    phaseMaintainPlanOutputs elaboratedPlan elaboratedShared = liftIO $ do+        debug verbosity "Updating plan.json"+        writePlanExternalRepresentation+          distDirLayout+          elaboratedPlan+          elaboratedShared+++    -- Improve the elaborated install plan. The elaborated plan consists+    -- mostly of source packages (with full nix-style hashed ids). Where+    -- corresponding installed packages already exist in the store, replace+    -- them in the plan.+    --+    -- Note that we do monitor the store's package db here, so we will redo+    -- this improvement phase when the db changes -- including as a result of+    -- executing a plan and installing things.+    --+    phaseImprovePlan :: ElaboratedInstallPlan+                     -> ElaboratedSharedConfig+                     -> Rebuild ElaboratedInstallPlan+    phaseImprovePlan elaboratedPlan elaboratedShared = do++        liftIO $ debug verbosity "Improving the install plan..."+        storePkgIdSet <- getStoreEntries cabalStoreDirLayout compid+        let improvedPlan = improveInstallPlanWithInstalledPackages+                             storePkgIdSet+                             elaboratedPlan+        liftIO $ debugNoWrap verbosity (InstallPlan.showInstallPlan improvedPlan)+        -- TODO: [nice to have] having checked which packages from the store+        -- we're using, it may be sensible to sanity check those packages+        -- by loading up the compiler package db and checking everything+        -- matches up as expected, e.g. no dangling deps, files deleted.+        return improvedPlan+      where+        compid = compilerId (pkgConfigCompiler elaboratedShared)+++programsMonitorFiles :: ProgramDb -> [MonitorFilePath]+programsMonitorFiles progdb =+    [ monitor+    | prog    <- configuredPrograms progdb+    , monitor <- monitorFileSearchPath (programMonitorFiles prog)+                                       (programPath prog)+    ]++-- | Select the bits of a 'ProgramDb' to monitor for value changes.+-- Use 'programsMonitorFiles' for the files to monitor.+--+programDbSignature :: ProgramDb -> [ConfiguredProgram]+programDbSignature progdb =+    [ prog { programMonitorFiles = []+           , programOverrideEnv  = filter ((/="PATH") . fst)+                                          (programOverrideEnv prog) }+    | prog <- configuredPrograms progdb ]++getInstalledPackages :: Verbosity+                     -> Compiler -> ProgramDb -> Platform+                     -> PackageDBStack+                     -> Rebuild InstalledPackageIndex+getInstalledPackages verbosity compiler progdb platform packagedbs = do+    monitorFiles . map monitorFileOrDirectory+      =<< liftIO (IndexUtils.getInstalledPackagesMonitorFiles+                    verbosity compiler+                    packagedbs progdb platform)+    liftIO $ IndexUtils.getInstalledPackages+               verbosity compiler+               packagedbs progdb++{-+--TODO: [nice to have] use this but for sanity / consistency checking+getPackageDBContents :: Verbosity+                     -> Compiler -> ProgramDb -> Platform+                     -> PackageDB+                     -> Rebuild InstalledPackageIndex+getPackageDBContents verbosity compiler progdb platform packagedb = do+    monitorFiles . map monitorFileOrDirectory+      =<< liftIO (IndexUtils.getInstalledPackagesMonitorFiles+                    verbosity compiler+                    [packagedb] progdb platform)+    liftIO $ do+      createPackageDBIfMissing verbosity compiler progdb packagedb+      Cabal.getPackageDBContents verbosity compiler+                                 packagedb progdb+-}++getSourcePackages :: Verbosity -> (forall a. (RepoContext -> IO a) -> IO a)+                  -> IndexUtils.IndexState -> Rebuild SourcePackageDb+getSourcePackages verbosity withRepoCtx idxState = do+    (sourcePkgDb, repos) <-+      liftIO $+        withRepoCtx $ \repoctx -> do+          sourcePkgDb <- IndexUtils.getSourcePackagesAtIndexState verbosity+                                                                  repoctx idxState+          return (sourcePkgDb, repoContextRepos repoctx)++    monitorFiles . map monitorFile+                 . IndexUtils.getSourcePackagesMonitorFiles+                 $ repos+    return sourcePkgDb+++getPkgConfigDb :: Verbosity -> ProgramDb -> Rebuild PkgConfigDb+getPkgConfigDb verbosity progdb = do+    dirs <- liftIO $ getPkgConfigDbDirs verbosity progdb+    -- Just monitor the dirs so we'll notice new .pc files.+    -- Alternatively we could monitor all the .pc files too.+    mapM_ monitorDirectoryStatus dirs+    liftIO $ readPkgConfigDb verbosity progdb+++-- | Select the config values to monitor for changes package source hashes.+packageLocationsSignature :: SolverInstallPlan+                          -> [(PackageId, PackageLocation (Maybe FilePath))]+packageLocationsSignature solverPlan =+    [ (packageId pkg, packageSource pkg)+    | SolverInstallPlan.Configured (SolverPackage { solverPkgSource = pkg})+        <- SolverInstallPlan.toList solverPlan+    ]+++-- | Get the 'HashValue' for all the source packages where we use hashes,+-- and download any packages required to do so.+--+-- Note that we don't get hashes for local unpacked packages.+--+getPackageSourceHashes :: Verbosity+                       -> (forall a. (RepoContext -> IO a) -> IO a)+                       -> SolverInstallPlan+                       -> Rebuild (Map PackageId PackageSourceHash)+getPackageSourceHashes verbosity withRepoCtx solverPlan = do++    -- Determine if and where to get the package's source hash from.+    --+    let allPkgLocations :: [(PackageId, PackageLocation (Maybe FilePath))]+        allPkgLocations =+          [ (packageId pkg, packageSource pkg)+          | SolverInstallPlan.Configured (SolverPackage { solverPkgSource = pkg})+              <- SolverInstallPlan.toList solverPlan ]++        -- Tarballs that were local in the first place.+        -- We'll hash these tarball files directly.+        localTarballPkgs :: [(PackageId, FilePath)]+        localTarballPkgs =+          [ (pkgid, tarball)+          | (pkgid, LocalTarballPackage tarball) <- allPkgLocations ]++        -- Tarballs from remote URLs. We must have downloaded these already+        -- (since we extracted the .cabal file earlier)+        --TODO: [required eventually] finish remote tarball functionality+--        allRemoteTarballPkgs =+--          [ (pkgid, )+--          | (pkgid, RemoteTarballPackage ) <- allPkgLocations ]++        -- Tarballs from repositories, either where the repository provides+        -- hashes as part of the repo metadata, or where we will have to+        -- download and hash the tarball.+        repoTarballPkgsWithMetadata    :: [(PackageId, Repo)]+        repoTarballPkgsWithoutMetadata :: [(PackageId, Repo)]+        (repoTarballPkgsWithMetadata,+         repoTarballPkgsWithoutMetadata) =+          partitionEithers+          [ case repo of+              RepoSecure{} -> Left  (pkgid, repo)+              _            -> Right (pkgid, repo)+          | (pkgid, RepoTarballPackage repo _ _) <- allPkgLocations ]++    -- For tarballs from repos that do not have hashes available we now have+    -- to check if the packages were downloaded already.+    --+    (repoTarballPkgsToDownload,+     repoTarballPkgsDownloaded)+      <- fmap partitionEithers $+         liftIO $ sequence+           [ do mtarball <- checkRepoTarballFetched repo pkgid+                case mtarball of+                  Nothing      -> return (Left  (pkgid, repo))+                  Just tarball -> return (Right (pkgid, tarball))+           | (pkgid, repo) <- repoTarballPkgsWithoutMetadata ]++    (hashesFromRepoMetadata,+     repoTarballPkgsNewlyDownloaded) <-+      -- Avoid having to initialise the repository (ie 'withRepoCtx') if we+      -- don't have to. (The main cost is configuring the http client.)+      if null repoTarballPkgsToDownload && null repoTarballPkgsWithMetadata+      then return (Map.empty, [])+      else liftIO $ withRepoCtx $ \repoctx -> do++      -- For tarballs from repos that do have hashes available as part of the+      -- repo metadata we now load up the index for each repo and retrieve+      -- the hashes for the packages+      --+      hashesFromRepoMetadata <-+        Sec.uncheckClientErrors $ --TODO: [code cleanup] wrap in our own exceptions+        fmap (Map.fromList . concat) $+        sequence+          -- Reading the repo index is expensive so we group the packages by repo+          [ repoContextWithSecureRepo repoctx repo $ \secureRepo ->+              Sec.withIndex secureRepo $ \repoIndex ->+                sequence+                  [ do hash <- Sec.trusted <$> -- strip off Trusted tag+                               Sec.indexLookupHash repoIndex pkgid+                       -- Note that hackage-security currently uses SHA256+                       -- but this API could in principle give us some other+                       -- choice in future.+                       return (pkgid, hashFromTUF hash)+                  | pkgid <- pkgids ]+          | (repo, pkgids) <-+                map (\grp@((_,repo):_) -> (repo, map fst grp))+              . groupBy ((==)    `on` (remoteRepoName . repoRemote . snd))+              . sortBy  (compare `on` (remoteRepoName . repoRemote . snd))+              $ repoTarballPkgsWithMetadata+          ]++      -- For tarballs from repos that do not have hashes available, download+      -- the ones we previously determined we need.+      --+      repoTarballPkgsNewlyDownloaded <-+        sequence+          [ do tarball <- fetchRepoTarball verbosity repoctx repo pkgid+               return (pkgid, tarball)+          | (pkgid, repo) <- repoTarballPkgsToDownload ]++      return (hashesFromRepoMetadata,+              repoTarballPkgsNewlyDownloaded)++    -- Hash tarball files for packages where we have to do that. This includes+    -- tarballs that were local in the first place, plus tarballs from repos,+    -- either previously cached or freshly downloaded.+    --+    let allTarballFilePkgs :: [(PackageId, FilePath)]+        allTarballFilePkgs = localTarballPkgs+                          ++ repoTarballPkgsDownloaded+                          ++ repoTarballPkgsNewlyDownloaded+    hashesFromTarballFiles <- liftIO $+      fmap Map.fromList $+      sequence+        [ do srchash <- readFileHashValue tarball+             return (pkgid, srchash)+        | (pkgid, tarball) <- allTarballFilePkgs+        ]+    monitorFiles [ monitorFile tarball+                 | (_pkgid, tarball) <- allTarballFilePkgs ]++    -- Return the combination+    return $! hashesFromRepoMetadata+           <> hashesFromTarballFiles+++-- ------------------------------------------------------------+-- * Installation planning+-- ------------------------------------------------------------++planPackages :: Verbosity+             -> Compiler+             -> Platform+             -> Solver -> SolverSettings+             -> InstalledPackageIndex+             -> SourcePackageDb+             -> PkgConfigDb+             -> [UnresolvedSourcePackage]+             -> Map PackageName (Map OptionalStanza Bool)+             -> Progress String String SolverInstallPlan+planPackages verbosity comp platform solver SolverSettings{..}+             installedPkgIndex sourcePkgDb pkgConfigDB+             localPackages pkgStanzasEnable =++    resolveDependencies+      platform (compilerInfo comp)+      pkgConfigDB solver+      resolverParams++  where++    --TODO: [nice to have] disable multiple instances restriction in the solver, but then+    -- make sure we can cope with that in the output.+    resolverParams =++        setMaxBackjumps solverSettingMaxBackjumps++        --TODO: [required eventually] should only be configurable for custom installs+   -- . setIndependentGoals solverSettingIndependentGoals++      . setReorderGoals solverSettingReorderGoals++      . setCountConflicts solverSettingCountConflicts++        --TODO: [required eventually] should only be configurable for custom installs+   -- . setAvoidReinstalls solverSettingAvoidReinstalls++        --TODO: [required eventually] should only be configurable for custom installs+   -- . setShadowPkgs solverSettingShadowPkgs++      . setStrongFlags solverSettingStrongFlags++      . setAllowBootLibInstalls solverSettingAllowBootLibInstalls++      . setSolverVerbosity verbosity++        --TODO: [required eventually] decide if we need to prefer installed for+        -- global packages, or prefer latest even for global packages. Perhaps+        -- should be configurable but with a different name than "upgrade-dependencies".+      . setPreferenceDefault PreferLatestForSelected+                           {-(if solverSettingUpgradeDeps+                                then PreferAllLatest+                                else PreferLatestForSelected)-}++      . removeLowerBounds solverSettingAllowOlder+      . removeUpperBounds solverSettingAllowNewer++      . addDefaultSetupDependencies (defaultSetupDeps comp platform+                                   . PD.packageDescription+                                   . packageDescription)++      . addSetupCabalMinVersionConstraint (mkVersion [1,20])+          -- While we can talk to older Cabal versions (we need to be able to+          -- do so for custom Setup scripts that require older Cabal lib+          -- versions), we have problems talking to some older versions that+          -- don't support certain features.+          --+          -- For example, Cabal-1.16 and older do not know about build targets.+          -- Even worse, 1.18 and older only supported the --constraint flag+          -- with source package ids, not --dependency with installed package+          -- ids. That is bad because we cannot reliably select the right+          -- dependencies in the presence of multiple instances (i.e. the+          -- store). See issue #3932. So we require Cabal 1.20 as a minimum.++      . addPreferences+          -- preferences from the config file or command line+          [ PackageVersionPreference name ver+          | Dependency name ver <- solverSettingPreferences ]++      . addConstraints+          -- version constraints from the config file or command line+            [ LabeledPackageConstraint (userToPackageConstraint pc) src+            | (pc, src) <- solverSettingConstraints ]++      . addPreferences+          -- enable stanza preference where the user did not specify+          [ PackageStanzasPreference pkgname stanzas+          | pkg <- localPackages+          , let pkgname = packageName pkg+                stanzaM = Map.findWithDefault Map.empty pkgname pkgStanzasEnable+                stanzas = [ stanza | stanza <- [minBound..maxBound]+                          , Map.lookup stanza stanzaM == Nothing ]+          , not (null stanzas)+          ]++      . addConstraints+          -- enable stanza constraints where the user asked to enable+          [ LabeledPackageConstraint+              (PackageConstraint (scopeToplevel pkgname)+                                 (PackagePropertyStanzas stanzas))+              ConstraintSourceConfigFlagOrTarget+          | pkg <- localPackages+          , let pkgname = packageName pkg+                stanzaM = Map.findWithDefault Map.empty pkgname pkgStanzasEnable+                stanzas = [ stanza | stanza <- [minBound..maxBound]+                          , Map.lookup stanza stanzaM == Just True ]+          , not (null stanzas)+          ]++      . addConstraints+          --TODO: [nice to have] should have checked at some point that the+          -- package in question actually has these flags.+          [ LabeledPackageConstraint+              (PackageConstraint (scopeToplevel pkgname)+                                 (PackagePropertyFlags flags))+              ConstraintSourceConfigFlagOrTarget+          | (pkgname, flags) <- Map.toList solverSettingFlagAssignments ]++      . addConstraints+          --TODO: [nice to have] we have user-supplied flags for unspecified+          -- local packages (as well as specific per-package flags). For the+          -- former we just apply all these flags to all local targets which+          -- is silly. We should check if the flags are appropriate.+          [ LabeledPackageConstraint+              (PackageConstraint (scopeToplevel pkgname)+                                 (PackagePropertyFlags flags))+              ConstraintSourceConfigFlagOrTarget+          | let flags = solverSettingFlagAssignment+          , not (null flags)+          , pkg <- localPackages+          , let pkgname = packageName pkg ]++      $ stdResolverParams++    stdResolverParams =+      -- Note: we don't use the standardInstallPolicy here, since that uses+      -- its own addDefaultSetupDependencies that is not appropriate for us.+      basicInstallPolicy+        installedPkgIndex sourcePkgDb+        (map SpecificSourcePackage localPackages)+++------------------------------------------------------------------------------+-- * Install plan post-processing+------------------------------------------------------------------------------++-- This phase goes from the InstallPlan we get from the solver and has to+-- make an elaborated install plan.+--+-- We go in two steps:+--+--  1. elaborate all the source packages that the solver has chosen.+--  2. swap source packages for pre-existing installed packages wherever+--     possible.+--+-- We do it in this order, elaborating and then replacing, because the easiest+-- way to calculate the installed package ids used for the replacement step is+-- from the elaborated configuration for each package.+++++------------------------------------------------------------------------------+-- * Install plan elaboration+------------------------------------------------------------------------------++-- Note [SolverId to ConfiguredId]+-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+-- Dependency solving is a per package affair, so after we're done, we+-- end up with 'SolverInstallPlan' that records in 'solverPkgLibDeps'+-- and 'solverPkgExeDeps' what packages provide the libraries and executables+-- needed by each component of the package (phew!)  For example, if I have+--+--      library+--          build-depends: lib+--          build-tool-depends: pkg:exe1+--          build-tools: alex+--+-- After dependency solving, I find out that this library component has+-- library dependencies on lib-0.2, and executable dependencies on pkg-0.1+-- and alex-0.3 (other components of the package may have different+-- dependencies).  Note that I've "lost" the knowledge that I depend+-- *specifically* on the exe1 executable from pkg.+--+-- So, we have a this graph of packages, and we need to transform it into+-- a graph of components which we are actually going to build.  In particular:+--+-- NODE changes from PACKAGE (SolverPackage) to COMPONENTS (ElaboratedConfiguredPackage)+-- EDGE changes from PACKAGE DEP (SolverId) to COMPONENT DEPS (ConfiguredId)+--+-- In both cases, what was previously a single node/edge may turn into multiple+-- nodes/edges.  Multiple components, because there may be multiple components+-- in a package; multiple component deps, because we may depend upon multiple+-- executables from the same package (and maybe, some day, multiple libraries+-- from the same package.)+--+-- Let's talk about how to do this transformation. Naively, we might consider+-- just processing each package, converting it into (zero or) one or more+-- components.  But we also have to update the edges; this leads to+-- two complications:+--+--      1. We don't know what the ConfiguredId of a component is until+--      we've configured it, but we cannot configure a component unless+--      we know the ConfiguredId of all its dependencies.  Thus, we must+--      process the 'SolverInstallPlan' in topological order.+--+--      2. When we process a package, we know the SolverIds of its+--      dependencies, but we have to do some work to turn these into+--      ConfiguredIds.  For example, in the case of build-tool-depends, the+--      SolverId isn't enough to uniquely determine the ConfiguredId we should+--      elaborate to: we have to look at the executable name attached to+--      the package name in the package description to figure it out.+--      At the same time, we NEED to use the SolverId, because there might+--      be multiple versions of the same package in the build plan+--      (due to setup dependencies); we can't just look up the package name+--      from the package description.+--+-- However, we do have the following INVARIANT: a component never directly+-- depends on multiple versions of the same package.  Thus, we can+-- adopt the following strategy:+--+--      * When a package is transformed into components, record+--        a mapping from SolverId to ALL of the components+--        which were elaborated.+--+--      * When we look up an edge, we use our knowledge of the+--        component name to *filter* the list of components into+--        the ones we actually wanted to refer to.+--+-- By the way, we can tell that SolverInstallPlan is not the "right" type+-- because a SolverId cannot adequately represent all possible dependency+-- solver states: we may need to record foo-0.1 multiple times in+-- the solver install plan with different dependencies.  The solver probably+-- doesn't handle this correctly... but it should.  The right way to solve+-- this is to come up with something very much like a 'ConfiguredId', in that+-- it incorporates the version choices of its dependencies, but less+-- fine grained.  Fortunately, this doesn't seem to have affected anyone,+-- but it is good to watch out about.+++-- | Produce an elaborated install plan using the policy for local builds with+-- a nix-style shared store.+--+-- In theory should be able to make an elaborated install plan with a policy+-- matching that of the classic @cabal install --user@ or @--global@+--+elaborateInstallPlan+  :: Verbosity -> Platform -> Compiler -> ProgramDb -> PkgConfigDb+  -> DistDirLayout+  -> StoreDirLayout+  -> SolverInstallPlan+  -> [SourcePackage loc]+  -> Map PackageId PackageSourceHash+  -> InstallDirs.InstallDirTemplates+  -> ProjectConfigShared+  -> PackageConfig+  -> Map PackageName PackageConfig+  -> LogProgress (ElaboratedInstallPlan, ElaboratedSharedConfig)+elaborateInstallPlan verbosity platform compiler compilerprogdb pkgConfigDB+                     DistDirLayout{..}+                     storeDirLayout@StoreDirLayout{storePackageDBStack}+                     solverPlan localPackages+                     sourcePackageHashes+                     defaultInstallDirs+                     sharedPackageConfig+                     localPackagesConfig+                     perPackageConfig = do+    x <- elaboratedInstallPlan+    return (x, elaboratedSharedConfig)+  where+    elaboratedSharedConfig =+      ElaboratedSharedConfig {+        pkgConfigPlatform      = platform,+        pkgConfigCompiler      = compiler,+        pkgConfigCompilerProgs = compilerprogdb+      }++    preexistingInstantiatedPkgs =+        Map.fromList (mapMaybe f (SolverInstallPlan.toList solverPlan))+      where+        f (SolverInstallPlan.PreExisting inst)+            | let ipkg = instSolverPkgIPI inst+            , not (IPI.indefinite ipkg)+            = Just (IPI.installedUnitId ipkg,+                     (FullUnitId (IPI.installedComponentId ipkg)+                                 (Map.fromList (IPI.instantiatedWith ipkg))))+        f _ = Nothing++    elaboratedInstallPlan =+      flip InstallPlan.fromSolverInstallPlanWithProgress solverPlan $ \mapDep planpkg ->+        case planpkg of+          SolverInstallPlan.PreExisting pkg ->+            return [InstallPlan.PreExisting (instSolverPkgIPI pkg)]++          SolverInstallPlan.Configured  pkg ->+            let inplace_doc | shouldBuildInplaceOnly pkg = text "inplace"+                            | otherwise                  = Disp.empty+            in addProgressCtx (text "In the" <+> inplace_doc <+> text "package" <+>+                             quotes (disp (packageId pkg))) $+               map InstallPlan.Configured <$> elaborateSolverToComponents mapDep pkg++    -- NB: We don't INSTANTIATE packages at this point.  That's+    -- a post-pass.  This makes it simpler to compute dependencies.+    elaborateSolverToComponents+        :: (SolverId -> [ElaboratedPlanPackage])+        -> SolverPackage UnresolvedPkgLoc+        -> LogProgress [ElaboratedConfiguredPackage]+    elaborateSolverToComponents mapDep spkg@(SolverPackage _ _ _ deps0 exe_deps0)+        = case mkComponentsGraph (elabEnabledSpec elab0) pd of+           Right g -> do+            let src_comps = componentsGraphToList g+            infoProgress $ hang (text "Component graph for" <+> disp pkgid <<>> colon)+                            4 (dispComponentsWithDeps src_comps)+            (_, comps) <- mapAccumM buildComponent+                            (Map.empty, Map.empty, Map.empty)+                            (map fst src_comps)+            let not_per_component_reasons = why_not_per_component src_comps+            if null not_per_component_reasons+                then return comps+                else do checkPerPackageOk comps not_per_component_reasons+                        return [elaborateSolverToPackage mapDep spkg g $+                                comps ++ maybeToList setupComponent]+           Left cns ->+            dieProgress $+                hang (text "Dependency cycle between the following components:") 4+                     (vcat (map (text . componentNameStanza) cns))+      where+        -- You are eligible to per-component build if this list is empty+        why_not_per_component g+            = cuz_custom ++ cuz_spec ++ cuz_length ++ cuz_flag+          where+            cuz reason = [text reason]+            -- At this point in time, only non-Custom setup scripts+            -- are supported.  Implementing per-component builds with+            -- Custom would require us to create a new 'ElabSetup'+            -- type, and teach all of the code paths how to handle it.+            -- Once you've implemented this, swap it for the code below.+            cuz_custom =+                case PD.buildType (elabPkgDescription elab0) of+                    Nothing        -> cuz "build-type is not specified"+                    Just PD.Custom -> cuz "build-type is Custom"+                    Just _         -> []+            -- cabal-format versions prior to 1.8 have different build-depends semantics+            -- for now it's easier to just fallback to legacy-mode when specVersion < 1.8+            -- see, https://github.com/haskell/cabal/issues/4121+            cuz_spec+                | PD.specVersion pd >= mkVersion [1,8] = []+                | otherwise = cuz "cabal-version is less than 1.8"+            -- In the odd corner case that a package has no components at all+            -- then keep it as a whole package, since otherwise it turns into+            -- 0 component graph nodes and effectively vanishes. We want to+            -- keep it around at least for error reporting purposes.+            cuz_length+                | length g > 0 = []+                | otherwise    = cuz "there are no buildable components"+            -- For ease of testing, we let per-component builds be toggled+            -- at the top level+            cuz_flag+                | fromFlagOrDefault True (projectConfigPerComponent sharedPackageConfig)+                = []+                | otherwise = cuz "you passed --disable-per-component"++        -- | Sometimes a package may make use of features which are only+        -- supported in per-package mode.  If this is the case, we should+        -- give an error when this occurs.+        checkPerPackageOk comps reasons = do+            let is_sublib (CSubLibName _) = True+                is_sublib _ = False+            when (any (matchElabPkg is_sublib) comps) $+                dieProgress $+                    text "Internal libraries only supported with per-component builds." $$+                    text "Per-component builds were disabled because" <+>+                        fsep (punctuate comma reasons)+            -- TODO: Maybe exclude Backpack too++        elab0 = elaborateSolverToCommon mapDep spkg+        pkgid = elabPkgSourceId    elab0+        pd    = elabPkgDescription elab0++        -- TODO: This is just a skeleton to get elaborateSolverToPackage+        -- working correctly+        -- TODO: When we actually support building these components, we+        -- have to add dependencies on this from all other components+        setupComponent :: Maybe ElaboratedConfiguredPackage+        setupComponent+            | fromMaybe PD.Custom (PD.buildType (elabPkgDescription elab0)) == PD.Custom+            = Just elab0 {+                elabModuleShape = emptyModuleShape,+                elabUnitId = notImpl "elabUnitId",+                elabComponentId = notImpl "elabComponentId",+                elabLinkedInstantiatedWith = Map.empty,+                elabInstallDirs = notImpl "elabInstallDirs",+                elabPkgOrComp = ElabComponent (ElaboratedComponent {..})+              }+            | otherwise+            = Nothing+          where+            compSolverName      = CD.ComponentSetup+            compComponentName   = Nothing+            dep_pkgs = elaborateLibSolverId mapDep =<< CD.setupDeps deps0+            compLibDependencies+                = map configuredId dep_pkgs+            compLinkedLibDependencies = notImpl "compLinkedLibDependencies"+            compOrderLibDependencies = notImpl "compOrderLibDependencies"+            -- Not supported:+            compExeDependencies         = []+            compExeDependencyPaths      = []+            compPkgConfigDependencies   = []++            notImpl f =+                error $ "Distribution.Client.ProjectPlanning.setupComponent: " +++                        f ++ " not implemented yet"+++        buildComponent+            :: (ConfiguredComponentMap,+                LinkedComponentMap,+                Map ComponentId FilePath)+            -> Cabal.Component+            -> LogProgress+                ((ConfiguredComponentMap,+                  LinkedComponentMap,+                  Map ComponentId FilePath),+                ElaboratedConfiguredPackage)+        buildComponent (cc_map, lc_map, exe_map) comp =+          addProgressCtx (text "In the stanza" <+>+                          quotes (text (componentNameStanza cname))) $ do++            -- 1. Configure the component, but with a place holder ComponentId.+            cc0 <- toConfiguredComponent pd+                    (error "Distribution.Client.ProjectPlanning.cc_cid: filled in later")+                    (Map.unionWith Map.union external_cc_map cc_map) comp++            -- 2. Read out the dependencies from the ConfiguredComponent cc0+            let compLibDependencies =+                    -- Nub because includes can show up multiple times+                    ordNub (map (annotatedIdToConfiguredId . ci_ann_id)+                                (cc_includes cc0))+                compExeDependencies =+                    map annotatedIdToConfiguredId+                        (cc_exe_deps cc0)+                compExeDependencyPaths =+                    [ (annotatedIdToConfiguredId aid', path)+                    | aid' <- cc_exe_deps cc0+                    , Just path <- [Map.lookup (ann_id aid') exe_map1]]+                elab_comp = ElaboratedComponent {..}++            -- 3. Construct a preliminary ElaboratedConfiguredPackage,+            -- and use this to compute the component ID.  Fix up cc_id+            -- correctly.+            let elab1 = elab0 {+                        elabPkgOrComp = ElabComponent $ elab_comp+                     }+                cid = case elabBuildStyle elab0 of+                        BuildInplaceOnly ->+                          mkComponentId $+                            display pkgid ++ "-inplace" +++                              (case Cabal.componentNameString cname of+                                  Nothing -> ""+                                  Just s -> "-" ++ display s)+                        BuildAndInstall ->+                          hashedInstalledPackageId+                            (packageHashInputs+                                elaboratedSharedConfig+                                elab1) -- knot tied+                cc = cc0 { cc_ann_id = fmap (const cid) (cc_ann_id cc0) }+            infoProgress $ dispConfiguredComponent cc++            -- 4. Perform mix-in linking+            let lookup_uid def_uid =+                    case Map.lookup (unDefUnitId def_uid) preexistingInstantiatedPkgs of+                        Just full -> full+                        Nothing -> error ("lookup_uid: " ++ display def_uid)+            lc <- toLinkedComponent verbosity lookup_uid (elabPkgSourceId elab0)+                        (Map.union external_lc_map lc_map) cc+            infoProgress $ dispLinkedComponent lc+            -- NB: elab is setup to be the correct form for an+            -- indefinite library, or a definite library with no holes.+            -- We will modify it in 'instantiateInstallPlan' to handle+            -- instantiated packages.++            -- 5. Construct the final ElaboratedConfiguredPackage+            let+                elab = elab1 {+                    elabModuleShape = lc_shape lc,+                    elabUnitId      = abstractUnitId (lc_uid lc),+                    elabComponentId = lc_cid lc,+                    elabLinkedInstantiatedWith = Map.fromList (lc_insts lc),+                    elabPkgOrComp = ElabComponent $ elab_comp {+                        compLinkedLibDependencies = ordNub (map ci_id (lc_includes lc)),+                        compOrderLibDependencies =+                          ordNub (map (abstractUnitId . ci_id)+                                      (lc_includes lc ++ lc_sig_includes lc))+                      },+                    elabInstallDirs = install_dirs cid+                   }++            -- 6. Construct the updated local maps+            let cc_map'  = extendConfiguredComponentMap cc cc_map+                lc_map'  = extendLinkedComponentMap lc lc_map+                exe_map' = Map.insert cid (inplace_bin_dir elab) exe_map++            return ((cc_map', lc_map', exe_map'), elab)+          where+            compLinkedLibDependencies = error "buildComponent: compLinkedLibDependencies"+            compOrderLibDependencies = error "buildComponent: compOrderLibDependencies"++            cname = Cabal.componentName comp+            compComponentName = Just cname+            compSolverName = CD.componentNameToComponent cname++            -- NB: compLinkedLibDependencies and+            -- compOrderLibDependencies are defined when we define+            -- 'elab'.+            external_lib_dep_sids = CD.select (== compSolverName) deps0+            external_exe_dep_sids = CD.select (== compSolverName) exe_deps0+            -- TODO: The fact that lib SolverIds and exe SolverIds are+            -- jammed together here means that we're losing information!+            external_dep_sids = external_lib_dep_sids ++ external_exe_dep_sids+            external_dep_pkgs = concatMap mapDep external_dep_sids++            external_exe_map = Map.fromList $+                [ (getComponentId pkg, path)+                | pkg <- external_dep_pkgs+                , Just path <- [planPackageExePath pkg] ]+            exe_map1 = Map.union external_exe_map exe_map++            external_cc_map = Map.fromListWith Map.union+                            $ map mkCCMapping external_dep_pkgs+            external_lc_map = Map.fromList (map mkShapeMapping external_dep_pkgs)++            compPkgConfigDependencies =+                [ (pn, fromMaybe (error $ "compPkgConfigDependencies: impossible! "+                                            ++ display pn ++ " from "+                                            ++ display (elabPkgSourceId elab0))+                                 (pkgConfigDbPkgVersion pkgConfigDB pn))+                | PkgconfigDependency pn _ <- PD.pkgconfigDepends+                                                (Cabal.componentBuildInfo comp) ]++            install_dirs cid+              | shouldBuildInplaceOnly spkg+              -- use the ordinary default install dirs+              = (InstallDirs.absoluteInstallDirs+                   pkgid+                   (newSimpleUnitId cid)+                   (compilerInfo compiler)+                   InstallDirs.NoCopyDest+                   platform+                   defaultInstallDirs) {++                  -- absoluteInstallDirs sets these as 'undefined' but we have+                  -- to use them as "Setup.hs configure" args+                  InstallDirs.libsubdir  = "",+                  InstallDirs.libexecsubdir  = "",+                  InstallDirs.datasubdir = ""+                }++              | otherwise+              -- use special simplified install dirs+              = storePackageInstallDirs+                  storeDirLayout+                  (compilerId compiler)+                  cid++            -- NB: For inplace NOT InstallPaths.bindir installDirs; for an+            -- inplace build those values are utter nonsense.  So we+            -- have to guess where the directory is going to be.+            -- Fortunately this is "stable" part of Cabal API.+            -- But the way we get the build directory is A HORRIBLE+            -- HACK.+            inplace_bin_dir elab+              | shouldBuildInplaceOnly spkg+              = distBuildDirectory+                    (elabDistDirParams elaboratedSharedConfig elab) </>+                    "build" </> case Cabal.componentNameString cname of+                                    Just n -> display n+                                    Nothing -> ""+              | otherwise+              = InstallDirs.bindir (elabInstallDirs elab)++    -- | Given a 'SolverId' referencing a dependency on a library, return+    -- the 'ElaboratedPlanPackage' corresponding to the library.  This+    -- returns at most one result.+    elaborateLibSolverId :: (SolverId -> [ElaboratedPlanPackage])+                         -> SolverId -> [ElaboratedPlanPackage]+    elaborateLibSolverId mapDep = filter (matchPlanPkg (== CLibName)) . mapDep++    -- | Given an 'ElaboratedPlanPackage', return the path to where the+    -- executable that this package represents would be installed.+    planPackageExePath :: ElaboratedPlanPackage -> Maybe FilePath+    planPackageExePath =+        -- Pre-existing executables are assumed to be in PATH+        -- already.  In fact, this should be impossible.+        -- Modest duplication with 'inplace_bin_dir'+        InstallPlan.foldPlanPackage (const Nothing) $ \elab -> Just $+            if elabBuildStyle elab == BuildInplaceOnly+              then distBuildDirectory+                    (elabDistDirParams elaboratedSharedConfig elab) </>+                    "build" </>+                        case elabPkgOrComp elab of+                            ElabPackage _ -> ""+                            ElabComponent comp ->+                                case fmap Cabal.componentNameString+                                          (compComponentName comp) of+                                    Just (Just n) -> display n+                                    _ -> ""+              else InstallDirs.bindir (elabInstallDirs elab)++    elaborateSolverToPackage :: (SolverId -> [ElaboratedPlanPackage])+                             -> SolverPackage UnresolvedPkgLoc+                             -> ComponentsGraph+                             -> [ElaboratedConfiguredPackage]+                             -> ElaboratedConfiguredPackage+    elaborateSolverToPackage+        mapDep+        pkg@(SolverPackage (SourcePackage pkgid _gdesc _srcloc _descOverride)+                           _flags _stanzas _deps0 _exe_deps0)+        compGraph comps =+        -- Knot tying: the final elab includes the+        -- pkgInstalledId, which is calculated by hashing many+        -- of the other fields of the elaboratedPackage.+        elab+      where+        elab0@ElaboratedConfiguredPackage{..} = elaborateSolverToCommon mapDep pkg+        elab = elab0 {+                elabUnitId = newSimpleUnitId pkgInstalledId,+                elabComponentId = pkgInstalledId,+                elabLinkedInstantiatedWith = Map.empty,+                elabInstallDirs = install_dirs,+                elabPkgOrComp = ElabPackage $ ElaboratedPackage {..},+                elabModuleShape = modShape+            }++        modShape = case find (matchElabPkg (== CLibName)) comps of+                        Nothing -> emptyModuleShape+                        Just e -> Ty.elabModuleShape e++        pkgInstalledId+          | shouldBuildInplaceOnly pkg+          = mkComponentId (display pkgid ++ "-inplace")++          | otherwise+          = assert (isJust elabPkgSourceHash) $+            hashedInstalledPackageId+              (packageHashInputs+                elaboratedSharedConfig+                elab)  -- recursive use of elab++          | otherwise+          = error $ "elaborateInstallPlan: non-inplace package "+                 ++ " is missing a source hash: " ++ display pkgid++        -- Need to filter out internal dependencies, because they don't+        -- correspond to anything real anymore.+        isExt confid = confSrcId confid /= pkgid+        filterExt  = filter isExt+        filterExt' = filter (isExt . fst)++        pkgLibDependencies+            = buildComponentDeps (filterExt  . compLibDependencies)+        pkgExeDependencies+            = buildComponentDeps (filterExt  . compExeDependencies)+        pkgExeDependencyPaths+            = buildComponentDeps (filterExt' . compExeDependencyPaths)+        -- TODO: Why is this flat?+        pkgPkgConfigDependencies+            = CD.flatDeps $ buildComponentDeps compPkgConfigDependencies++        pkgDependsOnSelfLib+            = CD.fromList [ (CD.componentNameToComponent cn, [()])+                          | Graph.N _ cn _ <- fromMaybe [] mb_closure ]+          where+            mb_closure = Graph.revClosure compGraph [ k | k <- Graph.keys compGraph, is_lib k ]+            is_lib CLibName = True+            -- NB: this case should not occur, because sub-libraries+            -- are not supported without per-component builds+            is_lib (CSubLibName _) = True+            is_lib _ = False++        buildComponentDeps f+            = CD.fromList [ (compSolverName comp, f comp)+                          | ElaboratedConfiguredPackage{+                                elabPkgOrComp = ElabComponent comp+                            } <- comps+                          ]++        -- Filled in later+        pkgStanzasEnabled  = Set.empty++        install_dirs+          | shouldBuildInplaceOnly pkg+          -- use the ordinary default install dirs+          = (InstallDirs.absoluteInstallDirs+               pkgid+               (newSimpleUnitId pkgInstalledId)+               (compilerInfo compiler)+               InstallDirs.NoCopyDest+               platform+               defaultInstallDirs) {++              -- absoluteInstallDirs sets these as 'undefined' but we have to+              -- use them as "Setup.hs configure" args+              InstallDirs.libsubdir  = "",+              InstallDirs.libexecsubdir = "",+              InstallDirs.datasubdir = ""+            }++          | otherwise+          -- use special simplified install dirs+          = storePackageInstallDirs+              storeDirLayout+              (compilerId compiler)+              pkgInstalledId++    elaborateSolverToCommon :: (SolverId -> [ElaboratedPlanPackage])+                            -> SolverPackage UnresolvedPkgLoc+                            -> ElaboratedConfiguredPackage+    elaborateSolverToCommon mapDep+        pkg@(SolverPackage (SourcePackage pkgid gdesc srcloc descOverride)+                           flags stanzas deps0 _exe_deps0) =+        elaboratedPackage+      where+        elaboratedPackage = ElaboratedConfiguredPackage {..}++        -- These get filled in later+        elabUnitId          = error "elaborateSolverToCommon: elabUnitId"+        elabComponentId     = error "elaborateSolverToCommon: elabComponentId"+        elabInstantiatedWith = Map.empty+        elabLinkedInstantiatedWith = error "elaborateSolverToCommon: elabLinkedInstantiatedWith"+        elabPkgOrComp       = error "elaborateSolverToCommon: elabPkgOrComp"+        elabInstallDirs     = error "elaborateSolverToCommon: elabInstallDirs"+        elabModuleShape     = error "elaborateSolverToCommon: elabModuleShape"++        elabIsCanonical     = True+        elabPkgSourceId     = pkgid+        elabPkgDescription  = let Right (desc, _) =+                                    PD.finalizePD+                                    flags elabEnabledSpec (const True)+                                    platform (compilerInfo compiler)+                                    [] gdesc+                               in desc+        elabFlagAssignment  = flags+        elabFlagDefaults    = [ (Cabal.flagName flag, Cabal.flagDefault flag)+                              | flag <- PD.genPackageFlags gdesc ]++        elabEnabledSpec      = enableStanzas stanzas+        elabStanzasAvailable = Set.fromList stanzas+        elabStanzasRequested =+            -- NB: even if a package stanza is requested, if the package+            -- doesn't actually have any of that stanza we omit it from+            -- the request, to ensure that we don't decide that this+            -- package needs to be rebuilt.  (It needs to be done here,+            -- because the ElaboratedConfiguredPackage is where we test+            -- whether or not there have been changes.)+            Map.fromList $ [ (TestStanzas,  v) | v <- maybeToList tests+                                               , _ <- PD.testSuites elabPkgDescription ]+                        ++ [ (BenchStanzas, v) | v <- maybeToList benchmarks+                                               , _ <- PD.benchmarks elabPkgDescription ]+          where+            tests, benchmarks :: Maybe Bool+            tests      = perPkgOptionMaybe pkgid packageConfigTests+            benchmarks = perPkgOptionMaybe pkgid packageConfigBenchmarks++        -- This is a placeholder which will get updated by 'pruneInstallPlanPass1'+        -- and 'pruneInstallPlanPass2'.  We can't populate it here+        -- because whether or not tests/benchmarks should be enabled+        -- is heuristically calculated based on whether or not the+        -- dependencies of the test suite have already been installed,+        -- but this function doesn't know what is installed (since+        -- we haven't improved the plan yet), so we do it in another pass.+        -- Check the comments of those functions for more details.+        elabBuildTargets    = []+        elabTestTargets     = []+        elabReplTarget      = Nothing+        elabBuildHaddocks   = False++        elabPkgSourceLocation = srcloc+        elabPkgSourceHash   = Map.lookup pkgid sourcePackageHashes+        elabLocalToProject  = isLocalToProject pkg+        elabBuildStyle      = if shouldBuildInplaceOnly pkg+                                then BuildInplaceOnly else BuildAndInstall+        elabBuildPackageDBStack    = buildAndRegisterDbs+        elabRegisterPackageDBStack = buildAndRegisterDbs++        elabSetupScriptStyle       = packageSetupScriptStyle elabPkgDescription+        -- Computing the deps here is a little awful+        deps = fmap (concatMap (elaborateLibSolverId mapDep)) deps0+        elabSetupScriptCliVersion  = packageSetupScriptSpecVersion+                                      elabSetupScriptStyle elabPkgDescription deps+        elabSetupPackageDBStack    = buildAndRegisterDbs++        buildAndRegisterDbs+          | shouldBuildInplaceOnly pkg = inplacePackageDbs+          | otherwise                  = storePackageDbs++        elabPkgDescriptionOverride = descOverride++        elabVanillaLib    = perPkgOptionFlag pkgid True packageConfigVanillaLib --TODO: [required feature]: also needs to be handled recursively+        elabSharedLib     = pkgid `Set.member` pkgsUseSharedLibrary+        elabDynExe        = perPkgOptionFlag pkgid False packageConfigDynExe+        elabGHCiLib       = perPkgOptionFlag pkgid False packageConfigGHCiLib --TODO: [required feature] needs to default to enabled on windows still++        elabProfExe       = perPkgOptionFlag pkgid False packageConfigProf+        elabProfLib       = pkgid `Set.member` pkgsUseProfilingLibrary++        (elabProfExeDetail,+         elabProfLibDetail) = perPkgOptionLibExeFlag pkgid ProfDetailDefault+                               packageConfigProfDetail+                               packageConfigProfLibDetail+        elabCoverage      = perPkgOptionFlag pkgid False packageConfigCoverage++        elabOptimization  = perPkgOptionFlag pkgid NormalOptimisation packageConfigOptimization+        elabSplitObjs     = perPkgOptionFlag pkgid False packageConfigSplitObjs+        elabStripLibs     = perPkgOptionFlag pkgid False packageConfigStripLibs+        elabStripExes     = perPkgOptionFlag pkgid False packageConfigStripExes+        elabDebugInfo     = perPkgOptionFlag pkgid NoDebugInfo packageConfigDebugInfo++        -- Combine the configured compiler prog settings with the user-supplied+        -- config. For the compiler progs any user-supplied config was taken+        -- into account earlier when configuring the compiler so its ok that+        -- our configured settings for the compiler override the user-supplied+        -- config here.+        elabProgramPaths  = Map.fromList+                             [ (programId prog, programPath prog)+                             | prog <- configuredPrograms compilerprogdb ]+                        <> perPkgOptionMapLast pkgid packageConfigProgramPaths+        elabProgramArgs   = Map.fromList+                             [ (programId prog, args)+                             | prog <- configuredPrograms compilerprogdb+                             , let args = programOverrideArgs prog+                             , not (null args)+                             ]+                        <> perPkgOptionMapMappend pkgid packageConfigProgramArgs+        elabProgramPathExtra    = perPkgOptionNubList pkgid packageConfigProgramPathExtra+        elabConfigureScriptArgs = perPkgOptionList pkgid packageConfigConfigureArgs+        elabExtraLibDirs        = perPkgOptionList pkgid packageConfigExtraLibDirs+        elabExtraFrameworkDirs  = perPkgOptionList pkgid packageConfigExtraFrameworkDirs+        elabExtraIncludeDirs    = perPkgOptionList pkgid packageConfigExtraIncludeDirs+        elabProgPrefix          = perPkgOptionMaybe pkgid packageConfigProgPrefix+        elabProgSuffix          = perPkgOptionMaybe pkgid packageConfigProgSuffix+++        elabHaddockHoogle       = perPkgOptionFlag pkgid False packageConfigHaddockHoogle+        elabHaddockHtml         = perPkgOptionFlag pkgid False packageConfigHaddockHtml+        elabHaddockHtmlLocation = perPkgOptionMaybe pkgid packageConfigHaddockHtmlLocation+        elabHaddockForeignLibs  = perPkgOptionFlag pkgid False packageConfigHaddockForeignLibs+        elabHaddockExecutables  = perPkgOptionFlag pkgid False packageConfigHaddockExecutables+        elabHaddockTestSuites   = perPkgOptionFlag pkgid False packageConfigHaddockTestSuites+        elabHaddockBenchmarks   = perPkgOptionFlag pkgid False packageConfigHaddockBenchmarks+        elabHaddockInternal     = perPkgOptionFlag pkgid False packageConfigHaddockInternal+        elabHaddockCss          = perPkgOptionMaybe pkgid packageConfigHaddockCss+        elabHaddockHscolour     = perPkgOptionFlag pkgid False packageConfigHaddockHscolour+        elabHaddockHscolourCss  = perPkgOptionMaybe pkgid packageConfigHaddockHscolourCss+        elabHaddockContents     = perPkgOptionMaybe pkgid packageConfigHaddockContents++    perPkgOptionFlag  :: PackageId -> a ->  (PackageConfig -> Flag a) -> a+    perPkgOptionMaybe :: PackageId ->       (PackageConfig -> Flag a) -> Maybe a+    perPkgOptionList  :: PackageId ->       (PackageConfig -> [a])    -> [a]++    perPkgOptionFlag  pkgid def f = fromFlagOrDefault def (lookupPerPkgOption pkgid f)+    perPkgOptionMaybe pkgid     f = flagToMaybe (lookupPerPkgOption pkgid f)+    perPkgOptionList  pkgid     f = lookupPerPkgOption pkgid f+    perPkgOptionNubList    pkgid f = fromNubList   (lookupPerPkgOption pkgid f)+    perPkgOptionMapLast    pkgid f = getMapLast    (lookupPerPkgOption pkgid f)+    perPkgOptionMapMappend pkgid f = getMapMappend (lookupPerPkgOption pkgid f)++    perPkgOptionLibExeFlag pkgid def fboth flib = (exe, lib)+      where+        exe = fromFlagOrDefault def bothflag+        lib = fromFlagOrDefault def (bothflag <> libflag)++        bothflag = lookupPerPkgOption pkgid fboth+        libflag  = lookupPerPkgOption pkgid flib++    lookupPerPkgOption :: (Package pkg, Monoid m)+                       => pkg -> (PackageConfig -> m) -> m+    lookupPerPkgOption pkg f+      -- the project config specifies values that apply to packages local to+      -- but by default non-local packages get all default config values+      -- the project, and can specify per-package values for any package,+      | isLocalToProject pkg = local `mappend` perpkg+      | otherwise            =                 perpkg+      where+        local  = f localPackagesConfig+        perpkg = maybe mempty f (Map.lookup (packageName pkg) perPackageConfig)++    inplacePackageDbs = storePackageDbs+                     ++ [ distPackageDB (compilerId compiler) ]++    storePackageDbs   = storePackageDBStack (compilerId compiler)++    -- For this local build policy, every package that lives in a local source+    -- dir (as opposed to a tarball), or depends on such a package, will be+    -- built inplace into a shared dist dir. Tarball packages that depend on+    -- source dir packages will also get unpacked locally.+    shouldBuildInplaceOnly :: SolverPackage loc -> Bool+    shouldBuildInplaceOnly pkg = Set.member (packageId pkg)+                                            pkgsToBuildInplaceOnly++    pkgsToBuildInplaceOnly :: Set PackageId+    pkgsToBuildInplaceOnly =+        Set.fromList+      $ map packageId+      $ SolverInstallPlan.reverseDependencyClosure+          solverPlan+          [ PlannedId (packageId pkg)+          | pkg <- localPackages ]++    isLocalToProject :: Package pkg => pkg -> Bool+    isLocalToProject pkg = Set.member (packageId pkg)+                                      pkgsLocalToProject++    pkgsLocalToProject :: Set PackageId+    pkgsLocalToProject = Set.fromList [ packageId pkg | pkg <- localPackages ]++    pkgsUseSharedLibrary :: Set PackageId+    pkgsUseSharedLibrary =+        packagesWithLibDepsDownwardClosedProperty needsSharedLib+      where+        needsSharedLib pkg =+            fromMaybe compilerShouldUseSharedLibByDefault+                      (liftM2 (||) pkgSharedLib pkgDynExe)+          where+            pkgid        = packageId pkg+            pkgSharedLib = perPkgOptionMaybe pkgid packageConfigSharedLib+            pkgDynExe    = perPkgOptionMaybe pkgid packageConfigDynExe++    --TODO: [code cleanup] move this into the Cabal lib. It's currently open+    -- coded in Distribution.Simple.Configure, but should be made a proper+    -- function of the Compiler or CompilerInfo.+    compilerShouldUseSharedLibByDefault =+      case compilerFlavor compiler of+        GHC   -> GHC.isDynamic compiler+        GHCJS -> GHCJS.isDynamic compiler+        _     -> False++    pkgsUseProfilingLibrary :: Set PackageId+    pkgsUseProfilingLibrary =+        packagesWithLibDepsDownwardClosedProperty needsProfilingLib+      where+        needsProfilingLib pkg =+            fromFlagOrDefault False (profBothFlag <> profLibFlag)+          where+            pkgid        = packageId pkg+            profBothFlag = lookupPerPkgOption pkgid packageConfigProf+            profLibFlag  = lookupPerPkgOption pkgid packageConfigProfLib+            --TODO: [code cleanup] unused: the old deprecated packageConfigProfExe++    libDepGraph = Graph.fromDistinctList $+                    map NonSetupLibDepSolverPlanPackage+                        (SolverInstallPlan.toList solverPlan)++    packagesWithLibDepsDownwardClosedProperty property =+        Set.fromList+      . map packageId+      . fromMaybe []+      $ Graph.closure+          libDepGraph+          [ Graph.nodeKey pkg+          | pkg <- SolverInstallPlan.toList solverPlan+          , property pkg ] -- just the packages that satisfy the property+      --TODO: [nice to have] this does not check the config consistency,+      -- e.g. a package explicitly turning off profiling, but something+      -- depending on it that needs profiling. This really needs a separate+      -- package config validation/resolution pass.++      --TODO: [nice to have] config consistency checking:+      -- + profiling libs & exes, exe needs lib, recursive+      -- + shared libs & exes, exe needs lib, recursive+      -- + vanilla libs & exes, exe needs lib, recursive+      -- + ghci or shared lib needed by TH, recursive, ghc version dependent++-- TODO: Drop matchPlanPkg/matchElabPkg in favor of mkCCMapping++-- | Given a 'ElaboratedPlanPackage', report if it matches a 'ComponentName'.+matchPlanPkg :: (ComponentName -> Bool) -> ElaboratedPlanPackage -> Bool+matchPlanPkg p = InstallPlan.foldPlanPackage (p . ipiComponentName) (matchElabPkg p)++-- | Get the appropriate 'ComponentName' which identifies an installed+-- component.+ipiComponentName :: IPI.InstalledPackageInfo -> ComponentName+ipiComponentName ipkg =+    case IPI.sourceLibName ipkg of+        Nothing -> CLibName+        Just n  -> (CSubLibName n)++-- | Given a 'ElaboratedConfiguredPackage', report if it matches a+-- 'ComponentName'.+matchElabPkg :: (ComponentName -> Bool) -> ElaboratedConfiguredPackage -> Bool+matchElabPkg p elab =+    case elabPkgOrComp elab of+        ElabComponent comp -> maybe False p (compComponentName comp)+        ElabPackage _ ->+            -- So, what should we do here?  One possibility is to+            -- unconditionally return 'True', because whatever it is+            -- that we're looking for, it better be in this package.+            -- But this is a bit dodgy if the package doesn't actually+            -- have, e.g., a library.  Fortunately, it's not possible+            -- for the build of the library/executables to be toggled+            -- by 'pkgStanzasEnabled', so the only thing we have to+            -- test is if the component in question is *buildable.*+            any (p . componentName)+                (Cabal.pkgBuildableComponents (elabPkgDescription elab))++-- | Given an 'ElaboratedPlanPackage', generate the mapping from 'PackageName'+-- and 'ComponentName' to the 'ComponentId' that that should be used+-- in this case.+mkCCMapping :: ElaboratedPlanPackage+            -> (PackageName, Map ComponentName (AnnotatedId ComponentId))+mkCCMapping =+    InstallPlan.foldPlanPackage+       (\ipkg -> (packageName ipkg,+                    Map.singleton (ipiComponentName ipkg)+                                  -- TODO: libify+                                  (AnnotatedId {+                                    ann_id = IPI.installedComponentId ipkg,+                                    ann_pid = packageId ipkg,+                                    ann_cname = IPI.sourceComponentName ipkg+                                  })))+      $ \elab ->+        let mk_aid cn = AnnotatedId {+                            ann_id = elabComponentId elab,+                            ann_pid = packageId elab,+                            ann_cname = cn+                        }+        in (packageName elab,+            case elabPkgOrComp elab of+                ElabComponent comp ->+                    case compComponentName comp of+                        Nothing -> Map.empty+                        Just n  -> Map.singleton n (mk_aid n)+                ElabPackage _ ->+                    Map.fromList $+                        map (\comp -> let cn = Cabal.componentName comp in (cn, mk_aid cn))+                            (Cabal.pkgBuildableComponents (elabPkgDescription elab)))++-- | Given an 'ElaboratedPlanPackage', generate the mapping from 'ComponentId'+-- to the shape of this package, as per mix-in linking.+mkShapeMapping :: ElaboratedPlanPackage+               -> (ComponentId, (OpenUnitId, ModuleShape))+mkShapeMapping dpkg =+    (getComponentId dpkg, (indef_uid, shape))+  where+    (dcid, shape) =+        InstallPlan.foldPlanPackage+            -- Uses Monad (->)+            (liftM2 (,) IPI.installedComponentId shapeInstalledPackage)+            (liftM2 (,) elabComponentId elabModuleShape)+            dpkg+    indef_uid =+        IndefFullUnitId dcid+            (Map.fromList [ (req, OpenModuleVar req)+                          | req <- Set.toList (modShapeRequires shape)])++-- | A newtype for 'SolverInstallPlan.SolverPlanPackage' for which the+-- dependency graph considers only dependencies on libraries which are+-- NOT from setup dependencies.  Used to compute the set+-- of packages needed for profiling and dynamic libraries.+newtype NonSetupLibDepSolverPlanPackage+    = NonSetupLibDepSolverPlanPackage+    { unNonSetupLibDepSolverPlanPackage :: SolverInstallPlan.SolverPlanPackage }++instance Package NonSetupLibDepSolverPlanPackage where+    packageId = packageId . unNonSetupLibDepSolverPlanPackage++instance IsNode NonSetupLibDepSolverPlanPackage where+    type Key NonSetupLibDepSolverPlanPackage = SolverId+    nodeKey = nodeKey . unNonSetupLibDepSolverPlanPackage+    nodeNeighbors (NonSetupLibDepSolverPlanPackage spkg)+        = ordNub $ CD.nonSetupDeps (resolverPackageLibDeps spkg)++type InstS = Map UnitId ElaboratedPlanPackage+type InstM a = State InstS a++getComponentId :: ElaboratedPlanPackage+               -> ComponentId+getComponentId (InstallPlan.PreExisting dipkg) = IPI.installedComponentId dipkg+getComponentId (InstallPlan.Configured elab) = elabComponentId elab+getComponentId (InstallPlan.Installed elab) = elabComponentId elab++instantiateInstallPlan :: ElaboratedInstallPlan -> ElaboratedInstallPlan+instantiateInstallPlan plan =+    InstallPlan.new (IndependentGoals False)+                    (Graph.fromDistinctList (Map.elems ready_map))+  where+    pkgs = InstallPlan.toList plan++    cmap = Map.fromList [ (getComponentId pkg, pkg) | pkg <- pkgs ]++    instantiateUnitId :: ComponentId -> Map ModuleName Module+                      -> InstM DefUnitId+    instantiateUnitId cid insts = state $ \s ->+        case Map.lookup uid s of+            Nothing ->+                -- Knot tied+                let (r, s') = runState (instantiateComponent uid cid insts)+                                       (Map.insert uid r s)+                in (def_uid, Map.insert uid r s')+            Just _ -> (def_uid, s)+      where+        def_uid = mkDefUnitId cid insts+        uid = unDefUnitId def_uid++    instantiateComponent+        :: UnitId -> ComponentId -> Map ModuleName Module+        -> InstM ElaboratedPlanPackage+    instantiateComponent uid cid insts+      | Just planpkg <- Map.lookup cid cmap+      = case planpkg of+          InstallPlan.Configured (elab@ElaboratedConfiguredPackage+                                    { elabPkgOrComp = ElabComponent comp }) -> do+            deps <- mapM (substUnitId insts)+                         (compLinkedLibDependencies comp)+            let getDep (Module dep_uid _) = [dep_uid]+            return $ InstallPlan.Configured elab {+                    elabUnitId = uid,+                    elabComponentId = cid,+                    elabInstantiatedWith = insts,+                    elabIsCanonical = Map.null insts,+                    elabPkgOrComp = ElabComponent comp {+                        compOrderLibDependencies =+                            (if Map.null insts then [] else [newSimpleUnitId cid]) +++                            ordNub (map unDefUnitId+                                (deps ++ concatMap getDep (Map.elems insts)))+                    }+                }+          _ -> return planpkg+      | otherwise = error ("instantiateComponent: " ++ display cid)++    substUnitId :: Map ModuleName Module -> OpenUnitId -> InstM DefUnitId+    substUnitId _ (DefiniteUnitId uid) =+        return uid+    substUnitId subst (IndefFullUnitId cid insts) = do+        insts' <- substSubst subst insts+        instantiateUnitId cid insts'++    -- NB: NOT composition+    substSubst :: Map ModuleName Module+               -> Map ModuleName OpenModule+               -> InstM (Map ModuleName Module)+    substSubst subst insts = T.mapM (substModule subst) insts++    substModule :: Map ModuleName Module -> OpenModule -> InstM Module+    substModule subst (OpenModuleVar mod_name)+        | Just m <- Map.lookup mod_name subst = return m+        | otherwise = error "substModule: non-closing substitution"+    substModule subst (OpenModule uid mod_name) = do+        uid' <- substUnitId subst uid+        return (Module uid' mod_name)++    indefiniteUnitId :: ComponentId -> InstM UnitId+    indefiniteUnitId cid = do+        let uid = newSimpleUnitId cid+        r <- indefiniteComponent uid cid+        state $ \s -> (uid, Map.insert uid r s)++    indefiniteComponent :: UnitId -> ComponentId -> InstM ElaboratedPlanPackage+    indefiniteComponent _uid cid+      | Just planpkg <- Map.lookup cid cmap+      = return planpkg+      | otherwise = error ("indefiniteComponent: " ++ display cid)++    ready_map = execState work Map.empty++    work = forM_ pkgs $ \pkg ->+            case pkg of+                InstallPlan.Configured elab+                    | not (Map.null (elabLinkedInstantiatedWith elab))+                    -> indefiniteUnitId (elabComponentId elab)+                        >> return ()+                _ -> instantiateUnitId (getComponentId pkg) Map.empty+                        >> return ()++---------------------------+-- Build targets+--++-- Refer to ProjectPlanning.Types for details of these important types:++-- data ComponentTarget = ...+-- data SubComponentTarget = ...++-- One step in the build system is to translate higher level intentions like+-- "build this package", "test that package", or "repl that component" into+-- a more detailed specification of exactly which components to build (or other+-- actions like repl or build docs). This translation is somewhat different for+-- different commands. For example "test" for a package will build a different+-- set of components than "build". In addition, the translation of these+-- intentions can fail. For example "run" for a package is only unambiguous+-- when the package has a single executable.+--+-- So we need a little bit of infrastructure to make it easy for the command+-- implementations to select what component targets are meant when a user asks+-- to do something with a package or component. To do this (and to be able to+-- produce good error messages for mistakes and when targets are not available)+-- we need to gather and summarise accurate information about all the possible+-- targets, both available and unavailable. Then a command implementation can+-- decide which of the available component targets should be selected.++-- | An available target represents a component within a package that a user+-- command could plausibly refer to. In this sense, all the components defined+-- within the package are things the user could refer to, whether or not it+-- would actually be possible to build that component.+--+-- In particular the available target contains an 'AvailableTargetStatus' which+-- informs us about whether it's actually possible to select this component to+-- be built, and if not why not. This detail makes it possible for command+-- implementations (like @build@, @test@ etc) to accurately report why a target+-- cannot be used.+--+-- Note that the type parameter is used to help enforce that command+-- implementations can only select targets that can actually be built (by+-- forcing them to return the @k@ value for the selected targets).+-- In particular 'resolveTargets' makes use of this (with @k@ as+-- @('UnitId', ComponentName')@) to identify the targets thus selected.+--+data AvailableTarget k = AvailableTarget {+       availableTargetPackageId      :: PackageId,+       availableTargetComponentName  :: ComponentName,+       availableTargetStatus         :: AvailableTargetStatus k,+       availableTargetLocalToProject :: Bool+     }+  deriving (Eq, Show, Functor)++-- | The status of a an 'AvailableTarget' component. This tells us whether+-- it's actually possible to select this component to be built, and if not+-- why not.+--+data AvailableTargetStatus k =+       TargetDisabledByUser   -- ^ When the user does @tests: False@+     | TargetDisabledBySolver -- ^ When the solver could not enable tests+     | TargetNotBuildable     -- ^ When the component has @buildable: False@+     | TargetNotLocal         -- ^ When the component is non-core in a non-local package+     | TargetBuildable k TargetRequested -- ^ The target can or should be built+  deriving (Eq, Ord, Show, Functor)++-- | This tells us whether a target ought to be built by default, or only if+-- specifically requested. The policy is that components like libraries and+-- executables are built by default by @build@, but test suites and benchmarks+-- are not, unless this is overridden in the project configuration.+--+data TargetRequested =+       TargetRequestedByDefault    -- ^ To be built by default+     | TargetNotRequestedByDefault -- ^ Not to be built by default+  deriving (Eq, Ord, Show)++-- | Given the install plan, produce the set of 'AvailableTarget's for each+-- package-component pair.+--+-- Typically there will only be one such target for each component, but for+-- example if we have a plan with both normal and profiling variants of a+-- component then we would get both as available targets, or similarly if we+-- had a plan that contained two instances of the same version of a package.+-- This approach makes it relatively easy to select all instances\/variants+-- of a component.+--+availableTargets :: ElaboratedInstallPlan+                 -> Map (PackageId, ComponentName)+                        [AvailableTarget (UnitId, ComponentName)]+availableTargets installPlan =+    let rs = [ (pkgid, cname, fake, target)+             | pkg <- InstallPlan.toList installPlan+             , (pkgid, cname, fake, target) <- case pkg of+                 InstallPlan.PreExisting ipkg -> availableInstalledTargets ipkg+                 InstallPlan.Installed   elab -> availableSourceTargets elab+                 InstallPlan.Configured  elab -> availableSourceTargets elab+             ]+     in Map.union+         (Map.fromListWith (++)+            [ ((pkgid, cname), [target])+            | (pkgid, cname, fake, target) <- rs, not fake])+         (Map.fromList+            [ ((pkgid, cname), [target])+            | (pkgid, cname, fake, target) <- rs, fake])+    -- The normal targets mask the fake ones. We get all instances of the+    -- normal ones and only one copy of the fake ones (as there are many+    -- duplicates of the fake ones). See 'availableSourceTargets' below for+    -- more details on this fake stuff is about.++availableInstalledTargets :: IPI.InstalledPackageInfo+                          -> [(PackageId, ComponentName, Bool,+                               AvailableTarget (UnitId, ComponentName))]+availableInstalledTargets ipkg =+    let unitid = installedUnitId ipkg+        cname  = CLibName+        status = TargetBuildable (unitid, cname) TargetRequestedByDefault+        target = AvailableTarget (packageId ipkg) cname status False+        fake   = False+     in [(packageId ipkg, cname, fake, target)]++availableSourceTargets :: ElaboratedConfiguredPackage+                       -> [(PackageId, ComponentName, Bool,+                            AvailableTarget (UnitId, ComponentName))]+availableSourceTargets elab =+    -- We have a somewhat awkward problem here. We need to know /all/ the+    -- components from /all/ the packages because these are the things that+    -- users could refer to. Unfortunately, at this stage the elaborated install+    -- plan does /not/ contain all components: some components have already+    -- been deleted because they cannot possibly be built. This is the case+    -- for components that are marked @buildable: False@ in their .cabal files.+    -- (It's not unreasonable that the unbuildable components have been pruned+    -- as the plan invariant is considerably simpler if all nodes can be built)+    --+    -- We can recover the missing components but it's not exactly elegant. For+    -- a graph node corresponding to a component we still have the information+    -- about the package that it came from, and this includes the names of+    -- /all/ the other components in the package. So in principle this lets us+    -- find the names of all components, plus full details of the buildable+    -- components.+    --+    -- Consider for example a package with 3 exe components: foo, bar and baz+    -- where foo and bar are buildable, but baz is not. So the plan contains+    -- nodes for the components foo and bar. Now we look at each of these two+    -- nodes and look at the package they come from and the names of the+    -- components in this package. This will give us the names foo, bar and+    -- baz, twice (once for each of the two buildable components foo and bar).+    --+    -- We refer to these reconstructed missing components as fake targets.+    -- It is an invariant that they are not available to be built.+    --+    -- To produce the final set of targets we put the fake targets in a finite+    -- map (thus eliminating the duplicates) and then we overlay that map with+    -- the normal buildable targets. (This is done above in 'availableTargets'.)+    --+    [ (packageId elab, cname, fake, target)+    | component <- pkgComponents (elabPkgDescription elab)+    , let cname  = componentName component+          status = componentAvailableTargetStatus component+          target = AvailableTarget {+                     availableTargetPackageId      = packageId elab,+                     availableTargetComponentName  = cname,+                     availableTargetStatus         = status,+                     availableTargetLocalToProject = elabLocalToProject elab+                   }+          fake   = isFakeTarget cname++    -- TODO: The goal of this test is to exclude "instantiated"+    -- packages as available targets. This means that you can't+    -- ask for a particular instantiated component to be built;+    -- it will only get built by a dependency.  Perhaps the+    -- correct way to implement this is to run selection+    -- prior to instantiating packages.  If you refactor+    -- this, then you can delete this test.+    , elabIsCanonical elab++      -- Filter out some bogus parts of the cross product that are never needed+    , case status of+        TargetBuildable{} | fake -> False+        _                        -> True+    ]+  where+    isFakeTarget cname =+      case elabPkgOrComp elab of+        ElabPackage _               -> False+        ElabComponent elabComponent -> compComponentName elabComponent+                                       /= Just cname++    componentAvailableTargetStatus+      :: Component -> AvailableTargetStatus (UnitId, ComponentName)+    componentAvailableTargetStatus component =+        case componentOptionalStanza (componentName component) of+          -- it is not an optional stanza, so a library, exe or foreign lib+          Nothing+            | not buildable  -> TargetNotBuildable+            | otherwise      -> TargetBuildable (elabUnitId elab, cname)+                                                TargetRequestedByDefault++          -- it is not an optional stanza, so a testsuite or benchmark+          Just stanza ->+            case (Map.lookup stanza (elabStanzasRequested elab),+                  Set.member stanza (elabStanzasAvailable elab)) of+              _ | not withinPlan -> TargetNotLocal+              (Just False,   _)  -> TargetDisabledByUser+              (Nothing,  False)  -> TargetDisabledBySolver+              _ | not buildable  -> TargetNotBuildable+              (Just True, True)  -> TargetBuildable (elabUnitId elab, cname)+                                                    TargetRequestedByDefault+              (Nothing,   True)  -> TargetBuildable (elabUnitId elab, cname)+                                                    TargetNotRequestedByDefault+              (Just True, False) ->+                error "componentAvailableTargetStatus: impossible"+      where+        cname      = componentName component+        buildable  = PD.buildable (componentBuildInfo component)+        withinPlan = elabLocalToProject elab+                  || case elabPkgOrComp elab of+                       ElabComponent elabComponent ->+                         compComponentName elabComponent == Just cname+                       ElabPackage _ ->+                         case componentName component of+                           CLibName   -> True+                           CExeName _ -> True+                           --TODO: what about sub-libs and foreign libs?+                           _          -> False++-- | Merge component targets that overlap each other. Specially when we have+-- multiple targets for the same component and one of them refers to the whole+-- component (rather than a module or file within) then all the other targets+-- for that component are subsumed.+--+-- We also allow for information associated with each component target, and+-- whenever we targets subsume each other we aggregate their associated info.+--+nubComponentTargets :: [(ComponentTarget, a)] -> [(ComponentTarget, [a])]+nubComponentTargets =+    concatMap (wholeComponentOverrides . map snd)+  . groupBy ((==)    `on` fst)+  . sortBy  (compare `on` fst)+  . map (\t@((ComponentTarget cname _, _)) -> (cname, t))+  . map compatSubComponentTargets+  where+    -- If we're building the whole component then that the only target all we+    -- need, otherwise we can have several targets within the component.+    wholeComponentOverrides :: [(ComponentTarget,  a )]+                            -> [(ComponentTarget, [a])]+    wholeComponentOverrides ts =+      case [ t | (t@(ComponentTarget _ WholeComponent), _) <- ts ] of+        (t:_) -> [ (t, map snd ts) ]+        []    -> [ (t,[x]) | (t,x) <- ts ]++    -- Not all Cabal Setup.hs versions support sub-component targets, so switch+    -- them over to the whole component+    compatSubComponentTargets :: (ComponentTarget, a) -> (ComponentTarget, a)+    compatSubComponentTargets target@(ComponentTarget cname _subtarget, x)+      | not setupHsSupportsSubComponentTargets+                  = (ComponentTarget cname WholeComponent, x)+      | otherwise = target++    -- Actually the reality is that no current version of Cabal's Setup.hs+    -- build command actually support building specific files or modules.+    setupHsSupportsSubComponentTargets = False+    -- TODO: when that changes, adjust this test, e.g.+    -- | pkgSetupScriptCliVersion >= Version [x,y] []++pkgHasEphemeralBuildTargets :: ElaboratedConfiguredPackage -> Bool+pkgHasEphemeralBuildTargets elab =+    isJust (elabReplTarget elab)+ || (not . null) (elabTestTargets elab)+ || (not . null) [ () | ComponentTarget _ subtarget <- elabBuildTargets elab+                      , subtarget /= WholeComponent ]++-- | The components that we'll build all of, meaning that after they're built+-- we can skip building them again (unlike with building just some modules or+-- other files within a component).+--+elabBuildTargetWholeComponents :: ElaboratedConfiguredPackage+                              -> Set ComponentName+elabBuildTargetWholeComponents elab =+    Set.fromList+      [ cname | ComponentTarget cname WholeComponent <- elabBuildTargets elab ]++++------------------------------------------------------------------------------+-- * Install plan pruning+------------------------------------------------------------------------------++-- | How 'pruneInstallPlanToTargets' should interpret the per-package+-- 'ComponentTarget's: as build, repl or haddock targets.+--+data TargetAction = TargetActionBuild+                  | TargetActionRepl+                  | TargetActionTest+                  | TargetActionHaddock++-- | Given a set of per-package\/per-component targets, take the subset of the+-- install plan needed to build those targets. Also, update the package config+-- to specify which optional stanzas to enable, and which targets within each+-- package to build.+--+pruneInstallPlanToTargets :: TargetAction+                          -> Map UnitId [ComponentTarget]+                          -> ElaboratedInstallPlan -> ElaboratedInstallPlan+pruneInstallPlanToTargets targetActionType perPkgTargetsMap elaboratedPlan =+    InstallPlan.new (InstallPlan.planIndepGoals elaboratedPlan)+  . Graph.fromDistinctList+    -- We have to do the pruning in two passes+  . pruneInstallPlanPass2+  . pruneInstallPlanPass1+    -- Set the targets that will be the roots for pruning+  . setRootTargets targetActionType perPkgTargetsMap+  . InstallPlan.toList+  $ elaboratedPlan++-- | This is a temporary data type, where we temporarily+-- override the graph dependencies of an 'ElaboratedPackage',+-- so we can take a closure over them.  We'll throw out the+-- overriden dependencies when we're done so it's strictly temporary.+--+-- For 'ElaboratedComponent', this the cached unit IDs always+-- coincide with the real thing.+data PrunedPackage = PrunedPackage ElaboratedConfiguredPackage [UnitId]++instance Package PrunedPackage where+    packageId (PrunedPackage elab _) = packageId elab++instance HasUnitId PrunedPackage where+    installedUnitId = nodeKey++instance IsNode PrunedPackage where+    type Key PrunedPackage = UnitId+    nodeKey (PrunedPackage elab _)  = nodeKey elab+    nodeNeighbors (PrunedPackage _ deps) = deps++fromPrunedPackage :: PrunedPackage -> ElaboratedConfiguredPackage+fromPrunedPackage (PrunedPackage elab _) = elab++-- | Set the build targets based on the user targets (but not rev deps yet).+-- This is required before we can prune anything.+--+setRootTargets :: TargetAction+               -> Map UnitId [ComponentTarget]+               -> [ElaboratedPlanPackage]+               -> [ElaboratedPlanPackage]+setRootTargets targetAction perPkgTargetsMap =+    assert (not (Map.null perPkgTargetsMap)) $+    assert (all (not . null) (Map.elems perPkgTargetsMap)) $++    map (mapConfiguredPackage setElabBuildTargets)+  where+    -- Set the targets we'll build for this package/component. This is just+    -- based on the root targets from the user, not targets implied by reverse+    -- dependencies. Those comes in the second pass once we know the rev deps.+    --+    setElabBuildTargets elab =+      case (Map.lookup (installedUnitId elab) perPkgTargetsMap,+            targetAction) of+        (Nothing, _)                      -> elab+        (Just tgts,  TargetActionBuild)   -> elab { elabBuildTargets = tgts }+        (Just tgts,  TargetActionTest)    -> elab { elabTestTargets  = tgts }+        (Just [tgt], TargetActionRepl)    -> elab { elabReplTarget = Just tgt }+        (Just _,     TargetActionHaddock) -> elab { elabBuildHaddocks = True }+        (Just _,     TargetActionRepl)    ->+          error "pruneInstallPlanToTargets: multiple repl targets"++-- | Assuming we have previously set the root build targets (i.e. the user+-- targets but not rev deps yet), the first pruning pass does two things:+--+-- * A first go at determining which optional stanzas (testsuites, benchmarks)+--   are needed. We have a second go in the next pass.+-- * Take the dependency closure using pruned dependencies. We prune deps that+--   are used only by unneeded optional stanzas. These pruned deps are only+--   used for the dependency closure and are not persisted in this pass.+--+pruneInstallPlanPass1 :: [ElaboratedPlanPackage]+                      -> [ElaboratedPlanPackage]+pruneInstallPlanPass1 pkgs =+    map (mapConfiguredPackage fromPrunedPackage)+        (fromMaybe [] $ Graph.closure graph roots)+  where+    pkgs' = map (mapConfiguredPackage prune) pkgs+    graph = Graph.fromDistinctList pkgs'+    roots = mapMaybe find_root pkgs'++    prune elab = PrunedPackage elab' (pruneOptionalDependencies elab')+      where elab' = pruneOptionalStanzas elab++    find_root (InstallPlan.Configured (PrunedPackage elab _)) =+        if not (null (elabBuildTargets elab)+                    && null (elabTestTargets elab)+                    && isNothing (elabReplTarget elab)+                    && not (elabBuildHaddocks elab))+            then Just (installedUnitId elab)+            else Nothing+    find_root _ = Nothing++    -- Decide whether or not to enable testsuites and benchmarks+    --+    -- The testsuite and benchmark targets are somewhat special in that we need+    -- to configure the packages with them enabled, and we need to do that even+    -- if we only want to build one of several testsuites.+    --+    -- There are two cases in which we will enable the testsuites (or+    -- benchmarks): if one of the targets is a testsuite, or if all of the+    -- testsuite dependencies are already cached in the store. The rationale+    -- for the latter is to minimise how often we have to reconfigure due to+    -- the particular targets we choose to build. Otherwise choosing to build+    -- a testsuite target, and then later choosing to build an exe target+    -- would involve unnecessarily reconfiguring the package with testsuites+    -- disabled. Technically this introduces a little bit of stateful+    -- behaviour to make this "sticky", but it should be benign.+    --+    pruneOptionalStanzas :: ElaboratedConfiguredPackage -> ElaboratedConfiguredPackage+    pruneOptionalStanzas elab@ElaboratedConfiguredPackage{ elabPkgOrComp = ElabPackage pkg } =+        elab {+            elabPkgOrComp = ElabPackage (pkg { pkgStanzasEnabled = stanzas })+        }+      where+        stanzas :: Set OptionalStanza+        stanzas = optionalStanzasRequiredByTargets  elab+               <> optionalStanzasRequestedByDefault elab+               <> optionalStanzasWithDepsAvailable availablePkgs elab pkg+    pruneOptionalStanzas elab = elab++    -- Calculate package dependencies but cut out those needed only by+    -- optional stanzas that we've determined we will not enable.+    -- These pruned deps are not persisted in this pass since they're based on+    -- the optional stanzas and we'll make further tweaks to the optional+    -- stanzas in the next pass.+    --+    pruneOptionalDependencies :: ElaboratedConfiguredPackage -> [UnitId]+    pruneOptionalDependencies elab@ElaboratedConfiguredPackage{ elabPkgOrComp = ElabComponent _ }+        = InstallPlan.depends elab -- no pruning+    pruneOptionalDependencies ElaboratedConfiguredPackage{ elabPkgOrComp = ElabPackage pkg }+        = (CD.flatDeps . CD.filterDeps keepNeeded) (pkgOrderDependencies pkg)+      where+        keepNeeded (CD.ComponentTest  _) _ = TestStanzas  `Set.member` stanzas+        keepNeeded (CD.ComponentBench _) _ = BenchStanzas `Set.member` stanzas+        keepNeeded _                     _ = True+        stanzas = pkgStanzasEnabled pkg++    optionalStanzasRequiredByTargets :: ElaboratedConfiguredPackage+                                     -> Set OptionalStanza+    optionalStanzasRequiredByTargets pkg =+      Set.fromList+        [ stanza+        | ComponentTarget cname _ <- elabBuildTargets pkg+                                  ++ elabTestTargets pkg+                                  ++ maybeToList (elabReplTarget pkg)+        , stanza <- maybeToList (componentOptionalStanza cname)+        ]++    optionalStanzasRequestedByDefault :: ElaboratedConfiguredPackage+                                      -> Set OptionalStanza+    optionalStanzasRequestedByDefault =+        Map.keysSet+      . Map.filter (id :: Bool -> Bool)+      . elabStanzasRequested++    availablePkgs =+      Set.fromList+        [ installedUnitId pkg+        | InstallPlan.PreExisting pkg <- pkgs ]++-- | Given a set of already installed packages @availablePkgs@,+-- determine the set of available optional stanzas from @pkg@+-- which have all of their dependencies already installed.  This is used+-- to implement "sticky" testsuites, where once we have installed+-- all of the deps needed for the test suite, we go ahead and+-- enable it always.+optionalStanzasWithDepsAvailable :: Set UnitId+                                 -> ElaboratedConfiguredPackage+                                 -> ElaboratedPackage+                                 -> Set OptionalStanza+optionalStanzasWithDepsAvailable availablePkgs elab pkg =+    Set.fromList+      [ stanza+      | stanza <- Set.toList (elabStanzasAvailable elab)+      , let deps :: [UnitId]+            deps = CD.select (optionalStanzaDeps stanza)+                             -- TODO: probably need to select other+                             -- dep types too eventually+                             (pkgOrderDependencies pkg)+      , all (`Set.member` availablePkgs) deps+      ]+  where+    optionalStanzaDeps TestStanzas  (CD.ComponentTest  _) = True+    optionalStanzaDeps BenchStanzas (CD.ComponentBench _) = True+    optionalStanzaDeps _            _                     = False+++-- The second pass does three things:+--+-- * A second go at deciding which optional stanzas to enable.+-- * Prune the dependencies based on the final choice of optional stanzas.+-- * Extend the targets within each package to build, now we know the reverse+--   dependencies, ie we know which libs are needed as deps by other packages.+--+-- Achieving sticky behaviour with enabling\/disabling optional stanzas is+-- tricky. The first approximation was handled by the first pass above, but+-- it's not quite enough. That pass will enable stanzas if all of the deps+-- of the optional stanza are already installed /in the store/. That's important+-- but it does not account for dependencies that get built inplace as part of+-- the project. We cannot take those inplace build deps into account in the+-- pruning pass however because we don't yet know which ones we're going to+-- build. Once we do know, we can have another go and enable stanzas that have+-- all their deps available. Now we can consider all packages in the pruned+-- plan to be available, including ones we already decided to build from+-- source.+--+-- Deciding which targets to build depends on knowing which packages have+-- reverse dependencies (ie are needed). This requires the result of first+-- pass, which is another reason we have to split it into two passes.+--+-- Note that just because we might enable testsuites or benchmarks (in the+-- first or second pass) doesn't mean that we build all (or even any) of them.+-- That depends on which targets we picked in the first pass.+--+pruneInstallPlanPass2 :: [ElaboratedPlanPackage]+                      -> [ElaboratedPlanPackage]+pruneInstallPlanPass2 pkgs =+    map (mapConfiguredPackage setStanzasDepsAndTargets) pkgs+  where+    setStanzasDepsAndTargets elab =+        elab {+          elabBuildTargets = ordNub+                           $ elabBuildTargets elab+                          ++ libTargetsRequiredForRevDeps+                          ++ exeTargetsRequiredForRevDeps,+          elabPkgOrComp =+            case elabPkgOrComp elab of+              ElabPackage pkg ->+                let stanzas = pkgStanzasEnabled pkg+                           <> optionalStanzasWithDepsAvailable availablePkgs elab pkg+                    keepNeeded (CD.ComponentTest  _) _ = TestStanzas  `Set.member` stanzas+                    keepNeeded (CD.ComponentBench _) _ = BenchStanzas `Set.member` stanzas+                    keepNeeded _                     _ = True+                in ElabPackage $ pkg {+                  pkgStanzasEnabled = stanzas,+                  pkgLibDependencies   = CD.filterDeps keepNeeded (pkgLibDependencies pkg),+                  pkgExeDependencies   = CD.filterDeps keepNeeded (pkgExeDependencies pkg),+                  pkgExeDependencyPaths = CD.filterDeps keepNeeded (pkgExeDependencyPaths pkg)+                }+              r@(ElabComponent _) -> r+        }+      where+        libTargetsRequiredForRevDeps =+          [ ComponentTarget Cabal.defaultLibName WholeComponent+          | installedUnitId elab `Set.member` hasReverseLibDeps+          ]+        exeTargetsRequiredForRevDeps =+          -- TODO: allow requesting executable with different name+          -- than package name+          [ ComponentTarget (Cabal.CExeName+                             $ packageNameToUnqualComponentName+                             $ packageName $ elabPkgSourceId elab)+                            WholeComponent+          | installedUnitId elab `Set.member` hasReverseExeDeps+          ]+++    availablePkgs :: Set UnitId+    availablePkgs = Set.fromList (map installedUnitId pkgs)++    hasReverseLibDeps :: Set UnitId+    hasReverseLibDeps =+      Set.fromList [ depid+                   | InstallPlan.Configured pkg <- pkgs+                   , depid <- elabOrderLibDependencies pkg ]++    hasReverseExeDeps :: Set UnitId+    hasReverseExeDeps =+      Set.fromList [ depid+                   | InstallPlan.Configured pkg <- pkgs+                   , depid <- elabOrderExeDependencies pkg ]++mapConfiguredPackage :: (srcpkg -> srcpkg')+                     -> InstallPlan.GenericPlanPackage ipkg srcpkg+                     -> InstallPlan.GenericPlanPackage ipkg srcpkg'+mapConfiguredPackage f (InstallPlan.Configured pkg) =+  InstallPlan.Configured (f pkg)+mapConfiguredPackage f (InstallPlan.Installed pkg) =+  InstallPlan.Installed (f pkg)+mapConfiguredPackage _ (InstallPlan.PreExisting pkg) =+  InstallPlan.PreExisting pkg++componentOptionalStanza :: Cabal.ComponentName -> Maybe OptionalStanza+componentOptionalStanza (Cabal.CTestName  _) = Just TestStanzas+componentOptionalStanza (Cabal.CBenchName _) = Just BenchStanzas+componentOptionalStanza _                    = Nothing++------------------------------------+-- Support for --only-dependencies+--++-- | Try to remove the given targets from the install plan.+--+-- This is not always possible.+--+pruneInstallPlanToDependencies :: Set UnitId+                               -> ElaboratedInstallPlan+                               -> Either CannotPruneDependencies+                                         ElaboratedInstallPlan+pruneInstallPlanToDependencies pkgTargets installPlan =+    assert (all (isJust . InstallPlan.lookup installPlan)+                (Set.toList pkgTargets)) $++    fmap (InstallPlan.new (InstallPlan.planIndepGoals installPlan))+  . checkBrokenDeps+  . Graph.fromDistinctList+  . filter (\pkg -> installedUnitId pkg `Set.notMember` pkgTargets)+  . InstallPlan.toList+  $ installPlan+    where+      -- Our strategy is to remove the packages we don't want and then check+      -- if the remaining graph is broken or not, ie any packages with dangling+      -- dependencies. If there are then we cannot prune the given targets.+      checkBrokenDeps :: Graph.Graph ElaboratedPlanPackage+                      -> Either CannotPruneDependencies+                                (Graph.Graph ElaboratedPlanPackage)+      checkBrokenDeps graph =+        case Graph.broken graph of+          []             -> Right graph+          brokenPackages ->+            Left $ CannotPruneDependencies+             [ (pkg, missingDeps)+             | (pkg, missingDepIds) <- brokenPackages+             , let missingDeps = catMaybes (map lookupDep missingDepIds)+             ]+            where+              -- lookup in the original unpruned graph+              lookupDep = InstallPlan.lookup installPlan++-- | It is not always possible to prune to only the dependencies of a set of+-- targets. It may be the case that removing a package leaves something else+-- that still needed the pruned package.+--+-- This lists all the packages that would be broken, and their dependencies+-- that would be missing if we did prune.+--+newtype CannotPruneDependencies =+        CannotPruneDependencies [(ElaboratedPlanPackage,+                                  [ElaboratedPlanPackage])]+  deriving (Show)+++---------------------------+-- Setup.hs script policy+--++-- Handling for Setup.hs scripts is a bit tricky, part of it lives in the+-- solver phase, and part in the elaboration phase. We keep the helper+-- functions for both phases together here so at least you can see all of it+-- in one place.+--+-- There are four major cases for Setup.hs handling:+--+--  1. @build-type@ Custom with a @custom-setup@ section+--  2. @build-type@ Custom without a @custom-setup@ section+--  3. @build-type@ not Custom with @cabal-version >  $our-cabal-version@+--  4. @build-type@ not Custom with @cabal-version <= $our-cabal-version@+--+-- It's also worth noting that packages specifying @cabal-version: >= 1.23@+-- or later that have @build-type@ Custom will always have a @custom-setup@+-- section. Therefore in case 2, the specified @cabal-version@ will always be+-- less than 1.23.+--+-- In cases 1 and 2 we obviously have to build an external Setup.hs script,+-- while in case 4 we can use the internal library API. In case 3 we also have+-- to build an external Setup.hs script because the package needs a later+-- Cabal lib version than we can support internally.+--+-- data SetupScriptStyle = ...  -- see ProjectPlanning.Types++-- | Work out the 'SetupScriptStyle' given the package description.+--+packageSetupScriptStyle :: PD.PackageDescription -> SetupScriptStyle+packageSetupScriptStyle pkg+  | buildType == PD.Custom+  , Just setupbi <- PD.setupBuildInfo pkg -- does have a custom-setup stanza+  , not (PD.defaultSetupDepends setupbi)  -- but not one we added internally+  = SetupCustomExplicitDeps++  | buildType == PD.Custom+  , Just setupbi <- PD.setupBuildInfo pkg -- we get this case post-solver as+  , PD.defaultSetupDepends setupbi        -- the solver fills in the deps+  = SetupCustomImplicitDeps++  | buildType == PD.Custom+  , Nothing <- PD.setupBuildInfo pkg      -- we get this case pre-solver+  = SetupCustomImplicitDeps++  | PD.specVersion pkg > cabalVersion -- one cabal-install is built against+  = SetupNonCustomExternalLib++  | otherwise+  = SetupNonCustomInternalLib+  where+    buildType = fromMaybe PD.Custom (PD.buildType pkg)+++-- | Part of our Setup.hs handling policy is implemented by getting the solver+-- to work out setup dependencies for packages. The solver already handles+-- packages that explicitly specify setup dependencies, but we can also tell+-- the solver to treat other packages as if they had setup dependencies.+-- That's what this function does, it gets called by the solver for all+-- packages that don't already have setup dependencies.+--+-- The dependencies we want to add is different for each 'SetupScriptStyle'.+--+-- Note that adding default deps means these deps are actually /added/ to the+-- packages that we get out of the solver in the 'SolverInstallPlan'. Making+-- implicit setup deps explicit is a problem in the post-solver stages because+-- we still need to distinguish the case of explicit and implict setup deps.+-- See 'rememberImplicitSetupDeps'.+--+-- Note in addition to adding default setup deps, we also use+-- 'addSetupCabalMinVersionConstraint' (in 'planPackages') to require+-- @Cabal >= 1.20@ for Setup scripts.+--+defaultSetupDeps :: Compiler -> Platform+                 -> PD.PackageDescription+                 -> Maybe [Dependency]+defaultSetupDeps compiler platform pkg =+    case packageSetupScriptStyle pkg of++      -- For packages with build type custom that do not specify explicit+      -- setup dependencies, we add a dependency on Cabal and a number+      -- of other packages.+      SetupCustomImplicitDeps ->+        Just $+        [ Dependency depPkgname anyVersion+        | depPkgname <- legacyCustomSetupPkgs compiler platform ] +++        [ Dependency cabalPkgname cabalConstraint+        | packageName pkg /= cabalPkgname ]+        where+          -- The Cabal dep is slightly special:+          -- * We omit the dep for the Cabal lib itself, since it bootstraps.+          -- * We constrain it to be < 1.25+          --+          -- Note: we also add a global constraint to require Cabal >= 1.20+          -- for Setup scripts (see use addSetupCabalMinVersionConstraint).+          --+          cabalConstraint   = orLaterVersion (PD.specVersion pkg)+                                `intersectVersionRanges`+                              earlierVersion cabalCompatMaxVer+          -- The idea here is that at some point we will make significant+          -- breaking changes to the Cabal API that Setup.hs scripts use.+          -- So for old custom Setup scripts that do not specify explicit+          -- constraints, we constrain them to use a compatible Cabal version.+          cabalCompatMaxVer = mkVersion [1,25]++      -- For other build types (like Simple) if we still need to compile an+      -- external Setup.hs, it'll be one of the simple ones that only depends+      -- on Cabal and base.+      SetupNonCustomExternalLib ->+        Just [ Dependency cabalPkgname cabalConstraint+             , Dependency basePkgname  anyVersion ]+        where+          cabalConstraint = orLaterVersion (PD.specVersion pkg)++      -- The internal setup wrapper method has no deps at all.+      SetupNonCustomInternalLib -> Just []++      -- This case gets ruled out by the caller, planPackages, see the note+      -- above in the SetupCustomImplicitDeps case.+      SetupCustomExplicitDeps ->+        error $ "defaultSetupDeps: called for a package with explicit "+             ++ "setup deps: " ++ display (packageId pkg)+++-- | Work out which version of the Cabal spec we will be using to talk to the+-- Setup.hs interface for this package.+--+-- This depends somewhat on the 'SetupScriptStyle' but most cases are a result+-- of what the solver picked for us, based on the explicit setup deps or the+-- ones added implicitly by 'defaultSetupDeps'.+--+packageSetupScriptSpecVersion :: Package pkg+                              => SetupScriptStyle+                              -> PD.PackageDescription+                              -> ComponentDeps [pkg]+                              -> Version++-- We're going to be using the internal Cabal library, so the spec version of+-- that is simply the version of the Cabal library that cabal-install has been+-- built with.+packageSetupScriptSpecVersion SetupNonCustomInternalLib _ _ =+    cabalVersion++-- If we happen to be building the Cabal lib itself then because that+-- bootstraps itself then we use the version of the lib we're building.+packageSetupScriptSpecVersion SetupCustomImplicitDeps pkg _+  | packageName pkg == cabalPkgname+  = packageVersion pkg++-- In all other cases we have a look at what version of the Cabal lib the+-- solver picked. Or if it didn't depend on Cabal at all (which is very rare)+-- then we look at the .cabal file to see what spec version it declares.+packageSetupScriptSpecVersion _ pkg deps =+    case find ((cabalPkgname ==) . packageName) (CD.setupDeps deps) of+      Just dep -> packageVersion dep+      Nothing  -> PD.specVersion pkg+++cabalPkgname, basePkgname :: PackageName+cabalPkgname = mkPackageName "Cabal"+basePkgname  = mkPackageName "base"+++legacyCustomSetupPkgs :: Compiler -> Platform -> [PackageName]+legacyCustomSetupPkgs compiler (Platform _ os) =+    map mkPackageName $+        [ "array", "base", "binary", "bytestring", "containers"+        , "deepseq", "directory", "filepath", "old-time", "pretty"+        , "process", "time", "transformers" ]+     ++ [ "Win32" | os == Windows ]+     ++ [ "unix"  | os /= Windows ]+     ++ [ "ghc-prim"         | isGHC ]+     ++ [ "template-haskell" | isGHC ]+  where+    isGHC = compilerCompatFlavor GHC compiler++-- The other aspects of our Setup.hs policy lives here where we decide on+-- the 'SetupScriptOptions'.+--+-- Our current policy for the 'SetupCustomImplicitDeps' case is that we+-- try to make the implicit deps cover everything, and we don't allow the+-- compiler to pick up other deps. This may or may not be sustainable, and+-- we might have to allow the deps to be non-exclusive, but that itself would+-- be tricky since we would have to allow the Setup access to all the packages+-- in the store and local dbs.++setupHsScriptOptions :: ElaboratedReadyPackage+                     -> ElaboratedSharedConfig+                     -> FilePath+                     -> FilePath+                     -> Bool+                     -> Lock+                     -> SetupScriptOptions+-- TODO: Fix this so custom is a separate component.  Custom can ALWAYS+-- be a separate component!!!+setupHsScriptOptions (ReadyPackage elab@ElaboratedConfiguredPackage{..})+                     ElaboratedSharedConfig{..} srcdir builddir+                     isParallelBuild cacheLock =+    SetupScriptOptions {+      useCabalVersion          = thisVersion elabSetupScriptCliVersion,+      useCabalSpecVersion      = Just elabSetupScriptCliVersion,+      useCompiler              = Just pkgConfigCompiler,+      usePlatform              = Just pkgConfigPlatform,+      usePackageDB             = elabSetupPackageDBStack,+      usePackageIndex          = Nothing,+      useDependencies          = [ (uid, srcid)+                                 | ConfiguredId srcid (Just CLibName) uid+                                 <- elabSetupDependencies elab ],+      useDependenciesExclusive = True,+      useVersionMacros         = elabSetupScriptStyle == SetupCustomExplicitDeps,+      useProgramDb             = pkgConfigCompilerProgs,+      useDistPref              = builddir,+      useLoggingHandle         = Nothing, -- this gets set later+      useWorkingDir            = Just srcdir,+      useExtraPathEnv          = elabExeDependencyPaths elab,+      useWin32CleanHack        = False,   --TODO: [required eventually]+      forceExternalSetupMethod = isParallelBuild,+      setupCacheLock           = Just cacheLock,+      isInteractive            = False+    }+++-- | To be used for the input for elaborateInstallPlan.+--+-- TODO: [code cleanup] make InstallDirs.defaultInstallDirs pure.+--+userInstallDirTemplates :: Compiler+                        -> IO InstallDirs.InstallDirTemplates+userInstallDirTemplates compiler = do+    InstallDirs.defaultInstallDirs+                  (compilerFlavor compiler)+                  True  -- user install+                  False -- unused++storePackageInstallDirs :: StoreDirLayout+                        -> CompilerId+                        -> InstalledPackageId+                        -> InstallDirs.InstallDirs FilePath+storePackageInstallDirs StoreDirLayout{storePackageDirectory}+                        compid ipkgid =+    InstallDirs.InstallDirs {..}+  where+    prefix       = storePackageDirectory compid (newSimpleUnitId ipkgid)+    bindir       = prefix </> "bin"+    libdir       = prefix </> "lib"+    libsubdir    = ""+    dynlibdir    = libdir+    flibdir      = libdir+    libexecdir   = prefix </> "libexec"+    libexecsubdir= ""+    includedir   = libdir </> "include"+    datadir      = prefix </> "share"+    datasubdir   = ""+    docdir       = datadir </> "doc"+    mandir       = datadir </> "man"+    htmldir      = docdir  </> "html"+    haddockdir   = htmldir+    sysconfdir   = prefix </> "etc"+++--TODO: [code cleanup] perhaps reorder this code+-- based on the ElaboratedInstallPlan + ElaboratedSharedConfig,+-- make the various Setup.hs {configure,build,copy} flags+++setupHsConfigureFlags :: ElaboratedReadyPackage+                      -> ElaboratedSharedConfig+                      -> Verbosity+                      -> FilePath+                      -> Cabal.ConfigFlags+setupHsConfigureFlags (ReadyPackage elab@ElaboratedConfiguredPackage{..})+                      sharedConfig@ElaboratedSharedConfig{..}+                      verbosity builddir =+    sanityCheckElaboratedConfiguredPackage sharedConfig elab+        (Cabal.ConfigFlags {..})+  where+    configArgs                = mempty -- unused, passed via args+    configDistPref            = toFlag builddir+    configCabalFilePath       = mempty+    configVerbosity           = toFlag verbosity++    configInstantiateWith     = Map.toList elabInstantiatedWith++    configDeterministic       = mempty -- doesn't matter, configIPID/configCID overridese+    configIPID                = case elabPkgOrComp of+                                  ElabPackage pkg -> toFlag (display (pkgInstalledId pkg))+                                  ElabComponent _ -> mempty+    configCID                 = case elabPkgOrComp of+                                  ElabPackage _ -> mempty+                                  ElabComponent _ -> toFlag elabComponentId++    configProgramPaths        = Map.toList elabProgramPaths+    configProgramArgs         = Map.toList elabProgramArgs+    configProgramPathExtra    = toNubList elabProgramPathExtra+    configHcFlavor            = toFlag (compilerFlavor pkgConfigCompiler)+    configHcPath              = mempty -- we use configProgramPaths instead+    configHcPkg               = mempty -- we use configProgramPaths instead++    configVanillaLib          = toFlag elabVanillaLib+    configSharedLib           = toFlag elabSharedLib+    configDynExe              = toFlag elabDynExe+    configGHCiLib             = toFlag elabGHCiLib+    configProfExe             = mempty+    configProfLib             = toFlag elabProfLib+    configProf                = toFlag elabProfExe++    -- configProfDetail is for exe+lib, but overridden by configProfLibDetail+    -- so we specify both so we can specify independently+    configProfDetail          = toFlag elabProfExeDetail+    configProfLibDetail       = toFlag elabProfLibDetail++    configCoverage            = toFlag elabCoverage+    configLibCoverage         = mempty++    configOptimization        = toFlag elabOptimization+    configSplitObjs           = toFlag elabSplitObjs+    configStripExes           = toFlag elabStripExes+    configStripLibs           = toFlag elabStripLibs+    configDebugInfo           = toFlag elabDebugInfo+    configAllowOlder          = mempty -- we use configExactConfiguration True+    configAllowNewer          = mempty -- we use configExactConfiguration True++    configConfigurationsFlags = elabFlagAssignment+    configConfigureArgs       = elabConfigureScriptArgs+    configExtraLibDirs        = elabExtraLibDirs+    configExtraFrameworkDirs  = elabExtraFrameworkDirs+    configExtraIncludeDirs    = elabExtraIncludeDirs+    configProgPrefix          = maybe mempty toFlag elabProgPrefix+    configProgSuffix          = maybe mempty toFlag elabProgSuffix++    configInstallDirs         = fmap (toFlag . InstallDirs.toPathTemplate)+                                     elabInstallDirs++    -- we only use configDependencies, unless we're talking to an old Cabal+    -- in which case we use configConstraints+    -- NB: This does NOT use InstallPlan.depends, which includes executable+    -- dependencies which should NOT be fed in here (also you don't have+    -- enough info anyway)+    configDependencies        = [ (case mb_cn of+                                    -- Special case for internal libraries+                                    Just (CSubLibName uqn)+                                        | packageId elab == srcid+                                        -> mkPackageName (unUnqualComponentName uqn)+                                    _ -> packageName srcid,+                                   cid)+                                | ConfiguredId srcid mb_cn cid <- elabLibDependencies elab ]+    configConstraints         =+        case elabPkgOrComp of+            ElabPackage _ ->+                [ thisPackageVersion srcid+                | ConfiguredId srcid _ _uid <- elabLibDependencies elab ]+            ElabComponent _ -> []+++    -- explicitly clear, then our package db stack+    -- TODO: [required eventually] have to do this differently for older Cabal versions+    configPackageDBs          = Nothing : map Just elabBuildPackageDBStack++    configTests               = case elabPkgOrComp of+                                    ElabPackage pkg -> toFlag (TestStanzas  `Set.member` pkgStanzasEnabled pkg)+                                    ElabComponent _ -> mempty+    configBenchmarks          = case elabPkgOrComp of+                                    ElabPackage pkg -> toFlag (BenchStanzas `Set.member` pkgStanzasEnabled pkg)+                                    ElabComponent _ -> mempty++    configExactConfiguration  = toFlag True+    configFlagError           = mempty --TODO: [research required] appears not to be implemented+    configRelocatable         = mempty --TODO: [research required] ???+    configScratchDir          = mempty -- never use+    configUserInstall         = mempty -- don't rely on defaults+    configPrograms_           = mempty -- never use, shouldn't exist+++setupHsConfigureArgs :: ElaboratedConfiguredPackage+                     -> [String]+setupHsConfigureArgs (ElaboratedConfiguredPackage { elabPkgOrComp = ElabPackage _ }) = []+setupHsConfigureArgs elab@(ElaboratedConfiguredPackage { elabPkgOrComp = ElabComponent comp }) =+    [showComponentTarget (packageId elab) (ComponentTarget cname WholeComponent)]+  where+    cname = fromMaybe (error "setupHsConfigureArgs: trying to configure setup")+                      (compComponentName comp)++setupHsBuildFlags :: ElaboratedConfiguredPackage+                  -> ElaboratedSharedConfig+                  -> Verbosity+                  -> FilePath+                  -> Cabal.BuildFlags+setupHsBuildFlags _ _ verbosity builddir =+    Cabal.BuildFlags {+      buildProgramPaths = mempty, --unused, set at configure time+      buildProgramArgs  = mempty, --unused, set at configure time+      buildVerbosity    = toFlag verbosity,+      buildDistPref     = toFlag builddir,+      buildNumJobs      = mempty, --TODO: [nice to have] sometimes want to use toFlag (Just numBuildJobs),+      buildArgs         = mempty  -- unused, passed via args not flags+    }+++setupHsBuildArgs :: ElaboratedConfiguredPackage -> [String]+setupHsBuildArgs elab@(ElaboratedConfiguredPackage { elabPkgOrComp = ElabPackage _ })+    -- Fix for #3335, don't pass build arguments if it's not supported+    | elabSetupScriptCliVersion elab >= mkVersion [1,17]+    = map (showComponentTarget (packageId elab)) (elabBuildTargets elab)+    | otherwise+    = []+setupHsBuildArgs (ElaboratedConfiguredPackage { elabPkgOrComp = ElabComponent _ })+    = []+++setupHsTestFlags :: ElaboratedConfiguredPackage+                 -> ElaboratedSharedConfig+                 -> Verbosity+                 -> FilePath+                 -> Cabal.TestFlags+setupHsTestFlags _ _ verbosity builddir = Cabal.TestFlags+    { testDistPref    = toFlag builddir+    , testVerbosity   = toFlag verbosity+    , testMachineLog  = mempty+    , testHumanLog    = mempty+    , testShowDetails = toFlag Cabal.Always+    , testKeepTix     = mempty+    , testOptions     = mempty+    }++setupHsTestArgs :: ElaboratedConfiguredPackage -> [String]+-- TODO: Does the issue #3335 affects test as well+setupHsTestArgs elab =+    mapMaybe (showTestComponentTarget (packageId elab)) (elabTestTargets elab)++setupHsReplFlags :: ElaboratedConfiguredPackage+                 -> ElaboratedSharedConfig+                 -> Verbosity+                 -> FilePath+                 -> Cabal.ReplFlags+setupHsReplFlags _ _ verbosity builddir =+    Cabal.ReplFlags {+      replProgramPaths = mempty, --unused, set at configure time+      replProgramArgs  = mempty, --unused, set at configure time+      replVerbosity    = toFlag verbosity,+      replDistPref     = toFlag builddir,+      replReload       = mempty  --only used as callback from repl+    }+++setupHsReplArgs :: ElaboratedConfiguredPackage -> [String]+setupHsReplArgs elab =+    maybe [] (\t -> [showComponentTarget (packageId elab) t]) (elabReplTarget elab)+    --TODO: should be able to give multiple modules in one component+++setupHsCopyFlags :: ElaboratedConfiguredPackage+                 -> ElaboratedSharedConfig+                 -> Verbosity+                 -> FilePath+                 -> FilePath+                 -> Cabal.CopyFlags+setupHsCopyFlags _ _ verbosity builddir destdir =+    Cabal.CopyFlags {+      copyArgs      = [], -- TODO: could use this to only copy what we enabled+      copyDest      = toFlag (InstallDirs.CopyTo destdir),+      copyDistPref  = toFlag builddir,+      copyVerbosity = toFlag verbosity+    }++setupHsRegisterFlags :: ElaboratedConfiguredPackage+                     -> ElaboratedSharedConfig+                     -> Verbosity+                     -> FilePath+                     -> FilePath+                     -> Cabal.RegisterFlags+setupHsRegisterFlags ElaboratedConfiguredPackage{..} _+                     verbosity builddir pkgConfFile =+    Cabal.RegisterFlags {+      regPackageDB   = mempty,  -- misfeature+      regGenScript   = mempty,  -- never use+      regGenPkgConf  = toFlag (Just pkgConfFile),+      regInPlace     = case elabBuildStyle of+                         BuildInplaceOnly -> toFlag True+                         _                -> toFlag False,+      regPrintId     = mempty,  -- never use+      regDistPref    = toFlag builddir,+      regArgs        = [],+      regVerbosity   = toFlag verbosity+    }++setupHsHaddockFlags :: ElaboratedConfiguredPackage+                    -> ElaboratedSharedConfig+                    -> Verbosity+                    -> FilePath+                    -> Cabal.HaddockFlags+-- TODO: reconsider whether or not Executables/TestSuites/...+-- needed for component+setupHsHaddockFlags (ElaboratedConfiguredPackage{..}) _ verbosity builddir =+    Cabal.HaddockFlags {+      haddockProgramPaths  = mempty, --unused, set at configure time+      haddockProgramArgs   = mempty, --unused, set at configure time+      haddockHoogle        = toFlag elabHaddockHoogle,+      haddockHtml          = toFlag elabHaddockHtml,+      haddockHtmlLocation  = maybe mempty toFlag elabHaddockHtmlLocation,+      haddockForHackage    = mempty, --TODO: new flag+      haddockForeignLibs   = toFlag elabHaddockForeignLibs,+      haddockExecutables   = toFlag elabHaddockExecutables,+      haddockTestSuites    = toFlag elabHaddockTestSuites,+      haddockBenchmarks    = toFlag elabHaddockBenchmarks,+      haddockInternal      = toFlag elabHaddockInternal,+      haddockCss           = maybe mempty toFlag elabHaddockCss,+      haddockHscolour      = toFlag elabHaddockHscolour,+      haddockHscolourCss   = maybe mempty toFlag elabHaddockHscolourCss,+      haddockContents      = maybe mempty toFlag elabHaddockContents,+      haddockDistPref      = toFlag builddir,+      haddockKeepTempFiles = mempty, --TODO: from build settings+      haddockVerbosity     = toFlag verbosity+    }++{-+setupHsTestFlags :: ElaboratedConfiguredPackage+                 -> ElaboratedSharedConfig+                 -> Verbosity+                 -> FilePath+                 -> Cabal.TestFlags+setupHsTestFlags _ _ verbosity builddir =+    Cabal.TestFlags {+    }+-}++------------------------------------------------------------------------------+-- * Sharing installed packages+------------------------------------------------------------------------------++--+-- Nix style store management for tarball packages+--+-- So here's our strategy:+--+-- We use a per-user nix-style hashed store, but /only/ for tarball packages.+-- So that includes packages from hackage repos (and other http and local+-- tarballs). For packages in local directories we do not register them into+-- the shared store by default, we just build them locally inplace.+--+-- The reason we do it like this is that it's easy to make stable hashes for+-- tarball packages, and these packages benefit most from sharing. By contrast+-- unpacked dir packages are harder to hash and they tend to change more+-- frequently so there's less benefit to sharing them.+--+-- When using the nix store approach we have to run the solver *without*+-- looking at the packages installed in the store, just at the source packages+-- (plus core\/global installed packages). Then we do a post-processing pass+-- to replace configured packages in the plan with pre-existing ones, where+-- possible. Where possible of course means where the nix-style package hash+-- equals one that's already in the store.+--+-- One extra wrinkle is that unless we know package tarball hashes upfront, we+-- will have to download the tarballs to find their hashes. So we have two+-- options: delay replacing source with pre-existing installed packages until+-- the point during the execution of the install plan where we have the+-- tarball, or try to do as much up-front as possible and then check again+-- during plan execution. The former isn't great because we would end up+-- telling users we're going to re-install loads of packages when in fact we+-- would just share them. It'd be better to give as accurate a prediction as+-- we can. The latter is better for users, but we do still have to check+-- during plan execution because it's important that we don't replace existing+-- installed packages even if they have the same package hash, because we+-- don't guarantee ABI stability.++-- TODO: [required eventually] for safety of concurrent installs, we must make sure we register but+-- not replace installed packages with ghc-pkg.++packageHashInputs :: ElaboratedSharedConfig+                  -> ElaboratedConfiguredPackage+                  -> PackageHashInputs+packageHashInputs+    pkgshared+    elab@(ElaboratedConfiguredPackage {+      elabPkgSourceHash = Just srchash+    }) =+    PackageHashInputs {+      pkgHashPkgId       = packageId elab,+      pkgHashComponent   =+        case elabPkgOrComp elab of+          ElabPackage _ -> Nothing+          ElabComponent comp -> Just (compSolverName comp),+      pkgHashSourceHash  = srchash,+      pkgHashPkgConfigDeps = Set.fromList (elabPkgConfigDependencies elab),+      pkgHashDirectDeps  =+        case elabPkgOrComp elab of+          ElabPackage (ElaboratedPackage{..}) ->+            Set.fromList $+             [ confInstId dep+             | dep <- CD.select relevantDeps pkgLibDependencies ] +++             [ confInstId dep+             | dep <- CD.select relevantDeps pkgExeDependencies ]+          ElabComponent comp ->+            Set.fromList (map confInstId (compLibDependencies comp+                                       ++ compExeDependencies comp)),+      pkgHashOtherConfig = packageHashConfigInputs pkgshared elab+    }+  where+    -- Obviously the main deps are relevant+    relevantDeps CD.ComponentLib       = True+    relevantDeps (CD.ComponentSubLib _) = True+    relevantDeps (CD.ComponentFLib _)   = True+    relevantDeps (CD.ComponentExe _)   = True+    -- Setup deps can affect the Setup.hs behaviour and thus what is built+    relevantDeps  CD.ComponentSetup    = True+    -- However testsuites and benchmarks do not get installed and should not+    -- affect the result, so we do not include them.+    relevantDeps (CD.ComponentTest  _) = False+    relevantDeps (CD.ComponentBench _) = False++packageHashInputs _ pkg =+    error $ "packageHashInputs: only for packages with source hashes. "+         ++ display (packageId pkg)++packageHashConfigInputs :: ElaboratedSharedConfig+                        -> ElaboratedConfiguredPackage+                        -> PackageHashConfigInputs+packageHashConfigInputs+    ElaboratedSharedConfig{..}+    ElaboratedConfiguredPackage{..} =++    PackageHashConfigInputs {+      pkgHashCompilerId          = compilerId pkgConfigCompiler,+      pkgHashPlatform            = pkgConfigPlatform,+      pkgHashFlagAssignment      = elabFlagAssignment,+      pkgHashConfigureScriptArgs = elabConfigureScriptArgs,+      pkgHashVanillaLib          = elabVanillaLib,+      pkgHashSharedLib           = elabSharedLib,+      pkgHashDynExe              = elabDynExe,+      pkgHashGHCiLib             = elabGHCiLib,+      pkgHashProfLib             = elabProfLib,+      pkgHashProfExe             = elabProfExe,+      pkgHashProfLibDetail       = elabProfLibDetail,+      pkgHashProfExeDetail       = elabProfExeDetail,+      pkgHashCoverage            = elabCoverage,+      pkgHashOptimization        = elabOptimization,+      pkgHashSplitObjs           = elabSplitObjs,+      pkgHashStripLibs           = elabStripLibs,+      pkgHashStripExes           = elabStripExes,+      pkgHashDebugInfo           = elabDebugInfo,+      pkgHashProgramArgs         = elabProgramArgs,+      pkgHashExtraLibDirs        = elabExtraLibDirs,+      pkgHashExtraFrameworkDirs  = elabExtraFrameworkDirs,+      pkgHashExtraIncludeDirs    = elabExtraIncludeDirs,+      pkgHashProgPrefix          = elabProgPrefix,+      pkgHashProgSuffix          = elabProgSuffix+    }+++-- | Given the 'InstalledPackageIndex' for a nix-style package store, and an+-- 'ElaboratedInstallPlan', replace configured source packages by installed+-- packages from the store whenever they exist.+--+improveInstallPlanWithInstalledPackages :: Set UnitId+                                        -> ElaboratedInstallPlan+                                        -> ElaboratedInstallPlan+improveInstallPlanWithInstalledPackages installedPkgIdSet =+    InstallPlan.installed canPackageBeImproved+  where+    canPackageBeImproved pkg =+      installedUnitId pkg `Set.member` installedPkgIdSet+    --TODO: sanity checks:+    -- * the installed package must have the expected deps etc+    -- * the installed package must not be broken, valid dep closure++    --TODO: decide what to do if we encounter broken installed packages,+    -- since overwriting is never safe.
Distribution/Client/ProjectPlanning/Types.hs view
@@ -1,4 +1,6 @@-{-# LANGUAGE DeriveGeneric, DeriveDataTypeable #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE TypeFamilies #-}  -- | Types used while planning how to build everything in a project. --@@ -10,46 +12,69 @@     -- * Elaborated install plan types     ElaboratedInstallPlan,     ElaboratedConfiguredPackage(..),++    elabDistDirParams,+    elabExeDependencyPaths,+    elabLibDependencies,+    elabOrderLibDependencies,+    elabExeDependencies,+    elabOrderExeDependencies,+    elabSetupDependencies,+    elabPkgConfigDependencies,+    elabInplaceDependencyBuildCacheFiles,+    elabRequiresRegistration,++    elabPlanPackageName,+    elabConfiguredName,+    elabComponentName,++    ElaboratedPackageOrComponent(..),+    ElaboratedComponent(..),+    ElaboratedPackage(..),+    pkgOrderDependencies,     ElaboratedPlanPackage,     ElaboratedSharedConfig(..),     ElaboratedReadyPackage,     BuildStyle(..),     CabalFileText, -    -- * Types used in executing an install plan-    --TODO: [code cleanup] these types should live with execution, not with-    --      plan definition. Need to better separate InstallPlan definition.-    GenericBuildResult(..),-    BuildResult,-    BuildSuccess(..),-    BuildFailure(..),-    DocsResult(..),-    TestsResult(..),-     -- * Build targets-    PackageTarget(..),     ComponentTarget(..),+    showComponentTarget,+    showTestComponentTarget,     SubComponentTarget(..), +    isTestComponentTarget,+     -- * Setup script     SetupScriptStyle(..),   ) where +import           Distribution.Client.TargetSelector+                   ( SubComponentTarget(..) ) import           Distribution.Client.PackageHash  import           Distribution.Client.Types-                   hiding ( BuildResult, BuildSuccess(..), BuildFailure(..)-                          , DocsResult(..), TestsResult(..) )+import qualified Distribution.Client.InstallPlan as InstallPlan import           Distribution.Client.InstallPlan-                   ( GenericInstallPlan, InstallPlan, GenericPlanPackage )-import           Distribution.Client.ComponentDeps (ComponentDeps)+                   ( GenericInstallPlan, GenericPlanPackage(..) )+import           Distribution.Client.SolverInstallPlan+                   ( SolverInstallPlan )+import           Distribution.Client.DistDirLayout +import           Distribution.Backpack+import           Distribution.Backpack.ModuleShape++import           Distribution.Verbosity+import           Distribution.Text+import           Distribution.Types.ComponentRequestedSpec import           Distribution.Package                    hiding (InstalledPackageId, installedPackageId) import           Distribution.System import qualified Distribution.PackageDescription as Cabal import           Distribution.InstalledPackageInfo (InstalledPackageInfo) import           Distribution.Simple.Compiler+import qualified Distribution.Simple.BuildTarget as Cabal import           Distribution.Simple.Program.Db import           Distribution.ModuleName (ModuleName) import           Distribution.Simple.LocalBuildInfo (ComponentName(..))@@ -57,23 +82,23 @@ import           Distribution.Simple.InstallDirs (PathTemplate) import           Distribution.Version +import qualified Distribution.Solver.Types.ComponentDeps as CD+import           Distribution.Solver.Types.ComponentDeps (ComponentDeps)+import           Distribution.Solver.Types.OptionalStanza+import           Distribution.Compat.Graph (IsNode(..))+import           Distribution.Simple.Utils (ordNub)+ import           Data.Map (Map) import           Data.Set (Set) import qualified Data.ByteString.Lazy as LBS import           Distribution.Compat.Binary import           GHC.Generics (Generic)-import           Data.Typeable (Typeable)-import           Control.Exception+import qualified Data.Monoid as Mon+import           Data.Typeable+import           Control.Monad   --- | The type of install plan produced by the solver and used as the starting--- point for the 'ElaboratedInstallPlan'.----type SolverInstallPlan-   = InstallPlan --TODO: [code cleanup] redefine locally or move def to solver interface-- -- | The combination of an elaborated install plan plus a -- 'ElaboratedSharedConfig' contains all the details necessary to be able -- to execute the plan without having to make further policy decisions.@@ -84,13 +109,21 @@ type ElaboratedInstallPlan    = GenericInstallPlan InstalledPackageInfo                         ElaboratedConfiguredPackage-                        BuildSuccess BuildFailure  type ElaboratedPlanPackage    = GenericPlanPackage InstalledPackageInfo                         ElaboratedConfiguredPackage-                        BuildSuccess BuildFailure +-- | User-friendly display string for an 'ElaboratedPlanPackage'.+elabPlanPackageName :: Verbosity -> ElaboratedPlanPackage -> String+elabPlanPackageName verbosity (PreExisting ipkg)+    | verbosity <= normal = display (packageName ipkg)+    | otherwise           = display (installedUnitId ipkg)+elabPlanPackageName verbosity (Configured elab)+    = elabConfiguredName verbosity elab+elabPlanPackageName verbosity (Installed elab)+    = elabConfiguredName verbosity elab+ --TODO: [code cleanup] decide if we really need this, there's not much in it, and in principle --      even platform and compiler could be different if we're building things --      like a server + client with ghc + ghcjs@@ -104,136 +137,463 @@        -- used.        pkgConfigCompilerProgs :: ProgramDb      }-  deriving (Show, Generic)+  deriving (Show, Generic, Typeable)   --TODO: [code cleanup] no Eq instance  instance Binary ElaboratedSharedConfig  data ElaboratedConfiguredPackage    = ElaboratedConfiguredPackage {--       pkgInstalledId :: InstalledPackageId,-       pkgSourceId    :: PackageId,+       -- | The 'UnitId' which uniquely identifies this item in a build plan+       elabUnitId        :: UnitId, -       -- | TODO: [code cleanup] we don't need this, just a few bits from it:-       --   build type, spec version-       pkgDescription :: Cabal.PackageDescription,+       elabComponentId :: ComponentId,+       elabInstantiatedWith :: Map ModuleName Module,+       elabLinkedInstantiatedWith :: Map ModuleName OpenModule, -       -- | A total flag assignment for the package-       pkgFlagAssignment   :: Cabal.FlagAssignment,+       -- | This is true if this is an indefinite package, or this is a+       -- package with no signatures.  (Notably, it's not true for instantiated+       -- packages.)  The motivation for this is if you ask to build+       -- @foo-indef@, this probably means that you want to typecheck+       -- it, NOT that you want to rebuild all of the various+       -- instantiations of it.+       elabIsCanonical :: Bool, -       -- | The original default flag assignment, used only for reporting.-       pkgFlagDefaults     :: Cabal.FlagAssignment,+       -- | The 'PackageId' of the originating package+       elabPkgSourceId    :: PackageId, -       -- | The exact dependencies (on other plan packages)-       ---       pkgDependencies     :: ComponentDeps [ConfiguredId],+       -- | Shape of the package/component, for Backpack.+       elabModuleShape    :: ModuleShape, -       -- | Which optional stanzas (ie testsuites, benchmarks) can be built.-       -- This means the solver produced a plan that has them available.-       -- This doesn't necessary mean we build them by default.-       pkgStanzasAvailable :: Set OptionalStanza,+       -- | A total flag assignment for the package.+       -- TODO: Actually this can be per-component if we drop+       -- all flags that don't affect a component.+       elabFlagAssignment   :: Cabal.FlagAssignment, -       -- | Which optional stanzas the user explicitly asked to enable or-       -- to disable. This tells us which ones we build by default, and-       -- helps with error messages when the user asks to build something-       -- they explicitly disabled.-       pkgStanzasRequested :: Map OptionalStanza Bool,+       -- | The original default flag assignment, used only for reporting.+       elabFlagDefaults     :: Cabal.FlagAssignment, -       -- | Which optional stanzas (ie testsuites, benchmarks) will actually-       -- be enabled during the package configure step.-       pkgStanzasEnabled :: Set OptionalStanza,+       elabPkgDescription :: Cabal.PackageDescription,         -- | Where the package comes from, e.g. tarball, local dir etc. This        --   is not the same as where it may be unpacked to for the build.-       pkgSourceLocation :: PackageLocation (Maybe FilePath),+       elabPkgSourceLocation :: PackageLocation (Maybe FilePath),         -- | The hash of the source, e.g. the tarball. We don't have this for        -- local source dir packages.-       pkgSourceHash     :: Maybe PackageSourceHash,+       elabPkgSourceHash     :: Maybe PackageSourceHash, -       --pkgSourceDir ? -- currently passed in later because they can use temp locations-       --pkgBuildDir  ? -- but could in principle still have it here, with optional instr to use temp loc+       -- | Is this package one of the ones specified by location in the+       -- project file? (As opposed to a dependency, or a named package pulled+       -- in)+       elabLocalToProject         :: Bool, -       pkgBuildStyle             :: BuildStyle,+       -- | Are we going to build and install this package to the store, or are+       -- we going to build it and register it locally.+       elabBuildStyle             :: BuildStyle, -       pkgSetupPackageDBStack    :: PackageDBStack,-       pkgBuildPackageDBStack    :: PackageDBStack,-       pkgRegisterPackageDBStack :: PackageDBStack,+       -- | Another way of phrasing 'pkgStanzasAvailable'.+       elabEnabledSpec      :: ComponentRequestedSpec, -       -- | The package contains a library and so must be registered-       pkgRequiresRegistration :: Bool,-       pkgDescriptionOverride  :: Maybe CabalFileText,+       -- | Which optional stanzas (ie testsuites, benchmarks) can be built.+       -- This means the solver produced a plan that has them available.+       -- This doesn't necessary mean we build them by default.+       elabStanzasAvailable :: Set OptionalStanza, -       pkgVanillaLib           :: Bool,-       pkgSharedLib            :: Bool,-       pkgDynExe               :: Bool,-       pkgGHCiLib              :: Bool,-       pkgProfLib              :: Bool,-       pkgProfExe              :: Bool,-       pkgProfLibDetail        :: ProfDetailLevel,-       pkgProfExeDetail        :: ProfDetailLevel,-       pkgCoverage             :: Bool,-       pkgOptimization         :: OptimisationLevel,-       pkgSplitObjs            :: Bool,-       pkgStripLibs            :: Bool,-       pkgStripExes            :: Bool,-       pkgDebugInfo            :: DebugInfoLevel,+       -- | Which optional stanzas the user explicitly asked to enable or+       -- to disable. This tells us which ones we build by default, and+       -- helps with error messages when the user asks to build something+       -- they explicitly disabled.+       --+       -- TODO: The 'Bool' here should be refined into an ADT with three+       -- cases: NotRequested, ExplicitlyRequested and+       -- ImplicitlyRequested.  A stanza is explicitly requested if+       -- the user asked, for this *specific* package, that the stanza+       -- be enabled; it's implicitly requested if the user asked for+       -- all global packages to have this stanza enabled.  The+       -- difference between an explicit and implicit request is+       -- error reporting behavior: if a user asks for tests to be+       -- enabled for a specific package that doesn't have any tests,+       -- we should warn them about it, but we shouldn't complain+       -- that a user enabled tests globally, and some local packages+       -- just happen not to have any tests.  (But perhaps we should+       -- warn if ALL local packages don't have any tests.)+       elabStanzasRequested :: Map OptionalStanza Bool, -       pkgProgramPaths          :: Map String FilePath,-       pkgProgramArgs           :: Map String [String],-       pkgProgramPathExtra      :: [FilePath],-       pkgConfigureScriptArgs   :: [String],-       pkgExtraLibDirs          :: [FilePath],-       pkgExtraFrameworkDirs    :: [FilePath],-       pkgExtraIncludeDirs      :: [FilePath],-       pkgProgPrefix            :: Maybe PathTemplate,-       pkgProgSuffix            :: Maybe PathTemplate,+       elabSetupPackageDBStack    :: PackageDBStack,+       elabBuildPackageDBStack    :: PackageDBStack,+       elabRegisterPackageDBStack :: PackageDBStack, -       pkgInstallDirs           :: InstallDirs.InstallDirs FilePath,+       elabPkgDescriptionOverride  :: Maybe CabalFileText, -       pkgHaddockHoogle         :: Bool,-       pkgHaddockHtml           :: Bool,-       pkgHaddockHtmlLocation   :: Maybe String,-       pkgHaddockExecutables    :: Bool,-       pkgHaddockTestSuites     :: Bool,-       pkgHaddockBenchmarks     :: Bool,-       pkgHaddockInternal       :: Bool,-       pkgHaddockCss            :: Maybe FilePath,-       pkgHaddockHscolour       :: Bool,-       pkgHaddockHscolourCss    :: Maybe FilePath,-       pkgHaddockContents       :: Maybe PathTemplate,+       -- TODO: make per-component variants of these flags+       elabVanillaLib           :: Bool,+       elabSharedLib            :: Bool,+       elabDynExe               :: Bool,+       elabGHCiLib              :: Bool,+       elabProfLib              :: Bool,+       elabProfExe              :: Bool,+       elabProfLibDetail        :: ProfDetailLevel,+       elabProfExeDetail        :: ProfDetailLevel,+       elabCoverage             :: Bool,+       elabOptimization         :: OptimisationLevel,+       elabSplitObjs            :: Bool,+       elabStripLibs            :: Bool,+       elabStripExes            :: Bool,+       elabDebugInfo            :: DebugInfoLevel, +       elabProgramPaths          :: Map String FilePath,+       elabProgramArgs           :: Map String [String],+       elabProgramPathExtra      :: [FilePath],+       elabConfigureScriptArgs   :: [String],+       elabExtraLibDirs          :: [FilePath],+       elabExtraFrameworkDirs    :: [FilePath],+       elabExtraIncludeDirs      :: [FilePath],+       elabProgPrefix            :: Maybe PathTemplate,+       elabProgSuffix            :: Maybe PathTemplate,++       elabInstallDirs           :: InstallDirs.InstallDirs FilePath,++       elabHaddockHoogle         :: Bool,+       elabHaddockHtml           :: Bool,+       elabHaddockHtmlLocation   :: Maybe String,+       elabHaddockForeignLibs    :: Bool,+       elabHaddockExecutables    :: Bool,+       elabHaddockTestSuites     :: Bool,+       elabHaddockBenchmarks     :: Bool,+       elabHaddockInternal       :: Bool,+       elabHaddockCss            :: Maybe FilePath,+       elabHaddockHscolour       :: Bool,+       elabHaddockHscolourCss    :: Maybe FilePath,+       elabHaddockContents       :: Maybe PathTemplate,+        -- Setup.hs related things:         -- | One of four modes for how we build and interact with the Setup.hs        -- script, based on whether it's a build-type Custom, with or without        -- explicit deps and the cabal spec version the .cabal file needs.-       pkgSetupScriptStyle      :: SetupScriptStyle,+       elabSetupScriptStyle      :: SetupScriptStyle,         -- | The version of the Cabal command line interface that we are using        -- for this package. This is typically the version of the Cabal lib        -- that the Setup.hs is built against.-       pkgSetupScriptCliVersion :: Version,+       elabSetupScriptCliVersion :: Version,         -- Build time related:-       pkgBuildTargets          :: [ComponentTarget],-       pkgReplTarget            :: Maybe ComponentTarget,-       pkgBuildHaddocks         :: Bool-     }-  deriving (Eq, Show, Generic)+       elabBuildTargets          :: [ComponentTarget],+       elabTestTargets           :: [ComponentTarget],+       elabReplTarget            :: Maybe ComponentTarget,+       elabBuildHaddocks         :: Bool, -instance Binary ElaboratedConfiguredPackage+       --pkgSourceDir ? -- currently passed in later because they can use temp locations+       --pkgBuildDir  ? -- but could in principle still have it here, with optional instr to use temp loc +       -- | Component/package specific information+       elabPkgOrComp :: ElaboratedPackageOrComponent+   }+  deriving (Eq, Show, Generic, Typeable)++-- | The package/component contains/is a library and so must be registered+elabRequiresRegistration :: ElaboratedConfiguredPackage -> Bool+elabRequiresRegistration elab =+    case elabPkgOrComp elab of+        ElabComponent comp ->+            case compComponentName comp of+                Just cn -> is_lib cn && build_target+                _ -> False+        ElabPackage pkg ->+            -- Tricky! Not only do we have to test if the user selected+            -- a library as a build target, we also have to test if+            -- the library was TRANSITIVELY depended upon, since we will+            -- also require a register in this case.+            --+            -- NB: It would have been far nicer to just unconditionally+            -- register in all cases, but some Custom Setups will fall+            -- over if you try to do that, ESPECIALLY if there actually is+            -- a library but they hadn't built it.+            build_target || any (depends_on_lib pkg) (elabBuildTargets elab)+  where+    depends_on_lib pkg (ComponentTarget cn _) =+        not (null (CD.select (== CD.componentNameToComponent cn)+                             (pkgDependsOnSelfLib pkg)))+    build_target =+        if not (null (elabBuildTargets elab))+            then any is_lib_target (elabBuildTargets elab)+            -- Empty build targets mean we build /everything/;+            -- that means we have to look more carefully to see+            -- if there is anything to register+            else Cabal.hasLibs (elabPkgDescription elab)+    -- NB: this means we DO NOT reregister if you just built a+    -- single file+    is_lib_target (ComponentTarget cn WholeComponent) = is_lib cn+    is_lib_target _ = False+    is_lib CLibName = True+    is_lib (CSubLibName _) = True+    is_lib _ = False+ instance Package ElaboratedConfiguredPackage where-  packageId = pkgSourceId+  packageId = elabPkgSourceId +instance HasConfiguredId ElaboratedConfiguredPackage where+  configuredId elab =+    ConfiguredId (packageId elab) (elabComponentName elab) (elabComponentId elab)+ instance HasUnitId ElaboratedConfiguredPackage where-  installedUnitId = pkgInstalledId+  installedUnitId = elabUnitId -instance PackageFixedDeps ElaboratedConfiguredPackage where-  depends = fmap (map installedPackageId) . pkgDependencies+instance IsNode ElaboratedConfiguredPackage where+    type Key ElaboratedConfiguredPackage = UnitId+    nodeKey = elabUnitId+    nodeNeighbors = elabOrderDependencies +instance Binary ElaboratedConfiguredPackage++data ElaboratedPackageOrComponent+    = ElabPackage   ElaboratedPackage+    | ElabComponent ElaboratedComponent+  deriving (Eq, Show, Generic)++instance Binary ElaboratedPackageOrComponent++elabComponentName :: ElaboratedConfiguredPackage -> Maybe ComponentName+elabComponentName elab =+    case elabPkgOrComp elab of+        ElabPackage _      -> Just CLibName -- there could be more, but default this+        ElabComponent comp -> compComponentName comp++-- | A user-friendly descriptor for an 'ElaboratedConfiguredPackage'.+elabConfiguredName :: Verbosity -> ElaboratedConfiguredPackage -> String+elabConfiguredName verbosity elab+    | verbosity <= normal+    = (case elabPkgOrComp elab of+        ElabPackage _ -> ""+        ElabComponent comp ->+            case compComponentName comp of+                Nothing -> "setup from "+                Just CLibName -> ""+                Just cname -> display cname ++ " from ")+      ++ display (packageId elab)+    | otherwise+    = display (elabUnitId elab)++elabDistDirParams :: ElaboratedSharedConfig -> ElaboratedConfiguredPackage -> DistDirParams+elabDistDirParams shared elab = DistDirParams {+        distParamUnitId = installedUnitId elab,+        distParamComponentId = elabComponentId elab,+        distParamPackageId = elabPkgSourceId elab,+        distParamComponentName = case elabPkgOrComp elab of+            ElabComponent comp -> compComponentName comp+            ElabPackage _ -> Nothing,+        distParamCompilerId = compilerId (pkgConfigCompiler shared),+        distParamPlatform = pkgConfigPlatform shared,+        distParamOptimization = elabOptimization elab+    }++-- | The full set of dependencies which dictate what order we+-- need to build things in the install plan: "order dependencies"+-- balls everything together.  This is mostly only useful for+-- ordering; if you are, for example, trying to compute what+-- @--dependency@ flags to pass to a Setup script, you need to+-- use 'elabLibDependencies'.  This method is the same as+-- 'nodeNeighbors'.+--+-- NB: this method DOES include setup deps.+elabOrderDependencies :: ElaboratedConfiguredPackage -> [UnitId]+elabOrderDependencies elab =+    case elabPkgOrComp elab of+        -- Important not to have duplicates: otherwise InstallPlan gets+        -- confused.+        ElabPackage pkg    -> ordNub (CD.flatDeps (pkgOrderDependencies pkg))+        ElabComponent comp -> compOrderDependencies comp++-- | Like 'elabOrderDependencies', but only returns dependencies on+-- libraries.+elabOrderLibDependencies :: ElaboratedConfiguredPackage -> [UnitId]+elabOrderLibDependencies elab =+    case elabPkgOrComp elab of+        ElabPackage _      -> map (newSimpleUnitId . confInstId) (elabLibDependencies elab)+        ElabComponent comp -> compOrderLibDependencies comp++-- | The library dependencies (i.e., the libraries we depend on, NOT+-- the dependencies of the library), NOT including setup dependencies.+-- These are passed to the @Setup@ script via @--dependency@.+elabLibDependencies :: ElaboratedConfiguredPackage -> [ConfiguredId]+elabLibDependencies elab =+    case elabPkgOrComp elab of+        ElabPackage pkg    -> ordNub (CD.nonSetupDeps (pkgLibDependencies pkg))+        ElabComponent comp -> compLibDependencies comp++-- | Like 'elabOrderDependencies', but only returns dependencies on+-- executables.  (This coincides with 'elabExeDependencies'.)+elabOrderExeDependencies :: ElaboratedConfiguredPackage -> [UnitId]+elabOrderExeDependencies =+    map newSimpleUnitId . elabExeDependencies++-- | The executable dependencies (i.e., the executables we depend on);+-- these are the executables we must add to the PATH before we invoke+-- the setup script.+elabExeDependencies :: ElaboratedConfiguredPackage -> [ComponentId]+elabExeDependencies elab = map confInstId $+    case elabPkgOrComp elab of+        ElabPackage pkg    -> CD.nonSetupDeps (pkgExeDependencies pkg)+        ElabComponent comp -> compExeDependencies comp++-- | This returns the paths of all the executables we depend on; we+-- must add these paths to PATH before invoking the setup script.+-- (This is usually what you want, not 'elabExeDependencies', if you+-- actually want to build something.)+elabExeDependencyPaths :: ElaboratedConfiguredPackage -> [FilePath]+elabExeDependencyPaths elab =+    case elabPkgOrComp elab of+        ElabPackage pkg    -> map snd $ CD.nonSetupDeps (pkgExeDependencyPaths pkg)+        ElabComponent comp -> map snd (compExeDependencyPaths comp)++-- | The setup dependencies (the library dependencies of the setup executable;+-- note that it is not legal for setup scripts to have executable+-- dependencies at the moment.)+elabSetupDependencies :: ElaboratedConfiguredPackage -> [ConfiguredId]+elabSetupDependencies elab =+    case elabPkgOrComp elab of+        ElabPackage pkg -> CD.setupDeps (pkgLibDependencies pkg)+        -- TODO: Custom setups not supported for components yet.  When+        -- they are, need to do this differently+        ElabComponent _ -> []++elabPkgConfigDependencies :: ElaboratedConfiguredPackage -> [(PkgconfigName, Maybe Version)]+elabPkgConfigDependencies ElaboratedConfiguredPackage { elabPkgOrComp = ElabPackage pkg }+    = pkgPkgConfigDependencies pkg+elabPkgConfigDependencies ElaboratedConfiguredPackage { elabPkgOrComp = ElabComponent comp }+    = compPkgConfigDependencies comp++-- | The cache files of all our inplace dependencies which,+-- when updated, require us to rebuild.  See #4202 for+-- more details.  Essentially, this is a list of filepaths+-- that, if our dependencies get rebuilt, will themselves+-- get updated.+--+-- Note: the hash of these cache files gets built into+-- the build cache ourselves, which means that we end+-- up tracking transitive dependencies!+--+-- Note: This tracks the "build" cache file, but not+-- "registration" or "config" cache files.  Why not?+-- Arguably we should...+--+-- Note: This is a bit of a hack, because it is not really+-- the hashes of the SOURCES of our (transitive) dependencies+-- that we should use to decide whether or not to rebuild,+-- but the output BUILD PRODUCTS.  The strategy we use+-- here will never work if we want to implement unchanging+-- rebuilds.+elabInplaceDependencyBuildCacheFiles+    :: DistDirLayout+    -> ElaboratedSharedConfig+    -> ElaboratedInstallPlan+    -> ElaboratedConfiguredPackage+    -> [FilePath]+elabInplaceDependencyBuildCacheFiles layout sconf plan root_elab =+    go =<< InstallPlan.directDeps plan (nodeKey root_elab)+  where+    go = InstallPlan.foldPlanPackage (const []) $ \elab -> do+            guard (elabBuildStyle elab == BuildInplaceOnly)+            return $ distPackageCacheFile layout (elabDistDirParams sconf elab) "build"++-- | Some extra metadata associated with an+-- 'ElaboratedConfiguredPackage' which indicates that the "package"+-- in question is actually a single component to be built.  Arguably+-- it would be clearer if there were an ADT which branched into+-- package work items and component work items, but I've structured+-- it this way to minimize change to the existing code (which I+-- don't feel qualified to rewrite.)+data ElaboratedComponent+   = ElaboratedComponent {+    -- | The name of the component to be built according to the solver+    compSolverName :: CD.Component,+    -- | The name of the component to be built.  Nothing if+    -- it's a setup dep.+    compComponentName :: Maybe ComponentName,+    -- | The *external* library dependencies of this component.  We+    -- pass this to the configure script.+    compLibDependencies :: [ConfiguredId],+    -- | In a component prior to instantiation, this list specifies+    -- the 'OpenUnitId's which, after instantiation, are the+    -- actual dependencies of this package.  Note that this does+    -- NOT include signature packages, which do not turn into real+    -- ordering dependencies when we instantiate.  This is intended to be+    -- a purely temporary field, to carry some information to the+    -- instantiation phase. It's more precise than+    -- 'compLibDependencies', and also stores information about internal+    -- dependencies.+    compLinkedLibDependencies :: [OpenUnitId],+    -- | The executable dependencies of this component (including+    -- internal executables).+    compExeDependencies :: [ConfiguredId],+    -- | The @pkg-config@ dependencies of the component+    compPkgConfigDependencies :: [(PkgconfigName, Maybe Version)],+    -- | The paths all our executable dependencies will be installed+    -- to once they are installed.+    compExeDependencyPaths :: [(ConfiguredId, FilePath)],+    compOrderLibDependencies :: [UnitId]+   }+  deriving (Eq, Show, Generic)++instance Binary ElaboratedComponent++-- | See 'elabOrderDependencies'.+compOrderDependencies :: ElaboratedComponent -> [UnitId]+compOrderDependencies comp =+    compOrderLibDependencies comp+ ++ compOrderExeDependencies comp++-- | See 'elabOrderExeDependencies'.+compOrderExeDependencies :: ElaboratedComponent -> [UnitId]+compOrderExeDependencies = map (newSimpleUnitId . confInstId) . compExeDependencies++data ElaboratedPackage+   = ElaboratedPackage {+       pkgInstalledId :: InstalledPackageId,++       -- | The exact dependencies (on other plan packages)+       --+       pkgLibDependencies :: ComponentDeps [ConfiguredId],++       -- | Components which depend (transitively) on an internally+       -- defined library.  These are used by 'elabRequiresRegistration',+       -- to determine if a user-requested build is going to need+       -- a library registration+       --+       pkgDependsOnSelfLib :: ComponentDeps [()],++       -- | Dependencies on executable packages.+       --+       pkgExeDependencies :: ComponentDeps [ConfiguredId],++       -- | Paths where executable dependencies live.+       --+       pkgExeDependencyPaths :: ComponentDeps [(ConfiguredId, FilePath)],++       -- | Dependencies on @pkg-config@ packages.+       -- NB: this is NOT per-component (although it could be)+       -- because Cabal library does not track per-component+       -- pkg-config depends; it always does them all at once.+       --+       pkgPkgConfigDependencies :: [(PkgconfigName, Maybe Version)],++       -- | Which optional stanzas (ie testsuites, benchmarks) will actually+       -- be enabled during the package configure step.+       pkgStanzasEnabled :: Set OptionalStanza+     }+  deriving (Eq, Show, Generic)++instance Binary ElaboratedPackage++-- | See 'elabOrderDependencies'.  This gives the unflattened version,+-- which can be useful in some circumstances.+pkgOrderDependencies :: ElaboratedPackage -> ComponentDeps [UnitId]+pkgOrderDependencies pkg =+    fmap (map (newSimpleUnitId . confInstId)) (pkgLibDependencies pkg) `Mon.mappend`+    fmap (map (newSimpleUnitId . confInstId)) (pkgExeDependencies pkg)+ -- | This is used in the install plan to indicate how the package will be -- built. --@@ -261,83 +621,41 @@ type CabalFileText = LBS.ByteString  type ElaboratedReadyPackage = GenericReadyPackage ElaboratedConfiguredPackage-                                                  InstalledPackageInfo ---TODO: [code cleanup] this duplicates the InstalledPackageInfo quite a bit in an install plan--- because the same ipkg is used by many packages. So the binary file will be big.--- Could we keep just (ipkgid, deps) instead of the whole InstalledPackageInfo?--- or transform to a shared form when serialising / deserialising -data GenericBuildResult ipkg iresult ifailure-                  = BuildFailure ifailure-                  | BuildSuccess (Maybe ipkg) iresult-  deriving (Eq, Show, Generic)--instance (Binary ipkg, Binary iresult, Binary ifailure) =>-         Binary (GenericBuildResult ipkg iresult ifailure)--type BuildResult  = GenericBuildResult InstalledPackageInfo -                                       BuildSuccess BuildFailure--data BuildSuccess = BuildOk DocsResult TestsResult-  deriving (Eq, Show, Generic)--data DocsResult  = DocsNotTried  | DocsFailed  | DocsOk-  deriving (Eq, Show, Generic)--data TestsResult = TestsNotTried | TestsOk-  deriving (Eq, Show, Generic)--data BuildFailure = PlanningFailed              --TODO: [required eventually] not yet used-                  | DependentFailed PackageId-                  | DownloadFailed  String      --TODO: [required eventually] not yet used-                  | UnpackFailed    String      --TODO: [required eventually] not yet used-                  | ConfigureFailed String-                  | BuildFailed     String-                  | TestsFailed     String      --TODO: [required eventually] not yet used-                  | InstallFailed   String-  deriving (Eq, Show, Typeable, Generic)--instance Exception BuildFailure--instance Binary BuildFailure-instance Binary BuildSuccess-instance Binary DocsResult-instance Binary TestsResult-- --------------------------- -- Build targets -- --- | The various targets within a package. This is more of a high level--- specification than a elaborated prescription.+-- | Specific targets within a package or component to act on e.g. to build,+-- haddock or open a repl. ---data PackageTarget =-     -- | Build the default components in this package. This usually means-     -- just the lib and exes, but it can also mean the testsuites and-     -- benchmarks if the user explicitly requested them.-     BuildDefaultComponents-     -- | Build a specific component in this package.-   | BuildSpecificComponent ComponentTarget-   | ReplDefaultComponent-   | ReplSpecificComponent  ComponentTarget-   | HaddockDefaultComponents-  deriving (Eq, Show, Generic)- data ComponentTarget = ComponentTarget ComponentName SubComponentTarget-  deriving (Eq, Show, Generic)--data SubComponentTarget = WholeComponent-                        | ModuleTarget ModuleName-                        | FileTarget   FilePath-  deriving (Eq, Show, Generic)+  deriving (Eq, Ord, Show, Generic) -instance Binary PackageTarget instance Binary ComponentTarget-instance Binary SubComponentTarget +-- | Unambiguously render a 'ComponentTarget', e.g., to pass+-- to a Cabal Setup script.+showComponentTarget :: PackageId -> ComponentTarget -> String+showComponentTarget pkgid =+    Cabal.showBuildTarget pkgid . toBuildTarget+  where+    toBuildTarget :: ComponentTarget -> Cabal.BuildTarget+    toBuildTarget (ComponentTarget cname subtarget) =+      case subtarget of+        WholeComponent     -> Cabal.BuildTargetComponent cname+        ModuleTarget mname -> Cabal.BuildTargetModule    cname mname+        FileTarget   fname -> Cabal.BuildTargetFile      cname fname +showTestComponentTarget :: PackageId -> ComponentTarget -> Maybe String+showTestComponentTarget _ (ComponentTarget (CTestName n) _) = Just $ display n+showTestComponentTarget _ _ = Nothing++isTestComponentTarget :: ComponentTarget -> Bool+isTestComponentTarget (ComponentTarget (CTestName _) _) = True+isTestComponentTarget _                                 = False+ --------------------------- -- Setup.hs script policy --@@ -363,7 +681,7 @@                       | SetupCustomImplicitDeps                       | SetupNonCustomExternalLib                       | SetupNonCustomInternalLib-  deriving (Eq, Show, Generic)+  deriving (Eq, Show, Generic, Typeable)  instance Binary SetupScriptStyle 
Distribution/Client/RebuildMonad.hs view
@@ -1,4 +1,3 @@-{-# LANGUAGE CPP #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-}  -- | An abstraction for re-running actions if values or files have changed.@@ -13,6 +12,7 @@     -- * Rebuild monad     Rebuild,     runRebuild,+    execRebuild,     askRoot,      -- * Setting up file monitoring@@ -42,8 +42,20 @@      -- * Utils     matchFileGlob,+    getDirectoryContentsMonitored,+    createDirectoryMonitored,+    monitorDirectoryStatus,+    doesFileExistMonitored,+    need,+    needIfExists,+    findFileWithExtensionMonitored,+    findFirstFileMonitored,+    findFileMonitored,   ) where +import Prelude ()+import Distribution.Client.Compat.Prelude+ import Distribution.Client.FileMonitor import Distribution.Client.Glob hiding (matchFileGlob) import qualified Distribution.Client.Glob as Glob (matchFileGlob)@@ -51,13 +63,10 @@ import Distribution.Simple.Utils (debug) import Distribution.Verbosity    (Verbosity) -#if !MIN_VERSION_base(4,8,0)-import Control.Applicative-#endif import Control.Monad.State as State import Control.Monad.Reader as Reader-import Distribution.Compat.Binary     (Binary)-import System.FilePath (takeFileName)+import System.FilePath+import System.Directory   -- | A monad layered on top of 'IO' to help with re-running actions when the@@ -86,6 +95,10 @@ runRebuild :: FilePath -> Rebuild a -> IO a runRebuild rootDir (Rebuild action) = evalStateT (runReaderT action rootDir) [] +-- | Run a 'Rebuild' IO action.+execRebuild :: FilePath -> Rebuild a -> IO [MonitorFilePath]+execRebuild rootDir (Rebuild action) = execStateT (runReaderT action rootDir) []+ -- | The root that relative paths are interpreted as being relative to. askRoot :: Rebuild FilePath askRoot = Rebuild Reader.ask@@ -145,3 +158,80 @@     monitorFiles [monitorFileGlobExistence glob]     liftIO $ Glob.matchFileGlob root glob +getDirectoryContentsMonitored :: FilePath -> Rebuild [FilePath]+getDirectoryContentsMonitored dir = do+    exists <- monitorDirectoryStatus dir+    if exists+      then liftIO $ getDirectoryContents dir+      else return []++createDirectoryMonitored :: Bool -> FilePath -> Rebuild ()+createDirectoryMonitored createParents dir = do+    monitorFiles [monitorDirectoryExistence dir]+    liftIO $ createDirectoryIfMissing createParents dir++-- | Monitor a directory as in 'monitorDirectory' if it currently exists or+-- as 'monitorNonExistentDirectory' if it does not.+monitorDirectoryStatus :: FilePath -> Rebuild Bool+monitorDirectoryStatus dir = do+    exists <- liftIO $ doesDirectoryExist dir+    monitorFiles [if exists+                    then monitorDirectory dir+                    else monitorNonExistentDirectory dir]+    return exists++-- | Like 'doesFileExist', but in the 'Rebuild' monad.  This does+-- NOT track the contents of 'FilePath'; use 'need' in that case.+doesFileExistMonitored :: FilePath -> Rebuild Bool+doesFileExistMonitored f = do+    root <- askRoot+    exists <- liftIO $ doesFileExist (root </> f)+    monitorFiles [if exists+                    then monitorFileExistence f+                    else monitorNonExistentFile f]+    return exists++-- | Monitor a single file+need :: FilePath -> Rebuild ()+need f = monitorFiles [monitorFileHashed f]++-- | Monitor a file if it exists; otherwise check for when it+-- gets created.  This is a bit better for recompilation avoidance+-- because sometimes users give bad package metadata, and we don't+-- want to repeatedly rebuild in this case (which we would if we+-- need'ed a non-existent file).+needIfExists :: FilePath -> Rebuild ()+needIfExists f = do+    root <- askRoot+    exists <- liftIO $ doesFileExist (root </> f)+    monitorFiles [if exists+                    then monitorFileHashed f+                    else monitorNonExistentFile f]++-- | Like 'findFileWithExtension', but in the 'Rebuild' monad.+findFileWithExtensionMonitored+    :: [String]+    -> [FilePath]+    -> FilePath+    -> Rebuild (Maybe FilePath)+findFileWithExtensionMonitored extensions searchPath baseName =+  findFirstFileMonitored id+    [ path </> baseName <.> ext+    | path <- nub searchPath+    , ext <- nub extensions ]++-- | Like 'findFirstFile', but in the 'Rebuild' monad.+findFirstFileMonitored :: (a -> FilePath) -> [a] -> Rebuild (Maybe a)+findFirstFileMonitored file = findFirst+  where findFirst []     = return Nothing+        findFirst (x:xs) = do exists <- doesFileExistMonitored (file x)+                              if exists+                                then return (Just x)+                                else findFirst xs++-- | Like 'findFile', but in the 'Rebuild' monad.+findFileMonitored :: [FilePath] -> FilePath -> Rebuild (Maybe FilePath)+findFileMonitored searchPath fileName =+  findFirstFileMonitored id+    [ path </> fileName+    | path <- nub searchPath]
+ Distribution/Client/Reconfigure.hs view
@@ -0,0 +1,235 @@+module Distribution.Client.Reconfigure ( Check(..), reconfigure ) where++import Control.Monad ( unless, when )+import Data.Maybe ( isJust )+import Data.Monoid hiding ( (<>) )+import System.Directory ( doesFileExist )++import Distribution.Compat.Semigroup++import Distribution.Verbosity++import Distribution.Simple.Configure ( localBuildInfoFile )+import Distribution.Simple.Setup ( Flag, flagToMaybe, toFlag )+import Distribution.Simple.Utils+       ( existsAndIsMoreRecentThan, defaultPackageDesc, info )++import Distribution.Client.Config ( SavedConfig(..) )+import Distribution.Client.Configure ( readConfigFlags )+import Distribution.Client.Nix ( findNixExpr, inNixShell, nixInstantiate )+import Distribution.Client.Sandbox+       ( WereDepsReinstalled(..), findSavedDistPref, getSandboxConfigFilePath+       , maybeReinstallAddSourceDeps, updateInstallDirs )+import Distribution.Client.Sandbox.PackageEnvironment+       ( userPackageEnvironmentFile )+import Distribution.Client.Sandbox.Types ( UseSandbox(..) )+import Distribution.Client.Setup+       ( ConfigFlags(..), ConfigExFlags, GlobalFlags(..)+       , SkipAddSourceDepsCheck(..) )+++-- | @Check@ represents a function to check some condition on type @a@. The+-- returned 'Any' is 'True' if any part of the condition failed.+newtype Check a = Check {+  runCheck :: Any  -- ^ Did any previous check fail?+           -> a  -- ^ value returned by previous checks+           -> IO (Any, a)  -- ^ Did this check fail? What value is returned?+}++instance Semigroup (Check a) where+  (<>) c d = Check $ \any0 a0 -> do+    (any1, a1) <- runCheck c any0 a0+    (any2, a2) <- runCheck d (any0 <> any1) a1+    return (any0 <> any1 <> any2, a2)++instance Monoid (Check a) where+  mempty = Check $ \_ a -> return (mempty, a)+  mappend = (<>)+++-- | Re-configure the package in the current directory if needed. Deciding+-- when to reconfigure and with which options is convoluted:+--+-- If we are reconfiguring, we must always run @configure@ with the+-- verbosity option we are given; however, that a previous configuration+-- uses a different verbosity setting is not reason enough to reconfigure.+--+-- The package should be configured to use the same \"dist\" prefix as+-- given to the @build@ command, otherwise the build will probably+-- fail. Not only does this determine the \"dist\" prefix setting if we+-- need to reconfigure anyway, but an existing configuration should be+-- invalidated if its \"dist\" prefix differs.+--+-- If the package has never been configured (i.e., there is no+-- LocalBuildInfo), we must configure first, using the default options.+--+-- If the package has been configured, there will be a 'LocalBuildInfo'.+-- If there no package description file, we assume that the+-- 'PackageDescription' is up to date, though the configuration may need+-- to be updated for other reasons (see above). If there is a package+-- description file, and it has been modified since the 'LocalBuildInfo'+-- was generated, then we need to reconfigure.+--+-- The caller of this function may also have specific requirements+-- regarding the flags the last configuration used. For example,+-- 'testAction' requires that the package be configured with test suites+-- enabled. The caller may pass the required settings to this function+-- along with a function to check the validity of the saved 'ConfigFlags';+-- these required settings will be checked first upon determining that+-- a previous configuration exists.+reconfigure+  :: ((ConfigFlags, ConfigExFlags) -> [String] -> GlobalFlags -> IO ())+     -- ^ configure action+  -> Verbosity+     -- ^ Verbosity setting+  -> FilePath+     -- ^ \"dist\" prefix+  -> UseSandbox+  -> SkipAddSourceDepsCheck+     -- ^ Should we skip the timestamp check for modified+     -- add-source dependencies?+  -> Flag (Maybe Int)+     -- ^ -j flag for reinstalling add-source deps.+  -> Check (ConfigFlags, ConfigExFlags)+     -- ^ Check that the required flags are set.+     -- If they are not set, provide a message explaining the+     -- reason for reconfiguration.+  -> [String]     -- ^ Extra arguments+  -> GlobalFlags  -- ^ Global flags+  -> SavedConfig+  -> IO SavedConfig+reconfigure+  configureAction+  verbosity+  dist+  useSandbox+  skipAddSourceDepsCheck+  numJobsFlag+  check+  extraArgs+  globalFlags+  config+  = do++  savedFlags@(_, _) <- readConfigFlags dist++  useNix <- fmap isJust (findNixExpr globalFlags config)+  alreadyInNixShell <- inNixShell++  if useNix && not alreadyInNixShell+    then do++      -- If we are using Nix, we must reinstantiate the derivation outside+      -- the shell. Eventually, the caller will invoke 'nixShell' which will+      -- rerun cabal inside the shell. That will bring us back to 'reconfigure',+      -- but inside the shell we'll take the second branch, below.++      -- This seems to have a problem: won't 'configureAction' call 'nixShell'+      -- yet again, spawning an infinite tree of subprocesses?+      -- No, because 'nixShell' doesn't spawn a new process if it is already+      -- running in a Nix shell.++      nixInstantiate verbosity dist False globalFlags config+      return config++    else do++      let checks =+            checkVerb+            <> checkDist+            <> checkOutdated+            <> check+            <> checkAddSourceDeps+      (Any force, flags@(configFlags, _)) <- runCheck checks mempty savedFlags++      let (_, config') =+            updateInstallDirs+            (configUserInstall configFlags)+            (useSandbox, config)++      when force $ configureAction flags extraArgs globalFlags+      return config'++  where++    -- Changing the verbosity does not require reconfiguration, but the new+    -- verbosity should be used if reconfiguring.+    checkVerb = Check $ \_ (configFlags, configExFlags) -> do+      let configFlags' = configFlags { configVerbosity = toFlag verbosity}+      return (mempty, (configFlags', configExFlags))++    -- Reconfiguration is required if @--build-dir@ changes.+    checkDist = Check $ \_ (configFlags, configExFlags) -> do+      -- Always set the chosen @--build-dir@ before saving the flags,+      -- or bad things could happen.+      savedDist <- findSavedDistPref config (configDistPref configFlags)+      let distChanged = dist /= savedDist+      when distChanged $ info verbosity "build directory changed"+      let configFlags' = configFlags { configDistPref = toFlag dist }+      return (Any distChanged, (configFlags', configExFlags))++    checkOutdated = Check $ \_ flags@(configFlags, _) -> do+      let buildConfig = localBuildInfoFile dist++      -- Has the package ever been configured? If not, reconfiguration is+      -- required.+      configured <- doesFileExist buildConfig+      unless configured $ info verbosity "package has never been configured"++      -- Is the configuration older than the sandbox configuration file?+      -- If so, reconfiguration is required.+      sandboxConfig <- getSandboxConfigFilePath globalFlags+      sandboxConfigNewer <- existsAndIsMoreRecentThan sandboxConfig buildConfig+      when sandboxConfigNewer $+        info verbosity "sandbox was created after the package was configured"++      -- Is the @cabal.config@ file newer than @dist/setup.config@? Then we need+      -- to force reconfigure. Note that it's possible to use @cabal.config@+      -- even without sandboxes.+      userPackageEnvironmentFileModified <-+        existsAndIsMoreRecentThan userPackageEnvironmentFile buildConfig+      when userPackageEnvironmentFileModified $+        info verbosity ("user package environment file ('"+        ++ userPackageEnvironmentFile ++ "') was modified")++      -- Is the configuration older than the package description?+      descrFile <- maybe (defaultPackageDesc verbosity) return+                   (flagToMaybe (configCabalFilePath configFlags))+      outdated <- existsAndIsMoreRecentThan descrFile buildConfig+      when outdated $ info verbosity (descrFile ++ " was changed")++      let failed =+            Any outdated+            <> Any userPackageEnvironmentFileModified+            <> Any sandboxConfigNewer+            <> Any (not configured)+      return (failed, flags)++    checkAddSourceDeps = Check $ \(Any force') flags@(configFlags, _) -> do+      let (_, config') =+            updateInstallDirs+            (configUserInstall configFlags)+            (useSandbox, config)++          skipAddSourceDepsCheck'+            | force'    = SkipAddSourceDepsCheck+            | otherwise = skipAddSourceDepsCheck++      when (skipAddSourceDepsCheck' == SkipAddSourceDepsCheck) $+        info verbosity "skipping add-source deps check"++      -- Were any add-source dependencies reinstalled in the sandbox?+      depsReinstalled <-+        case skipAddSourceDepsCheck' of+          DontSkipAddSourceDepsCheck ->+            maybeReinstallAddSourceDeps+            verbosity numJobsFlag configFlags globalFlags+            (useSandbox, config')+          SkipAddSourceDepsCheck -> do+            return NoDepsReinstalled++      case depsReinstalled of+        NoDepsReinstalled -> return (mempty, flags)+        ReinstalledSomeDeps -> do+          info verbosity "some add-source dependencies were reinstalled"+          return (Any True, flags)
Distribution/Client/Run.hs view
@@ -1,4 +1,3 @@-{-# LANGUAGE CPP #-} ----------------------------------------------------------------------------- -- | -- Module      :  Distribution.Client.Run@@ -11,8 +10,15 @@ module Distribution.Client.Run ( run, splitRunArgs )        where +import Prelude ()+import Distribution.Client.Compat.Prelude++import Distribution.Types.TargetInfo     (targetCLBI)+import Distribution.Types.LocalBuildInfo (componentNameTargets')+ import Distribution.Client.Utils             (tryCanonicalizePath) +import Distribution.Types.UnqualComponentName import Distribution.PackageDescription       (Executable (..),                                               TestSuite(..),                                               Benchmark(..),@@ -23,21 +29,16 @@ import Distribution.Simple.BuildPaths        (exeExtension) import Distribution.Simple.LocalBuildInfo    (ComponentName (..),                                               LocalBuildInfo (..),-                                              getComponentLocalBuildInfo,                                               depLibraryPaths)-import Distribution.Simple.Utils             (die, notice, warn,+import Distribution.Simple.Utils             (die', notice, warn,                                               rawSystemExitWithEnv,                                               addLibraryPath) import Distribution.System                   (Platform (..)) import Distribution.Verbosity                (Verbosity)+import Distribution.Text                     (display)  import qualified Distribution.Simple.GHCJS as GHCJS -#if !MIN_VERSION_base(4,8,0)-import Data.Functor                          ((<$>))-#endif-import Data.List                             (find)-import Data.Foldable                         (traverse_) import System.Directory                      (getCurrentDirectory) import Distribution.Compat.Environment       (getEnvironment) import System.FilePath                       ((<.>), (</>))@@ -51,7 +52,7 @@   case whichExecutable of -- Either err (wasManuallyChosen, exe, paramsRest)     Left err               -> do       warn verbosity `traverse_` maybeWarning -- If there is a warning, print it.-      die err+      die' verbosity err     Right (True, exe, xs)  -> return (exe, xs)     Right (False, exe, xs) -> do       let addition = " Interpreting all parameters to `run` as a parameter to"@@ -70,14 +71,14 @@       ([]   , _)           -> Left "Couldn't find any enabled executables."       ([exe], [])          -> return (False, exe, [])       ([exe], (x:xs))-        | x == exeName exe -> return (True, exe, xs)-        | otherwise        -> return (False, exe, args)-      (_    , [])          -> Left+        | x == unUnqualComponentName (exeName exe) -> return (True, exe, xs)+        | otherwise                                -> return (False, exe, args)+      (_    , [])                                  -> Left         $ "This package contains multiple executables. "         ++ "You must pass the executable name as the first argument "         ++ "to 'cabal run'."       (_    , (x:xs))      ->-        case find (\exe -> exeName exe == x) enabledExes of+        case find (\exe -> unUnqualComponentName (exeName exe) == x) enabledExes of           Nothing  -> Left $ "No executable named '" ++ x ++ "'."           Just exe -> return (True, exe, xs)       where@@ -86,20 +87,20 @@     maybeWarning :: Maybe String     maybeWarning = case args of       []    -> Nothing-      (x:_) -> lookup x components+      (x:_) -> lookup (mkUnqualComponentName x) components       where-        components :: [(String, String)] -- Component name, message.+        components :: [(UnqualComponentName, String)] -- Component name, message.         components =-          [ (name, "The executable '" ++ name ++ "' is disabled.")+          [ (name, "The executable '" ++ display name ++ "' is disabled.")           | e <- executables pkg_descr           , not . buildable . buildInfo $ e, let name = exeName e] -          ++ [ (name, "There is a test-suite '" ++ name ++ "',"+          ++ [ (name, "There is a test-suite '" ++ display name ++ "',"                       ++ " but the `run` command is only for executables.")              | t <- testSuites pkg_descr              , let name = testName t] -          ++ [ (name, "There is a benchmark '" ++ name ++ "',"+          ++ [ (name, "There is a benchmark '" ++ display name ++ "',"                       ++ " but the `run` command is only for executables.")              | b <- benchmarks pkg_descr              , let name = benchmarkName b]@@ -114,26 +115,29 @@                        curDir </> dataDir pkg_descr)    (path, runArgs) <--    case compilerFlavor (compiler lbi) of+    let exeName' = display $ exeName exe+    in case compilerFlavor (compiler lbi) of       GHCJS -> do         let (script, cmd, cmdArgs) =               GHCJS.runCmd (withPrograms lbi)-                           (buildPref </> exeName exe </> exeName exe)+                           (buildPref </> exeName' </> exeName')         script' <- tryCanonicalizePath script         return (cmd, cmdArgs ++ [script'])       _     -> do          p <- tryCanonicalizePath $-            buildPref </> exeName exe </> (exeName exe <.> exeExtension)+            buildPref </> exeName' </> (exeName' <.> exeExtension)          return (p, [])    env  <- (dataDirEnvVar:) <$> getEnvironment   -- Add (DY)LD_LIBRARY_PATH if needed   env' <- if withDynExe lbi              then do let (Platform _ os) = hostPlatform lbi-                         clbi = getComponentLocalBuildInfo lbi-                                  (CExeName (exeName exe))+                     clbi <- case componentNameTargets' pkg_descr lbi (CExeName (exeName exe)) of+                                [target] -> return (targetCLBI target)+                                [] -> die' verbosity "run: Could not find executable in LocalBuildInfo"+                                _ -> die' verbosity "run: Found multiple matching exes in LocalBuildInfo"                      paths <- depLibraryPaths True False lbi clbi                      return (addLibraryPath os paths env)              else return env-  notice verbosity $ "Running " ++ exeName exe ++ "..."+  notice verbosity $ "Running " ++ display (exeName exe) ++ "..."   rawSystemExitWithEnv verbosity path (runArgs++exeArgs) env'
Distribution/Client/Sandbox.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE CPP #-}+{-# LANGUAGE FlexibleContexts #-} ----------------------------------------------------------------------------- -- | -- Module      :  Distribution.Client.Sandbox@@ -38,12 +39,16 @@     updateSandboxConfigFileFlag,     updateInstallDirs, -    configPackageDB', configCompilerAux', getPersistOrConfigCompiler+    getPersistOrConfigCompiler   ) where +import Prelude ()+import Distribution.Client.Compat.Prelude+ import Distribution.Client.Setup   ( SandboxFlags(..), ConfigFlags(..), ConfigExFlags(..), InstallFlags(..)-  , GlobalFlags(..), defaultConfigExFlags, defaultInstallFlags+  , GlobalFlags(..), configCompilerAux', configPackageDB'+  , defaultConfigExFlags, defaultInstallFlags   , defaultSandboxLocation, withRepoContext ) import Distribution.Client.Sandbox.Timestamp  ( listModifiedDeps                                               , maybeAddCompilerTimestampRecord@@ -70,59 +75,55 @@                                               , UseSandbox(..) ) import Distribution.Client.SetupWrapper   ( SetupScriptOptions(..), defaultSetupScriptOptions )-import Distribution.Client.Types              ( PackageLocation(..)-                                              , SourcePackage(..) )+import Distribution.Client.Types              ( PackageLocation(..) ) import Distribution.Client.Utils              ( inDir, tryCanonicalizePath                                               , tryFindAddSourcePackageDesc) import Distribution.PackageDescription.Configuration                                               ( flattenPackageDescription )-import Distribution.PackageDescription.Parse  ( readPackageDescription )-import Distribution.Simple.Compiler           ( Compiler(..), PackageDB(..)-                                              , PackageDBStack )+#ifdef CABAL_PARSEC+import Distribution.PackageDescription.Parsec ( readGenericPackageDescription )+#else+import Distribution.PackageDescription.Parse  ( readGenericPackageDescription )+#endif+import Distribution.Simple.Compiler           ( Compiler(..), PackageDB(..) ) import Distribution.Simple.Configure          ( configCompilerAuxEx-                                              , interpretPackageDbFlags                                               , getPackageDBContents                                               , maybeGetPersistBuildConfig                                               , findDistPrefOrDefault                                               , findDistPref ) import qualified Distribution.Simple.LocalBuildInfo as LocalBuildInfo import Distribution.Simple.PreProcess         ( knownSuffixHandlers )-import Distribution.Simple.Program            ( ProgramConfiguration )+import Distribution.Simple.Program            ( ProgramDb ) import Distribution.Simple.Setup              ( Flag(..), HaddockFlags(..)                                               , fromFlagOrDefault, flagToMaybe ) import Distribution.Simple.SrcDist            ( prepareTree )-import Distribution.Simple.Utils              ( die, debug, notice, info, warn+import Distribution.Simple.Utils              ( die', debug, notice, info, warn                                               , debugNoWrap, defaultPackageDesc-                                              , intercalate, topHandlerWith+                                              , topHandlerWith                                               , createDirectoryIfMissingVerbose ) import Distribution.Package                   ( Package(..) ) import Distribution.System                    ( Platform ) import Distribution.Text                      ( display )-import Distribution.Verbosity                 ( Verbosity, lessVerbose )+import Distribution.Verbosity                 ( Verbosity ) import Distribution.Compat.Environment        ( lookupEnv, setEnv ) import Distribution.Client.Compat.FilePerms   ( setFileHidden ) import qualified Distribution.Client.Sandbox.Index as Index import Distribution.Simple.PackageIndex       ( InstalledPackageIndex ) import qualified Distribution.Simple.PackageIndex  as InstalledPackageIndex import qualified Distribution.Simple.Register      as Register++import Distribution.Solver.Types.SourcePackage+ import qualified Data.Map                          as M import qualified Data.Set                          as S import Data.Either                            (partitionEithers) import Control.Exception                      ( assert, bracket_ )-import Control.Monad                          ( forM, liftM, liftM2, unless, when )+import Control.Monad                          ( forM, mapM, mapM_ ) import Data.Bits                              ( shiftL, shiftR, xor )-import Data.Char                              ( ord ) import Data.IORef                             ( newIORef, writeIORef, readIORef ) import Data.List                              ( delete-                                              , foldl'-                                              , intersperse-                                              , isPrefixOf                                               , groupBy ) import Data.Maybe                             ( fromJust )-#if !MIN_VERSION_base(4,8,0)-import Data.Monoid                            ( mempty, mappend )-#endif-import Data.Word                              ( Word32 ) import Numeric                                ( showHex ) import System.Directory                       ( canonicalizePath                                               , createDirectory@@ -210,16 +211,16 @@     (globalConfigFile globalFlags)  -- | Return the name of the package index file for this package environment.-tryGetIndexFilePath :: SavedConfig -> IO FilePath-tryGetIndexFilePath config = tryGetIndexFilePath' (savedGlobalFlags config)+tryGetIndexFilePath :: Verbosity -> SavedConfig -> IO FilePath+tryGetIndexFilePath verbosity config = tryGetIndexFilePath' verbosity (savedGlobalFlags config)  -- | The same as 'tryGetIndexFilePath', but takes 'GlobalFlags' instead of -- 'SavedConfig'.-tryGetIndexFilePath' :: GlobalFlags -> IO FilePath-tryGetIndexFilePath' globalFlags = do+tryGetIndexFilePath' :: Verbosity -> GlobalFlags -> IO FilePath+tryGetIndexFilePath' verbosity globalFlags = do   let paths = fromNubList $ globalLocalRepos globalFlags   case paths of-    []  -> die $ "Distribution.Client.Sandbox.tryGetIndexFilePath: " +++    []  -> die' verbosity $ "Distribution.Client.Sandbox.tryGetIndexFilePath: " ++            "no local repos found. " ++ checkConfiguration     _   -> return $ (last paths) </> Index.defaultIndexFileName   where@@ -228,19 +229,19 @@  -- | Try to extract a 'PackageDB' from 'ConfigFlags'. Gives a better error -- message than just pattern-matching.-getSandboxPackageDB :: ConfigFlags -> IO PackageDB-getSandboxPackageDB configFlags = do+getSandboxPackageDB :: Verbosity -> ConfigFlags -> IO PackageDB+getSandboxPackageDB verbosity configFlags = do   case configPackageDBs configFlags of     [Just sandboxDB@(SpecificPackageDB _)] -> return sandboxDB     -- TODO: should we allow multiple package DBs (e.g. with 'inherit')?      []                                     ->-      die $ "Sandbox package DB is not specified. " ++ sandboxConfigCorrupt+      die' verbosity $ "Sandbox package DB is not specified. " ++ sandboxConfigCorrupt     [_]                                    ->-      die $ "Unexpected contents of the 'package-db' field. "+      die' verbosity $ "Unexpected contents of the 'package-db' field. "             ++ sandboxConfigCorrupt     _                                      ->-      die $ "Too many package DBs provided. " ++ sandboxConfigCorrupt+      die' verbosity $ "Too many package DBs provided. " ++ sandboxConfigCorrupt    where     sandboxConfigCorrupt = "Your 'cabal.sandbox.config' is probably corrupt."@@ -248,11 +249,11 @@  -- | Which packages are installed in the sandbox package DB? getInstalledPackagesInSandbox :: Verbosity -> ConfigFlags-                                 -> Compiler -> ProgramConfiguration+                                 -> Compiler -> ProgramDb                                  -> IO InstalledPackageIndex-getInstalledPackagesInSandbox verbosity configFlags comp conf = do-    sandboxDB <- getSandboxPackageDB configFlags-    getPackageDBContents verbosity comp sandboxDB conf+getInstalledPackagesInSandbox verbosity configFlags comp progdb = do+    sandboxDB <- getSandboxPackageDB verbosity configFlags+    getPackageDBContents verbosity comp sandboxDB progdb  -- | Temporarily add $SANDBOX_DIR/bin to $PATH. withSandboxBinDirOnSearchPath :: FilePath -> IO a -> IO a@@ -280,13 +281,13 @@  -- | Initialise a package DB for this compiler if it doesn't exist. initPackageDBIfNeeded :: Verbosity -> ConfigFlags-                         -> Compiler -> ProgramConfiguration+                         -> Compiler -> ProgramDb                          -> IO ()-initPackageDBIfNeeded verbosity configFlags comp conf = do-  SpecificPackageDB dbPath <- getSandboxPackageDB configFlags+initPackageDBIfNeeded verbosity configFlags comp progdb = do+  SpecificPackageDB dbPath <- getSandboxPackageDB verbosity configFlags   packageDBExists <- doesDirectoryExist dbPath   unless packageDBExists $-    Register.initPackageDB verbosity comp conf dbPath+    Register.initPackageDB verbosity comp progdb dbPath   when packageDBExists $     debug verbosity $ "The package database already exists: " ++ dbPath @@ -318,7 +319,7 @@    -- Determine which compiler to use (using the value from ~/.cabal/config).   userConfig <- loadConfig verbosity (globalConfigFile globalFlags)-  (comp, platform, conf) <- configCompilerAuxEx (savedConfigureFlags userConfig)+  (comp, platform, progdb) <- configCompilerAuxEx (savedConfigureFlags userConfig)    -- Create the package environment file.   pkgEnvFile <- getSandboxConfigFilePath globalFlags@@ -328,7 +329,7 @@       configFlags = savedConfigureFlags config    -- Create the index file if it doesn't exist.-  indexFile <- tryGetIndexFilePath config+  indexFile <- tryGetIndexFilePath verbosity config   indexFileExists <- doesFileExist indexFile   if indexFileExists     then notice verbosity $ "Using an existing sandbox located at " ++ sandboxDir@@ -336,7 +337,7 @@   Index.createEmpty verbosity indexFile    -- Create the package DB for the default compiler.-  initPackageDBIfNeeded verbosity configFlags comp conf+  initPackageDBIfNeeded verbosity configFlags comp progdb   maybeAddCompilerTimestampRecord verbosity sandboxDir indexFile     (compilerId comp) platform @@ -367,7 +368,7 @@                                         curDir </> defaultSandboxLocation        when isNonDefaultSandboxLocation $-        die $ "Non-default sandbox location used: '" ++ sandboxDir+        die' verbosity $ "Non-default sandbox location used: '" ++ sandboxDir         ++ "'.\nAssuming a shared sandbox. Please delete '"         ++ sandboxDir ++ "' manually." @@ -399,7 +400,7 @@                -> IO () doAddSource verbosity buildTreeRefs sandboxDir pkgEnv refType = do   let savedConfig       = pkgEnvSavedConfig pkgEnv-  indexFile            <- tryGetIndexFilePath savedConfig+  indexFile            <- tryGetIndexFilePath verbosity savedConfig    -- If we're running 'sandbox add-source' for the first time for this compiler,   -- we need to create an initial timestamp record.@@ -407,7 +408,7 @@   maybeAddCompilerTimestampRecord verbosity sandboxDir indexFile     (compilerId comp) platform -  withAddTimestamps sandboxDir $ do+  withAddTimestamps verbosity sandboxDir $ do     -- Path canonicalisation is done in addBuildTreeRefs, but we do it     -- twice because of the timestamps file.     buildTreeRefs' <- mapM tryCanonicalizePath buildTreeRefs@@ -440,7 +441,7 @@   pkgs      <- forM buildTreeRefs $ \buildTreeRef ->     inDir (Just buildTreeRef) $     return . flattenPackageDescription-            =<< readPackageDescription verbosity+            =<< readGenericPackageDescription verbosity             =<< defaultPackageDesc     verbosity    -- Copy the package sources to "snapshots/$PKGNAME-$VERSION-tmp". If@@ -474,7 +475,7 @@                        -> IO () sandboxDeleteSource verbosity buildTreeRefs _sandboxFlags globalFlags = do   (sandboxDir, pkgEnv) <- tryLoadSandboxConfig verbosity globalFlags-  indexFile            <- tryGetIndexFilePath (pkgEnvSavedConfig pkgEnv)+  indexFile            <- tryGetIndexFilePath verbosity (pkgEnvSavedConfig pkgEnv)    (results, convDict) <-     Index.removeBuildTreeRefs verbosity indexFile buildTreeRefs@@ -483,7 +484,7 @@       removedRefs = fmap convDict removedPaths    unless (null removedPaths) $ do-    removeTimestamps sandboxDir removedPaths+    removeTimestamps verbosity sandboxDir removedPaths      notice verbosity $ "Success deleting sources: " ++       showL removedRefs ++ "\n\n"@@ -491,7 +492,7 @@   unless (null failedPaths) $ do     let groupedFailures = groupBy errorType failedPaths     mapM_ handleErrors groupedFailures-    die $ "The sources with the above errors were skipped. (" +++    die' verbosity $ "The sources with the above errors were skipped. (" ++       showL (fmap getPath failedPaths) ++ ")"    notice verbosity $ "Note: 'sandbox delete-source' only unregisters the " ++@@ -529,7 +530,7 @@                       -> IO () sandboxListSources verbosity _sandboxFlags globalFlags = do   (sandboxDir, pkgEnv) <- tryLoadSandboxConfig verbosity globalFlags-  indexFile            <- tryGetIndexFilePath (pkgEnvSavedConfig pkgEnv)+  indexFile            <- tryGetIndexFilePath verbosity (pkgEnvSavedConfig pkgEnv)    refs <- Index.listBuildTreeRefs verbosity           Index.ListIgnored Index.LinksAndSnapshots indexFile@@ -551,10 +552,10 @@   let configFlags = savedConfigureFlags . pkgEnvSavedConfig $ pkgEnv   -- Invoke hc-pkg for the most recently configured compiler (if any),   -- using the right package-db for the compiler (see #1935).-  (comp, platform, conf) <- getPersistOrConfigCompiler configFlags+  (comp, platform, progdb) <- getPersistOrConfigCompiler configFlags   let dir         = sandboxPackageDBPath sandboxDir comp platform       dbStack     = [GlobalPackageDB, SpecificPackageDB dir]-  Register.invokeHcPkg verbosity comp conf dbStack extraArgs+  Register.invokeHcPkg verbosity comp progdb dbStack extraArgs  updateInstallDirs :: Flag Bool                   -> (UseSandbox, SavedConfig) -> (UseSandbox, SavedConfig)@@ -596,7 +597,7 @@     -- A @cabal.sandbox.config@ file (and possibly @cabal.config@) is present.     SandboxPackageEnvironment -> do       (sandboxDir, pkgEnv) <- tryLoadSandboxConfig verbosity globalFlags-                              -- ^ Prints an error message and exits on error.+                              -- Prints an error message and exits on error.       let config = pkgEnvSavedConfig pkgEnv       return (UseSandbox sandboxDir, config) @@ -635,7 +636,7 @@         flag = (globalRequireSandbox . savedGlobalFlags $ config)                `mappend` (globalRequireSandbox globalFlags)         checkFlag (Flag True)  =-          die $ "'require-sandbox' is set to True, but no sandbox is present. "+          die' verbosity $ "'require-sandbox' is set to True, but no sandbox is present. "              ++ "Use '--no-require-sandbox' if you want to override "              ++ "'require-sandbox' temporarily."         checkFlag (Flag False) = return ()@@ -673,37 +674,37 @@                             { configDistPref  = Flag sandboxDistPref }       haddockFlags        = mempty                             { haddockDistPref = Flag sandboxDistPref }-  (comp, platform, conf) <- configCompilerAux' configFlags-  retVal                 <- newIORef NoDepsReinstalled+  (comp, platform, progdb) <- configCompilerAux' configFlags+  retVal                   <- newIORef NoDepsReinstalled    withSandboxPackageInfo verbosity configFlags globalFlags-                         comp platform conf sandboxDir $ \sandboxPkgInfo ->+                         comp platform progdb sandboxDir $ \sandboxPkgInfo ->     unless (null $ modifiedAddSourceDependencies sandboxPkgInfo) $ do -     withRepoContext verbosity globalFlags $ \repoContext -> do-      let args :: InstallArgs-          args = ((configPackageDB' configFlags)-                 ,repoContext-                 ,comp, platform, conf-                 ,UseSandbox sandboxDir, Just sandboxPkgInfo-                 ,globalFlags, configFlags, configExFlags, installFlags-                 ,haddockFlags)+      withRepoContext verbosity globalFlags $ \repoContext -> do+        let args :: InstallArgs+            args = ((configPackageDB' configFlags)+                  ,repoContext+                  ,comp, platform, progdb+                  ,UseSandbox sandboxDir, Just sandboxPkgInfo+                  ,globalFlags, configFlags, configExFlags, installFlags+                  ,haddockFlags) -      -- This can actually be replaced by a call to 'install', but we use a-      -- lower-level API because of layer separation reasons. Additionally, we-      -- might want to use some lower-level features this in the future.-      withSandboxBinDirOnSearchPath sandboxDir $ do-        installContext <- makeInstallContext verbosity args Nothing-        installPlan    <- foldProgress logMsg die' return =<<-                          makeInstallPlan verbosity args installContext+        -- This can actually be replaced by a call to 'install', but we use a+        -- lower-level API because of layer separation reasons. Additionally, we+        -- might want to use some lower-level features this in the future.+        withSandboxBinDirOnSearchPath sandboxDir $ do+          installContext <- makeInstallContext verbosity args Nothing+          installPlan    <- foldProgress logMsg die'' return =<<+                            makeInstallPlan verbosity args installContext -        processInstallPlan verbosity args installContext installPlan-        writeIORef retVal ReinstalledSomeDeps+          processInstallPlan verbosity args installContext installPlan+          writeIORef retVal ReinstalledSomeDeps    readIORef retVal      where-      die' message = die (message ++ installFailedInSandbox)+      die'' message = die' verbosity (message ++ installFailedInSandbox)       -- TODO: use a better error message, remove duplication.       installFailedInSandbox =         "Note: when using a sandbox, all packages are required to have "@@ -721,25 +722,25 @@ -- we don't update the timestamp file here - this is done in -- 'postInstallActions'. withSandboxPackageInfo :: Verbosity -> ConfigFlags -> GlobalFlags-                          -> Compiler -> Platform -> ProgramConfiguration+                          -> Compiler -> Platform -> ProgramDb                           -> FilePath                           -> (SandboxPackageInfo -> IO ())                           -> IO () withSandboxPackageInfo verbosity configFlags globalFlags-                       comp platform conf sandboxDir cont = do+                       comp platform progdb sandboxDir cont = do   -- List all add-source deps.-  indexFile              <- tryGetIndexFilePath' globalFlags+  indexFile              <- tryGetIndexFilePath' verbosity globalFlags   buildTreeRefs          <- Index.listBuildTreeRefs verbosity                             Index.DontListIgnored Index.OnlyLinks indexFile   let allAddSourceDepsSet = S.fromList buildTreeRefs    -- List all packages installed in the sandbox.   installedPkgIndex <- getInstalledPackagesInSandbox verbosity-                       configFlags comp conf+                       configFlags comp progdb   let err = "Error reading sandbox package information."   -- Get the package descriptions for all add-source deps.-  depsCabalFiles <- mapM (flip tryFindAddSourcePackageDesc err) buildTreeRefs-  depsPkgDescs   <- mapM (readPackageDescription verbosity) depsCabalFiles+  depsCabalFiles <- mapM (flip (tryFindAddSourcePackageDesc verbosity) err) buildTreeRefs+  depsPkgDescs   <- mapM (readGenericPackageDescription verbosity) depsCabalFiles   let depsMap           = M.fromList (zip buildTreeRefs depsPkgDescs)       isInstalled pkgid = not . null         . InstalledPackageIndex.lookupSourcePackageId installedPkgIndex $ pkgid@@ -775,17 +776,17 @@ -- | Same as 'withSandboxPackageInfo' if we're inside a sandbox and the -- identity otherwise. maybeWithSandboxPackageInfo :: Verbosity -> ConfigFlags -> GlobalFlags-                               -> Compiler -> Platform -> ProgramConfiguration+                               -> Compiler -> Platform -> ProgramDb                                -> UseSandbox                                -> (Maybe SandboxPackageInfo -> IO ())                                -> IO () maybeWithSandboxPackageInfo verbosity configFlags globalFlags-                            comp platform conf useSandbox cont =+                            comp platform progdb useSandbox cont =   case useSandbox of     NoSandbox             -> cont Nothing     UseSandbox sandboxDir -> withSandboxPackageInfo verbosity                              configFlags globalFlags-                             comp platform conf sandboxDir+                             comp platform progdb sandboxDir                              (\spi -> cont (Just spi))  -- | Check if a sandbox is present and call @reinstallAddSourceDeps@ in that@@ -853,28 +854,12 @@ -- -- Utils (transitionary) ----- FIXME: configPackageDB' and configCompilerAux' don't really belong in this--- module--- -configPackageDB' :: ConfigFlags -> PackageDBStack-configPackageDB' cfg =-    interpretPackageDbFlags userInstall (configPackageDBs cfg)-  where-    userInstall = fromFlagOrDefault True (configUserInstall cfg)--configCompilerAux' :: ConfigFlags-                   -> IO (Compiler, Platform, ProgramConfiguration)-configCompilerAux' configFlags =-  configCompilerAuxEx configFlags-    --FIXME: make configCompilerAux use a sensible verbosity-    { configVerbosity = fmap lessVerbose (configVerbosity configFlags) }- -- | Try to read the most recently configured compiler from the -- 'localBuildInfoFile', falling back on 'configCompilerAuxEx' if it -- cannot be read. getPersistOrConfigCompiler :: ConfigFlags-                           -> IO (Compiler, Platform, ProgramConfiguration)+                           -> IO (Compiler, Platform, ProgramDb) getPersistOrConfigCompiler configFlags = do   distPref <- findDistPrefOrDefault (configDistPref configFlags)   mlbi <- maybeGetPersistBuildConfig distPref
Distribution/Client/Sandbox/Index.hs view
@@ -34,7 +34,7 @@                                  , makeAbsoluteToCwd, tryCanonicalizePath                                  , tryFindAddSourcePackageDesc  ) -import Distribution.Simple.Utils ( die, debug )+import Distribution.Simple.Utils ( die', debug ) import Distribution.Compat.Exception   ( tryIO ) import Distribution.Verbosity    ( Verbosity ) @@ -61,12 +61,12 @@ defaultIndexFileName = "00-index.tar"  -- | Given a path, ensure that it refers to a local build tree.-buildTreeRefFromPath :: BuildTreeRefType -> FilePath -> IO (Maybe BuildTreeRef)-buildTreeRefFromPath refType dir = do+buildTreeRefFromPath :: Verbosity -> BuildTreeRefType -> FilePath -> IO (Maybe BuildTreeRef)+buildTreeRefFromPath verbosity refType dir = do   dirExists <- doesDirectoryExist dir   unless dirExists $-    die $ "directory '" ++ dir ++ "' does not exist"-  _ <- tryFindAddSourcePackageDesc dir "Error adding source reference."+    die' verbosity $ "directory '" ++ dir ++ "' does not exist"+  _ <- tryFindAddSourcePackageDesc verbosity dir "Error adding source reference."   return . Just $ BuildTreeRef refType dir  -- | Given a tar archive entry, try to parse it as a local build tree reference.@@ -120,14 +120,14 @@  -- | Check that the provided path is either an existing directory, or a tar -- archive in an existing directory.-validateIndexPath :: FilePath -> IO FilePath-validateIndexPath path' = do+validateIndexPath :: Verbosity -> FilePath -> IO FilePath+validateIndexPath verbosity path' = do    path <- makeAbsoluteToCwd path'    if (== ".tar") . takeExtension $ path      then return path      else do dirExists <- doesDirectoryExist path              unless dirExists $-               die $ "directory does not exist: '" ++ path ++ "'"+               die' verbosity $ "directory does not exist: '" ++ path ++ "'"              return $ path </> defaultIndexFileName  -- | Create an empty index file.@@ -149,11 +149,11 @@ addBuildTreeRefs _         _   []  _ =   error "Distribution.Client.Sandbox.Index.addBuildTreeRefs: unexpected" addBuildTreeRefs verbosity path l' refType = do-  checkIndexExists path+  checkIndexExists verbosity path   l <- liftM nub . mapM tryCanonicalizePath $ l'   treesInIndex <- fmap (map buildTreePath) (readBuildTreeRefsFromFile path)   -- Add only those paths that aren't already in the index.-  treesToAdd <- mapM (buildTreeRefFromPath refType) (l \\ treesInIndex)+  treesToAdd <- mapM (buildTreeRefFromPath verbosity refType) (l \\ treesInIndex)   let entries = map writeBuildTreeRef (catMaybes treesToAdd)   unless (null entries) $ do     withBinaryFile path ReadWriteMode $ \h -> do@@ -176,7 +176,7 @@ removeBuildTreeRefs _         _   [] =   error "Distribution.Client.Sandbox.Index.removeBuildTreeRefs: unexpected" removeBuildTreeRefs verbosity indexPath l = do-  checkIndexExists indexPath+  checkIndexExists verbosity indexPath   let tmpFile = indexPath <.> "tmp"    canonRes <- mapM (\btr -> do res <- tryIO $ canonicalizePath btr@@ -240,7 +240,7 @@                      -> FilePath                      -> IO [FilePath] listBuildTreeRefs verbosity listIgnored refTypesToList path = do-  checkIndexExists path+  checkIndexExists verbosity path   buildTreeRefs <-     case listIgnored of       DontListIgnored -> do@@ -274,8 +274,8 @@   -- | Check that the package index file exists and exit with error if it does not.-checkIndexExists :: FilePath -> IO ()-checkIndexExists path = do+checkIndexExists :: Verbosity -> FilePath -> IO ()+checkIndexExists verbosity path = do   indexExists <- doesFileExist path   unless indexExists $-    die $ "index does not exist: '" ++ path ++ "'"+    die' verbosity $ "index does not exist: '" ++ path ++ "'"
Distribution/Client/Sandbox/PackageEnvironment.hs view
@@ -36,12 +36,12 @@                                        , installDirsFields, withProgramsFields                                        , withProgramOptionsFields                                        , defaultCompiler )-import Distribution.Client.Dependency.Types ( ConstraintSource (..) ) import Distribution.Client.ParseUtils  ( parseFields, ppFields, ppSection ) import Distribution.Client.Setup       ( GlobalFlags(..), ConfigExFlags(..)                                        , InstallFlags(..)                                        , defaultSandboxLocation )-import Distribution.Utils.NubList            ( toNubList )+import Distribution.Client.Targets     ( userConstraintPackageName )+import Distribution.Utils.NubList      ( toNubList ) import Distribution.Simple.Compiler    ( Compiler, PackageDB(..)                                        , compilerFlavor, showCompilerIdWithAbi ) import Distribution.Simple.InstallDirs ( InstallDirs(..), PathTemplate@@ -50,7 +50,8 @@ import Distribution.Simple.Setup       ( Flag(..)                                        , ConfigFlags(..), HaddockFlags(..)                                        , fromFlagOrDefault, toFlag, flagToMaybe )-import Distribution.Simple.Utils       ( die, info, notice, warn )+import Distribution.Simple.Utils       ( die', info, notice, warn, debug )+import Distribution.Solver.Types.ConstraintSource import Distribution.ParseUtils         ( FieldDescr(..), ParseResult(..)                                        , commaListField, commaNewLineListField                                        , liftField, lineNo, locatedErrorMsg@@ -60,8 +61,9 @@ import Distribution.System             ( Platform ) import Distribution.Verbosity          ( Verbosity, normal ) import Control.Monad                   ( foldM, liftM2, when, unless )-import Data.List                       ( partition )+import Data.List                       ( partition, sortBy ) import Data.Maybe                      ( isJust )+import Data.Ord                        ( comparing ) import Distribution.Compat.Exception   ( catchIO ) import Distribution.Compat.Semigroup import System.Directory                ( doesDirectoryExist, doesFileExist@@ -275,14 +277,24 @@  -- | Load the user package environment if it exists (the optional "cabal.config" -- file). If it does not exist locally, attempt to load an optional global one.-userPackageEnvironment :: Verbosity -> FilePath -> Maybe FilePath -> IO PackageEnvironment+userPackageEnvironment :: Verbosity -> FilePath -> Maybe FilePath+                       -> IO PackageEnvironment userPackageEnvironment verbosity pkgEnvDir globalConfigLocation = do     let path = pkgEnvDir </> userPackageEnvironmentFile-    minp <- readPackageEnvironmentFile (ConstraintSourceUserConfig path) mempty path+    minp <- readPackageEnvironmentFile (ConstraintSourceUserConfig path)+            mempty path     case (minp, globalConfigLocation) of       (Just parseRes, _)  -> processConfigParse path parseRes-      (_, Just globalLoc) -> maybe (warn verbosity ("no constraints file found at " ++ globalLoc) >> return mempty) (processConfigParse globalLoc) =<< readPackageEnvironmentFile (ConstraintSourceUserConfig globalLoc) mempty globalLoc-      _ -> return mempty+      (_, Just globalLoc) -> do+        minp' <- readPackageEnvironmentFile (ConstraintSourceUserConfig globalLoc)+                 mempty globalLoc+        maybe (warn verbosity ("no constraints file found at " ++ globalLoc)+               >> return mempty)+          (processConfigParse globalLoc)+          minp'+      _ -> do+        debug verbosity ("no user package environment file found at " ++ pkgEnvDir)+        return mempty   where     processConfigParse path (ParseOk warns parseResult) = do       when (not $ null warns) $ warn verbosity $@@ -297,7 +309,8 @@ -- | Same as @userPackageEnvironmentFile@, but returns a SavedConfig. loadUserConfig :: Verbosity -> FilePath -> Maybe FilePath -> IO SavedConfig loadUserConfig verbosity pkgEnvDir globalConfigLocation =-    fmap pkgEnvSavedConfig $ userPackageEnvironment verbosity pkgEnvDir globalConfigLocation+    fmap pkgEnvSavedConfig $+    userPackageEnvironment verbosity pkgEnvDir globalConfigLocation  -- | Common error handling code used by 'tryLoadSandboxPackageEnvironment' and -- 'updatePackageEnvironment'.@@ -306,7 +319,7 @@                      -> IO PackageEnvironment handleParseResult verbosity path minp =   case minp of-    Nothing -> die $+    Nothing -> die' verbosity $       "The package environment file '" ++ path ++ "' doesn't exist"     Just (ParseOk warns parseResult) -> do       when (not $ null warns) $ warn verbosity $@@ -314,7 +327,7 @@       return parseResult     Just (ParseFailed err) -> do       let (line, msg) = locatedErrorMsg err-      die $ "Error parsing package environment file " ++ path+      die' verbosity $ "Error parsing package environment file " ++ path         ++ maybe "" (\n -> ":" ++ show n) line ++ ":\n" ++ msg  -- | Try to load the given package environment file, exiting with error if it@@ -339,7 +352,7 @@   dirExists            <- doesDirectoryExist sandboxDir   -- TODO: Also check for an initialised package DB?   unless dirExists $-    die ("No sandbox exists at " ++ sandboxDir)+    die' verbosity ("No sandbox exists at " ++ sandboxDir)   info verbosity $ "Using a sandbox located at " ++ sandboxDir    let base   = basePackageEnvironment@@ -399,7 +412,8 @@    , commaNewLineListField "constraints"     (Text.disp . fst) ((\pc -> (pc, src)) `fmap` Text.parse)-    (configExConstraints . savedConfigureExFlags . pkgEnvSavedConfig)+    (sortConstraints . configExConstraints+     . savedConfigureExFlags . pkgEnvSavedConfig)     (\v pkgEnv -> updateConfigureExFlags pkgEnv                   (\flags -> flags { configExConstraints = v })) @@ -433,6 +447,8 @@                                  $ pkgEnv          }       }++    sortConstraints = sortBy (comparing $ userConstraintPackageName . fst)  -- | Read the package environment file. readPackageEnvironmentFile :: ConstraintSource -> PackageEnvironment -> FilePath
Distribution/Client/Sandbox/Timestamp.hs view
@@ -29,7 +29,7 @@ import qualified Data.Map as M  import Distribution.Compiler                         (CompilerId)-import Distribution.Simple.Utils                     (debug, die, warn)+import Distribution.Simple.Utils                     (debug, die', warn) import Distribution.System                           (Platform) import Distribution.Text                             (display) import Distribution.Verbosity                        (Verbosity)@@ -38,9 +38,10 @@ import Distribution.Client.Sandbox.Index   (ListIgnoredBuildTreeRefs (ListIgnored), RefTypesToList(OnlyLinks)   ,listBuildTreeRefs)+import Distribution.Client.SetupWrapper  import Distribution.Compat.Exception                 (catchIO)-import Distribution.Client.Compat.Time               (ModTime, getCurTime,+import Distribution.Compat.Time               (ModTime, getCurTime,                                                       getModTime,                                                       posixSecondsToModTime) @@ -66,8 +67,8 @@  -- | Read the timestamp file. Exits with error if the timestamp file is -- corrupted. Returns an empty list if the file doesn't exist.-readTimestampFile :: FilePath -> IO [TimestampFileRecord]-readTimestampFile timestampFile = do+readTimestampFile :: Verbosity -> FilePath -> IO [TimestampFileRecord]+readTimestampFile verbosity timestampFile = do   timestampString <- readFile timestampFile `catchIO` \_ -> return "[]"   case reads timestampString of     [(version, s)]@@ -90,8 +91,8 @@         _ -> dieCorrupted     _ -> dieCorrupted   where-    dieWrongFormat    = die $ wrongFormat ++ deleteAndRecreate-    dieCorrupted      = die $ corrupted ++ deleteAndRecreate+    dieWrongFormat    = die' verbosity $ wrongFormat ++ deleteAndRecreate+    dieCorrupted      = die' verbosity $ corrupted ++ deleteAndRecreate     wrongFormat       = "The timestamps file is in the wrong format."     corrupted         = "The timestamps file is corrupted."     deleteAndRecreate = " Please delete and recreate the sandbox."@@ -106,12 +107,12 @@     timestampTmpFile = timestampFile <.> "tmp"  -- | Read, process and write the timestamp file in one go.-withTimestampFile :: FilePath+withTimestampFile :: Verbosity -> FilePath                      -> ([TimestampFileRecord] -> IO [TimestampFileRecord])                      -> IO ()-withTimestampFile sandboxDir process = do+withTimestampFile verbosity sandboxDir process = do   let timestampFile = sandboxDir </> timestampFileName-  timestampRecords <- readTimestampFile timestampFile >>= process+  timestampRecords <- readTimestampFile verbosity timestampFile >>= process   writeTimestampFile timestampFile timestampRecords  -- | Given a list of 'AddSourceTimestamp's, a list of paths to add-source deps@@ -155,7 +156,7 @@ maybeAddCompilerTimestampRecord verbosity sandboxDir indexFile                                 compId platform = do   let key = timestampRecordKey compId platform-  withTimestampFile sandboxDir $ \timestampRecords -> do+  withTimestampFile verbosity sandboxDir $ \timestampRecords -> do     case lookup key timestampRecords of       Just _  -> return timestampRecords       Nothing -> do@@ -167,21 +168,21 @@  -- | Given an IO action that returns a list of build tree refs, add those -- build tree refs to the timestamps file (for all compilers).-withAddTimestamps :: FilePath -> IO [FilePath] -> IO ()-withAddTimestamps sandboxDir act = do+withAddTimestamps :: Verbosity -> FilePath -> IO [FilePath] -> IO ()+withAddTimestamps verbosity sandboxDir act = do   let initialTimestamp = minBound-  withActionOnAllTimestamps (addTimestamps initialTimestamp) sandboxDir act+  withActionOnAllTimestamps (addTimestamps initialTimestamp) verbosity sandboxDir act  -- | Given a list of build tree refs, remove those -- build tree refs from the timestamps file (for all compilers).-removeTimestamps :: FilePath -> [FilePath] -> IO ()-removeTimestamps idxFile =-  withActionOnAllTimestamps removeTimestamps' idxFile . return+removeTimestamps :: Verbosity -> FilePath -> [FilePath] -> IO ()+removeTimestamps verbosity idxFile =+  withActionOnAllTimestamps removeTimestamps' verbosity idxFile . return  -- | Given an IO action that returns a list of build tree refs, update the -- timestamps of the returned build tree refs to the current time (only for the -- given compiler & platform).-withUpdateTimestamps :: FilePath -> CompilerId -> Platform+withUpdateTimestamps :: Verbosity -> FilePath -> CompilerId -> Platform                         ->([AddSourceTimestamp] -> IO [FilePath])                         -> IO () withUpdateTimestamps =@@ -193,11 +194,12 @@ -- updates the timestamp file. The IO action is run only once. withActionOnAllTimestamps :: ([AddSourceTimestamp] -> [FilePath]                               -> [AddSourceTimestamp])+                             -> Verbosity                              -> FilePath                              -> IO [FilePath]                              -> IO ()-withActionOnAllTimestamps f sandboxDir act =-  withTimestampFile sandboxDir $ \timestampRecords -> do+withActionOnAllTimestamps f verbosity sandboxDir act =+  withTimestampFile verbosity sandboxDir $ \timestampRecords -> do     paths <- act     return [(key, f timestamps paths) | (key, timestamps) <- timestampRecords] @@ -207,14 +209,15 @@ withActionOnCompilerTimestamps :: ([AddSourceTimestamp]                                    -> [FilePath] -> ModTime                                    -> [AddSourceTimestamp])+                                  -> Verbosity                                   -> FilePath                                   -> CompilerId                                   -> Platform                                   -> ([AddSourceTimestamp] -> IO [FilePath])                                   -> IO ()-withActionOnCompilerTimestamps f sandboxDir compId platform act = do+withActionOnCompilerTimestamps f verbosity sandboxDir compId platform act = do   let needle = timestampRecordKey compId platform-  withTimestampFile sandboxDir $ \timestampRecords -> do+  withTimestampFile verbosity sandboxDir $ \timestampRecords -> do     timestampRecords' <- forM timestampRecords $ \r@(key, timestamps) ->       if key == needle       then do paths <- act timestamps@@ -227,7 +230,9 @@ isDepModified :: Verbosity -> ModTime -> AddSourceTimestamp -> IO Bool isDepModified verbosity now (packageDir, timestamp) = do   debug verbosity ("Checking whether the dependency is modified: " ++ packageDir)-  depSources <- allPackageSourceFiles verbosity packageDir+  -- TODO: we should properly plumb the correct options through+  -- instead of using defaultSetupScriptOptions+  depSources <- allPackageSourceFiles verbosity defaultSetupScriptOptions packageDir   go depSources    where@@ -252,7 +257,7 @@                        -- ^ The set of all installed add-source deps.                     -> IO [FilePath] listModifiedDeps verbosity sandboxDir compId platform installedDepsMap = do-  timestampRecords <- readTimestampFile (sandboxDir </> timestampFileName)+  timestampRecords <- readTimestampFile verbosity (sandboxDir </> timestampFileName)   let needle        = timestampRecordKey compId platform   timestamps       <- maybe noTimestampRecord return                       (lookup needle timestampRecords)@@ -262,7 +267,7 @@     $ timestamps    where-    noTimestampRecord = die $ "Сouldn't find a timestamp record for the given "+    noTimestampRecord = die' verbosity $ "Сouldn't find a timestamp record for the given "                         ++ "compiler/platform pair. "                         ++ "Please report this on the Cabal bug tracker: "                         ++ "https://github.com/haskell/cabal/issues/new ."
Distribution/Client/Sandbox/Types.hs view
@@ -1,4 +1,3 @@-{-# LANGUAGE CPP #-} ----------------------------------------------------------------------------- -- | -- Module      :  Distribution.Client.Sandbox.Types@@ -13,13 +12,12 @@   SandboxPackageInfo(..)   ) where +import Prelude ()+import Distribution.Client.Compat.Prelude+ import qualified Distribution.Simple.PackageIndex as InstalledPackageIndex-import Distribution.Client.Types (SourcePackage)-import Distribution.Compat.Semigroup (Semigroup((<>)))+import Distribution.Client.Types (UnresolvedSourcePackage) -#if !MIN_VERSION_base(4,8,0)-import Data.Monoid (Monoid(..))-#endif import qualified Data.Set as S  -- | Are we using a sandbox?@@ -49,11 +47,11 @@ -- | Data about the packages installed in the sandbox that is passed from -- 'reinstallAddSourceDeps' to the solver. data SandboxPackageInfo = SandboxPackageInfo {-  modifiedAddSourceDependencies :: ![SourcePackage],+  modifiedAddSourceDependencies :: ![UnresolvedSourcePackage],   -- ^ Modified add-source deps that we want to reinstall. These are guaranteed   -- to be already installed in the sandbox. -  otherAddSourceDependencies    :: ![SourcePackage],+  otherAddSourceDependencies    :: ![UnresolvedSourcePackage],   -- ^ Remaining add-source deps. Some of these may be not installed in the   -- sandbox. 
+ Distribution/Client/SavedFlags.hs view
@@ -0,0 +1,83 @@+{-# LANGUAGE DeriveDataTypeable #-}++module Distribution.Client.SavedFlags+       ( readCommandFlags, writeCommandFlags+       , readSavedArgs, writeSavedArgs+       ) where++import Distribution.Simple.Command+import Distribution.Simple.UserHooks ( Args )+import Distribution.Simple.Utils+       ( createDirectoryIfMissingVerbose, unintersperse )+import Distribution.Verbosity++import Control.Exception ( Exception, throwIO )+import Control.Monad ( liftM )+import Data.List ( intercalate )+import Data.Maybe ( fromMaybe )+import Data.Typeable+import System.Directory ( doesFileExist )+import System.FilePath ( takeDirectory )+++writeSavedArgs :: Verbosity -> FilePath -> [String] -> IO ()+writeSavedArgs verbosity path args = do+  createDirectoryIfMissingVerbose+    (lessVerbose verbosity) True (takeDirectory path)+  writeFile path (intercalate "\0" args)+++-- | Write command-line flags to a file, separated by null characters. This+-- format is also suitable for the @xargs -0@ command. Using the null+-- character also avoids the problem of escaping newlines or spaces,+-- because unlike other whitespace characters, the null character is+-- not valid in command-line arguments.+writeCommandFlags :: Verbosity -> FilePath -> CommandUI flags -> flags -> IO ()+writeCommandFlags verbosity path command flags =+  writeSavedArgs verbosity path (commandShowOptions command flags)+++readSavedArgs :: FilePath -> IO (Maybe [String])+readSavedArgs path = do+  exists <- doesFileExist path+  if exists+     then liftM (Just . unintersperse '\0') (readFile path)+    else return Nothing+++-- | Read command-line arguments, separated by null characters, from a file.+-- Returns the default flags if the file does not exist.+readCommandFlags :: FilePath -> CommandUI flags -> IO flags+readCommandFlags path command = do+  savedArgs <- liftM (fromMaybe []) (readSavedArgs path)+  case (commandParseArgs command True savedArgs) of+    CommandHelp _ -> throwIO (SavedArgsErrorHelp savedArgs)+    CommandList _ -> throwIO (SavedArgsErrorList savedArgs)+    CommandErrors errs -> throwIO (SavedArgsErrorOther savedArgs errs)+    CommandReadyToGo (mkFlags, _) ->+      return (mkFlags (commandDefaultFlags command))++-- -----------------------------------------------------------------------------+-- * Exceptions+-- -----------------------------------------------------------------------------++data SavedArgsError+    = SavedArgsErrorHelp Args+    | SavedArgsErrorList Args+    | SavedArgsErrorOther Args [String]+  deriving (Typeable)++instance Show SavedArgsError where+  show (SavedArgsErrorHelp args) =+    "unexpected flag '--help', saved command line was:\n"+    ++ intercalate " " args+  show (SavedArgsErrorList args) =+    "unexpected flag '--list-options', saved command line was:\n"+    ++ intercalate " " args+  show (SavedArgsErrorOther args errs) =+    "saved command line was:\n"+    ++ intercalate " " args ++ "\n"+    ++ "encountered errors:\n"+    ++ intercalate "\n" errs++instance Exception SavedArgsError
+ Distribution/Client/Security/DNS.hs view
@@ -0,0 +1,146 @@+module Distribution.Client.Security.DNS+    ( queryBootstrapMirrors+    ) where++import Prelude ()+import Distribution.Client.Compat.Prelude++import Control.Monad+import Control.DeepSeq (force)+import Control.Exception (SomeException, evaluate, try)+import Network.URI (URI(..), URIAuth(..), parseURI)++import Distribution.Simple.Utils+import Distribution.Verbosity+import Distribution.Simple.Program.Db+         ( emptyProgramDb, addKnownProgram+         , configureAllKnownPrograms, lookupProgram )+import Distribution.Simple.Program+         ( simpleProgram+         , programInvocation+         , getProgramInvocationOutput )+import Distribution.Compat.Exception (displayException)++-- | Try to lookup RFC1464-encoded mirror urls for a Hackage+-- repository url by performing a DNS TXT lookup on the+-- @_mirrors.@-prefixed URL hostname.+--+-- Example: for @http://hackage.haskell.org/@+-- perform a DNS TXT query for the hostname+-- @_mirrors.hackage.haskell.org@ which may look like e.g.+--+-- > _mirrors.hackage.haskell.org. 300 IN TXT+-- >    "0.urlbase=http://hackage.fpcomplete.com/"+-- >    "1.urlbase=http://objects-us-west-1.dream.io/hackage-mirror/"+--+-- NB: hackage-security doesn't require DNS lookups being trustworthy,+-- as the trust is established via the cryptographically signed TUF+-- meta-data that is retrieved from the resolved Hackage repository.+-- Moreover, we already have to protect against a compromised+-- @hackage.haskell.org@ DNS entry, so an the additional+-- @_mirrors.hackage.haskell.org@ DNS entry in the same SOA doesn't+-- constitute a significant new attack vector anyway.+--+queryBootstrapMirrors :: Verbosity -> URI -> IO [URI]+queryBootstrapMirrors verbosity repoUri+  | Just auth <- uriAuthority repoUri = do+        progdb <- configureAllKnownPrograms verbosity $+                  addKnownProgram nslookupProg emptyProgramDb++        case lookupProgram nslookupProg progdb of+          Nothing -> do+              warn verbosity "'nslookup' tool missing - can't locate mirrors"+              return []++          Just nslookup -> do+              let mirrorsDnsName = "_mirrors." ++ uriRegName auth++              mirrors' <- try $ do+                  out <- getProgramInvocationOutput verbosity $+                         programInvocation nslookup ["-query=TXT", mirrorsDnsName]+                  evaluate (force $ extractMirrors mirrorsDnsName out)++              mirrors <- case mirrors' of+                Left e -> do+                    warn verbosity ("Caught exception during _mirrors lookup:"+++                                    displayException (e :: SomeException))+                    return []+                Right v -> return v++              if null mirrors+              then warn verbosity ("No mirrors found for " ++ show repoUri)+              else do info verbosity ("located " ++ show (length mirrors) +++                                      " mirrors for " ++ show repoUri ++ " :")+                      forM_ mirrors $ \url -> info verbosity ("- " ++ show url)++              return mirrors++  | otherwise = return []+  where+    nslookupProg = simpleProgram "nslookup"++-- | Extract list of mirrors from @nslookup -query=TXT@ output.+extractMirrors :: String -> String -> [URI]+extractMirrors hostname s0 = mapMaybe (parseURI . snd) . sort $ vals+  where+    vals = [ (kn,v) | (h,ents) <- fromMaybe [] $ parseNsLookupTxt s0+                    , h == hostname+                    , e <- ents+                    , Just (k,v) <- [splitRfc1464 e]+                    , Just kn <- [isUrlBase k]+                    ]++    isUrlBase :: String -> Maybe Int+    isUrlBase s+      | isSuffixOf ".urlbase" s, not (null ns), all isDigit ns = readMaybe ns+      | otherwise = Nothing+      where+        ns = take (length s - 8) s++-- | Parse output of @nslookup -query=TXT $HOSTNAME@ tolerantly+parseNsLookupTxt :: String -> Maybe [(String,[String])]+parseNsLookupTxt = go0 [] []+  where+    -- approximate grammar:+    -- <entries> := { <entry> }+    -- (<entry> starts at begin of line, but may span multiple lines)+    -- <entry> := ^ <hostname> TAB "text =" { <qstring> }+    -- <qstring> := string enclosed by '"'s ('\' and '"' are \-escaped)++    -- scan for ^ <word> <TAB> "text ="+    go0 []  _  []                                = Nothing+    go0 res _  []                                = Just (reverse res)+    go0 res _  ('\n':xs)                         = go0 res [] xs+    go0 res lw ('\t':'t':'e':'x':'t':' ':'=':xs) = go1 res (reverse lw) [] (dropWhile isSpace xs)+    go0 res lw (x:xs)                            = go0 res (x:lw) xs++    -- collect at least one <qstring>+    go1 res lw qs ('"':xs) = case qstr "" xs of+      Just (s, xs') -> go1 res lw (s:qs) (dropWhile isSpace xs')+      Nothing       -> Nothing -- bad quoting+    go1 _   _  [] _  = Nothing -- missing qstring+    go1 res lw qs xs = go0 ((lw,reverse qs):res) [] xs++    qstr _   ('\n':_) = Nothing -- We don't support unquoted LFs+    qstr acc ('\\':'\\':cs) = qstr ('\\':acc) cs+    qstr acc ('\\':'"':cs)  = qstr ('"':acc) cs+    qstr acc ('"':cs) = Just (reverse acc, cs)+    qstr acc (c:cs)   = qstr (c:acc) cs+    qstr _   []       = Nothing++-- | Split a TXT string into key and value according to RFC1464.+-- Returns 'Nothing' if parsing fails.+splitRfc1464 :: String -> Maybe (String,String)+splitRfc1464 = go ""+  where+    go _ [] = Nothing+    go acc ('`':c:cs) = go (c:acc) cs+    go acc ('=':cs)   = go2 (reverse acc) "" cs+    go acc (c:cs)+      | isSpace c = go acc cs+      | otherwise = go (c:acc) cs++    go2 k acc [] = Just (k,reverse acc)+    go2 _ _   ['`'] = Nothing+    go2 k acc ('`':c:cs) = go2 k (c:acc) cs+    go2 k acc (c:cs) = go2 k (c:acc) cs
Distribution/Client/Setup.hs view
@@ -18,10 +18,11 @@     ( globalCommand, GlobalFlags(..), defaultGlobalFlags     , RepoContext(..), withRepoContext     , configureCommand, ConfigFlags(..), filterConfigureFlags+    , configPackageDB', configCompilerAux'     , configureExCommand, ConfigExFlags(..), defaultConfigExFlags-                        , configureExOptions     , buildCommand, BuildFlags(..), BuildExFlags(..), SkipAddSourceDepsCheck(..)     , replCommand, testCommand, benchmarkCommand+                        , configureExOptions, reconfigureCommand     , installCommand, InstallFlags(..), installOptions, defaultInstallFlags     , defaultSolver, defaultMaxBackjumps     , listCommand, ListFlags(..)@@ -32,10 +33,11 @@     , fetchCommand, FetchFlags(..)     , freezeCommand, FreezeFlags(..)     , genBoundsCommand+    , outdatedCommand, OutdatedFlags(..), IgnoreMajorVersionBumps(..)     , getCommand, unpackCommand, GetFlags(..)     , checkCommand     , formatCommand-    , uploadCommand, UploadFlags(..)+    , uploadCommand, UploadFlags(..), IsCandidate(..)     , reportCommand, ReportFlags(..)     , runCommand     , initCommand, IT.InitFlags(..)@@ -47,6 +49,7 @@     , userConfigCommand, UserConfigFlags(..)     , manpageCommand +    , applyFlagDefaults     , parsePackageArgs     --TODO: stop exporting these:     , showRepo@@ -54,12 +57,17 @@     , readRepo     ) where +import Prelude ()+import Distribution.Client.Compat.Prelude hiding (get)+ import Distribution.Client.Types          ( Username(..), Password(..), RemoteRepo(..) ) import Distribution.Client.BuildReports.Types          ( ReportLevel(..) ) import Distribution.Client.Dependency.Types-         ( PreSolver(..), ConstraintSource(..) )+         ( PreSolver(..) )+import Distribution.Client.IndexUtils.Timestamp+         ( IndexState ) import qualified Distribution.Client.Init.Types as IT          ( InitFlags(..), PackageType(..) ) import Distribution.Client.Targets@@ -67,40 +75,46 @@ import Distribution.Utils.NubList          ( NubList, toNubList, fromNubList) +import Distribution.Solver.Types.ConstraintSource+import Distribution.Solver.Types.Settings -import Distribution.Simple.Compiler (PackageDB)-import Distribution.Simple.Program-         ( defaultProgramConfiguration )+import Distribution.Simple.Compiler ( Compiler, PackageDB, PackageDBStack )+import Distribution.Simple.Program (ProgramDb, defaultProgramDb) import Distribution.Simple.Command hiding (boolOpt, boolOpt') import qualified Distribution.Simple.Command as Command-import Distribution.Simple.Configure ( computeEffectiveProfiling )+import Distribution.Simple.Configure+       ( configCompilerAuxEx, interpretPackageDbFlags, computeEffectiveProfiling ) import qualified Distribution.Simple.Setup as Cabal import Distribution.Simple.Setup          ( ConfigFlags(..), BuildFlags(..), ReplFlags          , TestFlags(..), BenchmarkFlags(..)          , SDistFlags(..), HaddockFlags(..)          , readPackageDbList, showPackageDbList-         , Flag(..), toFlag, flagToMaybe, flagToList-         , optionVerbosity, boolOpt, boolOpt', trueArg, falseArg-         , readPToMaybe, optionNumJobs )+         , Flag(..), toFlag, flagToMaybe, flagToList, maybeToFlag+         , BooleanFlag(..), optionVerbosity+         , boolOpt, boolOpt', trueArg, falseArg+         , optionNumJobs ) import Distribution.Simple.InstallDirs-         ( PathTemplate, InstallDirs(dynlibdir, sysconfdir)-         , toPathTemplate, fromPathTemplate )+         ( PathTemplate, InstallDirs(..)+         , toPathTemplate, fromPathTemplate, combinePathTemplate ) import Distribution.Version-         ( Version(Version), anyVersion, thisVersion )+         ( Version, mkVersion, nullVersion, anyVersion, thisVersion ) import Distribution.Package-         ( PackageIdentifier, packageName, packageVersion, Dependency(..) )+         ( PackageIdentifier, PackageName, packageName, packageVersion )+import Distribution.Types.Dependency import Distribution.PackageDescription          ( BuildType(..), RepoKind(..) )+import Distribution.System ( Platform ) import Distribution.Text          ( Text(..), display ) import Distribution.ReadE          ( ReadE(..), readP_to_E, succeedReadE ) import qualified Distribution.Compat.ReadP as Parse-         ( ReadP, char, munch1, pfail,  (+++) )-import Distribution.Compat.Semigroup+         ( ReadP, char, munch1, pfail, sepBy1, (+++) )+import Distribution.ParseUtils+         ( readPToMaybe ) import Distribution.Verbosity-         ( Verbosity, normal )+         ( Verbosity, lessVerbose, normal, verboseNoFlags ) import Distribution.Simple.Utils          ( wrapText, wrapLine ) import Distribution.Client.GlobalFlags@@ -108,21 +122,22 @@          , RepoContext(..), withRepoContext          ) -import Data.Char-         ( isAlphaNum ) import Data.List-         ( intercalate, deleteFirstsBy )-import Data.Maybe-         ( maybeToList, fromMaybe )-import GHC.Generics (Generic)-import Distribution.Compat.Binary (Binary)-import Control.Monad-         ( liftM )+         ( deleteFirstsBy ) import System.FilePath          ( (</>) ) import Network.URI          ( parseAbsoluteURI, uriToString ) +applyFlagDefaults :: (ConfigFlags, ConfigExFlags, InstallFlags, HaddockFlags)+                  -> (ConfigFlags, ConfigExFlags, InstallFlags, HaddockFlags)+applyFlagDefaults (configFlags, configExFlags, installFlags, haddockFlags) =+  ( commandDefaultFlags configureCommand <> configFlags+  , defaultConfigExFlags <> configExFlags+  , defaultInstallFlags <> installFlags+  , Cabal.defaultHaddockFlags <> haddockFlags+  )+ globalCommand :: [Command action] -> CommandUI GlobalFlags globalCommand commands = CommandUI {     commandName         = "",@@ -153,6 +168,7 @@           , "get"           , "init"           , "configure"+          , "reconfigure"           , "build"           , "clean"           , "run"@@ -165,12 +181,22 @@           , "report"           , "freeze"           , "gen-bounds"+          , "outdated"+          , "doctest"           , "haddock"           , "hscolour"           , "copy"           , "register"           , "sandbox"           , "exec"+          , "new-build"+          , "new-configure"+          , "new-repl"+          , "new-freeze"+          , "new-run"+          , "new-test"+          , "new-bench"+          , "new-haddock"           ]         maxlen    = maximum $ [length name | (name, _) <- cmdDescs]         align str = str ++ replicate (maxlen - length str) ' '@@ -216,15 +242,28 @@         , par         , addCmd "freeze"         , addCmd "gen-bounds"+        , addCmd "outdated"+        , addCmd "doctest"         , addCmd "haddock"         , addCmd "hscolour"         , addCmd "copy"         , addCmd "register"+        , addCmd "reconfigure"         , par         , startGroup "sandbox"         , addCmd "sandbox"         , addCmd "exec"         , addCmdCustom "repl" "Open interpreter with access to sandbox packages."+        , par+        , startGroup "new-style projects (beta)"+        , addCmd "new-build"+        , addCmd "new-configure"+        , addCmd "new-repl"+        , addCmd "new-run"+        , addCmd "new-test"+        , addCmd "new-bench"+        , addCmd "new-freeze"+        , addCmd "new-haddock"         ] ++ if null otherCmds then [] else par                                            :startGroup "other"                                            :[addCmd n | n <- otherCmds])@@ -294,6 +333,10 @@          "Set a transport for http(s) requests. Accepts 'curl', 'wget', 'powershell', and 'plain-http'. (default: 'curl')"          globalHttpTransport (\v flags -> flags { globalHttpTransport = v })          (reqArgFlag "HttpTransport")+      ,option [] ["nix"]+         "Nix integration: run commands through nix-shell if a 'shell.nix' file exists"+         globalNix (\v flags -> flags { globalNix = v })+         (boolOpt [] [])       ]      -- arguments we don't want shown in the help@@ -343,74 +386,100 @@        ++ "    with some package-specific flag.\n"   }  where-  c = Cabal.configureCommand defaultProgramConfiguration+  c = Cabal.configureCommand defaultProgramDb  configureOptions ::  ShowOrParseArgs -> [OptionField ConfigFlags] configureOptions = commandOptions configureCommand +-- | Given some 'ConfigFlags' for the version of Cabal that+-- cabal-install was built with, and a target older 'Version' of+-- Cabal that we want to pass these flags to, convert the+-- flags into a form that will be accepted by the older+-- Setup script.  Generally speaking, this just means filtering+-- out flags that the old Cabal library doesn't understand, but+-- in some cases it may also mean "emulating" a feature using+-- some more legacy flags. filterConfigureFlags :: ConfigFlags -> Version -> ConfigFlags filterConfigureFlags flags cabalLibVersion-  | cabalLibVersion >= Version [1,24,1] [] = flags_latest-  -- ^ NB: we expect the latest version to be the most common case.-  | cabalLibVersion <  Version [1,3,10] [] = flags_1_3_10-  | cabalLibVersion <  Version [1,10,0] [] = flags_1_10_0-  | cabalLibVersion <  Version [1,12,0] [] = flags_1_12_0-  | cabalLibVersion <  Version [1,14,0] [] = flags_1_14_0-  | cabalLibVersion <  Version [1,18,0] [] = flags_1_18_0-  | cabalLibVersion <  Version [1,19,1] [] = flags_1_19_0-  | cabalLibVersion <  Version [1,19,2] [] = flags_1_19_1-  | cabalLibVersion <  Version [1,21,1] [] = flags_1_20_0-  | cabalLibVersion <  Version [1,22,0] [] = flags_1_21_0-  | cabalLibVersion <  Version [1,23,0] [] = flags_1_22_0-  | cabalLibVersion <  Version [1,24,1] [] = flags_1_24_0+  -- NB: we expect the latest version to be the most common case,+  -- so test it first.+  | cabalLibVersion >= mkVersion [1,25,0] = flags_latest+  -- The naming convention is that flags_version gives flags with+  -- all flags *introduced* in version eliminated.+  -- It is NOT the latest version of Cabal library that+  -- these flags work for; version of introduction is a more+  -- natural metric.+  | cabalLibVersion < mkVersion [1,3,10] = flags_1_3_10+  | cabalLibVersion < mkVersion [1,10,0] = flags_1_10_0+  | cabalLibVersion < mkVersion [1,12,0] = flags_1_12_0+  | cabalLibVersion < mkVersion [1,14,0] = flags_1_14_0+  | cabalLibVersion < mkVersion [1,18,0] = flags_1_18_0+  | cabalLibVersion < mkVersion [1,19,1] = flags_1_19_1+  | cabalLibVersion < mkVersion [1,19,2] = flags_1_19_2+  | cabalLibVersion < mkVersion [1,21,1] = flags_1_21_1+  | cabalLibVersion < mkVersion [1,22,0] = flags_1_22_0+  | cabalLibVersion < mkVersion [1,23,0] = flags_1_23_0+  | cabalLibVersion < mkVersion [1,25,0] = flags_1_25_0   | otherwise = flags_latest   where-    (profEnabledLib, profEnabledExe) = computeEffectiveProfiling flags     flags_latest = flags        {       -- Cabal >= 1.19.1 uses '--dependency' and does not need '--constraint'.       configConstraints = [],-      -- Passing '--allow-newer' to Setup.hs is unnecessary, we use+      -- Passing '--allow-{older,newer}' to Setup.hs is unnecessary, we use       -- '--exact-configuration' instead.-      configAllowNewer  = Just Cabal.AllowNewerNone+      configAllowOlder  = Just (Cabal.AllowOlder Cabal.RelaxDepsNone),+      configAllowNewer  = Just (Cabal.AllowNewer Cabal.RelaxDepsNone)       } -    -- Cabal < 1.24.1 doesn't know about --dynlibdir.-    flags_1_24_0 = flags_latest { configInstallDirs = configInstallDirs_1_24_0}-    configInstallDirs_1_24_0 = (configInstallDirs flags) { dynlibdir = NoFlag }-+    flags_1_25_0 = flags_latest {+      -- Cabal < 1.25.0 doesn't know about --dynlibdir.+      configInstallDirs = configInstallDirs_1_25_0,+      -- Cabal < 1.25 doesn't have extended verbosity syntax+      configVerbosity   = fmap verboseNoFlags (configVerbosity flags_latest),+      -- Cabal < 1.25 doesn't support --deterministic+      configDeterministic = mempty+      }+    configInstallDirs_1_25_0 = let dirs = configInstallDirs flags in+        dirs { dynlibdir = NoFlag+             , libexecsubdir = NoFlag+             , libexecdir = maybeToFlag $+                 combinePathTemplate <$> flagToMaybe (libexecdir dirs)+                                     <*> flagToMaybe (libexecsubdir dirs)+             }     -- Cabal < 1.23 doesn't know about '--profiling-detail'.     -- Cabal < 1.23 has a hacked up version of 'enable-profiling'     -- which we shouldn't use.-    flags_1_22_0 = flags_1_24_0 { configProfDetail    = NoFlag+    (tryLibProfiling, tryExeProfiling) = computeEffectiveProfiling flags+    flags_1_23_0 = flags_1_25_0 { configProfDetail    = NoFlag                                 , configProfLibDetail = NoFlag                                 , configIPID          = NoFlag                                 , configProf          = NoFlag-                                , configProfExe       = Flag profEnabledExe-                                , configProfLib       = Flag profEnabledLib+                                , configProfExe       = Flag tryExeProfiling+                                , configProfLib       = Flag tryLibProfiling                                 }      -- Cabal < 1.22 doesn't know about '--disable-debug-info'.-    flags_1_21_0 = flags_1_22_0 { configDebugInfo = NoFlag }+    flags_1_22_0 = flags_1_23_0 { configDebugInfo = NoFlag }      -- Cabal < 1.21.1 doesn't know about 'disable-relocatable'     -- Cabal < 1.21.1 doesn't know about 'enable-profiling'-    -- (but we already dealt with it in flags_1_22_0)-    flags_1_20_0 =-      flags_1_21_0 { configRelocatable = NoFlag+    -- (but we already dealt with it in flags_1_23_0)+    flags_1_21_1 =+      flags_1_22_0 { configRelocatable = NoFlag                    , configCoverage = NoFlag                    , configLibCoverage = configCoverage flags                    }     -- Cabal < 1.19.2 doesn't know about '--exact-configuration' and     -- '--enable-library-stripping'.-    flags_1_19_1 = flags_1_20_0 { configExactConfiguration = NoFlag+    flags_1_19_2 = flags_1_21_1 { configExactConfiguration = NoFlag                                 , configStripLibs = NoFlag }     -- Cabal < 1.19.1 uses '--constraint' instead of '--dependency'.-    flags_1_19_0 = flags_1_19_1 { configDependencies = []+    flags_1_19_1 = flags_1_19_2 { configDependencies = []                                 , configConstraints  = configConstraints flags }     -- Cabal < 1.18.0 doesn't know about --extra-prog-path and --sysconfdir.-    flags_1_18_0 = flags_1_19_0 { configProgramPathExtra = toNubList []+    flags_1_18_0 = flags_1_19_1 { configProgramPathExtra = toNubList []                                 , configInstallDirs = configInstallDirs_1_18_0}-    configInstallDirs_1_18_0 = (configInstallDirs flags_1_19_0) { sysconfdir = NoFlag }+    configInstallDirs_1_18_0 = (configInstallDirs flags_1_19_1) { sysconfdir = NoFlag }     -- Cabal < 1.14.0 doesn't know about '--disable-benchmarks'.     flags_1_14_0 = flags_1_18_0 { configBenchmarks  = NoFlag }     -- Cabal < 1.12.0 doesn't know about '--enable/disable-executable-dynamic'@@ -422,6 +491,21 @@     -- Cabal < 1.3.10 does not grok the '--constraints' flag.     flags_1_3_10 = flags_1_10_0 { configConstraints = [] } +-- | Get the package database settings from 'ConfigFlags', accounting for+-- @--package-db@ and @--user@ flags.+configPackageDB' :: ConfigFlags -> PackageDBStack+configPackageDB' cfg =+    interpretPackageDbFlags userInstall (configPackageDBs cfg)+  where+    userInstall = Cabal.fromFlagOrDefault True (configUserInstall cfg)++-- | Configure the compiler, but reduce verbosity during this step.+configCompilerAux' :: ConfigFlags -> IO (Compiler, Platform, ProgramDb)+configCompilerAux' configFlags =+  configCompilerAuxEx configFlags+    --FIXME: make configCompilerAux use a sensible verbosity+    { configVerbosity = fmap lessVerbose (configVerbosity configFlags) }+ -- ------------------------------------------------------------ -- * Config extra flags -- ------------------------------------------------------------@@ -490,6 +574,28 @@ instance Semigroup ConfigExFlags where   (<>) = gmappend +reconfigureCommand :: CommandUI (ConfigFlags, ConfigExFlags)+reconfigureCommand+  = configureExCommand+    { commandName         = "reconfigure"+    , commandSynopsis     = "Reconfigure the package if necessary."+    , commandDescription  = Just $ \pname -> wrapText $+         "Run `configure` with the most recently used flags, or append FLAGS "+         ++ "to the most recently used configuration. "+         ++ "Accepts the same flags as `" ++ pname ++ " configure'. "+         ++ "If the package has never been configured, the default flags are "+         ++ "used."+    , commandNotes        = Just $ \pname ->+        "Examples:\n"+        ++ "  " ++ pname ++ " reconfigure\n"+        ++ "    Configure with the most recently used flags.\n"+        ++ "  " ++ pname ++ " reconfigure -w PATH\n"+        ++ "    Reconfigure with the most recently used flags,\n"+        ++ "    but use the compiler at PATH.\n\n"+    , commandUsage        = usageAlternatives "reconfigure" [ "[FLAGS]" ]+    , commandDefaultFlags = mempty+    }+ -- ------------------------------------------------------------ -- * Build flags -- ------------------------------------------------------------@@ -524,7 +630,7 @@     setFst a (_,b) = (a,b)     setSnd b (a,_) = (a,b) -    parent = Cabal.buildCommand defaultProgramConfiguration+    parent = Cabal.buildCommand defaultProgramDb  instance Monoid BuildExFlags where   mempty = gmempty@@ -550,7 +656,7 @@     setFst a (_,b) = (a,b)     setSnd b (a,_) = (a,b) -    parent = Cabal.replCommand defaultProgramConfiguration+    parent = Cabal.replCommand defaultProgramDb  -- ------------------------------------------------------------ -- * Test command@@ -565,7 +671,7 @@                         (commandOptions parent showOrParseArgs)                         ++                         liftOptions get2 set2-                        (Cabal.buildOptions progConf showOrParseArgs)+                        (Cabal.buildOptions progDb showOrParseArgs)                         ++                         liftOptions get3 set3 (buildExOptions showOrParseArgs)   }@@ -574,8 +680,8 @@     get2 (_,b,_) = b; set2 b (a,_,c) = (a,b,c)     get3 (_,_,c) = c; set3 c (a,b,_) = (a,b,c) -    parent   = Cabal.testCommand-    progConf = defaultProgramConfiguration+    parent = Cabal.testCommand+    progDb = defaultProgramDb  -- ------------------------------------------------------------ -- * Bench command@@ -590,7 +696,7 @@                         (commandOptions parent showOrParseArgs)                         ++                         liftOptions get2 set2-                        (Cabal.buildOptions progConf showOrParseArgs)+                        (Cabal.buildOptions progDb showOrParseArgs)                         ++                         liftOptions get3 set3 (buildExOptions showOrParseArgs)   }@@ -599,8 +705,8 @@     get2 (_,b,_) = b; set2 b (a,_,c) = (a,b,c)     get3 (_,_,c) = c; set3 c (a,b,_) = (a,b,c) -    parent   = Cabal.benchmarkCommand-    progConf = defaultProgramConfiguration+    parent = Cabal.benchmarkCommand+    progDb = defaultProgramDb  -- ------------------------------------------------------------ -- * Fetch command@@ -612,10 +718,12 @@       fetchDryRun    :: Flag Bool,       fetchSolver           :: Flag PreSolver,       fetchMaxBackjumps     :: Flag Int,-      fetchReorderGoals     :: Flag Bool,-      fetchIndependentGoals :: Flag Bool,-      fetchShadowPkgs       :: Flag Bool,-      fetchStrongFlags      :: Flag Bool,+      fetchReorderGoals     :: Flag ReorderGoals,+      fetchCountConflicts   :: Flag CountConflicts,+      fetchIndependentGoals :: Flag IndependentGoals,+      fetchShadowPkgs       :: Flag ShadowPkgs,+      fetchStrongFlags      :: Flag StrongFlags,+      fetchAllowBootLibInstalls :: Flag AllowBootLibInstalls,       fetchVerbosity :: Flag Verbosity     } @@ -626,10 +734,12 @@     fetchDryRun    = toFlag False,     fetchSolver           = Flag defaultSolver,     fetchMaxBackjumps     = Flag defaultMaxBackjumps,-    fetchReorderGoals     = Flag False,-    fetchIndependentGoals = Flag False,-    fetchShadowPkgs       = Flag False,-    fetchStrongFlags      = Flag False,+    fetchReorderGoals     = Flag (ReorderGoals False),+    fetchCountConflicts   = Flag (CountConflicts True),+    fetchIndependentGoals = Flag (IndependentGoals False),+    fetchShadowPkgs       = Flag (ShadowPkgs False),+    fetchStrongFlags      = Flag (StrongFlags False),+    fetchAllowBootLibInstalls = Flag (AllowBootLibInstalls False),     fetchVerbosity = toFlag normal    } @@ -673,9 +783,11 @@        optionSolverFlags showOrParseArgs                          fetchMaxBackjumps     (\v flags -> flags { fetchMaxBackjumps     = v })                          fetchReorderGoals     (\v flags -> flags { fetchReorderGoals     = v })+                         fetchCountConflicts   (\v flags -> flags { fetchCountConflicts   = v })                          fetchIndependentGoals (\v flags -> flags { fetchIndependentGoals = v })                          fetchShadowPkgs       (\v flags -> flags { fetchShadowPkgs       = v })                          fetchStrongFlags      (\v flags -> flags { fetchStrongFlags      = v })+                         fetchAllowBootLibInstalls (\v flags -> flags { fetchAllowBootLibInstalls = v })    } @@ -689,10 +801,12 @@       freezeBenchmarks       :: Flag Bool,       freezeSolver           :: Flag PreSolver,       freezeMaxBackjumps     :: Flag Int,-      freezeReorderGoals     :: Flag Bool,-      freezeIndependentGoals :: Flag Bool,-      freezeShadowPkgs       :: Flag Bool,-      freezeStrongFlags      :: Flag Bool,+      freezeReorderGoals     :: Flag ReorderGoals,+      freezeCountConflicts   :: Flag CountConflicts,+      freezeIndependentGoals :: Flag IndependentGoals,+      freezeShadowPkgs       :: Flag ShadowPkgs,+      freezeStrongFlags      :: Flag StrongFlags,+      freezeAllowBootLibInstalls :: Flag AllowBootLibInstalls,       freezeVerbosity        :: Flag Verbosity     } @@ -703,10 +817,12 @@     freezeBenchmarks       = toFlag False,     freezeSolver           = Flag defaultSolver,     freezeMaxBackjumps     = Flag defaultMaxBackjumps,-    freezeReorderGoals     = Flag False,-    freezeIndependentGoals = Flag False,-    freezeShadowPkgs       = Flag False,-    freezeStrongFlags      = Flag False,+    freezeReorderGoals     = Flag (ReorderGoals False),+    freezeCountConflicts   = Flag (CountConflicts True),+    freezeIndependentGoals = Flag (IndependentGoals False),+    freezeShadowPkgs       = Flag (ShadowPkgs False),+    freezeStrongFlags      = Flag (StrongFlags False),+    freezeAllowBootLibInstalls = Flag (AllowBootLibInstalls False),     freezeVerbosity        = toFlag normal    } @@ -726,7 +842,8 @@     commandUsage        = usageFlags "freeze",     commandDefaultFlags = defaultFreezeFlags,     commandOptions      = \ showOrParseArgs -> [-         optionVerbosity freezeVerbosity (\v flags -> flags { freezeVerbosity = v })+         optionVerbosity freezeVerbosity+         (\v flags -> flags { freezeVerbosity = v })         , option [] ["dry-run"]            "Do not freeze anything, only print what would be frozen"@@ -734,34 +851,44 @@            trueArg         , option [] ["tests"]-           "freezing of the dependencies of any tests suites in the package description file."+           ("freezing of the dependencies of any tests suites "+            ++ "in the package description file.")            freezeTests (\v flags -> flags { freezeTests = v })            (boolOpt [] [])         , option [] ["benchmarks"]-           "freezing of the dependencies of any benchmarks suites in the package description file."+           ("freezing of the dependencies of any benchmarks suites "+            ++ "in the package description file.")            freezeBenchmarks (\v flags -> flags { freezeBenchmarks = v })            (boolOpt [] [])         ] ++ -       optionSolver      freezeSolver           (\v flags -> flags { freezeSolver           = v }) :+       optionSolver+         freezeSolver           (\v flags -> flags { freezeSolver           = v }):        optionSolverFlags showOrParseArgs                          freezeMaxBackjumps     (\v flags -> flags { freezeMaxBackjumps     = v })                          freezeReorderGoals     (\v flags -> flags { freezeReorderGoals     = v })+                         freezeCountConflicts   (\v flags -> flags { freezeCountConflicts   = v })                          freezeIndependentGoals (\v flags -> flags { freezeIndependentGoals = v })                          freezeShadowPkgs       (\v flags -> flags { freezeShadowPkgs       = v })                          freezeStrongFlags      (\v flags -> flags { freezeStrongFlags      = v })+                         freezeAllowBootLibInstalls (\v flags -> flags { freezeAllowBootLibInstalls = v })    } +-- ------------------------------------------------------------+-- * 'gen-bounds' command+-- ------------------------------------------------------------+ genBoundsCommand :: CommandUI FreezeFlags genBoundsCommand = CommandUI {     commandName         = "gen-bounds",     commandSynopsis     = "Generate dependency bounds.",     commandDescription  = Just $ \_ -> wrapText $          "Generates bounds for all dependencies that do not currently have them. "-      ++ "Generated bounds are printed to stdout.  You can then paste them into your .cabal file.\n"+      ++ "Generated bounds are printed to stdout.  "+      ++ "You can then paste them into your .cabal file.\n"       ++ "\n",     commandNotes        = Nothing,     commandUsage        = usageFlags "gen-bounds",@@ -772,6 +899,116 @@   }  -- ------------------------------------------------------------+-- * 'outdated' command+-- ------------------------------------------------------------++data IgnoreMajorVersionBumps = IgnoreMajorVersionBumpsNone+                             | IgnoreMajorVersionBumpsAll+                             | IgnoreMajorVersionBumpsSome [PackageName]++instance Monoid IgnoreMajorVersionBumps where+  mempty  = IgnoreMajorVersionBumpsNone+  mappend = (<>)++instance Semigroup IgnoreMajorVersionBumps where+  IgnoreMajorVersionBumpsNone       <> r                               = r+  l@IgnoreMajorVersionBumpsAll      <> _                               = l+  l@(IgnoreMajorVersionBumpsSome _) <> IgnoreMajorVersionBumpsNone     = l+  (IgnoreMajorVersionBumpsSome   _) <> r@IgnoreMajorVersionBumpsAll    = r+  (IgnoreMajorVersionBumpsSome   a) <> (IgnoreMajorVersionBumpsSome b) =+    IgnoreMajorVersionBumpsSome (a ++ b)++data OutdatedFlags = OutdatedFlags {+  outdatedVerbosity     :: Flag Verbosity,+  outdatedFreezeFile    :: Flag Bool,+  outdatedNewFreezeFile :: Flag Bool,+  outdatedSimpleOutput  :: Flag Bool,+  outdatedExitCode      :: Flag Bool,+  outdatedQuiet         :: Flag Bool,+  outdatedIgnore        :: [PackageName],+  outdatedMinor         :: Maybe IgnoreMajorVersionBumps+  }++defaultOutdatedFlags :: OutdatedFlags+defaultOutdatedFlags = OutdatedFlags {+  outdatedVerbosity     = toFlag normal,+  outdatedFreezeFile    = mempty,+  outdatedNewFreezeFile = mempty,+  outdatedSimpleOutput  = mempty,+  outdatedExitCode      = mempty,+  outdatedQuiet         = mempty,+  outdatedIgnore        = mempty,+  outdatedMinor         = mempty+  }++outdatedCommand :: CommandUI OutdatedFlags+outdatedCommand = CommandUI {+  commandName = "outdated",+  commandSynopsis = "Check for outdated dependencies",+  commandDescription  = Just $ \_ -> wrapText $+    "Checks for outdated dependencies in the package description file "+    ++ "or freeze file",+  commandNotes = Nothing,+  commandUsage = usageFlags "outdated",+  commandDefaultFlags = defaultOutdatedFlags,+  commandOptions      = \ _ -> [+    optionVerbosity outdatedVerbosity+      (\v flags -> flags { outdatedVerbosity = v })++    ,option [] ["freeze-file"]+     "Act on the freeze file"+     outdatedFreezeFile (\v flags -> flags { outdatedFreezeFile = v })+     trueArg++    ,option [] ["new-freeze-file"]+     "Act on the new-style freeze file"+     outdatedNewFreezeFile (\v flags -> flags { outdatedNewFreezeFile = v })+     trueArg++    ,option [] ["simple-output"]+     "Only print names of outdated dependencies, one per line"+     outdatedSimpleOutput (\v flags -> flags { outdatedSimpleOutput = v })+     trueArg++    ,option [] ["exit-code"]+     "Exit with non-zero when there are outdated dependencies"+     outdatedExitCode (\v flags -> flags { outdatedExitCode = v })+     trueArg++    ,option ['q'] ["quiet"]+     "Don't print any output. Implies '--exit-code' and '-v0'"+     outdatedQuiet (\v flags -> flags { outdatedQuiet = v })+     trueArg++   ,option [] ["ignore"]+    "Packages to ignore"+    outdatedIgnore (\v flags -> flags { outdatedIgnore = v })+    (reqArg "PKGS" pkgNameListParser (map display))++   ,option [] ["minor"]+    "Ignore major version bumps for these packages"+    outdatedMinor (\v flags -> flags { outdatedMinor = v })+    (optArg "PKGS" ignoreMajorVersionBumpsParser+      (Just IgnoreMajorVersionBumpsAll) ignoreMajorVersionBumpsPrinter)+   ]+  }+  where+    ignoreMajorVersionBumpsPrinter :: (Maybe IgnoreMajorVersionBumps)+                                   -> [Maybe String]+    ignoreMajorVersionBumpsPrinter Nothing = []+    ignoreMajorVersionBumpsPrinter (Just IgnoreMajorVersionBumpsNone)= []+    ignoreMajorVersionBumpsPrinter (Just IgnoreMajorVersionBumpsAll) = [Nothing]+    ignoreMajorVersionBumpsPrinter (Just (IgnoreMajorVersionBumpsSome pkgs)) =+      map (Just . display) $ pkgs++    ignoreMajorVersionBumpsParser  =+      (Just . IgnoreMajorVersionBumpsSome) `fmap` pkgNameListParser++    pkgNameListParser = readP_to_E+      ("Couldn't parse the list of package names: " ++)+      (Parse.sepBy1 parse (Parse.char ','))++-- ------------------------------------------------------------ -- * Other commands -- ------------------------------------------------------------ @@ -892,7 +1129,7 @@     setFst a (_,b) = (a,b)     setSnd b (a,_) = (a,b) -    parent = Cabal.buildCommand defaultProgramConfiguration+    parent = Cabal.buildCommand defaultProgramDb  -- ------------------------------------------------------------ -- * Report flags@@ -951,6 +1188,7 @@ data GetFlags = GetFlags {     getDestDir          :: Flag FilePath,     getPristine         :: Flag Bool,+    getIndexState       :: Flag IndexState,     getSourceRepository :: Flag (Maybe RepoKind),     getVerbosity        :: Flag Verbosity   } deriving Generic@@ -959,6 +1197,7 @@ defaultGetFlags = GetFlags {     getDestDir          = mempty,     getPristine         = mempty,+    getIndexState       = mempty,     getSourceRepository = mempty,     getVerbosity        = toFlag normal    }@@ -996,6 +1235,20 @@                                   (Flag Nothing)                                   (map (fmap show) . flagToList)) +      , option [] ["index-state"]+          ("Use source package index state as it existed at a previous time. " +++           "Accepts unix-timestamps (e.g. '@1474732068'), ISO8601 UTC timestamps " +++           "(e.g. '2016-09-24T17:47:48Z'), or 'HEAD' (default: 'HEAD'). " +++           "This determines which package versions are available as well as " +++           ".cabal file revision is selected (unless --pristine is used).")+          getIndexState (\v flags -> flags { getIndexState = v })+          (reqArg "STATE" (readP_to_E (const $ "index-state must be a  " +++                                       "unix-timestamps (e.g. '@1474732068'), " +++                                       "a ISO8601 UTC timestamp " +++                                       "(e.g. '2016-09-24T17:47:48Z'), or 'HEAD'")+                                      (toFlag `fmap` parse))+                          (flagToList . fmap display))+        , option [] ["pristine"]            ("Unpack the original pristine tarball, rather than updating the "            ++ ".cabal file with the latest revision from the package archive.")@@ -1150,26 +1403,39 @@     installHaddockIndex     :: Flag PathTemplate,     installDryRun           :: Flag Bool,     installMaxBackjumps     :: Flag Int,-    installReorderGoals     :: Flag Bool,-    installIndependentGoals :: Flag Bool,-    installShadowPkgs       :: Flag Bool,-    installStrongFlags      :: Flag Bool,+    installReorderGoals     :: Flag ReorderGoals,+    installCountConflicts   :: Flag CountConflicts,+    installIndependentGoals :: Flag IndependentGoals,+    installShadowPkgs       :: Flag ShadowPkgs,+    installStrongFlags      :: Flag StrongFlags,+    installAllowBootLibInstalls :: Flag AllowBootLibInstalls,     installReinstall        :: Flag Bool,-    installAvoidReinstalls  :: Flag Bool,+    installAvoidReinstalls  :: Flag AvoidReinstalls,     installOverrideReinstall :: Flag Bool,     installUpgradeDeps      :: Flag Bool,     installOnly             :: Flag Bool,     installOnlyDeps         :: Flag Bool,+    installIndexState       :: Flag IndexState,     installRootCmd          :: Flag String,     installSummaryFile      :: NubList PathTemplate,     installLogFile          :: Flag PathTemplate,     installBuildReports     :: Flag ReportLevel,     installReportPlanningFailure :: Flag Bool,     installSymlinkBinDir    :: Flag FilePath,+    installPerComponent     :: Flag Bool,     installOneShot          :: Flag Bool,     installNumJobs          :: Flag (Maybe Int),+    installKeepGoing        :: Flag Bool,     installRunTests         :: Flag Bool,-    installOfflineMode      :: Flag Bool+    installOfflineMode      :: Flag Bool,+    -- | The cabal project file name; defaults to @cabal.project@.+    -- Th name itself denotes the cabal project file name, but it also+    -- is the base of auxiliary project files, such as+    -- @cabal.project.local@ and @cabal.project.freeze@ which are also+    -- read and written out in some cases.  If the path is not found+    -- in the current working directory, we will successively probe+    -- relative to parent directories until this name is found.+    installProjectFileName   :: Flag FilePath   }   deriving (Eq, Generic) @@ -1181,26 +1447,32 @@     installHaddockIndex    = Flag docIndexFile,     installDryRun          = Flag False,     installMaxBackjumps    = Flag defaultMaxBackjumps,-    installReorderGoals    = Flag False,-    installIndependentGoals= Flag False,-    installShadowPkgs      = Flag False,-    installStrongFlags     = Flag False,+    installReorderGoals    = Flag (ReorderGoals False),+    installCountConflicts  = Flag (CountConflicts True),+    installIndependentGoals= Flag (IndependentGoals False),+    installShadowPkgs      = Flag (ShadowPkgs False),+    installStrongFlags     = Flag (StrongFlags False),+    installAllowBootLibInstalls = Flag (AllowBootLibInstalls False),     installReinstall       = Flag False,-    installAvoidReinstalls = Flag False,+    installAvoidReinstalls = Flag (AvoidReinstalls False),     installOverrideReinstall = Flag False,     installUpgradeDeps     = Flag False,     installOnly            = Flag False,     installOnlyDeps        = Flag False,+    installIndexState      = mempty,     installRootCmd         = mempty,     installSummaryFile     = mempty,     installLogFile         = mempty,     installBuildReports    = Flag NoReports,     installReportPlanningFailure = Flag False,     installSymlinkBinDir   = mempty,+    installPerComponent    = Flag True,     installOneShot         = Flag False,     installNumJobs         = mempty,+    installKeepGoing       = Flag False,     installRunTests        = mempty,-    installOfflineMode     = Flag False+    installOfflineMode     = Flag False,+    installProjectFileName = mempty   }   where     docIndexFile = toPathTemplate ("$datadir" </> "doc"@@ -1210,7 +1482,7 @@ defaultMaxBackjumps = 2000  defaultSolver :: PreSolver-defaultSolver = Choose+defaultSolver = AlwaysModular  allSolvers :: String allSolvers = intercalate ", " (map display ([minBound .. maxBound] :: [PreSolver]))@@ -1251,7 +1523,7 @@      ++ " continue working as long as bindir and datadir are left untouched.",   commandNotes        = Just $ \pname ->         ( case commandNotes-               $ Cabal.configureCommand defaultProgramConfiguration+               $ Cabal.configureCommand defaultProgramDb           of Just desc -> desc pname ++ "\n"              Nothing   -> ""         )@@ -1326,9 +1598,11 @@       optionSolverFlags showOrParseArgs                         installMaxBackjumps     (\v flags -> flags { installMaxBackjumps     = v })                         installReorderGoals     (\v flags -> flags { installReorderGoals     = v })+                        installCountConflicts   (\v flags -> flags { installCountConflicts   = v })                         installIndependentGoals (\v flags -> flags { installIndependentGoals = v })                         installShadowPkgs       (\v flags -> flags { installShadowPkgs       = v })-                        installStrongFlags      (\v flags -> flags { installStrongFlags      = v }) +++                        installStrongFlags      (\v flags -> flags { installStrongFlags      = v })+                        installAllowBootLibInstalls (\v flags -> flags { installAllowBootLibInstalls = v }) ++        [ option [] ["reinstall"]           "Install even if it means installing the same version again."@@ -1337,7 +1611,8 @@        , option [] ["avoid-reinstalls"]           "Do not select versions that would destructively overwrite installed packages."-          installAvoidReinstalls (\v flags -> flags { installAvoidReinstalls = v })+          (fmap asBool . installAvoidReinstalls)+          (\v flags -> flags { installAvoidReinstalls = fmap AvoidReinstalls v })           (yesNoOpt showOrParseArgs)        , option [] ["force-reinstalls"]@@ -1360,8 +1635,20 @@           installOnlyDeps (\v flags -> flags { installOnlyDeps = v })           (yesNoOpt showOrParseArgs) +      , option [] ["index-state"]+          ("Use source package index state as it existed at a previous time. " +++           "Accepts unix-timestamps (e.g. '@1474732068'), ISO8601 UTC timestamps " +++           "(e.g. '2016-09-24T17:47:48Z'), or 'HEAD' (default: 'HEAD').")+          installIndexState (\v flags -> flags { installIndexState = v })+          (reqArg "STATE" (readP_to_E (const $ "index-state must be a  " +++                                       "unix-timestamps (e.g. '@1474732068'), " +++                                       "a ISO8601 UTC timestamp " +++                                       "(e.g. '2016-09-24T17:47:48Z'), or 'HEAD'")+                                      (toFlag `fmap` parse))+                          (flagToList . fmap display))+       , option [] ["root-cmd"]-          "Command used to gain root privileges, when installing with --global."+          "(No longer supported, do not use.)"           installRootCmd (\v flags -> flags { installRootCmd = v })           (reqArg' "COMMAND" toFlag flagToList) @@ -1394,6 +1681,11 @@           installReportPlanningFailure (\v flags -> flags { installReportPlanningFailure = v })           trueArg +      , option "" ["per-component"]+          "Per-component builds when possible"+          installPerComponent (\v flags -> flags { installPerComponent = v })+          (boolOpt [] [])+       , option [] ["one-shot"]           "Do not record the packages in the world file."           installOneShot (\v flags -> flags { installOneShot = v })@@ -1407,10 +1699,20 @@       , optionNumJobs         installNumJobs (\v flags -> flags { installNumJobs = v }) +      , option [] ["keep-going"]+          "After a build failure, continue to build other unaffected packages."+          installKeepGoing (\v flags -> flags { installKeepGoing = v })+          trueArg+       , option [] ["offline"]           "Don't download packages from the Internet."           installOfflineMode (\v flags -> flags { installOfflineMode = v })           (yesNoOpt showOrParseArgs)++      , option [] ["project-file"]+          "Set the name of the cabal.project file to search for in parent directories"+          installProjectFileName (\v flags -> flags {installProjectFileName = v})+          (reqArgFlag "FILE")       ] ++ case showOrParseArgs of      -- TODO: remove when "cabal install"                                         -- avoids           ParseArgs ->@@ -1432,8 +1734,12 @@ -- * Upload flags -- ------------------------------------------------------------ +-- | Is this a candidate package or a package to be published?+data IsCandidate = IsCandidate | IsPublished+                 deriving Eq+ data UploadFlags = UploadFlags {-    uploadCheck       :: Flag Bool,+    uploadCandidate   :: Flag IsCandidate,     uploadDoc         :: Flag Bool,     uploadUsername    :: Flag Username,     uploadPassword    :: Flag Password,@@ -1443,7 +1749,7 @@  defaultUploadFlags :: UploadFlags defaultUploadFlags = UploadFlags {-    uploadCheck       = toFlag False,+    uploadCandidate   = toFlag IsCandidate,     uploadDoc         = toFlag False,     uploadUsername    = mempty,     uploadPassword    = mempty,@@ -1463,15 +1769,19 @@          "Usage: " ++ pname ++ " upload [FLAGS] TARFILES\n",     commandDefaultFlags = defaultUploadFlags,     commandOptions      = \_ ->-      [optionVerbosity uploadVerbosity (\v flags -> flags { uploadVerbosity = v })+      [optionVerbosity uploadVerbosity+       (\v flags -> flags { uploadVerbosity = v }) -      ,option ['c'] ["check"]-         "Do not upload, just do QA checks."-        uploadCheck (\v flags -> flags { uploadCheck = v })-        trueArg+      ,option [] ["publish"]+        "Publish the package instead of uploading it as a candidate."+        uploadCandidate (\v flags -> flags { uploadCandidate = v })+        (noArg (Flag IsPublished))        ,option ['d'] ["documentation"]-        "Upload documentation instead of a source package. Cannot be used together with --check."+        ("Upload documentation instead of a source package. "+        ++ "By default, this uploads documentation for a package candidate. "+        ++ "To upload documentation for "+        ++ "a published package, combine with --publish.")         uploadDoc (\v flags -> flags { uploadDoc = v })         trueArg @@ -1683,9 +1993,6 @@       , optionVerbosity IT.initVerbosity (\v flags -> flags { IT.initVerbosity = v })       ]   }-  where readMaybe s = case reads s of-                        [(x,"")]  -> Just x-                        _         -> Nothing  -- ------------------------------------------------------------ -- * SDist flags@@ -1698,7 +2005,7 @@   }   deriving (Show, Generic) -data ArchiveFormat = TargzFormat | ZipFormat -- | ...+data ArchiveFormat = TargzFormat | ZipFormat -- ...   deriving (Show, Eq)  defaultSDistExFlags :: SDistExFlags@@ -1936,12 +2243,14 @@ -- ------------------------------------------------------------  data ExecFlags = ExecFlags {-  execVerbosity :: Flag Verbosity+  execVerbosity :: Flag Verbosity,+  execDistPref  :: Flag FilePath } deriving Generic  defaultExecFlags :: ExecFlags defaultExecFlags = ExecFlags {-  execVerbosity = toFlag normal+  execVerbosity = toFlag normal,+  execDistPref  = NoFlag   }  execCommand :: CommandUI ExecFlags@@ -1982,9 +2291,12 @@        "Usage: " ++ pname ++ " exec [FLAGS] [--] COMMAND [--] [ARGS]\n",    commandDefaultFlags = defaultExecFlags,-  commandOptions      = \_ ->+  commandOptions      = \showOrParseArgs ->     [ optionVerbosity execVerbosity       (\v flags -> flags { execVerbosity = v })+    , Cabal.optionDistPref+       execDistPref (\d flags -> flags { execDistPref = d })+       showOrParseArgs     ]   } @@ -2067,7 +2379,7 @@              -> OptionField flags optionSolver get set =   option [] ["solver"]-    ("Select dependency solver to use (default: " ++ display defaultSolver ++ "). Choices: " ++ allSolvers ++ ", where 'choose' chooses between 'topdown' and 'modular' based on compiler version.")+    ("Select dependency solver to use (default: " ++ display defaultSolver ++ "). Choices: " ++ allSolvers ++ ".")     get set     (reqArg "SOLVER" (readP_to_E (const $ "solver must be one of: " ++ allSolvers)                                  (toFlag `fmap` parse))@@ -2075,12 +2387,15 @@  optionSolverFlags :: ShowOrParseArgs                   -> (flags -> Flag Int   ) -> (Flag Int    -> flags -> flags)-                  -> (flags -> Flag Bool  ) -> (Flag Bool   -> flags -> flags)-                  -> (flags -> Flag Bool  ) -> (Flag Bool   -> flags -> flags)-                  -> (flags -> Flag Bool  ) -> (Flag Bool   -> flags -> flags)-                  -> (flags -> Flag Bool  ) -> (Flag Bool   -> flags -> flags)+                  -> (flags -> Flag ReorderGoals)     -> (Flag ReorderGoals     -> flags -> flags)+                  -> (flags -> Flag CountConflicts)   -> (Flag CountConflicts   -> flags -> flags)+                  -> (flags -> Flag IndependentGoals) -> (Flag IndependentGoals -> flags -> flags)+                  -> (flags -> Flag ShadowPkgs)       -> (Flag ShadowPkgs       -> flags -> flags)+                  -> (flags -> Flag StrongFlags)      -> (Flag StrongFlags      -> flags -> flags)+                  -> (flags -> Flag AllowBootLibInstalls) -> (Flag AllowBootLibInstalls -> flags -> flags)                   -> [OptionField flags]-optionSolverFlags showOrParseArgs getmbj setmbj getrg setrg _getig _setig getsip setsip getstrfl setstrfl =+optionSolverFlags showOrParseArgs getmbj setmbj getrg setrg getcc setcc _getig _setig+                  getsip setsip getstrfl setstrfl getib setib =   [ option [] ["max-backjumps"]       ("Maximum number of backjumps allowed while solving (default: " ++ show defaultMaxBackjumps ++ "). Use a negative number to enable unlimited backtracking. Use 0 to disable backtracking completely.")       getmbj setmbj@@ -2088,23 +2403,37 @@                     (map show . flagToList))   , option [] ["reorder-goals"]       "Try to reorder goals according to certain heuristics. Slows things down on average, but may make backtracking faster for some packages."-      getrg setrg+      (fmap asBool . getrg)+      (setrg . fmap ReorderGoals)       (yesNoOpt showOrParseArgs)-  -- TODO: Disabled for now because it does not work as advertised (yet).+  , option [] ["count-conflicts"]+      "Try to speed up solving by preferring goals that are involved in a lot of conflicts (default)."+      (fmap asBool . getcc)+      (setcc . fmap CountConflicts)+      (yesNoOpt showOrParseArgs)+  -- TODO: Disabled for now because it may not be necessary {-   , option [] ["independent-goals"]       "Treat several goals on the command line as independent. If several goals depend on the same package, different versions can be chosen."-      getig setig+      (fmap asBool . getig)+      (setig . fmap IndependentGoals)       (yesNoOpt showOrParseArgs) -}   , option [] ["shadow-installed-packages"]       "If multiple package instances of the same version are installed, treat all but one as shadowed."-      getsip setsip+      (fmap asBool . getsip)+      (setsip . fmap ShadowPkgs)       (yesNoOpt showOrParseArgs)   , option [] ["strong-flags"]       "Do not defer flag choices (this used to be the default in cabal-install <= 1.20)."-      getstrfl setstrfl+      (fmap asBool . getstrfl)+      (setstrfl . fmap StrongFlags)       (yesNoOpt showOrParseArgs)+  , option [] ["allow-boot-library-installs"]+      "Allow cabal to install base, ghc-prim, integer-simple, integer-gmp, and template-haskell."+      (fmap asBool . getib)+      (setib . fmap AllowBootLibInstalls)+      (yesNoOpt showOrParseArgs)   ]  usageFlagsOrPackages :: String -> String -> String@@ -2137,8 +2466,8 @@   where     pkgidToDependency :: PackageIdentifier -> Dependency     pkgidToDependency p = case packageVersion p of-      Version [] _ -> Dependency (packageName p) anyVersion-      version      -> Dependency (packageName p) (thisVersion version)+      v | v == nullVersion -> Dependency (packageName p) anyVersion+        | otherwise        -> Dependency (packageName p) (thisVersion v)  showRepo :: RemoteRepo -> String showRepo repo = remoteRepoName repo ++ ":"
Distribution/Client/SetupWrapper.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE CPP #-}+{-# LANGUAGE FlexibleContexts #-} ----------------------------------------------------------------------------- -- | -- Module      :  Distribution.Client.SetupWrapper@@ -16,28 +17,36 @@ -- runs it with the given arguments.  module Distribution.Client.SetupWrapper (-    setupWrapper,+    getSetup, runSetup, runSetupCommand, setupWrapper,     SetupScriptOptions(..),     defaultSetupScriptOptions,   ) where +import Prelude ()+import Distribution.Client.Compat.Prelude+ import qualified Distribution.Make as Make import qualified Distribution.Simple as Simple import Distribution.Version-         ( Version(..), VersionRange, anyVersion+         ( Version, mkVersion, versionNumbers, VersionRange, anyVersion          , intersectVersionRanges, orLaterVersion          , withinRange )-import Distribution.InstalledPackageInfo (installedUnitId)+import qualified Distribution.Backpack as Backpack import Distribution.Package-         ( UnitId(..), PackageIdentifier(..), PackageId,-           PackageName(..), Package(..), packageName-         , packageVersion, Dependency(..) )+         ( newSimpleUnitId, unsafeMkDefUnitId, ComponentId, PackageId, mkPackageName+         , PackageIdentifier(..), packageVersion, packageName )+import Distribution.Types.Dependency import Distribution.PackageDescription          ( GenericPackageDescription(packageDescription)          , PackageDescription(..), specVersion          , BuildType(..), knownBuildTypes, defaultRenaming )+#ifdef CABAL_PARSEC+import Distribution.PackageDescription.Parsec+         ( readGenericPackageDescription )+#else import Distribution.PackageDescription.Parse-         ( readPackageDescription )+         ( readGenericPackageDescription )+#endif import Distribution.Simple.Configure          ( configCompilerEx ) import Distribution.Compiler@@ -49,11 +58,11 @@ import Distribution.Simple.Build.Macros          ( generatePackageVersionMacros ) import Distribution.Simple.Program-         ( ProgramConfiguration, emptyProgramConfiguration+         ( ProgramDb, emptyProgramDb          , getProgramSearchPath, getDbProgramOutput, runDbProgram, ghcProgram          , ghcjsProgram ) import Distribution.Simple.Program.Find-         ( programSearchPathAsPATHVar )+         ( programSearchPathAsPATHVar, ProgramSearchPathEntry(ProgramSearchPathDir) ) import Distribution.Simple.Program.Run          ( getEffectiveEnvironment ) import qualified Distribution.Simple.Program.Strip as Strip@@ -66,6 +75,8 @@          ( GhcMode(..), GhcOptions(..), renderGhcOptions ) import qualified Distribution.Simple.PackageIndex as PackageIndex import Distribution.Simple.PackageIndex (InstalledPackageIndex)+import qualified Distribution.InstalledPackageInfo as IPI+import Distribution.Client.Types import Distribution.Client.Config          ( defaultCabalDir ) import Distribution.Client.IndexUtils@@ -75,39 +86,36 @@ import Distribution.Simple.Setup          ( Flag(..) ) import Distribution.Simple.Utils-         ( die, debug, info, cabalVersion, tryFindPackageDesc, comparing+         ( die', debug, info, infoNoWrap, cabalVersion, tryFindPackageDesc, comparing          , createDirectoryIfMissingVerbose, installExecutableFile-         , copyFileVerbose, rewriteFile, intercalate )+         , copyFileVerbose, rewriteFileEx ) import Distribution.Client.Utils-         ( inDir, tryCanonicalizePath-         , existsAndIsMoreRecentThan, moreRecentFile-#if mingw32_HOST_OS+         ( inDir, tryCanonicalizePath, withExtraPathEnv+         , existsAndIsMoreRecentThan, moreRecentFile, withEnv+#ifdef mingw32_HOST_OS          , canonicalizePathNoThrow #endif          )++import Distribution.ReadE import Distribution.System ( Platform(..), buildPlatform ) import Distribution.Text          ( display ) import Distribution.Utils.NubList          ( toNubListR ) import Distribution.Verbosity-         ( Verbosity ) import Distribution.Compat.Exception          ( catchIO )+import Distribution.Compat.Stack  import System.Directory    ( doesFileExist ) import System.FilePath     ( (</>), (<.>) ) import System.IO           ( Handle, hPutStr ) import System.Exit         ( ExitCode(..), exitWith )-import System.Process      ( runProcess, waitForProcess )-#if !MIN_VERSION_base(4,8,0)-import Control.Applicative ( (<$>), (<*>) )-import Data.Monoid         ( mempty )-#endif-import Control.Monad       ( when, unless )-import Data.List           ( find, foldl1' )-import Data.Maybe          ( fromMaybe, isJust )-import Data.Char           ( isSpace )+import System.Process      ( createProcess, StdStream(..), proc, waitForProcess+                           , ProcessHandle )+import qualified System.Process as Process+import Data.List           ( foldl1' ) import Distribution.Client.Compat.ExecutablePath  ( getExecutablePath )  #ifdef mingw32_HOST_OS@@ -120,6 +128,25 @@ import qualified System.Win32 as Win32 #endif +-- | @Setup@ encapsulates the outcome of configuring a setup method to build a+-- particular package.+data Setup = Setup { setupMethod :: SetupMethod+                   , setupScriptOptions :: SetupScriptOptions+                   , setupVersion :: Version+                   , setupBuildType :: BuildType+                   , setupPackage :: PackageDescription+                   }++-- | @SetupMethod@ represents one of the methods used to run Cabal commands.+data SetupMethod = InternalMethod+                   -- ^ run Cabal commands through \"cabal\" in the+                   -- current process+                 | SelfExecMethod+                   -- ^ run Cabal commands through \"cabal\" as a+                   -- child process+                 | ExternalMethod FilePath+                   -- ^ run Cabal commands through a custom \"Setup\" executable+ --TODO: The 'setupWrapper' and 'SetupScriptOptions' should be split into two -- parts: one that has no policy and just does as it's told with all the -- explicit options, and an optional initial part that applies certain@@ -129,6 +156,8 @@ -- -- See also the discussion at https://github.com/haskell/cabal/pull/3094 +-- | @SetupScriptOptions@ are options used to configure and run 'Setup', as+-- opposed to options given to the Cabal command at runtime. data SetupScriptOptions = SetupScriptOptions {     -- | The version of the Cabal library to use (if 'useDependenciesExclusive'     -- is not set). A suitable version of the Cabal library must be installed@@ -155,14 +184,16 @@     usePlatform              :: Maybe Platform,     usePackageDB             :: PackageDBStack,     usePackageIndex          :: Maybe InstalledPackageIndex,-    useProgramConfig         :: ProgramConfiguration,+    useProgramDb             :: ProgramDb,     useDistPref              :: FilePath,     useLoggingHandle         :: Maybe Handle,     useWorkingDir            :: Maybe FilePath,+    -- | Extra things to add to PATH when invoking the setup script.+    useExtraPathEnv          :: [FilePath],     forceExternalSetupMethod :: Bool,      -- | List of dependencies to use when building Setup.hs.-    useDependencies :: [(UnitId, PackageId)],+    useDependencies :: [(ComponentId, PackageId)],      -- | Is the list of setup dependencies exclusive?     --@@ -209,7 +240,12 @@     -- version) combination the cache holds a compiled setup script     -- executable. This only affects the Simple build type; for the Custom,     -- Configure and Make build types we always compile the setup script anew.-    setupCacheLock           :: Maybe Lock+    setupCacheLock           :: Maybe Lock,++    -- | Is the task we are going to run an interactive foreground task,+    -- or an non-interactive background task? Based on this flag we+    -- decide whether or not to delegate ctrl+c to the spawned task+    isInteractive            :: Bool   }  defaultSetupScriptOptions :: SetupScriptOptions@@ -223,88 +259,170 @@     useDependencies          = [],     useDependenciesExclusive = False,     useVersionMacros         = False,-    useProgramConfig         = emptyProgramConfiguration,+    useProgramDb             = emptyProgramDb,     useDistPref              = defaultDistPref,     useLoggingHandle         = Nothing,     useWorkingDir            = Nothing,+    useExtraPathEnv          = [],     useWin32CleanHack        = False,     forceExternalSetupMethod = False,-    setupCacheLock           = Nothing+    setupCacheLock           = Nothing,+    isInteractive            = False   } -setupWrapper :: Verbosity-             -> SetupScriptOptions-             -> Maybe PackageDescription-             -> CommandUI flags-             -> (Version -> flags)-             -> [String]-             -> IO ()-setupWrapper verbosity options mpkg cmd flags extraArgs = do+workingDir :: SetupScriptOptions -> FilePath+workingDir options =+  case fromMaybe "" (useWorkingDir options) of+    []  -> "."+    dir -> dir++-- | A @SetupRunner@ implements a 'SetupMethod'.+type SetupRunner = Verbosity+                 -> SetupScriptOptions+                 -> BuildType+                 -> [String]+                 -> IO ()++-- | Prepare to build a package by configuring a 'SetupMethod'. The returned+-- 'Setup' object identifies the method. The 'SetupScriptOptions' may be changed+-- during the configuration process; the final values are given by+-- 'setupScriptOptions'.+getSetup :: Verbosity+         -> SetupScriptOptions+         -> Maybe PackageDescription+         -> IO Setup+getSetup verbosity options mpkg = do   pkg <- maybe getPkg return mpkg-  let setupMethod = determineSetupMethod options' buildType'-      options'    = options {+  let options'    = options {                       useCabalVersion = intersectVersionRanges                                           (useCabalVersion options)                                           (orLaterVersion (specVersion pkg))                     }       buildType'  = fromMaybe Custom (buildType pkg)-      mkArgs cabalLibVersion = commandName cmd-                             : commandShowOptions cmd (flags cabalLibVersion)-                            ++ extraArgs   checkBuildType buildType'-  setupMethod verbosity options' (packageId pkg) buildType' mkArgs+  (version, method, options'') <-+    getSetupMethod verbosity options' pkg buildType'+  return Setup { setupMethod = method+               , setupScriptOptions = options''+               , setupVersion = version+               , setupBuildType = buildType'+               , setupPackage = pkg+               }   where     getPkg = tryFindPackageDesc (fromMaybe "." (useWorkingDir options))-         >>= readPackageDescription verbosity+         >>= readGenericPackageDescription verbosity          >>= return . packageDescription      checkBuildType (UnknownBuildType name) =-      die $ "The build-type '" ++ name ++ "' is not known. Use one of: "+      die' verbosity $ "The build-type '" ++ name ++ "' is not known. Use one of: "          ++ intercalate ", " (map display knownBuildTypes) ++ "."     checkBuildType _ = return () + -- | Decide if we're going to be able to do a direct internal call to the -- entry point in the Cabal library or if we're going to have to compile -- and execute an external Setup.hs script. ---determineSetupMethod :: SetupScriptOptions -> BuildType -> SetupMethod-determineSetupMethod options buildType'-    -- This order is picked so that it's stable. The build type and-    -- required cabal version are external info, coming from .cabal-    -- files and the command line. Those do switch between the-    -- external and self & internal methods, but that info itself can-    -- be considered stable. The logging and force-external conditions-    -- are internally generated choices but now these only switch-    -- between the self and internal setup methods, which are-    -- consistent with each other.-  | buildType' == Custom             = externalSetupMethod-  | maybe False (cabalVersion /=)-        (useCabalSpecVersion options)- || not (cabalVersion `withinRange`-         useCabalVersion options)    = externalSetupMethod+getSetupMethod+  :: Verbosity -> SetupScriptOptions -> PackageDescription -> BuildType+  -> IO (Version, SetupMethod, SetupScriptOptions)+getSetupMethod verbosity options pkg buildType'+  | buildType' == Custom+    || maybe False (cabalVersion /=) (useCabalSpecVersion options)+    || not (cabalVersion `withinRange` useCabalVersion options)    =+         getExternalSetupMethod verbosity options pkg buildType'   | isJust (useLoggingHandle options)     -- Forcing is done to use an external process e.g. due to parallel     -- build concerns.- || forceExternalSetupMethod options = selfExecSetupMethod-  | otherwise                        = internalSetupMethod+    || forceExternalSetupMethod options =+        return (cabalVersion, SelfExecMethod, options)+  | otherwise = return (cabalVersion, InternalMethod, options) -type SetupMethod = Verbosity-                -> SetupScriptOptions-                -> PackageIdentifier-                -> BuildType-                -> (Version -> [String]) -> IO ()+runSetupMethod :: WithCallStack (SetupMethod -> SetupRunner)+runSetupMethod InternalMethod = internalSetupMethod+runSetupMethod (ExternalMethod path) = externalSetupMethod path+runSetupMethod SelfExecMethod = selfExecSetupMethod +-- | Run a configured 'Setup' with specific arguments.+runSetup :: Verbosity -> Setup+         -> [String]  -- ^ command-line arguments+         -> IO ()+runSetup verbosity setup args0 = do+  let method = setupMethod setup+      options = setupScriptOptions setup+      bt = setupBuildType setup+      args = verbosityHack (setupVersion setup) args0+  when (verbosity >= deafening {- avoid test if not debug -} && args /= args0) $+    infoNoWrap verbose $+        "Applied verbosity hack:\n" +++        "  Before: " ++ show args0 ++ "\n" +++        "  After:  " ++ show args ++ "\n"+  runSetupMethod method verbosity options bt args++-- | This is a horrible hack to make sure passing fancy verbosity+-- flags (e.g., @-v'info +callstack'@) doesn't break horribly on+-- old Setup.  We can't do it in 'filterConfigureFlags' because+-- verbosity applies to ALL commands.+verbosityHack :: Version -> [String] -> [String]+verbosityHack ver args0+    | ver >= mkVersion [1,25] = args0+    | otherwise = go args0+  where+    go (('-':'v':rest) : args)+        | Just rest' <- munch rest = ("-v" ++ rest') : go args+    go (('-':'-':'v':'e':'r':'b':'o':'s':'e':'=':rest) : args)+        | Just rest' <- munch rest = ("--verbose=" ++ rest') : go args+    go ("--verbose" : rest : args)+        | Just rest' <- munch rest = "--verbose" : rest' : go args+    go rest@("--" : _) = rest+    go (arg:args) = arg : go args+    go [] = []++    munch rest =+        case runReadE flagToVerbosity rest of+            Right v | verboseHasFlags v+              -- We could preserve the prefix, but since we're assuming+              -- it's Cabal's verbosity flag, we can assume that+              -- any format is OK+              -> Just (showForCabal (verboseNoFlags v))+            _ -> Nothing++-- | Run a command through a configured 'Setup'.+runSetupCommand :: Verbosity -> Setup+                -> CommandUI flags  -- ^ command definition+                -> flags  -- ^ command flags+                -> [String]  -- ^ extra command-line arguments+                -> IO ()+runSetupCommand verbosity setup cmd flags extraArgs = do+  let args = commandName cmd : commandShowOptions cmd flags ++ extraArgs+  runSetup verbosity setup args++-- | Configure a 'Setup' and run a command in one step. The command flags+-- may depend on the Cabal library version in use.+setupWrapper :: Verbosity+             -> SetupScriptOptions+             -> Maybe PackageDescription+             -> CommandUI flags+             -> (Version -> flags)+                -- ^ produce command flags given the Cabal library version+             -> [String]+             -> IO ()+setupWrapper verbosity options mpkg cmd flags extraArgs = do+  setup <- getSetup verbosity options mpkg+  runSetupCommand verbosity setup cmd (flags $ setupVersion setup) extraArgs+ -- ------------------------------------------------------------ -- * Internal SetupMethod -- ------------------------------------------------------------ -internalSetupMethod :: SetupMethod-internalSetupMethod verbosity options _ bt mkargs = do-  let args = mkargs cabalVersion-  debug verbosity $ "Using internal setup method with build-type " ++ show bt-                 ++ " and args:\n  " ++ show args-  inDir (useWorkingDir options) $-    buildTypeAction bt args+internalSetupMethod :: SetupRunner+internalSetupMethod verbosity options bt args = do+  info verbosity $ "Using internal setup method with build-type " ++ show bt+                ++ " and args:\n  " ++ show args+  inDir (useWorkingDir options) $ do+    withEnv "HASKELL_DIST_DIR" (useDistPref options) $+      withExtraPathEnv (useExtraPathEnv options) $+        buildTypeAction bt args  buildTypeAction :: BuildType -> ([String] -> IO ()) buildTypeAction Simple    = Simple.defaultMainArgs@@ -314,16 +432,46 @@ buildTypeAction Custom               = error "buildTypeAction Custom" buildTypeAction (UnknownBuildType _) = error "buildTypeAction UnknownBuildType" ++-- | @runProcess'@ is a version of @runProcess@ where we have+-- the additional option to decide whether or not we should+-- delegate CTRL+C to the spawned process.+runProcess' :: FilePath                 -- ^ Filename of the executable+            -> [String]                 -- ^ Arguments to pass to executable+            -> Maybe FilePath           -- ^ Optional path to working directory+            -> Maybe [(String, String)] -- ^ Optional environment+            -> Maybe Handle             -- ^ Handle for @stdin@+            -> Maybe Handle             -- ^ Handle for @stdout@+            -> Maybe Handle             -- ^ Handle for @stderr@+            -> Bool                     -- ^ Delegate Ctrl+C ?+            -> IO ProcessHandle+runProcess' cmd args mb_cwd mb_env mb_stdin mb_stdout mb_stderr _delegate = do+  (_,_,_,ph) <-+    createProcess+      (proc cmd args){ Process.cwd = mb_cwd+                     , Process.env = mb_env+                     , Process.std_in  = mbToStd mb_stdin+                     , Process.std_out = mbToStd mb_stdout+                     , Process.std_err = mbToStd mb_stderr+#if MIN_VERSION_process(1,2,0)+                     , Process.delegate_ctlc = _delegate+#endif+                     }+  return ph+  where+    mbToStd :: Maybe Handle -> StdStream+    mbToStd Nothing = Inherit+    mbToStd (Just hdl) = UseHandle hdl -- ------------------------------------------------------------ -- * Self-Exec SetupMethod -- ------------------------------------------------------------ -selfExecSetupMethod :: SetupMethod-selfExecSetupMethod verbosity options _pkg bt mkargs = do+selfExecSetupMethod :: SetupRunner+selfExecSetupMethod verbosity options bt args0 = do   let args = ["act-as-setup",               "--build-type=" ++ display bt,-              "--"] ++ mkargs cabalVersion-  debug verbosity $ "Using self-exec internal setup method with build-type "+              "--"] ++ args0+  info verbosity $ "Using self-exec internal setup method with build-type "                  ++ show bt ++ " and args:\n  " ++ show args   path <- getExecutablePath   info verbosity $ unwords (path : args)@@ -333,12 +481,14 @@                                     ++ show logHandle    searchpath <- programSearchPathAsPATHVar-                (getProgramSearchPath (useProgramConfig options))-  env        <- getEffectiveEnvironment [("PATH", Just searchpath)]--  process <- runProcess path args+                (map ProgramSearchPathDir (useExtraPathEnv options) +++                 getProgramSearchPath (useProgramDb options))+  env       <- getEffectiveEnvironment [("PATH", Just searchpath)+                                        ,("HASKELL_DIST_DIR", Just (useDistPref options))]+  process <- runProcess' path args              (useWorkingDir options) env Nothing              (useLoggingHandle options) (useLoggingHandle options)+             (isInteractive options)   exitCode <- waitForProcess process   unless (exitCode == ExitSuccess) $ exitWith exitCode @@ -346,8 +496,62 @@ -- * External SetupMethod -- ------------------------------------------------------------ -externalSetupMethod :: SetupMethod-externalSetupMethod verbosity options pkg bt mkargs = do+externalSetupMethod :: WithCallStack (FilePath -> SetupRunner)+externalSetupMethod path verbosity options _ args = do+  info verbosity $ unwords (path : args)+  case useLoggingHandle options of+    Nothing        -> return ()+    Just logHandle -> info verbosity $ "Redirecting build log to "+                                    ++ show logHandle++  -- See 'Note: win32 clean hack' above.+#ifdef mingw32_HOST_OS+  if useWin32CleanHack options then doWin32CleanHack path else doInvoke path+#else+  doInvoke path+#endif++  where+    doInvoke path' = do+      searchpath <- programSearchPathAsPATHVar+                    (map ProgramSearchPathDir (useExtraPathEnv options) +++                      getProgramSearchPath (useProgramDb options))+      env        <- getEffectiveEnvironment [("PATH", Just searchpath)+                                            ,("HASKELL_DIST_DIR", Just (useDistPref options))]++      process <- runProcess' path' args+                  (useWorkingDir options) env Nothing+                  (useLoggingHandle options) (useLoggingHandle options)+                  (isInteractive options)+      exitCode <- waitForProcess process+      unless (exitCode == ExitSuccess) $ exitWith exitCode++#ifdef mingw32_HOST_OS+    doWin32CleanHack path' = do+      info verbosity $ "Using the Win32 clean hack."+      -- Recursively removes the temp dir on exit.+      withTempDirectory verbosity (workingDir options) "cabal-tmp" $ \tmpDir ->+          bracket (moveOutOfTheWay tmpDir path')+                  (maybeRestore path')+                  doInvoke++    moveOutOfTheWay tmpDir path' = do+      let newPath = tmpDir </> "setup" <.> exeExtension+      Win32.moveFile path' newPath+      return newPath++    maybeRestore oldPath path' = do+      let oldPathDir = takeDirectory oldPath+      oldPathDirExists <- doesDirectoryExist oldPathDir+      -- 'setup clean' didn't complete, 'dist/setup' still exists.+      when oldPathDirExists $+        Win32.moveFile path' oldPath+#endif++getExternalSetupMethod+  :: Verbosity -> SetupScriptOptions -> PackageDescription -> BuildType+  -> IO (Version, SetupMethod, SetupScriptOptions)+getExternalSetupMethod verbosity options pkg bt = do   debug verbosity $ "Using external setup method with build-type " ++ show bt   debug verbosity $ "Using explicit dependencies: "     ++ show (useDependenciesExclusive options)@@ -359,13 +563,29 @@                cabalLibVersion mCabalLibInstalledPkgId           else compileSetupExecutable   options'                cabalLibVersion mCabalLibInstalledPkgId False-  invokeSetupScript options' path (mkargs cabalLibVersion) +  -- Since useWorkingDir can change the relative path, the path argument must+  -- be turned into an absolute path. On some systems, runProcess' will take+  -- path as relative to the new working directory instead of the current+  -- working directory.+  path' <- tryCanonicalizePath path++  -- See 'Note: win32 clean hack' above.+#ifdef mingw32_HOST_OS+  -- setupProgFile may not exist if we're using a cached program+  setupProgFile' <- canonicalizePathNoThrow setupProgFile+  let win32CleanHackNeeded = (useWin32CleanHack options)+                              -- Skip when a cached setup script is used.+                              && setupProgFile' `equalFilePath` path'+#else+  let win32CleanHackNeeded = False+#endif+  let options'' = options' { useWin32CleanHack = win32CleanHackNeeded }++  return (cabalLibVersion, ExternalMethod path', options'')+   where-  workingDir       = case fromMaybe "" (useWorkingDir options) of-                       []  -> "."-                       dir -> dir-  setupDir         = workingDir </> useDistPref options </> "setup"+  setupDir         = workingDir options </> useDistPref options </> "setup"   setupVersionFile = setupDir   </> "setup" <.> "version"   setupHs          = setupDir   </> "setup" <.> "hs"   setupProgFile    = setupDir   </> "setup" <.> exeExtension@@ -374,12 +594,12 @@   useCachedSetupExecutable = (bt == Simple || bt == Configure || bt == Make)    maybeGetInstalledPackages :: SetupScriptOptions -> Compiler-                            -> ProgramConfiguration -> IO InstalledPackageIndex-  maybeGetInstalledPackages options' comp conf =+                            -> ProgramDb -> IO InstalledPackageIndex+  maybeGetInstalledPackages options' comp progdb =     case usePackageIndex options' of       Just index -> return index       Nothing    -> getInstalledPackages verbosity-                    comp (usePackageDB options') conf+                    comp (usePackageDB options') progdb    -- Choose the version of Cabal to use if the setup script has a dependency on   -- Cabal, and possibly update the setup script options. The version also@@ -393,10 +613,10 @@   --   -- The version chosen here must match the one used in 'compileSetupExecutable'   -- (See issue #3433).-  cabalLibVersionToUse :: IO (Version, Maybe UnitId+  cabalLibVersionToUse :: IO (Version, Maybe ComponentId                              ,SetupScriptOptions)   cabalLibVersionToUse =-    case find (hasCabal . snd) (useDependencies options) of+    case find (isCabalPkgId . snd) (useDependencies options) of       Just (unitId, pkgId) -> do         let version = pkgVersion pkgId         updateSetupScript version bt@@ -439,14 +659,11 @@       writeSetupVersionFile version =           writeFile setupVersionFile (show version ++ "\n") -      hasCabal (PackageIdentifier (PackageName "Cabal") _) = True-      hasCabal _                                           = False--      installedVersion :: IO (Version, Maybe UnitId+      installedVersion :: IO (Version, Maybe InstalledPackageId                              ,SetupScriptOptions)       installedVersion = do-        (comp,    conf,    options')  <- configureCompiler options-        (version, mipkgid, options'') <- installedCabalVersion options' comp conf+        (comp,    progdb,  options')  <- configureCompiler options+        (version, mipkgid, options'') <- installedCabalVersion options' comp progdb         updateSetupScript version bt         writeSetupVersionFile version         return (version, mipkgid, options'')@@ -463,7 +680,7 @@   updateSetupScript _ Custom = do     useHs  <- doesFileExist customSetupHs     useLhs <- doesFileExist customSetupLhs-    unless (useHs || useLhs) $ die+    unless (useHs || useLhs) $ die' verbosity       "Using 'build-type: Custom' but there is no Setup.hs or Setup.lhs script."     let src = (if useHs then customSetupHs else customSetupLhs)     srcNewer <- src `moreRecentFile` setupHs@@ -471,38 +688,38 @@                     then copyFileVerbose verbosity src setupHs                     else runSimplePreProcessor ppUnlit src setupHs verbosity     where-      customSetupHs   = workingDir </> "Setup.hs"-      customSetupLhs  = workingDir </> "Setup.lhs"+      customSetupHs   = workingDir options </> "Setup.hs"+      customSetupLhs  = workingDir options </> "Setup.lhs"    updateSetupScript cabalLibVersion _ =-    rewriteFile setupHs (buildTypeScript cabalLibVersion)+    rewriteFileEx verbosity setupHs (buildTypeScript cabalLibVersion)    buildTypeScript :: Version -> String   buildTypeScript cabalLibVersion = case bt of     Simple    -> "import Distribution.Simple; main = defaultMain\n"     Configure -> "import Distribution.Simple; main = defaultMainWithHooks "-              ++ if cabalLibVersion >= Version [1,3,10] []+              ++ if cabalLibVersion >= mkVersion [1,3,10]                    then "autoconfUserHooks\n"                    else "defaultUserHooks\n"     Make      -> "import Distribution.Make; main = defaultMain\n"     Custom             -> error "buildTypeScript Custom"     UnknownBuildType _ -> error "buildTypeScript UnknownBuildType" -  installedCabalVersion :: SetupScriptOptions -> Compiler -> ProgramConfiguration-                        -> IO (Version, Maybe UnitId+  installedCabalVersion :: SetupScriptOptions -> Compiler -> ProgramDb+                        -> IO (Version, Maybe InstalledPackageId                               ,SetupScriptOptions)-  installedCabalVersion options' compiler conf = do-    index <- maybeGetInstalledPackages options' compiler conf-    let cabalDep   = Dependency (PackageName "Cabal") (useCabalVersion options')+  installedCabalVersion options' compiler progdb = do+    index <- maybeGetInstalledPackages options' compiler progdb+    let cabalDep   = Dependency (mkPackageName "Cabal") (useCabalVersion options')         options''  = options' { usePackageIndex = Just index }     case PackageIndex.lookupDependency index cabalDep of-      []   -> die $ "The package '" ++ display (packageName pkg)+      []   -> die' verbosity $ "The package '" ++ display (packageName pkg)                  ++ "' requires Cabal library version "                  ++ display (useCabalVersion options)                  ++ " but no suitable version is installed."       pkgs -> let ipkginfo = head . snd . bestVersion fst $ pkgs               in return (packageVersion ipkginfo-                        ,Just . installedUnitId $ ipkginfo, options'')+                        ,Just . IPI.installedComponentId $ ipkginfo, options'')    bestVersion :: (a -> Version) -> [a] -> a   bestVersion f = firstMaximumBy (comparing (preference . f))@@ -528,27 +745,27 @@         where           sameVersion      = version == cabalVersion           sameMajorVersion = majorVersion version == majorVersion cabalVersion-          majorVersion     = take 2 . versionBranch-          stableVersion    = case versionBranch version of+          majorVersion     = take 2 . versionNumbers+          stableVersion    = case versionNumbers version of                                (_:x:_) -> even x                                _       -> False           latestVersion    = version    configureCompiler :: SetupScriptOptions-                    -> IO (Compiler, ProgramConfiguration, SetupScriptOptions)+                    -> IO (Compiler, ProgramDb, SetupScriptOptions)   configureCompiler options' = do-    (comp, conf) <- case useCompiler options' of-      Just comp -> return (comp, useProgramConfig options')-      Nothing   -> do (comp, _, conf) <-+    (comp, progdb) <- case useCompiler options' of+      Just comp -> return (comp, useProgramDb options')+      Nothing   -> do (comp, _, progdb) <-                         configCompilerEx (Just GHC) Nothing Nothing-                        (useProgramConfig options') verbosity-                      return (comp, conf)+                        (useProgramDb options') verbosity+                      return (comp, progdb)     -- Whenever we need to call configureCompiler, we also need to access the     -- package index, so let's cache it in SetupScriptOptions.-    index <- maybeGetInstalledPackages options' comp conf-    return (comp, conf, options' { useCompiler      = Just comp,-                                   usePackageIndex  = Just index,-                                   useProgramConfig = conf })+    index <- maybeGetInstalledPackages options' comp progdb+    return (comp, progdb, options' { useCompiler      = Just comp,+                                     usePackageIndex  = Just index,+                                     useProgramDb = progdb })    -- | Path to the setup exe cache directory and path to the cached setup   -- executable.@@ -575,7 +792,7 @@   -- | Look up the setup executable in the cache; update the cache if the setup   -- executable is not found.   getCachedSetupExecutable :: SetupScriptOptions-                           -> Version -> Maybe UnitId+                           -> Version -> Maybe InstalledPackageId                            -> IO FilePath   getCachedSetupExecutable options' cabalLibVersion                            maybeCabalLibInstalledPkgId = do@@ -599,7 +816,7 @@           installExecutableFile verbosity src cachedSetupProgFile           -- Do not strip if we're using GHCJS, since the result may be a script           when (maybe True ((/=GHCJS).compilerFlavor) $ useCompiler options') $-            Strip.stripExe verbosity platform (useProgramConfig options')+            Strip.stripExe verbosity platform (useProgramDb options')               cachedSetupProgFile     return cachedSetupProgFile       where@@ -610,7 +827,7 @@   -- Currently this is GHC/GHCJS only. It should really be generalised.   --   compileSetupExecutable :: SetupScriptOptions-                         -> Version -> Maybe UnitId -> Bool+                         -> Version -> Maybe ComponentId -> Bool                          -> IO FilePath   compileSetupExecutable options' cabalLibVersion maybeCabalLibInstalledPkgId                          forceCompile = do@@ -619,8 +836,8 @@     let outOfDate = setupHsNewer || cabalVersionNewer     when (outOfDate || forceCompile) $ do       debug verbosity "Setup executable needs to be updated, compiling..."-      (compiler, conf, options'') <- configureCompiler options'-      let cabalPkgid = PackageIdentifier (PackageName "Cabal") cabalLibVersion+      (compiler, progdb, options'') <- configureCompiler options'+      let cabalPkgid = PackageIdentifier (mkPackageName "Cabal") cabalLibVersion           (program, extraOpts)             = case compilerFlavor compiler of                       GHCJS -> (ghcjsProgram, ["-build-runner"])@@ -638,19 +855,20 @@           -- Both of these options should be enabled for packages that have           -- opted-in and declared a custom-settup stanza.           ---          hasCabal (_, PackageIdentifier (PackageName "Cabal") _) = True-          hasCabal _                                              = False-           selectedDeps | useDependenciesExclusive options'                                    = useDependencies options'                        | otherwise = useDependencies options' ++-                                     if any hasCabal (useDependencies options')+                                     if any (isCabalPkgId . snd) (useDependencies options')                                      then []                                      else cabalDep-          addRenaming (ipid, pid) = (ipid, pid, defaultRenaming)+          addRenaming (ipid, _) =+            -- Assert 'DefUnitId' invariant+            (Backpack.DefiniteUnitId (unsafeMkDefUnitId (newSimpleUnitId ipid)), defaultRenaming)           cppMacrosFile = setupDir </> "setup_macros.h"           ghcOptions = mempty {-              ghcOptVerbosity       = Flag verbosity+              -- Respect -v0, but don't crank up verbosity on GHC if+              -- Cabal verbosity is requested. For that, use --ghc-option=-v instead!+              ghcOptVerbosity       = Flag (min verbosity normal)             , ghcOptMode            = Flag GhcModeMake             , ghcOptInputFiles      = toNubListR [setupHs]             , ghcOptOutputFile      = Flag setupProgFile@@ -658,7 +876,7 @@             , ghcOptHiDir           = Flag setupDir             , ghcOptSourcePathClear = Flag True             , ghcOptSourcePath      = case bt of-                                      Custom -> toNubListR [workingDir]+                                      Custom -> toNubListR [workingDir options']                                       _      -> mempty             , ghcOptPackageDBs      = usePackageDB options''             , ghcOptHideAllPackages = Flag (useDependenciesExclusive options')@@ -670,73 +888,17 @@             }       let ghcCmdLine = renderGhcOptions compiler platform ghcOptions       when (useVersionMacros options') $-        rewriteFile cppMacrosFile (generatePackageVersionMacros-                                     [ pid | (_ipid, pid) <- selectedDeps ])+        rewriteFileEx verbosity cppMacrosFile+            (generatePackageVersionMacros (map snd selectedDeps))       case useLoggingHandle options of-        Nothing          -> runDbProgram verbosity program conf ghcCmdLine+        Nothing          -> runDbProgram verbosity program progdb ghcCmdLine          -- If build logging is enabled, redirect compiler output to the log file.         (Just logHandle) -> do output <- getDbProgramOutput verbosity program-                                         conf ghcCmdLine+                                         progdb ghcCmdLine                                hPutStr logHandle output     return setupProgFile -  invokeSetupScript :: SetupScriptOptions -> FilePath -> [String] -> IO ()-  invokeSetupScript options' path args = do-    info verbosity $ unwords (path : args)-    case useLoggingHandle options' of-      Nothing        -> return ()-      Just logHandle -> info verbosity $ "Redirecting build log to "-                                      ++ show logHandle -    -- Since useWorkingDir can change the relative path, the path argument must-    -- be turned into an absolute path. On some systems, runProcess will take-    -- path as relative to the new working directory instead of the current-    -- working directory.-    path' <- tryCanonicalizePath path--    -- See 'Note: win32 clean hack' above.-#if mingw32_HOST_OS-    -- setupProgFile may not exist if we're using a cached program-    setupProgFile' <- canonicalizePathNoThrow setupProgFile-    let win32CleanHackNeeded = (useWin32CleanHack options')-                               -- Skip when a cached setup script is used.-                               && setupProgFile' `equalFilePath` path'-    if win32CleanHackNeeded then doWin32CleanHack path' else doInvoke path'-#else-    doInvoke path'-#endif--    where-      doInvoke path' = do-        searchpath <- programSearchPathAsPATHVar-                      (getProgramSearchPath (useProgramConfig options'))-        env        <- getEffectiveEnvironment [("PATH", Just searchpath)]--        process <- runProcess path' args-                   (useWorkingDir options') env Nothing-                   (useLoggingHandle options') (useLoggingHandle options')-        exitCode <- waitForProcess process-        unless (exitCode == ExitSuccess) $ exitWith exitCode--#if mingw32_HOST_OS-      doWin32CleanHack path' = do-        info verbosity $ "Using the Win32 clean hack."-        -- Recursively removes the temp dir on exit.-        withTempDirectory verbosity workingDir "cabal-tmp" $ \tmpDir ->-            bracket (moveOutOfTheWay tmpDir path')-                    (maybeRestore path')-                    doInvoke--      moveOutOfTheWay tmpDir path' = do-        let newPath = tmpDir </> "setup" <.> exeExtension-        Win32.moveFile path' newPath-        return newPath--      maybeRestore oldPath path' = do-        let oldPathDir = takeDirectory oldPath-        oldPathDirExists <- doesDirectoryExist oldPathDir-        -- 'setup clean' didn't complete, 'dist/setup' still exists.-        when oldPathDirExists $-          Win32.moveFile path' oldPath-#endif+isCabalPkgId :: PackageIdentifier -> Bool+isCabalPkgId (PackageIdentifier pname _) = pname == mkPackageName "Cabal"
+ Distribution/Client/SolverInstallPlan.hs view
@@ -0,0 +1,445 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE TypeFamilies #-}+-----------------------------------------------------------------------------+-- |+-- Module      :  Distribution.Client.SolverInstallPlan+-- Copyright   :  (c) Duncan Coutts 2008+-- License     :  BSD-like+--+-- Maintainer  :  duncan@community.haskell.org+-- Stability   :  provisional+-- Portability :  portable+--+-- The 'SolverInstallPlan' is the graph of packages produced by the+-- dependency solver, and specifies at the package-granularity what+-- things are going to be installed.  To put it another way: the+-- dependency solver produces a 'SolverInstallPlan', which is then+-- consumed by various other parts of Cabal.+--+-----------------------------------------------------------------------------+module Distribution.Client.SolverInstallPlan(+  SolverInstallPlan(..),+  SolverPlanPackage,+  ResolverPackage(..),++  -- * Operations on 'SolverInstallPlan's+  new,+  toList,+  toMap,++  remove,++  showPlanIndex,+  showInstallPlan,++  -- * Checking validity of plans+  valid,+  closed,+  consistent,+  acyclic,++  -- ** Details on invalid plans+  SolverPlanProblem(..),+  showPlanProblem,+  problems,++  -- ** Querying the install plan+  dependencyClosure,+  reverseDependencyClosure,+  topologicalOrder,+  reverseTopologicalOrder,+) where++import Distribution.Package+         ( PackageIdentifier(..), Package(..), PackageName+         , HasUnitId(..), PackageId, packageVersion, packageName )+import qualified Distribution.Solver.Types.ComponentDeps as CD+import Distribution.Text+         ( display )++import Distribution.Client.Types+         ( UnresolvedPkgLoc )+import Distribution.Version+         ( Version )++import           Distribution.Solver.Types.Settings+import           Distribution.Solver.Types.ResolverPackage+import           Distribution.Solver.Types.SolverId++import Data.List+         ( intercalate )+import Data.Maybe+         ( fromMaybe, catMaybes )+import Distribution.Compat.Binary (Binary(..))+import Distribution.Compat.Graph (Graph, IsNode(..))+import qualified Data.Graph as OldGraph+import qualified Distribution.Compat.Graph as Graph+import qualified Data.Map as Map+import Data.Map (Map)+import Data.Array ((!))+import Data.Typeable++type SolverPlanPackage = ResolverPackage UnresolvedPkgLoc++type SolverPlanIndex = Graph SolverPlanPackage++data SolverInstallPlan = SolverInstallPlan {+    planIndex      :: !SolverPlanIndex,+    planIndepGoals :: !IndependentGoals+  }+  deriving (Typeable)++{-+-- | Much like 'planPkgIdOf', but mapping back to full packages.+planPkgOf :: SolverInstallPlan+          -> Graph.Vertex+          -> SolverPlanPackage+planPkgOf plan v =+    case Graph.lookupKey (planIndex plan)+                         (planPkgIdOf plan v) of+      Just pkg -> pkg+      Nothing  -> error "InstallPlan: internal error: planPkgOf lookup failed"+-}++mkInstallPlan :: SolverPlanIndex+              -> IndependentGoals+              -> SolverInstallPlan+mkInstallPlan index indepGoals =+    SolverInstallPlan {+      planIndex      = index,+      planIndepGoals = indepGoals+    }++instance Binary SolverInstallPlan where+    put SolverInstallPlan {+              planIndex      = index,+              planIndepGoals = indepGoals+        } = put (index, indepGoals)++    get = do+      (index, indepGoals) <- get+      return $! mkInstallPlan index indepGoals++showPlanIndex :: [SolverPlanPackage] -> String+showPlanIndex = intercalate "\n" . map showPlanPackage++showInstallPlan :: SolverInstallPlan -> String+showInstallPlan = showPlanIndex . toList++showPlanPackage :: SolverPlanPackage -> String+showPlanPackage (PreExisting ipkg) = "PreExisting " ++ display (packageId ipkg)+                                            ++ " (" ++ display (installedUnitId ipkg)+                                            ++ ")"+showPlanPackage (Configured  spkg)   = "Configured " ++ display (packageId spkg)++-- | Build an installation plan from a valid set of resolved packages.+--+new :: IndependentGoals+    -> SolverPlanIndex+    -> Either [SolverPlanProblem] SolverInstallPlan+new indepGoals index =+  case problems indepGoals index of+    []    -> Right (mkInstallPlan index indepGoals)+    probs -> Left probs++toList :: SolverInstallPlan -> [SolverPlanPackage]+toList = Graph.toList . planIndex++toMap :: SolverInstallPlan -> Map SolverId SolverPlanPackage+toMap = Graph.toMap . planIndex++-- | Remove packages from the install plan. This will result in an+-- error if there are remaining packages that depend on any matching+-- package. This is primarily useful for obtaining an install plan for+-- the dependencies of a package or set of packages without actually+-- installing the package itself, as when doing development.+--+remove :: (SolverPlanPackage -> Bool)+       -> SolverInstallPlan+       -> Either [SolverPlanProblem]+                 (SolverInstallPlan)+remove shouldRemove plan =+    new (planIndepGoals plan) newIndex+  where+    newIndex = Graph.fromDistinctList $+                 filter (not . shouldRemove) (toList plan)++-- ------------------------------------------------------------+-- * Checking validity of plans+-- ------------------------------------------------------------++-- | A valid installation plan is a set of packages that is 'acyclic',+-- 'closed' and 'consistent'. Also, every 'ConfiguredPackage' in the+-- plan has to have a valid configuration (see 'configuredPackageValid').+--+-- * if the result is @False@ use 'problems' to get a detailed list.+--+valid :: IndependentGoals+      -> SolverPlanIndex+      -> Bool+valid indepGoals index =+    null $ problems indepGoals index++data SolverPlanProblem =+     PackageMissingDeps   SolverPlanPackage+                          [PackageIdentifier]+   | PackageCycle         [SolverPlanPackage]+   | PackageInconsistency PackageName [(PackageIdentifier, Version)]+   | PackageStateInvalid  SolverPlanPackage SolverPlanPackage++showPlanProblem :: SolverPlanProblem -> String+showPlanProblem (PackageMissingDeps pkg missingDeps) =+     "Package " ++ display (packageId pkg)+  ++ " depends on the following packages which are missing from the plan: "+  ++ intercalate ", " (map display missingDeps)++showPlanProblem (PackageCycle cycleGroup) =+     "The following packages are involved in a dependency cycle "+  ++ intercalate ", " (map (display.packageId) cycleGroup)++showPlanProblem (PackageInconsistency name inconsistencies) =+     "Package " ++ display name+  ++ " is required by several packages,"+  ++ " but they require inconsistent versions:\n"+  ++ unlines [ "  package " ++ display pkg ++ " requires "+                            ++ display (PackageIdentifier name ver)+             | (pkg, ver) <- inconsistencies ]++showPlanProblem (PackageStateInvalid pkg pkg') =+     "Package " ++ display (packageId pkg)+  ++ " is in the " ++ showPlanState pkg+  ++ " state but it depends on package " ++ display (packageId pkg')+  ++ " which is in the " ++ showPlanState pkg'+  ++ " state"+  where+    showPlanState (PreExisting _) = "pre-existing"+    showPlanState (Configured  _)   = "configured"++-- | For an invalid plan, produce a detailed list of problems as human readable+-- error messages. This is mainly intended for debugging purposes.+-- Use 'showPlanProblem' for a human readable explanation.+--+problems :: IndependentGoals+         -> SolverPlanIndex+         -> [SolverPlanProblem]+problems indepGoals index =++     [ PackageMissingDeps pkg+       (catMaybes+        (map+         (fmap packageId . flip Graph.lookup index)+         missingDeps))+     | (pkg, missingDeps) <- Graph.broken index ]++  ++ [ PackageCycle cycleGroup+     | cycleGroup <- Graph.cycles index ]++  ++ [ PackageInconsistency name inconsistencies+     | (name, inconsistencies) <-+       dependencyInconsistencies indepGoals index ]++  ++ [ PackageStateInvalid pkg pkg'+     | pkg <- Graph.toList index+     , Just pkg' <- map (flip Graph.lookup index)+                    (nodeNeighbors pkg)+     , not (stateDependencyRelation pkg pkg') ]+++-- | Compute all roots of the install plan, and verify that the transitive+-- plans from those roots are all consistent.+--+-- NOTE: This does not check for dependency cycles. Moreover, dependency cycles+-- may be absent from the subplans even if the larger plan contains a dependency+-- cycle. Such cycles may or may not be an issue; either way, we don't check+-- for them here.+dependencyInconsistencies :: IndependentGoals+                          -> SolverPlanIndex+                          -> [(PackageName, [(PackageIdentifier, Version)])]+dependencyInconsistencies indepGoals index  =+    concatMap dependencyInconsistencies' subplans+  where+    subplans :: [SolverPlanIndex]+    subplans = -- Not Graph.closure!!+               map (nonSetupClosure index)+                   (rootSets indepGoals index)++-- NB: When we check for inconsistencies, packages from the setup+-- scripts don't count as part of the closure (this way, we+-- can build, e.g., Cabal-1.24.1 even if its setup script is+-- built with Cabal-1.24.0).+--+-- This is a best effort function that swallows any non-existent+-- SolverIds.+nonSetupClosure :: SolverPlanIndex+                -> [SolverId]+                -> SolverPlanIndex+nonSetupClosure index pkgids0 = closure Graph.empty pkgids0+ where+    closure completed []             = completed+    closure completed (pkgid:pkgids) =+      case Graph.lookup pkgid index of+        Nothing   -> closure completed pkgids+        Just pkg  ->+          case Graph.lookup (nodeKey pkg) completed of+            Just _  -> closure completed  pkgids+            Nothing -> closure completed' pkgids'+              where completed' = Graph.insert pkg completed+                    pkgids'    = CD.nonSetupDeps (resolverPackageLibDeps pkg) ++ pkgids++-- | Compute the root sets of a plan+--+-- A root set is a set of packages whose dependency closure must be consistent.+-- This is the set of all top-level library roots (taken together normally, or+-- as singletons sets if we are considering them as independent goals), along+-- with all setup dependencies of all packages.+rootSets :: IndependentGoals -> SolverPlanIndex -> [[SolverId]]+rootSets (IndependentGoals indepGoals) index =+       if indepGoals then map (:[]) libRoots else [libRoots]+    ++ setupRoots index+  where+    libRoots = libraryRoots index++-- | Compute the library roots of a plan+--+-- The library roots are the set of packages with no reverse dependencies+-- (no reverse library dependencies but also no reverse setup dependencies).+libraryRoots :: SolverPlanIndex -> [SolverId]+libraryRoots index =+    map (nodeKey . toPkgId) roots+  where+    (graph, toPkgId, _) = Graph.toGraph index+    indegree = OldGraph.indegree graph+    roots    = filter isRoot (OldGraph.vertices graph)+    isRoot v = indegree ! v == 0++-- | The setup dependencies of each package in the plan+setupRoots :: SolverPlanIndex -> [[SolverId]]+setupRoots = filter (not . null)+           . map (CD.setupDeps . resolverPackageLibDeps)+           . Graph.toList++-- | Given a package index where we assume we want to use all the packages+-- (use 'dependencyClosure' if you need to get such a index subset) find out+-- if the dependencies within it use consistent versions of each package.+-- Return all cases where multiple packages depend on different versions of+-- some other package.+--+-- Each element in the result is a package name along with the packages that+-- depend on it and the versions they require. These are guaranteed to be+-- distinct.+--+dependencyInconsistencies' :: SolverPlanIndex+                           -> [(PackageName, [(PackageIdentifier, Version)])]+dependencyInconsistencies' index =+    [ (name, [ (pid, packageVersion dep) | (dep,pids) <- uses, pid <- pids])+    | (name, ipid_map) <- Map.toList inverseIndex+    , let uses = Map.elems ipid_map+    , reallyIsInconsistent (map fst uses)+    ]+  where+    -- For each package name (of a dependency, somewhere)+    --   and each installed ID of that that package+    --     the associated package instance+    --     and a list of reverse dependencies (as source IDs)+    inverseIndex :: Map PackageName (Map SolverId (SolverPlanPackage, [PackageId]))+    inverseIndex = Map.fromListWith (Map.unionWith (\(a,b) (_,b') -> (a,b++b')))+      [ (packageName dep, Map.fromList [(sid,(dep,[packageId pkg]))])+      | -- For each package @pkg@+        pkg <- Graph.toList index+        -- Find out which @sid@ @pkg@ depends on+      , sid <- CD.nonSetupDeps (resolverPackageLibDeps pkg)+        -- And look up those @sid@ (i.e., @sid@ is the ID of @dep@)+      , Just dep <- [Graph.lookup sid index]+      ]++    -- If, in a single install plan, we depend on more than one version of a+    -- package, then this is ONLY okay in the (rather special) case that we+    -- depend on precisely two versions of that package, and one of them+    -- depends on the other. This is necessary for example for the base where+    -- we have base-3 depending on base-4.+    reallyIsInconsistent :: [SolverPlanPackage] -> Bool+    reallyIsInconsistent []       = False+    reallyIsInconsistent [_p]     = False+    reallyIsInconsistent [p1, p2] =+      let pid1 = nodeKey p1+          pid2 = nodeKey p2+      in pid1 `notElem` CD.nonSetupDeps (resolverPackageLibDeps p2)+      && pid2 `notElem` CD.nonSetupDeps (resolverPackageLibDeps p1)+    reallyIsInconsistent _ = True+++-- | The graph of packages (nodes) and dependencies (edges) must be acyclic.+--+-- * if the result is @False@ use 'PackageIndex.dependencyCycles' to find out+--   which packages are involved in dependency cycles.+--+acyclic :: SolverPlanIndex -> Bool+acyclic = null . Graph.cycles++-- | An installation plan is closed if for every package in the set, all of+-- its dependencies are also in the set. That is, the set is closed under the+-- dependency relation.+--+-- * if the result is @False@ use 'PackageIndex.brokenPackages' to find out+--   which packages depend on packages not in the index.+--+closed :: SolverPlanIndex -> Bool+closed = null . Graph.broken++-- | An installation plan is consistent if all dependencies that target a+-- single package name, target the same version.+--+-- This is slightly subtle. It is not the same as requiring that there be at+-- most one version of any package in the set. It only requires that of+-- packages which have more than one other package depending on them. We could+-- actually make the condition even more precise and say that different+-- versions are OK so long as they are not both in the transitive closure of+-- any other package (or equivalently that their inverse closures do not+-- intersect). The point is we do not want to have any packages depending+-- directly or indirectly on two different versions of the same package. The+-- current definition is just a safe approximation of that.+--+-- * if the result is @False@ use 'PackageIndex.dependencyInconsistencies' to+--   find out which packages are.+--+consistent :: SolverPlanIndex -> Bool+consistent = null . dependencyInconsistencies (IndependentGoals False)++-- | The states of packages have that depend on each other must respect+-- this relation. That is for very case where package @a@ depends on+-- package @b@ we require that @dependencyStatesOk a b = True@.+--+stateDependencyRelation :: SolverPlanPackage+                        -> SolverPlanPackage+                        -> Bool+stateDependencyRelation PreExisting{}   PreExisting{}     = True++stateDependencyRelation (Configured  _) PreExisting{}     = True+stateDependencyRelation (Configured  _) (Configured  _)   = True++stateDependencyRelation _               _                 = False+++-- | Compute the dependency closure of a package in a install plan+--+dependencyClosure :: SolverInstallPlan+                  -> [SolverId]+                  -> [SolverPlanPackage]+dependencyClosure plan = fromMaybe [] . Graph.closure (planIndex plan)+++reverseDependencyClosure :: SolverInstallPlan+                         -> [SolverId]+                         -> [SolverPlanPackage]+reverseDependencyClosure plan = fromMaybe [] . Graph.revClosure (planIndex plan)+++topologicalOrder :: SolverInstallPlan+                 -> [SolverPlanPackage]+topologicalOrder plan = Graph.topSort (planIndex plan)+++reverseTopologicalOrder :: SolverInstallPlan+                        -> [SolverPlanPackage]+reverseTopologicalOrder plan = Graph.revTopSort (planIndex plan)
+ Distribution/Client/SourceFiles.hs view
@@ -0,0 +1,168 @@+-- | Contains an @sdist@ like function which computes the source files+-- that we should track to determine if a rebuild is necessary.+-- Unlike @sdist@, we can operate directly on the true+-- 'PackageDescription' (not flattened).+--+-- The naming convention, roughly, is that to declare we need the+-- source for some type T, you use the function needT; some functions+-- need auxiliary information.+--+-- We can only use this code for non-Custom scripts; Custom scripts+-- may have arbitrary extra dependencies (esp. new preprocessors) which+-- we cannot "see" easily.+module Distribution.Client.SourceFiles (needElaboratedConfiguredPackage) where++import Distribution.Client.ProjectPlanning.Types+import Distribution.Client.RebuildMonad++import Distribution.Solver.Types.OptionalStanza++import Distribution.Simple.PreProcess++import Distribution.Types.PackageDescription+import Distribution.Types.Component+import Distribution.Types.ComponentRequestedSpec+import Distribution.Types.Library+import Distribution.Types.Executable+import Distribution.Types.Benchmark+import Distribution.Types.BenchmarkInterface+import Distribution.Types.TestSuite+import Distribution.Types.TestSuiteInterface+import Distribution.Types.BuildInfo+import Distribution.Types.ForeignLib++import Distribution.ModuleName++import Prelude ()+import Distribution.Client.Compat.Prelude++import System.FilePath+import Control.Monad+import qualified Data.Set as Set++needElaboratedConfiguredPackage :: ElaboratedConfiguredPackage -> Rebuild ()+needElaboratedConfiguredPackage elab =+    case elabPkgOrComp elab of+        ElabComponent ecomp -> needElaboratedComponent elab ecomp+        ElabPackage   epkg  -> needElaboratedPackage   elab epkg++needElaboratedPackage :: ElaboratedConfiguredPackage -> ElaboratedPackage -> Rebuild ()+needElaboratedPackage elab epkg =+    mapM_ (needComponent pkg_descr) (enabledComponents pkg_descr enabled)+  where+    pkg_descr = elabPkgDescription elab+    enabled_stanzas = pkgStanzasEnabled epkg+    -- TODO: turn this into a helper function somewhere+    enabled =+        ComponentRequestedSpec {+            testsRequested      = TestStanzas  `Set.member` enabled_stanzas,+            benchmarksRequested = BenchStanzas `Set.member` enabled_stanzas+        }++needElaboratedComponent :: ElaboratedConfiguredPackage -> ElaboratedComponent -> Rebuild ()+needElaboratedComponent elab ecomp =+    case mb_comp of+        Nothing   -> needSetup+        Just comp -> needComponent pkg_descr comp+  where+    pkg_descr = elabPkgDescription elab+    mb_comp   = fmap (getComponent pkg_descr) (compComponentName ecomp)++needComponent :: PackageDescription -> Component -> Rebuild ()+needComponent pkg_descr comp =+    case comp of+        CLib lib     -> needLibrary    pkg_descr lib+        CFLib flib   -> needForeignLib pkg_descr flib+        CExe exe     -> needExecutable pkg_descr exe+        CTest test   -> needTestSuite  pkg_descr test+        CBench bench -> needBenchmark  pkg_descr bench++needSetup :: Rebuild ()+needSetup = findFirstFileMonitored id ["Setup.hs", "Setup.lhs"] >> return ()++needLibrary :: PackageDescription -> Library -> Rebuild ()+needLibrary pkg_descr (Library { exposedModules = modules+                               , signatures     = sigs+                               , libBuildInfo   = bi })+  = needBuildInfo pkg_descr bi (modules ++ sigs)++needForeignLib :: PackageDescription -> ForeignLib -> Rebuild ()+needForeignLib pkg_descr (ForeignLib { foreignLibModDefFile = fs+                                     , foreignLibBuildInfo = bi })+  = do mapM_ needIfExists fs+       needBuildInfo pkg_descr bi []++needExecutable :: PackageDescription -> Executable -> Rebuild ()+needExecutable pkg_descr (Executable { modulePath = mainPath+                                     , buildInfo  = bi })+  = do needBuildInfo pkg_descr bi []+       needMainFile  bi mainPath++needTestSuite :: PackageDescription -> TestSuite -> Rebuild ()+needTestSuite pkg_descr t+  = case testInterface t of+      TestSuiteExeV10 _ mainPath -> do+        needBuildInfo pkg_descr bi []+        needMainFile  bi mainPath+      TestSuiteLibV09 _ m ->+        needBuildInfo pkg_descr bi [m]+      TestSuiteUnsupported _ -> return () -- soft fail+ where+  bi = testBuildInfo t++needMainFile :: BuildInfo -> FilePath -> Rebuild ()+needMainFile bi mainPath = do+    -- The matter here is subtle.  It might *seem* that we+    -- should just search for mainPath, but as per+    -- b61cb051f63ed5869b8f4a6af996ff7e833e4b39 'main-is'+    -- will actually be the source file AFTER preprocessing,+    -- whereas we need to get the file *prior* to preprocessing.+    ppFile <- findFileWithExtensionMonitored+                (ppSuffixes knownSuffixHandlers)+                (hsSourceDirs bi)+                (dropExtension mainPath)+    case ppFile of+        -- But check the original path in the end, because+        -- maybe it's a non-preprocessed file with a non-traditional+        -- extension.+        Nothing -> findFileMonitored (hsSourceDirs bi) mainPath+                    >>= maybe (return ()) need+        Just pp -> need pp++needBenchmark :: PackageDescription -> Benchmark -> Rebuild ()+needBenchmark pkg_descr bm+  = case benchmarkInterface bm of+     BenchmarkExeV10 _ mainPath -> do+       needBuildInfo pkg_descr bi []+       needMainFile  bi mainPath+     BenchmarkUnsupported _ -> return () -- soft fail+ where+  bi = benchmarkBuildInfo bm++needBuildInfo :: PackageDescription -> BuildInfo -> [ModuleName] -> Rebuild ()+needBuildInfo pkg_descr bi modules = do+    -- NB: These are separate because there may be both A.hs and+    -- A.hs-boot; need to track both.+    findNeededModules ["hs", "lhs", "hsig", "lhsig"]+    findNeededModules ["hs-boot", "lhs-boot"]+    mapM_ needIfExists (cSources bi ++ jsSources bi)+    -- A MASSIVE HACK to (1) make sure we rebuild when header+    -- files change, but (2) not have to rebuild when anything+    -- in extra-src-files changes (most of these won't affect+    -- compilation).  It would be even better if we knew on a+    -- per-component basis which headers would be used but that+    -- seems to be too difficult.+    mapM_ needIfExists (filter ((==".h").takeExtension) (extraSrcFiles pkg_descr))+    forM_ (installIncludes bi) $ \f ->+        findFileMonitored ("." : includeDirs bi) f+            >>= maybe (return ()) need+  where+    findNeededModules exts =+        mapM_ (findNeededModule exts)+              (modules ++ otherModules bi)+    findNeededModule exts m =+        findFileWithExtensionMonitored+            (ppSuffixes knownSuffixHandlers ++ exts)+            (hsSourceDirs bi)+            (toFilePath m)+          >>= maybe (return ()) need
Distribution/Client/SrcDist.hs view
@@ -1,4 +1,6 @@+{-# LANGUAGE CPP #-} {-# LANGUAGE NondecreasingIndentation #-}+{-# LANGUAGE FlexibleContexts #-} -- Implements the \"@.\/cabal sdist@\" command, which creates a source -- distribution for this package.  That is, packs up the source code -- into a tarball, making use of the corresponding Cabal module.@@ -18,11 +20,16 @@          ( PackageDescription ) import Distribution.PackageDescription.Configuration          ( flattenPackageDescription )+#ifdef CABAL_PARSEC+import Distribution.PackageDescription.Parsec+         ( readGenericPackageDescription )+#else import Distribution.PackageDescription.Parse-         ( readPackageDescription )+         ( readGenericPackageDescription )+#endif import Distribution.Simple.Utils          ( createDirectoryIfMissingVerbose, defaultPackageDesc-         , warn, die, notice, withTempDirectory )+         , warn, die', notice, withTempDirectory ) import Distribution.Client.Setup          ( SDistFlags(..), SDistExFlags(..), ArchiveFormat(..) ) import Distribution.Simple.Setup@@ -33,7 +40,7 @@ import Distribution.Simple.Program.Db (emptyProgramDb) import Distribution.Text ( display ) import Distribution.Verbosity (Verbosity, normal, lessVerbose)-import Distribution.Version   (Version(..), orLaterVersion)+import Distribution.Version   (mkVersion, orLaterVersion, intersectVersionRanges)  import Distribution.Client.Utils   (tryFindAddSourcePackageDesc)@@ -50,8 +57,9 @@ sdist :: SDistFlags -> SDistExFlags -> IO () sdist flags exflags = do   pkg <- liftM flattenPackageDescription-    (readPackageDescription verbosity =<< defaultPackageDesc verbosity)-  let withDir = if not needMakeArchive then (\f -> f tmpTargetDir)+    (readGenericPackageDescription verbosity =<< defaultPackageDesc verbosity)+  let withDir :: (FilePath -> IO a) -> IO a+      withDir = if not needMakeArchive then \f -> f tmpTargetDir                 else withTempDirectory verbosity tmpTargetDir "sdist."   -- 'withTempDir' fails if we don't create 'tmpTargetDir'...   when needMakeArchive $@@ -94,8 +102,8 @@       -- The '--output-directory' sdist flag was introduced in Cabal 1.12, and       -- '--list-sources' in 1.17.       useCabalVersion = if isListSources-                        then orLaterVersion $ Version [1,17,0] []-                        else orLaterVersion $ Version [1,12,0] []+                        then orLaterVersion $ mkVersion [1,17,0]+                        else orLaterVersion $ mkVersion [1,12,0]       }     format        = fromFlag (sDistFormat exflags)     createArchive = case format of@@ -140,7 +148,7 @@                       Nothing Nothing Nothing Nothing     exitCode <- waitForProcess hnd     unless (exitCode == ExitSuccess) $-      die $ "Generating the zip file failed "+      die' verbosity $ "Generating the zip file failed "          ++ "(zip returned exit code " ++ show exitCode ++ ")"     notice verbosity $ "Source zip archive created: " ++ zipfile   where@@ -148,12 +156,13 @@  -- | List all source files of a given add-source dependency. Exits with error if -- something is wrong (e.g. there is no .cabal file in the given directory).-allPackageSourceFiles :: Verbosity -> FilePath -> IO [FilePath]-allPackageSourceFiles verbosity packageDir = do+allPackageSourceFiles :: Verbosity -> SetupScriptOptions -> FilePath+                         -> IO [FilePath]+allPackageSourceFiles verbosity setupOpts0 packageDir = do   pkg <- do     let err = "Error reading source files of package."-    desc <- tryFindAddSourcePackageDesc packageDir err-    flattenPackageDescription `fmap` readPackageDescription verbosity desc+    desc <- tryFindAddSourcePackageDesc verbosity packageDir err+    flattenPackageDescription `fmap` readGenericPackageDescription verbosity desc   globalTmp <- getTemporaryDirectory   withTempDirectory verbosity globalTmp "cabal-list-sources." $ \tempDir -> do   let file      = tempDir </> "cabal-sdist-list-sources"@@ -162,9 +171,11 @@                                   then lessVerbose verbosity else verbosity,         sDistListSources = Flag file         }-      setupOpts = defaultSetupScriptOptions {+      setupOpts = setupOpts0 {         -- 'sdist --list-sources' was introduced in Cabal 1.18.-        useCabalVersion = orLaterVersion $ Version [1,18,0] [],+        useCabalVersion = intersectVersionRanges+                            (orLaterVersion $ mkVersion [1,18,0])+                            (useCabalVersion setupOpts0),         useWorkingDir = Just packageDir         } 
+ Distribution/Client/Store.hs view
@@ -0,0 +1,246 @@+{-# LANGUAGE RecordWildCards, NamedFieldPuns #-}+++-- | Management for the installed package store.+--+module Distribution.Client.Store (++    -- * The store layout+    StoreDirLayout(..),+    defaultStoreDirLayout,++    -- * Reading store entries+    getStoreEntries,+    doesStoreEntryExist,++    -- * Creating store entries+    newStoreEntry,+    NewStoreEntryOutcome(..),++    -- * Concurrency strategy+    -- $concurrency+  ) where++import Prelude ()+import Distribution.Client.Compat.Prelude+import Distribution.Client.Compat.FileLock++import           Distribution.Client.DistDirLayout+import           Distribution.Client.RebuildMonad++import           Distribution.Package (UnitId, mkUnitId)+import           Distribution.Compiler (CompilerId)++import           Distribution.Simple.Utils+                   ( withTempDirectory, debug, info )+import           Distribution.Verbosity+import           Distribution.Text++import           Data.Set (Set)+import qualified Data.Set as Set+import           Control.Exception+import           System.FilePath+import           System.Directory+import           System.IO+++-- $concurrency+--+-- We access and update the store concurrently. Our strategy to do that safely+-- is as follows.+--+-- The store entries once created are immutable. This alone simplifies matters+-- considerably.+--+-- Additionally, the way 'UnitId' hashes are constructed means that if a store+-- entry exists already then we can assume its content is ok to reuse, rather+-- than having to re-recreate. This is the nix-style input hashing concept.+--+-- A consequence of this is that with a little care it is /safe/ to race+-- updates against each other. Consider two independent concurrent builds that+-- both want to build a particular 'UnitId', where that entry does not yet+-- exist in the store. It is safe for both to build and try to install this+-- entry into the store provided that:+--+-- * only one succeeds+-- * the looser discovers that they lost, they abandon their own build and+--   re-use the store entry installed by the winner.+--+-- Note that because builds are not reproducible in general (nor even+-- necessarily ABI compatible) then it is essential that the loser abandon+-- their build and use the one installed by the winner, so that subsequent+-- packages are built against the exact package from the store rather than some+-- morally equivalent package that may not be ABI compatible.+--+-- Our overriding goal is that store reads be simple, cheap and not require+-- locking. We will derive our write-side protocol to make this possible.+--+-- The read-side protocol is simply:+--+-- * check for the existence of a directory entry named after the 'UnitId' in+--   question. That is, if the dir entry @$root/foo-1.0-fe56a...@ exists then+--   the store entry can be assumed to be complete and immutable.+--+-- Given our read-side protocol, the final step on the write side must be to+-- atomically rename a fully-formed store entry directory into its final+-- location. While this will indeed be the final step, the preparatory steps+-- are more complicated. The tricky aspect is that the store also contains a+-- number of shared package databases (one per compiler version). Our read+-- strategy means that by the time we install the store dir entry the package+-- db must already have been updated. We cannot do the package db update+-- as part of atomically renaming the store entry directory however. Furthermore+-- it is not safe to allow either package db update because the db entry+-- contains the ABI hash and this is not guaranteed to be deterministic. So we+-- must register the new package prior to the atomic dir rename. Since this+-- combination of steps are not atomic then we need locking.+--+-- The write-side protocol is:+--+-- * Create a unique temp dir and write all store entry files into it.+--+-- * Take a lock named after the 'UnitId' in question.+--+-- * Once holding the lock, check again for the existence of the final store+--   entry directory. If the entry exists then the process lost the race and it+--   must abandon, unlock and re-use the existing store entry. If the entry+--   does not exist then the process won the race and it can proceed.+--+-- * Register the package into the package db. Note that the files are not in+--   their final location at this stage so registration file checks may need+--   to be disabled.+--+-- * Atomically rename the temp dir to the final store entry location.+--+-- * Release the previously-acquired lock.+--+-- Obviously this means it is possible to fail after registering but before+-- installing the store entry, leaving a dangling package db entry. This is not+-- much of a problem because this entry does not determine package existence+-- for cabal. It does mean however that the package db update should be insert+-- or replace, i.e. not failing if the db entry already exists.+++-- | Check if a particular 'UnitId' exists in the store.+--+doesStoreEntryExist :: StoreDirLayout -> CompilerId -> UnitId -> IO Bool+doesStoreEntryExist StoreDirLayout{storePackageDirectory} compid unitid =+    doesDirectoryExist (storePackageDirectory compid unitid)+++-- | Return the 'UnitId's of all packages\/components already installed in the+-- store.+--+getStoreEntries :: StoreDirLayout -> CompilerId -> Rebuild (Set UnitId)+getStoreEntries StoreDirLayout{storeDirectory} compid = do+    paths <- getDirectoryContentsMonitored (storeDirectory compid)+    return $! mkEntries paths+  where+    mkEntries     = Set.delete (mkUnitId "package.db")+                  . Set.delete (mkUnitId "incoming")+                  . Set.fromList+                  . map mkUnitId+                  . filter valid+    valid ('.':_) = False+    valid _       = True+++-- | The outcome of 'newStoreEntry': either the store entry was newly created+-- or it existed already. The latter case happens if there was a race between+-- two builds of the same store entry.+--+data NewStoreEntryOutcome = UseNewStoreEntry+                          | UseExistingStoreEntry+  deriving (Eq, Show)++-- | Place a new entry into the store. See the concurrency strategy description+-- for full details.+--+-- In particular, it takes two actions: one to place files into a temporary+-- location, and a second to perform any necessary registration. The first+-- action is executed without any locks held (the temp dir is unique). The+-- second action holds a lock that guarantees that only one cabal process is+-- able to install this store entry. This means it is safe to register into+-- the compiler package DB or do other similar actions.+--+-- Note that if you need to use the registration information later then you+-- /must/ check the 'NewStoreEntryOutcome' and if it's'UseExistingStoreEntry'+-- then you must read the existing registration information (unless your+-- registration information is constructed fully deterministically).+--+newStoreEntry :: Verbosity+              -> StoreDirLayout+              -> CompilerId+              -> UnitId+              -> (FilePath -> IO FilePath) -- ^ Action to place files.+              -> IO ()                     -- ^ Register action, if necessary.+              -> IO NewStoreEntryOutcome+newStoreEntry verbosity storeDirLayout@StoreDirLayout{..}+              compid unitid+              copyFiles register =+    -- See $concurrency above for an explanation of the concurrency protocol++    withTempIncomingDir storeDirLayout compid $ \incomingTmpDir -> do++      -- Write all store entry files within the temp dir and return the prefix.+      incomingEntryDir <- copyFiles incomingTmpDir++      -- Take a lock named after the 'UnitId' in question.+      withIncomingUnitIdLock verbosity storeDirLayout compid unitid $ do++        -- Check for the existence of the final store entry directory.+        exists <- doesStoreEntryExist storeDirLayout compid unitid++        if exists+          -- If the entry exists then we lost the race and we must abandon,+          -- unlock and re-use the existing store entry.+          then do+            info verbosity $+                "Concurrent build race: abandoning build in favour of existing "+             ++ "store entry " ++ display compid </> display unitid+            return UseExistingStoreEntry++          -- If the entry does not exist then we won the race and can proceed.+          else do++            -- Register the package into the package db (if appropriate).+            register++            -- Atomically rename the temp dir to the final store entry location.+            renameDirectory incomingEntryDir finalEntryDir++            debug verbosity $+              "Installed store entry " ++ display compid </> display unitid+            return UseNewStoreEntry+  where+    finalEntryDir = storePackageDirectory compid unitid+++withTempIncomingDir :: StoreDirLayout -> CompilerId+                    -> (FilePath -> IO a) -> IO a+withTempIncomingDir StoreDirLayout{storeIncomingDirectory} compid action = do+    createDirectoryIfMissing True incomingDir+    withTempDirectory silent incomingDir "new" action+  where+    incomingDir = storeIncomingDirectory compid+++withIncomingUnitIdLock :: Verbosity -> StoreDirLayout+                       -> CompilerId -> UnitId+                       -> IO a -> IO a+withIncomingUnitIdLock verbosity StoreDirLayout{storeIncomingLock}+                       compid unitid action =+    bracket takeLock releaseLock (\_hnd -> action)+  where+    takeLock = do+      h <- openFile (storeIncomingLock compid unitid) ReadWriteMode+      -- First try non-blocking, but if we would have to wait then+      -- log an explanation and do it again in blocking mode.+      gotlock <- hTryLock h ExclusiveLock+      unless gotlock $ do+        info verbosity $ "Waiting for file lock on store entry "+                      ++ display compid </> display unitid+        hLock h ExclusiveLock+      return h++    releaseLock = hClose+
+ Distribution/Client/TargetSelector.hs view
@@ -0,0 +1,2246 @@+{-# LANGUAGE CPP, DeriveGeneric, DeriveFunctor, RecordWildCards #-}+-----------------------------------------------------------------------------+-- |+-- Module      :  Distribution.Client.TargetSelector+-- Copyright   :  (c) Duncan Coutts 2012, 2015, 2016+-- License     :  BSD-like+--+-- Maintainer  :  duncan@community.haskell.org+--+-- Handling for user-specified target selectors.+--+-----------------------------------------------------------------------------+module Distribution.Client.TargetSelector (++    -- * Target selectors+    TargetSelector(..),+    TargetImplicitCwd(..),+    ComponentKind(..),+    SubComponentTarget(..),+    QualLevel(..),+    componentKind,++    -- * Reading target selectors+    readTargetSelectors,+    TargetSelectorProblem(..),+    reportTargetSelectorProblems,+    showTargetSelector,+    TargetString,+    showTargetString,+    parseTargetString,+    -- ** non-IO+    readTargetSelectorsWith,+    DirActions(..),+    defaultDirActions,+  ) where++import Distribution.Package+         ( Package(..), PackageId, PackageIdentifier(..), packageName+         , mkPackageName )+import Distribution.Version+         ( mkVersion )+import Distribution.Types.UnqualComponentName ( unUnqualComponentName )+import Distribution.Client.Types+         ( PackageLocation(..) )++import Distribution.Verbosity+import Distribution.PackageDescription+         ( PackageDescription+         , Executable(..)+         , TestSuite(..), TestSuiteInterface(..), testModules+         , Benchmark(..), BenchmarkInterface(..), benchmarkModules+         , BuildInfo(..), explicitLibModules, exeModules )+import Distribution.PackageDescription.Configuration+         ( flattenPackageDescription )+import Distribution.Solver.Types.SourcePackage+         ( SourcePackage(..) )+import Distribution.ModuleName+         ( ModuleName, toFilePath )+import Distribution.Simple.LocalBuildInfo+         ( Component(..), ComponentName(..)+         , pkgComponents, componentName, componentBuildInfo )+import Distribution.Types.ForeignLib++import Distribution.Text+         ( display, simpleParse )+import Distribution.Simple.Utils+         ( die', lowercase, ordNub )+import Distribution.Client.Utils+         ( makeRelativeCanonical )++import Data.Either+         ( partitionEithers )+import Data.Function+         ( on )+import Data.List+         ( nubBy, stripPrefix, partition, intercalate, sortBy, groupBy )+import Data.Maybe+         ( maybeToList )+import Data.Ord+         ( comparing )+import Distribution.Compat.Binary (Binary)+import GHC.Generics (Generic)+#if MIN_VERSION_containers(0,5,0)+import qualified Data.Map.Lazy   as Map.Lazy+import qualified Data.Map.Strict as Map+import Data.Map.Strict (Map)+#else+import qualified Data.Map as Map.Lazy+import qualified Data.Map as Map+import Data.Map (Map)+#endif+import qualified Data.Set as Set+import Control.Arrow ((&&&))+import Control.Monad+#if __GLASGOW_HASKELL__ < 710+import Control.Applicative (Applicative(..), (<$>))+#endif+import Control.Applicative (Alternative(..))+import qualified Distribution.Compat.ReadP as Parse+import Distribution.Compat.ReadP+         ( (+++), (<++) )+import Distribution.ParseUtils+         ( readPToMaybe )+import Data.Char+         ( isSpace, isAlphaNum )+import System.FilePath as FilePath+         ( takeExtension, dropExtension+         , splitDirectories, joinPath, splitPath )+import qualified System.Directory as IO+         ( doesFileExist, doesDirectoryExist, canonicalizePath+         , getCurrentDirectory )+import System.FilePath+         ( (</>), (<.>), normalise, dropTrailingPathSeparator )+import Text.EditDistance+         ( defaultEditCosts, restrictedDamerauLevenshteinDistance )+++-- ------------------------------------------------------------+-- * Target selector terms+-- ------------------------------------------------------------++-- | A target selector is expression selecting a set of components (as targets+-- for a actions like @build@, @run@, @test@ etc). A target selector+-- corresponds to the user syntax for referring to targets on the command line.+--+-- From the users point of view a target can be many things: packages, dirs,+-- component names, files etc. Internally we consider a target to be a specific+-- component (or module\/file within a component), and all the users' notions+-- of targets are just different ways of referring to these component targets.+--+-- So target selectors are expressions in the sense that they are interpreted+-- to refer to one or more components. For example a 'TargetPackage' gets+-- interpreted differently by different commands to refer to all or a subset+-- of components within the package.+--+-- The syntax has lots of optional parts:+--+-- > [ package name | package dir | package .cabal file ]+-- > [ [lib:|exe:] component name ]+-- > [ module name | source file ]+--+data TargetSelector pkg =++     -- | A package as a whole: the default components for the package or all+     -- components of a particular kind.+     --+     TargetPackage TargetImplicitCwd pkg (Maybe ComponentKindFilter)++     -- | All packages, or all components of a particular kind in all packages.+     --+   | TargetAllPackages (Maybe ComponentKindFilter)++     -- | A specific component in a package.+     --+   | TargetComponent pkg ComponentName SubComponentTarget+  deriving (Eq, Ord, Functor, Show, Generic)++-- | Does this 'TargetPackage' selector arise from syntax referring to a+-- packge in the current directory (e.g. @tests@ or no giving no explicit+-- target at all) or does it come from syntax referring to a package name+-- or location.+--+data TargetImplicitCwd = TargetImplicitCwd | TargetExplicitNamed+  deriving (Eq, Ord, Show, Generic)++data ComponentKind = LibKind | FLibKind | ExeKind | TestKind | BenchKind+  deriving (Eq, Ord, Enum, Show)++type ComponentKindFilter = ComponentKind++-- | Either the component as a whole or detail about a file or module target+-- within a component.+--+data SubComponentTarget =++     -- | The component as a whole+     WholeComponent++     -- | A specific module within a component.+   | ModuleTarget ModuleName++     -- | A specific file within a component.+   | FileTarget   FilePath+  deriving (Eq, Ord, Show, Generic)++instance Binary SubComponentTarget+++-- ------------------------------------------------------------+-- * Top level, do everything+-- ------------------------------------------------------------+++-- | Parse a bunch of command line args as 'TargetSelector's, failing with an+-- error if any are unrecognised. The possible target selectors are based on+-- the available packages (and their locations).+--+readTargetSelectors :: [SourcePackage (PackageLocation a)]+                    -> [String]+                    -> IO (Either [TargetSelectorProblem]+                                  [TargetSelector PackageId])+readTargetSelectors = readTargetSelectorsWith defaultDirActions++readTargetSelectorsWith :: (Applicative m, Monad m) => DirActions m+                        -> [SourcePackage (PackageLocation a)]+                        -> [String]+                        -> m (Either [TargetSelectorProblem]+                                     [TargetSelector PackageId])+readTargetSelectorsWith dirActions@DirActions{..} pkgs targetStrs =+    case parseTargetStrings targetStrs of+      ([], utargets) -> do+        utargets' <- mapM (getTargetStringFileStatus dirActions) utargets+        pkgs'     <- mapM (selectPackageInfo dirActions) pkgs+        cwd       <- getCurrentDirectory+        let (cwdPkg, otherPkgs) = selectCwdPackage cwd pkgs'+        case resolveTargetSelectors cwdPkg otherPkgs utargets' of+          ([], btargets) -> return (Right (map (fmap packageId) btargets))+          (problems, _)  -> return (Left problems)+      (strs, _)          -> return (Left (map TargetSelectorUnrecognised strs))+  where+    selectCwdPackage :: FilePath+                     -> [PackageInfo]+                     -> ([PackageInfo], [PackageInfo])+    selectCwdPackage cwd pkgs' =+        let (cwdpkg, others) = partition isPkgDirCwd pkgs'+         in (cwdpkg, others)+      where+        isPkgDirCwd PackageInfo { pinfoDirectory = Just (dir,_) }+          | dir == cwd = True+        isPkgDirCwd _  = False++data DirActions m = DirActions {+       doesFileExist       :: FilePath -> m Bool,+       doesDirectoryExist  :: FilePath -> m Bool,+       canonicalizePath    :: FilePath -> m FilePath,+       getCurrentDirectory :: m FilePath+     }++defaultDirActions :: DirActions IO+defaultDirActions =+    DirActions {+      doesFileExist       = IO.doesFileExist,+      doesDirectoryExist  = IO.doesDirectoryExist,+      -- Workaround for <https://github.com/haskell/directory/issues/63>+      canonicalizePath    = IO.canonicalizePath . dropTrailingPathSeparator,+      getCurrentDirectory = IO.getCurrentDirectory+    }++makeRelativeToCwd :: Applicative m => DirActions m -> FilePath -> m FilePath+makeRelativeToCwd DirActions{..} path =+    makeRelativeCanonical <$> canonicalizePath path <*> getCurrentDirectory+++-- ------------------------------------------------------------+-- * Parsing target strings+-- ------------------------------------------------------------++-- | The outline parse of a target selector. It takes one of the forms:+--+-- > str1+-- > str1:str2+-- > str1:str2:str3+-- > str1:str2:str3:str4+--+data TargetString =+     TargetString1 String+   | TargetString2 String String+   | TargetString3 String String String+   | TargetString4 String String String String+   | TargetString5 String String String String String+   | TargetString7 String String String String String String String+  deriving (Show, Eq)++-- | Parse a bunch of 'TargetString's (purely without throwing exceptions).+--+parseTargetStrings :: [String] -> ([String], [TargetString])+parseTargetStrings =+    partitionEithers+  . map (\str -> maybe (Left str) Right (parseTargetString str))++parseTargetString :: String -> Maybe TargetString+parseTargetString =+    readPToMaybe parseTargetApprox+  where+    parseTargetApprox :: Parse.ReadP r TargetString+    parseTargetApprox =+          (do a <- tokenQ+              return (TargetString1 a))+      +++ (do a <- tokenQ0+              _ <- Parse.char ':'+              b <- tokenQ+              return (TargetString2 a b))+      +++ (do a <- tokenQ0+              _ <- Parse.char ':'+              b <- tokenQ+              _ <- Parse.char ':'+              c <- tokenQ+              return (TargetString3 a b c))+      +++ (do a <- tokenQ0+              _ <- Parse.char ':'+              b <- token+              _ <- Parse.char ':'+              c <- tokenQ+              _ <- Parse.char ':'+              d <- tokenQ+              return (TargetString4 a b c d))+      +++ (do a <- tokenQ0+              _ <- Parse.char ':'+              b <- token+              _ <- Parse.char ':'+              c <- tokenQ+              _ <- Parse.char ':'+              d <- tokenQ+              _ <- Parse.char ':'+              e <- tokenQ+              return (TargetString5 a b c d e))+      +++ (do a <- tokenQ0+              _ <- Parse.char ':'+              b <- token+              _ <- Parse.char ':'+              c <- tokenQ+              _ <- Parse.char ':'+              d <- tokenQ+              _ <- Parse.char ':'+              e <- tokenQ+              _ <- Parse.char ':'+              f <- tokenQ+              _ <- Parse.char ':'+              g <- tokenQ+              return (TargetString7 a b c d e f g))++    token  = Parse.munch1 (\x -> not (isSpace x) && x /= ':')+    tokenQ = parseHaskellString <++ token+    token0 = Parse.munch (\x -> not (isSpace x) && x /= ':')+    tokenQ0= parseHaskellString <++ token0+    parseHaskellString :: Parse.ReadP r String+    parseHaskellString = Parse.readS_to_P reads+++-- | Render a 'TargetString' back as the external syntax. This is mainly for+-- error messages.+--+showTargetString :: TargetString -> String+showTargetString = intercalate ":" . components+  where+    components (TargetString1 s1)          = [s1]+    components (TargetString2 s1 s2)       = [s1,s2]+    components (TargetString3 s1 s2 s3)    = [s1,s2,s3]+    components (TargetString4 s1 s2 s3 s4) = [s1,s2,s3,s4]+    components (TargetString5 s1 s2 s3 s4 s5)       = [s1,s2,s3,s4,s5]+    components (TargetString7 s1 s2 s3 s4 s5 s6 s7) = [s1,s2,s3,s4,s5,s6,s7]++showTargetSelector :: Package p => TargetSelector p -> String+showTargetSelector ts =+  let (t':_) = [ t | ql <- [QL1 .. QLFull]+                   , t  <- renderTargetSelector ql ts ]+   in showTargetString (forgetFileStatus t')++showTargetSelectorKind :: TargetSelector a -> String+showTargetSelectorKind bt = case bt of+  TargetPackage TargetExplicitNamed _ Nothing  -> "package"+  TargetPackage TargetExplicitNamed _ (Just _) -> "package:filter"+  TargetPackage TargetImplicitCwd   _ Nothing  -> "cwd-package"+  TargetPackage TargetImplicitCwd   _ (Just _) -> "cwd-package:filter"+  TargetAllPackages Nothing                    -> "all-packages"+  TargetAllPackages (Just _)                   -> "all-packages:filter"+  TargetComponent _ _ WholeComponent           -> "component"+  TargetComponent _ _ ModuleTarget{}           -> "module"+  TargetComponent _ _ FileTarget{}             -> "file"+++-- ------------------------------------------------------------+-- * Checking if targets exist as files+-- ------------------------------------------------------------++data TargetStringFileStatus =+     TargetStringFileStatus1 String FileStatus+   | TargetStringFileStatus2 String FileStatus String+   | TargetStringFileStatus3 String FileStatus String String+   | TargetStringFileStatus4 String String String String+   | TargetStringFileStatus5 String String String String String+   | TargetStringFileStatus7 String String String String String String String+  deriving (Eq, Ord, Show)++data FileStatus = FileStatusExistsFile FilePath -- the canonicalised filepath+                | FileStatusExistsDir  FilePath -- the canonicalised filepath+                | FileStatusNotExists  Bool -- does the parent dir exist even?+  deriving (Eq, Ord, Show)++noFileStatus :: FileStatus+noFileStatus = FileStatusNotExists False++getTargetStringFileStatus :: (Applicative m, Monad m) => DirActions m+                          -> TargetString -> m TargetStringFileStatus+getTargetStringFileStatus DirActions{..} t =+    case t of+      TargetString1 s1 ->+        (\f1 -> TargetStringFileStatus1 s1 f1)          <$> fileStatus s1+      TargetString2 s1 s2 ->+        (\f1 -> TargetStringFileStatus2 s1 f1 s2)       <$> fileStatus s1+      TargetString3 s1 s2 s3 ->+        (\f1 -> TargetStringFileStatus3 s1 f1 s2 s3)    <$> fileStatus s1+      TargetString4 s1 s2 s3 s4 ->+        return (TargetStringFileStatus4 s1 s2 s3 s4)+      TargetString5 s1 s2 s3 s4 s5 ->+        return (TargetStringFileStatus5 s1 s2 s3 s4 s5)+      TargetString7 s1 s2 s3 s4 s5 s6 s7 ->+        return (TargetStringFileStatus7 s1 s2 s3 s4 s5 s6 s7)+  where+    fileStatus f = do+      fexists <- doesFileExist f+      dexists <- doesDirectoryExist f+      case splitPath f of+        _ | fexists -> FileStatusExistsFile <$> canonicalizePath f+          | dexists -> FileStatusExistsDir  <$> canonicalizePath f+        (d:_)       -> FileStatusNotExists  <$> doesDirectoryExist d+        _           -> pure (FileStatusNotExists False)++forgetFileStatus :: TargetStringFileStatus -> TargetString+forgetFileStatus t = case t of+    TargetStringFileStatus1 s1 _          -> TargetString1 s1+    TargetStringFileStatus2 s1 _ s2       -> TargetString2 s1 s2+    TargetStringFileStatus3 s1 _ s2 s3    -> TargetString3 s1 s2 s3+    TargetStringFileStatus4 s1   s2 s3 s4 -> TargetString4 s1 s2 s3 s4+    TargetStringFileStatus5 s1   s2 s3 s4+                                       s5 -> TargetString5 s1 s2 s3 s4 s5+    TargetStringFileStatus7 s1   s2 s3 s4+                                 s5 s6 s7 -> TargetString7 s1 s2 s3 s4 s5 s6 s7+++-- ------------------------------------------------------------+-- * Resolving target strings to target selectors+-- ------------------------------------------------------------+++-- | Given a bunch of user-specified targets, try to resolve what it is they+-- refer to.+--+resolveTargetSelectors :: [PackageInfo]     -- any pkg in the cur dir+                       -> [PackageInfo]     -- all the other local packages+                       -> [TargetStringFileStatus]+                       -> ([TargetSelectorProblem],+                           [TargetSelector PackageInfo])++-- default local dir target if there's no given target:+resolveTargetSelectors [] [] [] =+    ([TargetSelectorNoTargetsInProject], [])++resolveTargetSelectors [] _opinfo [] =+    ([TargetSelectorNoTargetsInCwd], [])++resolveTargetSelectors ppinfo _opinfo [] =+    ([], [TargetPackage TargetImplicitCwd (head ppinfo) Nothing])+    --TODO: in future allow multiple packages in the same dir++resolveTargetSelectors ppinfo opinfo targetStrs =+    partitionEithers+  . map (resolveTargetSelector ppinfo opinfo)+  $ targetStrs++resolveTargetSelector :: [PackageInfo] -> [PackageInfo]+                      -> TargetStringFileStatus+                      -> Either TargetSelectorProblem+                                (TargetSelector PackageInfo)+resolveTargetSelector ppinfo opinfo targetStrStatus =+    case findMatch (matcher targetStrStatus) of++      Unambiguous _+        | projectIsEmpty -> Left TargetSelectorNoTargetsInProject++      Unambiguous (TargetPackage TargetImplicitCwd _ mkfilter)+        | null ppinfo -> Left (TargetSelectorNoCurrentPackage targetStr)+        | otherwise   -> Right (TargetPackage TargetImplicitCwd+                                              (head ppinfo) mkfilter)+                       --TODO: in future allow multiple packages in the same dir++      Unambiguous target -> Right target++      None errs+        | projectIsEmpty       -> Left TargetSelectorNoTargetsInProject+        | otherwise            -> Left (classifyMatchErrors errs)++      Ambiguous exactMatch targets ->+        case disambiguateTargetSelectors+               matcher targetStrStatus exactMatch+               targets of+          Right targets'   -> Left (TargetSelectorAmbiguous targetStr+                                       (map (fmap (fmap packageId)) targets'))+          Left ((m, ms):_) -> Left (MatchingInternalError targetStr+                                       (fmap packageId m)+                                       (map (fmap (map (fmap packageId))) ms))+          Left []          -> internalError "resolveTargetSelector"+  where+    matcher = matchTargetSelector ppinfo opinfo++    targetStr = forgetFileStatus targetStrStatus++    projectIsEmpty = null ppinfo && null opinfo++    classifyMatchErrors errs+      | not (null expected)+      = let (things, got:_) = unzip expected in+        TargetSelectorExpected targetStr things got++      | not (null nosuch)+      = TargetSelectorNoSuch targetStr nosuch++      | otherwise+      = internalError $ "classifyMatchErrors: " ++ show errs+      where+        expected = [ (thing, got)+                   | (_, MatchErrorExpected thing got)+                           <- map (innerErr Nothing) errs ]+        -- Trim the list of alternatives by dropping duplicates and+        -- retaining only at most three most similar (by edit distance) ones.+        nosuch   = Map.foldrWithKey genResults [] $ Map.fromListWith Set.union $+          [ ((inside, thing, got), Set.fromList alts)+          | (inside, MatchErrorNoSuch thing got alts)+            <- map (innerErr Nothing) errs+          ]++        genResults (inside, thing, got) alts acc = (+            inside+          , thing+          , got+          , take maxResults+            $ map fst+            $ takeWhile distanceLow+            $ sortBy (comparing snd)+            $ map addLevDist+            $ Set.toList alts+          ) : acc+          where+            addLevDist = id &&& restrictedDamerauLevenshteinDistance+                                defaultEditCosts got++            distanceLow (_, dist) = dist < length got `div` 2++            maxResults = 3++        innerErr _ (MatchErrorIn kind thing m)+                     = innerErr (Just (kind,thing)) m+        innerErr c m = (c,m)++-- | The various ways that trying to resolve a 'TargetString' to a+-- 'TargetSelector' can fail.+--+data TargetSelectorProblem+   = TargetSelectorExpected TargetString [String]  String+     -- ^  [expected thing] (actually got)+   | TargetSelectorNoSuch  TargetString+                           [(Maybe (String, String), String, String, [String])]+     -- ^ [([in thing], no such thing,  actually got, alternatives)]+   | TargetSelectorAmbiguous  TargetString+                              [(TargetString, TargetSelector PackageId)]++   | MatchingInternalError TargetString (TargetSelector PackageId)+                           [(TargetString, [TargetSelector PackageId])]+   | TargetSelectorUnrecognised String+     -- ^ Syntax error when trying to parse a target string.+   | TargetSelectorNoCurrentPackage TargetString+   | TargetSelectorNoTargetsInCwd+   | TargetSelectorNoTargetsInProject+  deriving (Show, Eq)++data QualLevel = QL1 | QL2 | QL3 | QLFull+  deriving (Eq, Enum, Show)++disambiguateTargetSelectors+  :: (TargetStringFileStatus -> Match (TargetSelector PackageInfo))+  -> TargetStringFileStatus -> Bool+  -> [TargetSelector PackageInfo]+  -> Either [(TargetSelector PackageInfo,+              [(TargetString, [TargetSelector PackageInfo])])]+            [(TargetString, TargetSelector PackageInfo)]+disambiguateTargetSelectors matcher matchInput exactMatch matchResults =+    case partitionEithers results of+      (errs@(_:_), _) -> Left errs+      ([], ok)        -> Right ok+  where+    -- So, here's the strategy. We take the original match results, and make a+    -- table of all their renderings at all qualification levels.+    -- Note there can be multiple renderings at each qualification level.+    matchResultsRenderings :: [(TargetSelector PackageInfo,+                                [TargetStringFileStatus])]+    matchResultsRenderings =+      [ (matchResult, matchRenderings)+      | matchResult <- matchResults+      , let matchRenderings =+              [ rendering+              | ql <- [QL1 .. QLFull]+              , rendering <- renderTargetSelector ql matchResult ]+      ]++    -- Of course the point is that we're looking for renderings that are+    -- unambiguous matches. So we build another memo table of all the matches+    -- for all of those renderings. So by looking up in this table we can see+    -- if we've got an unambiguous match.++    memoisedMatches :: Map TargetStringFileStatus+                           (Match (TargetSelector PackageInfo))+    memoisedMatches =+        -- avoid recomputing the main one if it was an exact match+        (if exactMatch then Map.insert matchInput (ExactMatch 0 matchResults)+                       else id)+      $ Map.Lazy.fromList+          [ (rendering, matcher rendering)+          | rendering <- concatMap snd matchResultsRenderings ]++    -- Finally, for each of the match results, we go through all their+    -- possible renderings (in order of qualification level, though remember+    -- there can be multiple renderings per level), and find the first one+    -- that has an unambiguous match.+    results :: [Either (TargetSelector PackageInfo,+                        [(TargetString, [TargetSelector PackageInfo])])+                       (TargetString, TargetSelector PackageInfo)]+    results =+      [ case findUnambiguous originalMatch matchRenderings of+          Just unambiguousRendering ->+            Right ( forgetFileStatus unambiguousRendering+                  , originalMatch)++          -- This case is an internal error, but we bubble it up and report it+          Nothing ->+            Left  ( originalMatch+                  , [ (forgetFileStatus rendering, matches)+                    | rendering <- matchRenderings+                    , let (ExactMatch _ matches) =+                            memoisedMatches Map.! rendering+                    ] )++      | (originalMatch, matchRenderings) <- matchResultsRenderings ]++    findUnambiguous :: TargetSelector PackageInfo+                    -> [TargetStringFileStatus]+                    -> Maybe TargetStringFileStatus+    findUnambiguous _ []     = Nothing+    findUnambiguous t (r:rs) =+      case memoisedMatches Map.! r of+        ExactMatch _ [t'] | fmap packageName t == fmap packageName t'+                         -> Just r+        ExactMatch _  _  -> findUnambiguous t rs+        InexactMatch _ _ -> internalError "InexactMatch"+        NoMatch      _ _ -> internalError "NoMatch"++internalError :: String -> a+internalError msg =+  error $ "TargetSelector: internal error: " ++ msg+++-- | Throw an exception with a formatted message if there are any problems.+--+reportTargetSelectorProblems :: Verbosity -> [TargetSelectorProblem] -> IO a+reportTargetSelectorProblems verbosity problems = do++    case [ str | TargetSelectorUnrecognised str <- problems ] of+      []      -> return ()+      targets ->+        die' verbosity $ unlines+          [ "Unrecognised target syntax for '" ++ name ++ "'."+          | name <- targets ]++    case [ (t, m, ms) | MatchingInternalError t m ms <- problems ] of+      [] -> return ()+      ((target, originalMatch, renderingsAndMatches):_) ->+        die' verbosity $ "Internal error in target matching. It should always "+           ++ "be possible to find a syntax that's sufficiently qualified to "+           ++ "give an unambiguous match. However when matching '"+           ++ showTargetString target ++ "'  we found "+           ++ showTargetSelector originalMatch+           ++ " (" ++ showTargetSelectorKind originalMatch ++ ") which does "+           ++ "not have an unambiguous syntax. The possible syntax and the "+           ++ "targets they match are as follows:\n"+           ++ unlines+                [ "'" ++ showTargetString rendering ++ "' which matches "+                  ++ intercalate ", "+                       [ showTargetSelector match +++                         " (" ++ showTargetSelectorKind match ++ ")"+                       | match <- matches ]+                | (rendering, matches) <- renderingsAndMatches ]++    case [ (t, e, g) | TargetSelectorExpected t e g <- problems ] of+      []      -> return ()+      targets ->+        die' verbosity $ unlines+          [    "Unrecognised target '" ++ showTargetString target+            ++ "'.\n"+            ++ "Expected a " ++ intercalate " or " expected+            ++ ", rather than '" ++ got ++ "'."+          | (target, expected, got) <- targets ]++    case [ (t, e) | TargetSelectorNoSuch t e <- problems ] of+      []      -> return ()+      targets ->+        die' verbosity $ unlines+          [ "Unknown target '" ++ showTargetString target +++            "'.\n" ++ unlines+            [    (case inside of+                    Just (kind, "")+                            -> "The " ++ kind ++ " has no "+                    Just (kind, thing)+                            -> "The " ++ kind ++ " " ++ thing ++ " has no "+                    Nothing -> "There is no ")+              ++ intercalate " or " [ mungeThing thing ++ " '" ++ got ++ "'"+                                    | (thing, got, _alts) <- nosuch' ] ++ "."+              ++ if null alternatives then "" else+                 "\nPerhaps you meant " ++ intercalate ";\nor "+                 [ "the " ++ thing ++ " '" ++ intercalate "' or '" alts ++ "'?"+                 | (thing, alts) <- alternatives ]+            | (inside, nosuch') <- groupByContainer nosuch+            , let alternatives =+                    [ (thing, alts)+                    | (thing,_got,alts@(_:_)) <- nosuch' ]+            ]+          | (target, nosuch) <- targets+          , let groupByContainer =+                    map (\g@((inside,_,_,_):_) ->+                            (inside, [   (thing,got,alts)+                                     | (_,thing,got,alts) <- g ]))+                  . groupBy ((==)    `on` (\(x,_,_,_) -> x))+                  . sortBy  (compare `on` (\(x,_,_,_) -> x))+          ]+        where+          mungeThing "file" = "file target"+          mungeThing thing  = thing++    case [ (t, ts) | TargetSelectorAmbiguous t ts <- problems ] of+      []      -> return ()+      targets ->+        die' verbosity $ unlines+          [    "Ambiguous target '" ++ showTargetString target+            ++ "'. It could be:\n "+            ++ unlines [ "   "++ showTargetString ut +++                         " (" ++ showTargetSelectorKind bt ++ ")"+                       | (ut, bt) <- amb ]+          | (target, amb) <- targets ]++    case [ t | TargetSelectorNoCurrentPackage t <- problems ] of+      []       -> return ()+      target:_ ->+        die' verbosity $+            "The target '" ++ showTargetString target ++ "' refers to the "+         ++ "components in the package in the current directory, but there "+         ++ "is no package in the current directory (or at least not listed "+         ++ "as part of the project)."+        --TODO: report a different error if there is a .cabal file but it's+        -- not a member of the project++    case [ () | TargetSelectorNoTargetsInCwd <- problems ] of+      []  -> return ()+      _:_ ->+        die' verbosity $+            "No targets given and there is no package in the current "+         ++ "directory. Use the target 'all' for all packages in the "+         ++ "project or specify packages or components by name or location. "+         ++ "See 'cabal build --help' for more details on target options."++    case [ () | TargetSelectorNoTargetsInProject <- problems ] of+      []  -> return ()+      _:_ ->+        die' verbosity $+            "There is no <pkgname>.cabal package file or cabal.project file. "+         ++ "To build packages locally you need at minimum a <pkgname>.cabal "+         ++ "file. You can use 'cabal init' to create one.\n"+         ++ "\n"+         ++ "For non-trivial projects you will also want a cabal.project "+         ++ "file in the root directory of your project. This file lists the "+         ++ "packages in your project and all other build configuration. "+         ++ "See the Cabal user guide for full details."++    fail "reportTargetSelectorProblems: internal error"+++----------------------------------+-- Syntax type+--++-- | Syntax for the 'TargetSelector': the matcher and renderer+--+data Syntax = Syntax QualLevel Matcher Renderer+            | AmbiguousAlternatives Syntax Syntax+            | ShadowingAlternatives Syntax Syntax++type Matcher  = TargetStringFileStatus -> Match (TargetSelector PackageInfo)+type Renderer = TargetSelector PackageId -> [TargetStringFileStatus]++foldSyntax :: (a -> a -> a) -> (a -> a -> a)+           -> (QualLevel -> Matcher -> Renderer -> a)+           -> (Syntax -> a)+foldSyntax ambiguous unambiguous syntax = go+  where+    go (Syntax ql match render)    = syntax ql match render+    go (AmbiguousAlternatives a b) = ambiguous   (go a) (go b)+    go (ShadowingAlternatives a b) = unambiguous (go a) (go b)+++----------------------------------+-- Top level renderer and matcher+--++renderTargetSelector :: Package p => QualLevel -> TargetSelector p+                     -> [TargetStringFileStatus]+renderTargetSelector ql ts =+    foldSyntax+      (++) (++)+      (\ql' _ render -> guard (ql == ql') >> render (fmap packageId ts))+      syntax+  where+    syntax = syntaxForms [] [] -- don't need pinfo for rendering++matchTargetSelector :: [PackageInfo] -> [PackageInfo]+                    -> TargetStringFileStatus+                    -> Match (TargetSelector PackageInfo)+matchTargetSelector ppinfo opinfo = \utarget ->+    nubMatchesBy ((==) `on` (fmap packageName)) $++    let ql = targetQualLevel utarget in+    foldSyntax+      (<|>) (<//>)+      (\ql' match _ -> guard (ql == ql') >> match utarget)+      syntax+  where+    syntax = syntaxForms ppinfo opinfo++    targetQualLevel TargetStringFileStatus1{} = QL1+    targetQualLevel TargetStringFileStatus2{} = QL2+    targetQualLevel TargetStringFileStatus3{} = QL3+    targetQualLevel TargetStringFileStatus4{} = QLFull+    targetQualLevel TargetStringFileStatus5{} = QLFull+    targetQualLevel TargetStringFileStatus7{} = QLFull+++----------------------------------+-- Syntax forms+--++-- | All the forms of syntax for 'TargetSelector'.+--+syntaxForms :: [PackageInfo] -> [PackageInfo] -> Syntax+syntaxForms ppinfo opinfo =+    -- The various forms of syntax here are ambiguous in many cases.+    -- Our policy is by default we expose that ambiguity and report+    -- ambiguous matches. In certain cases we override the ambiguity+    -- by having some forms shadow others.+    --+    -- We make modules shadow files because module name "Q" clashes+    -- with file "Q" with no extension but these refer to the same+    -- thing anyway so it's not a useful ambiguity. Other cases are+    -- not ambiguous like "Q" vs "Q.hs" or "Data.Q" vs "Data/Q".++    ambiguousAlternatives+        -- convenient single-component forms+      [ shadowingAlternatives+          [ ambiguousAlternatives+              [ syntaxForm1All+              , syntaxForm1Filter+              , shadowingAlternatives+                  [ syntaxForm1Component pcinfo+                  , syntaxForm1Package   pinfo+                  ]+              ]+          , syntaxForm1Component ocinfo+          , syntaxForm1Module    cinfo+          , syntaxForm1File      pinfo+          ]++        -- two-component partially qualified forms+        -- fully qualified form for 'all'+      , syntaxForm2MetaAll+      , syntaxForm2AllFilter+      , syntaxForm2NamespacePackage pinfo+      , syntaxForm2PackageComponent pinfo+      , syntaxForm2PackageFilter    pinfo+      , syntaxForm2KindComponent    cinfo+      , shadowingAlternatives+          [ syntaxForm2PackageModule   pinfo+          , syntaxForm2PackageFile     pinfo+          ]+      , shadowingAlternatives+          [ syntaxForm2ComponentModule cinfo+          , syntaxForm2ComponentFile   cinfo+          ]++        -- rarely used partially qualified forms+      , syntaxForm3PackageKindComponent   pinfo+      , shadowingAlternatives+          [ syntaxForm3PackageComponentModule pinfo+          , syntaxForm3PackageComponentFile   pinfo+          ]+      , shadowingAlternatives+          [ syntaxForm3KindComponentModule    cinfo+          , syntaxForm3KindComponentFile      cinfo+          ]+      , syntaxForm3NamespacePackageFilter pinfo++        -- fully-qualified forms for all and cwd with filter+      , syntaxForm3MetaAllFilter+      , syntaxForm3MetaCwdFilter++        -- fully-qualified form for package and package with filter+      , syntaxForm3MetaNamespacePackage       pinfo+      , syntaxForm4MetaNamespacePackageFilter pinfo++        -- fully-qualified forms for component, module and file+      , syntaxForm5MetaNamespacePackageKindComponent                pinfo+      , syntaxForm7MetaNamespacePackageKindComponentNamespaceModule pinfo+      , syntaxForm7MetaNamespacePackageKindComponentNamespaceFile   pinfo+      ]+  where+    ambiguousAlternatives = foldr1 AmbiguousAlternatives+    shadowingAlternatives = foldr1 ShadowingAlternatives+    pinfo  = ppinfo ++ opinfo+    cinfo  = concatMap pinfoComponents pinfo+    pcinfo = concatMap pinfoComponents ppinfo+    ocinfo = concatMap pinfoComponents opinfo+++-- | Syntax: "all" to select all packages in the project+--+-- > cabal build all+--+syntaxForm1All :: Syntax+syntaxForm1All =+  syntaxForm1 render $ \str1 _fstatus1 -> do+    guardMetaAll str1+    return (TargetAllPackages Nothing)+  where+    render (TargetAllPackages Nothing) =+      [TargetStringFileStatus1 "all" noFileStatus]+    render _ = []++-- | Syntax: filter+--+-- > cabal build tests+--+syntaxForm1Filter :: Syntax+syntaxForm1Filter =+  syntaxForm1 render $ \str1 _fstatus1 -> do+    kfilter <- matchComponentKindFilter str1+    return (TargetPackage TargetImplicitCwd dummyPackageInfo (Just kfilter))+  where+    render (TargetPackage TargetImplicitCwd _ (Just kfilter)) =+      [TargetStringFileStatus1 (dispF kfilter) noFileStatus]+    render _ = []++-- Only used for TargetPackage TargetImplicitCwd+dummyPackageInfo :: PackageInfo+dummyPackageInfo =+    PackageInfo {+      pinfoId          = PackageIdentifier+                           (mkPackageName "dummyPackageInfo")+                           (mkVersion []),+      pinfoLocation    = unused,+      pinfoDirectory   = unused,+      pinfoPackageFile = unused,+      pinfoComponents  = unused+    }+  where+    unused = error "dummyPackageInfo"++-- | Syntax: package (name, dir or file)+--+-- > cabal build foo+-- > cabal build ../bar ../bar/bar.cabal+--+syntaxForm1Package :: [PackageInfo] -> Syntax+syntaxForm1Package pinfo =+  syntaxForm1 render $ \str1 fstatus1 -> do+    guardPackage            str1 fstatus1+    p <- matchPackage pinfo str1 fstatus1+    return (TargetPackage TargetExplicitNamed p Nothing)+  where+    render (TargetPackage TargetExplicitNamed p Nothing) =+      [TargetStringFileStatus1 (dispP p) noFileStatus]+    render _ = []++-- | Syntax: component+--+-- > cabal build foo+--+syntaxForm1Component :: [ComponentInfo] -> Syntax+syntaxForm1Component cs =+  syntaxForm1 render $ \str1 _fstatus1 -> do+    guardComponentName str1+    c <- matchComponentName cs str1+    return (TargetComponent (cinfoPackage c) (cinfoName c) WholeComponent)+  where+    render (TargetComponent p c WholeComponent) =+      [TargetStringFileStatus1 (dispC p c) noFileStatus]+    render _ = []++-- | Syntax: module+--+-- > cabal build Data.Foo+--+syntaxForm1Module :: [ComponentInfo] -> Syntax+syntaxForm1Module cs =+  syntaxForm1 render $  \str1 _fstatus1 -> do+    guardModuleName str1+    let ms = [ (m,c) | c <- cs, m <- cinfoModules c ]+    (m,c) <- matchModuleNameAnd ms str1+    return (TargetComponent (cinfoPackage c) (cinfoName c) (ModuleTarget m))+  where+    render (TargetComponent _p _c (ModuleTarget m)) =+      [TargetStringFileStatus1 (dispM m) noFileStatus]+    render _ = []++-- | Syntax: file name+--+-- > cabal build Data/Foo.hs bar/Main.hsc+--+syntaxForm1File :: [PackageInfo] -> Syntax+syntaxForm1File ps =+    -- Note there's a bit of an inconsistency here vs the other syntax forms+    -- for files. For the single-part syntax the target has to point to a file+    -- that exists (due to our use of matchPackageDirectoryPrefix), whereas for+    -- all the other forms we don't require that.+  syntaxForm1 render $ \str1 fstatus1 ->+    expecting "file" str1 $ do+    (pkgfile, p) <- matchPackageDirectoryPrefix ps fstatus1+    orNoThingIn "package" (display (packageName p)) $ do+      (filepath, c) <- matchComponentFile (pinfoComponents p) pkgfile+      return (TargetComponent p (cinfoName c) (FileTarget filepath))+  where+    render (TargetComponent _p _c (FileTarget f)) =+      [TargetStringFileStatus1 f noFileStatus]+    render _ = []++---++-- | Syntax:  :all+--+-- > cabal build :all+--+syntaxForm2MetaAll :: Syntax+syntaxForm2MetaAll =+  syntaxForm2 render $ \str1 _fstatus1 str2 -> do+    guardNamespaceMeta str1+    guardMetaAll str2+    return (TargetAllPackages Nothing)+  where+    render (TargetAllPackages Nothing) =+      [TargetStringFileStatus2 "" noFileStatus "all"]+    render _ = []++-- | Syntax:  all : filer+--+-- > cabal build all:tests+--+syntaxForm2AllFilter :: Syntax+syntaxForm2AllFilter =+  syntaxForm2 render $ \str1 _fstatus1 str2 -> do+    guardMetaAll str1+    kfilter <- matchComponentKindFilter str2+    return (TargetAllPackages (Just kfilter))+  where+    render (TargetAllPackages (Just kfilter)) =+      [TargetStringFileStatus2 "all" noFileStatus (dispF kfilter)]+    render _ = []++-- | Syntax:  package : filer+--+-- > cabal build foo:tests+--+syntaxForm2PackageFilter :: [PackageInfo] -> Syntax+syntaxForm2PackageFilter ps =+  syntaxForm2 render $ \str1 fstatus1 str2 -> do+    guardPackage         str1 fstatus1+    p <- matchPackage ps str1 fstatus1+    kfilter <- matchComponentKindFilter str2+    return (TargetPackage TargetExplicitNamed p (Just kfilter))+  where+    render (TargetPackage TargetExplicitNamed p (Just kfilter)) =+      [TargetStringFileStatus2 (dispP p) noFileStatus (dispF kfilter)]+    render _ = []++-- | Syntax: pkg : package name+--+-- > cabal build pkg:foo+--+syntaxForm2NamespacePackage :: [PackageInfo] -> Syntax+syntaxForm2NamespacePackage pinfo =+  syntaxForm2 render $ \str1 _fstatus1 str2 -> do+    guardNamespacePackage   str1+    guardPackageName        str2+    p <- matchPackage pinfo str2 noFileStatus+    return (TargetPackage TargetExplicitNamed p Nothing)+  where+    render (TargetPackage TargetExplicitNamed p Nothing) =+      [TargetStringFileStatus2 "pkg" noFileStatus (dispP p)]+    render _ = []++-- | Syntax: package : component+--+-- > cabal build foo:foo+-- > cabal build ./foo:foo+-- > cabal build ./foo.cabal:foo+--+syntaxForm2PackageComponent :: [PackageInfo] -> Syntax+syntaxForm2PackageComponent ps =+  syntaxForm2 render $ \str1 fstatus1 str2 -> do+    guardPackage         str1 fstatus1+    guardComponentName   str2+    p <- matchPackage ps str1 fstatus1+    orNoThingIn "package" (display (packageName p)) $ do+      c <- matchComponentName (pinfoComponents p) str2+      return (TargetComponent p (cinfoName c) WholeComponent)+    --TODO: the error here ought to say there's no component by that name in+    -- this package, and name the package+  where+    render (TargetComponent p c WholeComponent) =+      [TargetStringFileStatus2 (dispP p) noFileStatus (dispC p c)]+    render _ = []++-- | Syntax: namespace : component+--+-- > cabal build lib:foo exe:foo+--+syntaxForm2KindComponent :: [ComponentInfo] -> Syntax+syntaxForm2KindComponent cs =+  syntaxForm2 render $ \str1 _fstatus1 str2 -> do+    ckind <- matchComponentKind str1+    guardComponentName str2+    c <- matchComponentKindAndName cs ckind str2+    return (TargetComponent (cinfoPackage c) (cinfoName c) WholeComponent)+  where+    render (TargetComponent p c WholeComponent) =+      [TargetStringFileStatus2 (dispK c) noFileStatus (dispC p c)]+    render _ = []++-- | Syntax: package : module+--+-- > cabal build foo:Data.Foo+-- > cabal build ./foo:Data.Foo+-- > cabal build ./foo.cabal:Data.Foo+--+syntaxForm2PackageModule :: [PackageInfo] -> Syntax+syntaxForm2PackageModule ps =+  syntaxForm2 render $ \str1 fstatus1 str2 -> do+    guardPackage         str1 fstatus1+    guardModuleName      str2+    p <- matchPackage ps str1 fstatus1+    orNoThingIn "package" (display (packageName p)) $ do+      let ms = [ (m,c) | c <- pinfoComponents p, m <- cinfoModules c ]+      (m,c) <- matchModuleNameAnd ms str2+      return (TargetComponent p (cinfoName c) (ModuleTarget m))+  where+    render (TargetComponent p _c (ModuleTarget m)) =+      [TargetStringFileStatus2 (dispP p) noFileStatus (dispM m)]+    render _ = []++-- | Syntax: component : module+--+-- > cabal build foo:Data.Foo+--+syntaxForm2ComponentModule :: [ComponentInfo] -> Syntax+syntaxForm2ComponentModule cs =+  syntaxForm2 render $ \str1 _fstatus1 str2 -> do+    guardComponentName str1+    guardModuleName    str2+    c <- matchComponentName cs str1+    orNoThingIn "component" (cinfoStrName c) $ do+      let ms = cinfoModules c+      m <- matchModuleName ms str2+      return (TargetComponent (cinfoPackage c) (cinfoName c)+                              (ModuleTarget m))+  where+    render (TargetComponent p c (ModuleTarget m)) =+      [TargetStringFileStatus2 (dispC p c) noFileStatus (dispM m)]+    render _ = []++-- | Syntax: package : filename+--+-- > cabal build foo:Data/Foo.hs+-- > cabal build ./foo:Data/Foo.hs+-- > cabal build ./foo.cabal:Data/Foo.hs+--+syntaxForm2PackageFile :: [PackageInfo] -> Syntax+syntaxForm2PackageFile ps =+  syntaxForm2 render $ \str1 fstatus1 str2 -> do+    guardPackage         str1 fstatus1+    p <- matchPackage ps str1 fstatus1+    orNoThingIn "package" (display (packageName p)) $ do+      (filepath, c) <- matchComponentFile (pinfoComponents p) str2+      return (TargetComponent p (cinfoName c) (FileTarget filepath))+  where+    render (TargetComponent p _c (FileTarget f)) =+      [TargetStringFileStatus2 (dispP p) noFileStatus f]+    render _ = []++-- | Syntax: component : filename+--+-- > cabal build foo:Data/Foo.hs+--+syntaxForm2ComponentFile :: [ComponentInfo] -> Syntax+syntaxForm2ComponentFile cs =+  syntaxForm2 render $ \str1 _fstatus1 str2 -> do+    guardComponentName str1+    c <- matchComponentName cs str1+    orNoThingIn "component" (cinfoStrName c) $ do+      (filepath, _) <- matchComponentFile [c] str2+      return (TargetComponent (cinfoPackage c) (cinfoName c)+                              (FileTarget filepath))+  where+    render (TargetComponent p c (FileTarget f)) =+      [TargetStringFileStatus2 (dispC p c) noFileStatus f]+    render _ = []++---++-- | Syntax: :all : filter+--+-- > cabal build :all:tests+--+syntaxForm3MetaAllFilter :: Syntax+syntaxForm3MetaAllFilter =+  syntaxForm3 render $ \str1 _fstatus1 str2 str3 -> do+    guardNamespaceMeta str1+    guardMetaAll str2+    kfilter <- matchComponentKindFilter str3+    return (TargetAllPackages (Just kfilter))+  where+    render (TargetAllPackages (Just kfilter)) =+      [TargetStringFileStatus3 "" noFileStatus "all" (dispF kfilter)]+    render _ = []++syntaxForm3MetaCwdFilter :: Syntax+syntaxForm3MetaCwdFilter =+  syntaxForm3 render $ \str1 _fstatus1 str2 str3 -> do+    guardNamespaceMeta str1+    guardNamespaceCwd str2+    kfilter <- matchComponentKindFilter str3+    return (TargetPackage TargetImplicitCwd dummyPackageInfo (Just kfilter))+  where+    render (TargetPackage TargetImplicitCwd _ (Just kfilter)) =+      [TargetStringFileStatus3 "" noFileStatus "cwd" (dispF kfilter)]+    render _ = []++-- | Syntax: :pkg : package name+--+-- > cabal build :pkg:foo+--+syntaxForm3MetaNamespacePackage :: [PackageInfo] -> Syntax+syntaxForm3MetaNamespacePackage pinfo =+  syntaxForm3 render $ \str1 _fstatus1 str2 str3 -> do+    guardNamespaceMeta      str1+    guardNamespacePackage   str2+    guardPackageName        str3+    p <- matchPackage pinfo str3 noFileStatus+    return (TargetPackage TargetExplicitNamed p Nothing)+  where+    render (TargetPackage TargetExplicitNamed p Nothing) =+      [TargetStringFileStatus3 "" noFileStatus "pkg" (dispP p)]+    render _ = []++-- | Syntax: package : namespace : component+--+-- > cabal build foo:lib:foo+-- > cabal build foo/:lib:foo+-- > cabal build foo.cabal:lib:foo+--+syntaxForm3PackageKindComponent :: [PackageInfo] -> Syntax+syntaxForm3PackageKindComponent ps =+  syntaxForm3 render $ \str1 fstatus1 str2 str3 -> do+    guardPackage         str1 fstatus1+    ckind <- matchComponentKind str2+    guardComponentName   str3+    p <- matchPackage ps str1 fstatus1+    orNoThingIn "package" (display (packageName p)) $ do+      c <- matchComponentKindAndName (pinfoComponents p) ckind str3+      return (TargetComponent p (cinfoName c) WholeComponent)+  where+    render (TargetComponent p c WholeComponent) =+      [TargetStringFileStatus3 (dispP p) noFileStatus (dispK c) (dispC p c)]+    render _ = []++-- | Syntax: package : component : module+--+-- > cabal build foo:foo:Data.Foo+-- > cabal build foo/:foo:Data.Foo+-- > cabal build foo.cabal:foo:Data.Foo+--+syntaxForm3PackageComponentModule :: [PackageInfo] -> Syntax+syntaxForm3PackageComponentModule ps =+  syntaxForm3 render $ \str1 fstatus1 str2 str3 -> do+    guardPackage str1 fstatus1+    guardComponentName str2+    guardModuleName    str3+    p <- matchPackage ps str1 fstatus1+    orNoThingIn "package" (display (packageName p)) $ do+      c <- matchComponentName (pinfoComponents p) str2+      orNoThingIn "component" (cinfoStrName c) $ do+        let ms = cinfoModules c+        m <- matchModuleName ms str3+        return (TargetComponent p (cinfoName c) (ModuleTarget m))+  where+    render (TargetComponent p c (ModuleTarget m)) =+      [TargetStringFileStatus3 (dispP p) noFileStatus (dispC p c) (dispM m)]+    render _ = []++-- | Syntax: namespace : component : module+--+-- > cabal build lib:foo:Data.Foo+--+syntaxForm3KindComponentModule :: [ComponentInfo] -> Syntax+syntaxForm3KindComponentModule cs =+  syntaxForm3 render $ \str1 _fstatus1 str2 str3 -> do+    ckind <- matchComponentKind str1+    guardComponentName str2+    guardModuleName    str3+    c <- matchComponentKindAndName cs ckind str2+    orNoThingIn "component" (cinfoStrName c) $ do+      let ms = cinfoModules c+      m <- matchModuleName ms str3+      return (TargetComponent (cinfoPackage c) (cinfoName c)+                              (ModuleTarget m))+  where+    render (TargetComponent p c (ModuleTarget m)) =+      [TargetStringFileStatus3 (dispK c) noFileStatus (dispC p c) (dispM m)]+    render _ = []++-- | Syntax: package : component : filename+--+-- > cabal build foo:foo:Data/Foo.hs+-- > cabal build foo/:foo:Data/Foo.hs+-- > cabal build foo.cabal:foo:Data/Foo.hs+--+syntaxForm3PackageComponentFile :: [PackageInfo] -> Syntax+syntaxForm3PackageComponentFile ps =+  syntaxForm3 render $ \str1 fstatus1 str2 str3 -> do+    guardPackage         str1 fstatus1+    guardComponentName   str2+    p <- matchPackage ps str1 fstatus1+    orNoThingIn "package" (display (packageName p)) $ do+      c <- matchComponentName (pinfoComponents p) str2+      orNoThingIn "component" (cinfoStrName c) $ do+        (filepath, _) <- matchComponentFile [c] str3+        return (TargetComponent p (cinfoName c) (FileTarget filepath))+  where+    render (TargetComponent p c (FileTarget f)) =+      [TargetStringFileStatus3 (dispP p) noFileStatus (dispC p c) f]+    render _ = []++-- | Syntax: namespace : component : filename+--+-- > cabal build lib:foo:Data/Foo.hs+--+syntaxForm3KindComponentFile :: [ComponentInfo] -> Syntax+syntaxForm3KindComponentFile cs =+  syntaxForm3 render $ \str1 _fstatus1 str2 str3 -> do+    ckind <- matchComponentKind str1+    guardComponentName str2+    c <- matchComponentKindAndName cs ckind str2+    orNoThingIn "component" (cinfoStrName c) $ do+      (filepath, _) <- matchComponentFile [c] str3+      return (TargetComponent (cinfoPackage c) (cinfoName c)+                              (FileTarget filepath))+  where+    render (TargetComponent p c (FileTarget f)) =+      [TargetStringFileStatus3 (dispK c) noFileStatus (dispC p c) f]+    render _ = []++syntaxForm3NamespacePackageFilter :: [PackageInfo] -> Syntax+syntaxForm3NamespacePackageFilter ps =+  syntaxForm3 render $ \str1 _fstatus1 str2 str3 -> do+    guardNamespacePackage str1+    guardPackageName      str2+    p <- matchPackage  ps str2 noFileStatus+    kfilter <- matchComponentKindFilter str3+    return (TargetPackage TargetExplicitNamed p (Just kfilter))+  where+    render (TargetPackage TargetExplicitNamed p (Just kfilter)) =+      [TargetStringFileStatus3 "pkg" noFileStatus (dispP p) (dispF kfilter)]+    render _ = []++--++syntaxForm4MetaNamespacePackageFilter :: [PackageInfo] -> Syntax+syntaxForm4MetaNamespacePackageFilter ps =+  syntaxForm4 render $ \str1 str2 str3 str4 -> do+    guardNamespaceMeta    str1+    guardNamespacePackage str2+    guardPackageName      str3+    p <- matchPackage  ps str3 noFileStatus+    kfilter <- matchComponentKindFilter str4+    return (TargetPackage TargetExplicitNamed p (Just kfilter))+  where+    render (TargetPackage TargetExplicitNamed p (Just kfilter)) =+      [TargetStringFileStatus4 "" "pkg" (dispP p) (dispF kfilter)]+    render _ = []++-- | Syntax: :pkg : package : namespace : component+--+-- > cabal build :pkg:foo:lib:foo+--+syntaxForm5MetaNamespacePackageKindComponent :: [PackageInfo] -> Syntax+syntaxForm5MetaNamespacePackageKindComponent ps =+  syntaxForm5 render $ \str1 str2 str3 str4 str5 -> do+    guardNamespaceMeta    str1+    guardNamespacePackage str2+    guardPackageName      str3+    ckind <- matchComponentKind str4+    guardComponentName    str5+    p <- matchPackage  ps str3 noFileStatus+    orNoThingIn "package" (display (packageName p)) $ do+      c <- matchComponentKindAndName (pinfoComponents p) ckind str5+      return (TargetComponent p (cinfoName c) WholeComponent)+  where+    render (TargetComponent p c WholeComponent) =+      [TargetStringFileStatus5 "" "pkg" (dispP p) (dispK c) (dispC p c)]+    render _ = []++-- | Syntax: :pkg : package : namespace : component : module : module+--+-- > cabal build :pkg:foo:lib:foo:module:Data.Foo+--+syntaxForm7MetaNamespacePackageKindComponentNamespaceModule+  :: [PackageInfo] -> Syntax+syntaxForm7MetaNamespacePackageKindComponentNamespaceModule ps =+  syntaxForm7 render $ \str1 str2 str3 str4 str5 str6 str7 -> do+    guardNamespaceMeta    str1+    guardNamespacePackage str2+    guardPackageName      str3+    ckind <- matchComponentKind str4+    guardComponentName    str5+    guardNamespaceModule  str6+    p <- matchPackage  ps str3 noFileStatus+    orNoThingIn "package" (display (packageName p)) $ do+      c <- matchComponentKindAndName (pinfoComponents p) ckind str5+      orNoThingIn "component" (cinfoStrName c) $ do+        let ms = cinfoModules c+        m <- matchModuleName ms str7+        return (TargetComponent p (cinfoName c) (ModuleTarget m))+  where+    render (TargetComponent p c (ModuleTarget m)) =+      [TargetStringFileStatus7 "" "pkg" (dispP p)+                               (dispK c) (dispC p c)+                               "module" (dispM m)]+    render _ = []++-- | Syntax: :pkg : package : namespace : component : file : filename+--+-- > cabal build :pkg:foo:lib:foo:file:Data/Foo.hs+--+syntaxForm7MetaNamespacePackageKindComponentNamespaceFile+  :: [PackageInfo] -> Syntax+syntaxForm7MetaNamespacePackageKindComponentNamespaceFile ps =+  syntaxForm7 render $ \str1 str2 str3 str4 str5 str6 str7 -> do+    guardNamespaceMeta    str1+    guardNamespacePackage str2+    guardPackageName      str3+    ckind <- matchComponentKind str4+    guardComponentName    str5+    guardNamespaceFile    str6+    p <- matchPackage  ps str3 noFileStatus+    orNoThingIn "package" (display (packageName p)) $ do+      c <- matchComponentKindAndName (pinfoComponents p) ckind str5+      orNoThingIn "component" (cinfoStrName c) $ do+        (filepath,_) <- matchComponentFile [c] str7+        return (TargetComponent p (cinfoName c) (FileTarget filepath))+  where+    render (TargetComponent p c (FileTarget f)) =+      [TargetStringFileStatus7 "" "pkg" (dispP p)+                               (dispK c) (dispC p c)+                               "file" f]+    render _ = []+++---------------------------------------+-- Syntax utils+--++type Match1 = String -> FileStatus -> Match (TargetSelector PackageInfo)+type Match2 = String -> FileStatus -> String+              -> Match (TargetSelector PackageInfo)+type Match3 = String -> FileStatus -> String -> String+              -> Match (TargetSelector PackageInfo)+type Match4 = String -> String -> String -> String+              -> Match (TargetSelector PackageInfo)+type Match5 = String -> String -> String -> String -> String+              -> Match (TargetSelector PackageInfo)+type Match7 = String -> String -> String -> String -> String -> String -> String+              -> Match (TargetSelector PackageInfo)++syntaxForm1 :: Renderer -> Match1 -> Syntax+syntaxForm2 :: Renderer -> Match2 -> Syntax+syntaxForm3 :: Renderer -> Match3 -> Syntax+syntaxForm4 :: Renderer -> Match4 -> Syntax+syntaxForm5 :: Renderer -> Match5 -> Syntax+syntaxForm7 :: Renderer -> Match7 -> Syntax++syntaxForm1 render f =+    Syntax QL1 match render+  where+    match = \(TargetStringFileStatus1 str1 fstatus1) ->+              f str1 fstatus1++syntaxForm2 render f =+    Syntax QL2 match render+  where+    match = \(TargetStringFileStatus2 str1 fstatus1 str2) ->+              f str1 fstatus1 str2++syntaxForm3 render f =+    Syntax QL3 match render+  where+    match = \(TargetStringFileStatus3 str1 fstatus1 str2 str3) ->+              f str1 fstatus1 str2 str3++syntaxForm4 render f =+    Syntax QLFull match render+  where+    match (TargetStringFileStatus4 str1 str2 str3 str4)+            = f str1 str2 str3 str4+    match _ = mzero++syntaxForm5 render f =+    Syntax QLFull match render+  where+    match (TargetStringFileStatus5 str1 str2 str3 str4 str5)+            = f str1 str2 str3 str4 str5+    match _ = mzero++syntaxForm7 render f =+    Syntax QLFull match render+  where+    match (TargetStringFileStatus7 str1 str2 str3 str4 str5 str6 str7)+            = f str1 str2 str3 str4 str5 str6 str7+    match _ = mzero++dispP :: Package p => p -> String+dispP = display . packageName++dispC :: Package p => p -> ComponentName -> String+dispC = componentStringName++dispK :: ComponentName -> String+dispK = showComponentKindShort . componentKind++dispF :: ComponentKind -> String+dispF = showComponentKindFilterShort++dispM :: ModuleName -> String+dispM = display+++-------------------------------+-- Package and component info+--++data PackageInfo = PackageInfo {+       pinfoId          :: PackageId,+       pinfoLocation    :: PackageLocation (),+       pinfoDirectory   :: Maybe (FilePath, FilePath),+       pinfoPackageFile :: Maybe (FilePath, FilePath),+       pinfoComponents  :: [ComponentInfo]+     }+  -- not instance of Show due to recursive construction++data ComponentInfo = ComponentInfo {+       cinfoName    :: ComponentName,+       cinfoStrName :: ComponentStringName,+       cinfoPackage :: PackageInfo,+       cinfoSrcDirs :: [FilePath],+       cinfoModules :: [ModuleName],+       cinfoHsFiles :: [FilePath],   -- other hs files (like main.hs)+       cinfoCFiles  :: [FilePath],+       cinfoJsFiles :: [FilePath]+     }+  -- not instance of Show due to recursive construction++type ComponentStringName = String++instance Package PackageInfo where+  packageId = pinfoId++selectPackageInfo :: (Applicative m, Monad m) => DirActions m+                  -> SourcePackage (PackageLocation a) -> m PackageInfo+selectPackageInfo dirActions@DirActions{..}+                  SourcePackage {+                    packageDescription = pkg,+                    packageSource      = loc+                  } = do+    (pkgdir, pkgfile) <-+      case loc of+        --TODO: local tarballs, remote tarballs etc+        LocalUnpackedPackage dir -> do+          dirabs <- canonicalizePath dir+          dirrel <- makeRelativeToCwd dirActions dirabs+          --TODO: ought to get this earlier in project reading+          let fileabs = dirabs </> display (packageName pkg) <.> "cabal"+              filerel = dirrel </> display (packageName pkg) <.> "cabal"+          exists <- doesFileExist fileabs+          return ( Just (dirabs, dirrel)+                 , if exists then Just (fileabs, filerel) else Nothing+                 )+        _ -> return (Nothing, Nothing)+    let pinfo =+          PackageInfo {+            pinfoId          = packageId pkg,+            pinfoLocation    = fmap (const ()) loc,+            pinfoDirectory   = pkgdir,+            pinfoPackageFile = pkgfile,+            pinfoComponents  = selectComponentInfo pinfo+                                 (flattenPackageDescription pkg)+          }+    return pinfo+++selectComponentInfo :: PackageInfo -> PackageDescription -> [ComponentInfo]+selectComponentInfo pinfo pkg =+    [ ComponentInfo {+        cinfoName    = componentName c,+        cinfoStrName = componentStringName pkg (componentName c),+        cinfoPackage = pinfo,+        cinfoSrcDirs = ordNub (hsSourceDirs bi),+--                       [ pkgroot </> srcdir+--                       | (pkgroot,_) <- maybeToList (pinfoDirectory pinfo)+--                       , srcdir <- hsSourceDirs bi ],+        cinfoModules = ordNub (componentModules c),+        cinfoHsFiles = ordNub (componentHsFiles c),+        cinfoCFiles  = ordNub (cSources bi),+        cinfoJsFiles = ordNub (jsSources bi)+      }+    | c <- pkgComponents pkg+    , let bi = componentBuildInfo c ]+++componentStringName :: Package pkg => pkg -> ComponentName -> ComponentStringName+componentStringName pkg CLibName          = display (packageName pkg)+componentStringName _ (CSubLibName name) = unUnqualComponentName name+componentStringName _ (CFLibName name)  = unUnqualComponentName name+componentStringName _ (CExeName   name) = unUnqualComponentName name+componentStringName _ (CTestName  name) = unUnqualComponentName name+componentStringName _ (CBenchName name) = unUnqualComponentName name++componentModules :: Component -> [ModuleName]+-- I think it's unlikely users will ask to build a requirement+-- which is not mentioned locally.+componentModules (CLib   lib)   = explicitLibModules lib+componentModules (CFLib  flib)  = foreignLibModules flib+componentModules (CExe   exe)   = exeModules exe+componentModules (CTest  test)  = testModules test+componentModules (CBench bench) = benchmarkModules bench++componentHsFiles :: Component -> [FilePath]+componentHsFiles (CExe exe) = [modulePath exe]+componentHsFiles (CTest  TestSuite {+                           testInterface = TestSuiteExeV10 _ mainfile+                         }) = [mainfile]+componentHsFiles (CBench Benchmark {+                           benchmarkInterface = BenchmarkExeV10 _ mainfile+                         }) = [mainfile]+componentHsFiles _          = []+++------------------------------+-- Matching meta targets+--++guardNamespaceMeta :: String -> Match ()+guardNamespaceMeta = guardToken [""] "meta namespace"++guardMetaAll :: String -> Match ()+guardMetaAll = guardToken ["all"] "meta-target 'all'"++guardNamespacePackage :: String -> Match ()+guardNamespacePackage = guardToken ["pkg", "package"] "'pkg' namespace"++guardNamespaceCwd :: String -> Match ()+guardNamespaceCwd = guardToken ["cwd"] "'cwd' namespace"++guardNamespaceModule :: String -> Match ()+guardNamespaceModule = guardToken ["mod", "module"] "'module' namespace"++guardNamespaceFile :: String -> Match ()+guardNamespaceFile = guardToken ["file"] "'file' namespace"++guardToken :: [String] -> String -> String -> Match ()+guardToken tokens msg s +  | caseFold s `elem` tokens = increaseConfidence+  | otherwise                = matchErrorExpected msg s+++------------------------------+-- Matching component kinds+--++componentKind :: ComponentName -> ComponentKind+componentKind  CLibName      = LibKind+componentKind (CSubLibName _) = LibKind+componentKind (CFLibName _)  = FLibKind+componentKind (CExeName   _) = ExeKind+componentKind (CTestName  _) = TestKind+componentKind (CBenchName _) = BenchKind++cinfoKind :: ComponentInfo -> ComponentKind+cinfoKind = componentKind . cinfoName++matchComponentKind :: String -> Match ComponentKind+matchComponentKind s+  | s' `elem` liblabels   = increaseConfidence >> return LibKind+  | s' `elem` fliblabels  = increaseConfidence >> return FLibKind+  | s' `elem` exelabels   = increaseConfidence >> return ExeKind+  | s' `elem` testlabels  = increaseConfidence >> return TestKind+  | s' `elem` benchlabels = increaseConfidence >> return BenchKind+  | otherwise             = matchErrorExpected "component kind" s+  where+    s'         = caseFold s+    liblabels   = ["lib", "library"]+    fliblabels  = ["flib", "foreign-library"]+    exelabels   = ["exe", "executable"]+    testlabels  = ["tst", "test", "test-suite"]+    benchlabels = ["bench", "benchmark"]++matchComponentKindFilter :: String -> Match ComponentKind+matchComponentKindFilter s+  | s' `elem` liblabels   = increaseConfidence >> return LibKind+  | s' `elem` fliblabels  = increaseConfidence >> return FLibKind+  | s' `elem` exelabels   = increaseConfidence >> return ExeKind+  | s' `elem` testlabels  = increaseConfidence >> return TestKind+  | s' `elem` benchlabels = increaseConfidence >> return BenchKind+  | otherwise             = matchErrorExpected "component kind filter" s+  where+    s'          = caseFold s+    liblabels   = ["libs", "libraries"]+    fliblabels  = ["flibs", "foreign-libraries"]+    exelabels   = ["exes", "executables"]+    testlabels  = ["tests", "test-suites"]+    benchlabels = ["benches", "benchmarks"]++showComponentKind :: ComponentKind -> String+showComponentKind LibKind   = "library"+showComponentKind FLibKind  = "foreign library"+showComponentKind ExeKind   = "executable"+showComponentKind TestKind  = "test-suite"+showComponentKind BenchKind = "benchmark"++showComponentKindShort :: ComponentKind -> String+showComponentKindShort LibKind   = "lib"+showComponentKindShort FLibKind  = "flib"+showComponentKindShort ExeKind   = "exe"+showComponentKindShort TestKind  = "test"+showComponentKindShort BenchKind = "bench"++showComponentKindFilterShort :: ComponentKind -> String+showComponentKindFilterShort LibKind   = "libs"+showComponentKindFilterShort FLibKind  = "flibs"+showComponentKindFilterShort ExeKind   = "exes"+showComponentKindFilterShort TestKind  = "tests"+showComponentKindFilterShort BenchKind = "benchmarks"+++------------------------------+-- Matching package targets+--++guardPackage :: String -> FileStatus -> Match ()+guardPackage str fstatus =+      guardPackageName str+  <|> guardPackageDir  str fstatus+  <|> guardPackageFile str fstatus+++guardPackageName :: String -> Match ()+guardPackageName s+  | validPackageName s = increaseConfidence+  | otherwise          = matchErrorExpected "package name" s++validPackageName :: String -> Bool+validPackageName s =+       all validPackageNameChar s+    && not (null s)+  where+    validPackageNameChar c = isAlphaNum c || c == '-'+++guardPackageDir :: String -> FileStatus -> Match ()+guardPackageDir _ (FileStatusExistsDir _) = increaseConfidence+guardPackageDir str _ = matchErrorExpected "package directory" str+++guardPackageFile :: String -> FileStatus -> Match ()+guardPackageFile _ (FileStatusExistsFile file)+                       | takeExtension file == ".cabal"+                       = increaseConfidence+guardPackageFile str _ = matchErrorExpected "package .cabal file" str+++matchPackage :: [PackageInfo] -> String -> FileStatus -> Match PackageInfo+matchPackage pinfo = \str fstatus ->+    orNoThingIn "project" "" $+          matchPackageName pinfo str+    <//> (matchPackageDir  pinfo str fstatus+     <|>  matchPackageFile pinfo str fstatus)+++matchPackageName :: [PackageInfo] -> String -> Match PackageInfo+matchPackageName ps = \str -> do+    guard (validPackageName str)+    orNoSuchThing "package" str+                  (map (display . packageName) ps) $+      increaseConfidenceFor $+        matchInexactly caseFold (display . packageName) ps str+++matchPackageDir :: [PackageInfo]+                -> String -> FileStatus -> Match PackageInfo+matchPackageDir ps = \str fstatus ->+    case fstatus of+      FileStatusExistsDir canondir ->+        orNoSuchThing "package directory" str (map (snd . fst) dirs) $+          increaseConfidenceFor $+            fmap snd $ matchExactly (fst . fst) dirs canondir+      _ -> mzero+  where+    dirs = [ ((dabs,drel),p)+           | p@PackageInfo{ pinfoDirectory = Just (dabs,drel) } <- ps ]+++matchPackageFile :: [PackageInfo] -> String -> FileStatus -> Match PackageInfo+matchPackageFile ps = \str fstatus -> do+    case fstatus of+      FileStatusExistsFile canonfile ->+        orNoSuchThing "package .cabal file" str (map (snd . fst) files) $+          increaseConfidenceFor $+            fmap snd $ matchExactly (fst . fst) files canonfile+      _ -> mzero+  where+    files = [ ((fabs,frel),p)+            | p@PackageInfo{ pinfoPackageFile = Just (fabs,frel) } <- ps ]++--TODO: test outcome when dir exists but doesn't match any known one++--TODO: perhaps need another distinction, vs no such thing, point is the+--      thing is not known, within the project, but could be outside project+++------------------------------+-- Matching component targets+--+++guardComponentName :: String -> Match ()+guardComponentName s+  | all validComponentChar s+    && not (null s)  = increaseConfidence+  | otherwise        = matchErrorExpected "component name" s+  where+    validComponentChar c = isAlphaNum c || c == '.'+                        || c == '_' || c == '-' || c == '\''+++matchComponentName :: [ComponentInfo] -> String -> Match ComponentInfo+matchComponentName cs str =+    orNoSuchThing "component" str (map cinfoStrName cs)+  $ increaseConfidenceFor+  $ matchInexactly caseFold cinfoStrName cs str+++matchComponentKindAndName :: [ComponentInfo] -> ComponentKind -> String+                          -> Match ComponentInfo+matchComponentKindAndName cs ckind str =+    orNoSuchThing (showComponentKind ckind ++ " component") str+                  (map render cs)+  $ increaseConfidenceFor+  $ matchInexactly (\(ck, cn) -> (ck, caseFold cn))+                   (\c -> (cinfoKind c, cinfoStrName c))+                   cs+                   (ckind, str)+  where+    render c = showComponentKindShort (cinfoKind c) ++ ":" ++ cinfoStrName c+++------------------------------+-- Matching module targets+--++guardModuleName :: String -> Match ()+guardModuleName s =+  case simpleParse s :: Maybe ModuleName of+    Just _                   -> increaseConfidence+    _ | all validModuleChar s+        && not (null s)      -> return ()+      | otherwise            -> matchErrorExpected "module name" s+    where+      validModuleChar c = isAlphaNum c || c == '.' || c == '_' || c == '\''+++matchModuleName :: [ModuleName] -> String -> Match ModuleName+matchModuleName ms str =+    orNoSuchThing "module" str (map display ms)+  $ increaseConfidenceFor+  $ matchInexactly caseFold display ms str+++matchModuleNameAnd :: [(ModuleName, a)] -> String -> Match (ModuleName, a)+matchModuleNameAnd ms str =+    orNoSuchThing "module" str (map (display . fst) ms)+  $ increaseConfidenceFor+  $ matchInexactly caseFold (display . fst) ms str+++------------------------------+-- Matching file targets+--++matchPackageDirectoryPrefix :: [PackageInfo] -> FileStatus+                            -> Match (FilePath, PackageInfo)+matchPackageDirectoryPrefix ps (FileStatusExistsFile filepath) =+    increaseConfidenceFor $+      matchDirectoryPrefix pkgdirs filepath+  where+    pkgdirs = [ (dir, p)+              | p@PackageInfo { pinfoDirectory = Just (dir,_) } <- ps ]+matchPackageDirectoryPrefix _ _ = mzero+++matchComponentFile :: [ComponentInfo] -> String+                   -> Match (FilePath, ComponentInfo)+matchComponentFile cs str =+    orNoSuchThing "file" str [] $+        matchComponentModuleFile cs str+    <|> matchComponentOtherFile  cs str+++matchComponentOtherFile :: [ComponentInfo] -> String+                        -> Match (FilePath, ComponentInfo)+matchComponentOtherFile cs =+    matchFile+      [ (file, c)+      | c    <- cs+      , file <- cinfoHsFiles c+             ++ cinfoCFiles  c+             ++ cinfoJsFiles c+      ]+++matchComponentModuleFile :: [ComponentInfo] -> String+                         -> Match (FilePath, ComponentInfo)+matchComponentModuleFile cs str = do+    matchFile+      [ (normalise (d </> toFilePath m), c)+      | c <- cs+      , d <- cinfoSrcDirs c+      , m <- cinfoModules c+      ]+      (dropExtension (normalise str))++-- utils++matchFile :: [(FilePath, a)] -> FilePath -> Match (FilePath, a)+matchFile fs =+      increaseConfidenceFor+    . matchInexactly caseFold fst fs++matchDirectoryPrefix :: [(FilePath, a)] -> FilePath -> Match (FilePath, a)+matchDirectoryPrefix dirs filepath =+    tryEach $+      [ (file, x)+      | (dir,x) <- dirs+      , file <- maybeToList (stripDirectory dir) ]+  where+    stripDirectory :: FilePath -> Maybe FilePath+    stripDirectory dir =+      joinPath `fmap` stripPrefix (splitDirectories dir) filepathsplit++    filepathsplit = splitDirectories filepath+++------------------------------+-- Matching monad+--++-- | A matcher embodies a way to match some input as being some recognised+-- value. In particular it deals with multiple and ambiguous matches.+--+-- There are various matcher primitives ('matchExactly', 'matchInexactly'),+-- ways to combine matchers ('matchPlus', 'matchPlusShadowing') and finally we+-- can run a matcher against an input using 'findMatch'.+--+data Match a = NoMatch      Confidence [MatchError]+             | ExactMatch   Confidence [a]+             | InexactMatch Confidence [a]+  deriving Show++type Confidence = Int++data MatchError = MatchErrorExpected String String            -- thing got+                | MatchErrorNoSuch   String String [String]   -- thing got alts+                | MatchErrorIn       String String MatchError -- kind  thing+  deriving (Show, Eq)+++instance Functor Match where+    fmap _ (NoMatch      d ms) = NoMatch      d ms+    fmap f (ExactMatch   d xs) = ExactMatch   d (fmap f xs)+    fmap f (InexactMatch d xs) = InexactMatch d (fmap f xs)++instance Applicative Match where+    pure a = ExactMatch 0 [a]+    (<*>)  = ap++instance Alternative Match where+    empty = NoMatch 0 []+    (<|>) = matchPlus++instance Monad Match where+    return                  = pure+    NoMatch      d ms >>= _ = NoMatch d ms+    ExactMatch   d xs >>= f = addDepth d+                            $ msum (map f xs)+    InexactMatch d xs >>= f = addDepth d . forceInexact+                            $ msum (map f xs)++instance MonadPlus Match where+    mzero = empty+    mplus = matchPlus++(<//>) :: Match a -> Match a -> Match a+(<//>) = matchPlusShadowing++infixl 3 <//>++addDepth :: Confidence -> Match a -> Match a+addDepth d' (NoMatch      d msgs) = NoMatch      (d'+d) msgs+addDepth d' (ExactMatch   d xs)   = ExactMatch   (d'+d) xs+addDepth d' (InexactMatch d xs)   = InexactMatch (d'+d) xs++forceInexact :: Match a -> Match a+forceInexact (ExactMatch d ys) = InexactMatch d ys+forceInexact m                 = m++-- | Combine two matchers. Exact matches are used over inexact matches+-- but if we have multiple exact, or inexact then the we collect all the+-- ambiguous matches.+--+-- This operator is associative, has unit 'mzero' and is also commutative.+--+matchPlus :: Match a -> Match a -> Match a+matchPlus   (ExactMatch   d1 xs)   (ExactMatch   d2 xs') =+  ExactMatch (max d1 d2) (xs ++ xs')+matchPlus a@(ExactMatch   _  _ )   (InexactMatch _  _  ) = a+matchPlus a@(ExactMatch   _  _ )   (NoMatch      _  _  ) = a+matchPlus   (InexactMatch _  _ ) b@(ExactMatch   _  _  ) = b+matchPlus   (InexactMatch d1 xs)   (InexactMatch d2 xs') =+  InexactMatch (max d1 d2) (xs ++ xs')+matchPlus a@(InexactMatch _  _ )   (NoMatch      _  _  ) = a+matchPlus   (NoMatch      _  _ ) b@(ExactMatch   _  _  ) = b+matchPlus   (NoMatch      _  _ ) b@(InexactMatch _  _  ) = b+matchPlus a@(NoMatch      d1 ms) b@(NoMatch      d2 ms')+                                             | d1 >  d2  = a+                                             | d1 <  d2  = b+                                             | otherwise = NoMatch d1 (ms ++ ms')++-- | Combine two matchers. This is similar to 'matchPlus' with the+-- difference that an exact match from the left matcher shadows any exact+-- match on the right. Inexact matches are still collected however.+--+-- This operator is associative, has unit 'mzero' and is not commutative.+--+matchPlusShadowing :: Match a -> Match a -> Match a+matchPlusShadowing a@(ExactMatch _ _)  _ = a+matchPlusShadowing a                   b = matchPlus a b+++------------------------------+-- Various match primitives+--++matchErrorExpected :: String -> String -> Match a+matchErrorExpected thing got      = NoMatch 0 [MatchErrorExpected thing got]++matchErrorNoSuch :: String -> String -> [String] -> Match a+matchErrorNoSuch thing got alts = NoMatch 0 [MatchErrorNoSuch thing got alts]++expecting :: String -> String -> Match a -> Match a+expecting thing got (NoMatch 0 _) = matchErrorExpected thing got+expecting _     _   m             = m++orNoSuchThing :: String -> String -> [String] -> Match a -> Match a+orNoSuchThing thing got alts (NoMatch 0 _) = matchErrorNoSuch thing got alts+orNoSuchThing _     _   _    m             = m++orNoThingIn :: String -> String -> Match a -> Match a+orNoThingIn kind name (NoMatch n ms) =+    NoMatch n [ MatchErrorIn kind name m | m <- ms ]+orNoThingIn _ _ m = m++increaseConfidence :: Match ()+increaseConfidence = ExactMatch 1 [()]++increaseConfidenceFor :: Match a -> Match a+increaseConfidenceFor m = m >>= \r -> increaseConfidence >> return r++nubMatchesBy :: (a -> a -> Bool) -> Match a -> Match a+nubMatchesBy _  (NoMatch      d msgs) = NoMatch      d msgs+nubMatchesBy eq (ExactMatch   d xs)   = ExactMatch   d (nubBy eq xs)+nubMatchesBy eq (InexactMatch d xs)   = InexactMatch d (nubBy eq xs)++-- | Lift a list of matches to an exact match.+--+exactMatches, inexactMatches :: [a] -> Match a++exactMatches [] = mzero+exactMatches xs = ExactMatch 0 xs++inexactMatches [] = mzero+inexactMatches xs = InexactMatch 0 xs++tryEach :: [a] -> Match a+tryEach = exactMatches+++------------------------------+-- Top level match runner+--++-- | Given a matcher and a key to look up, use the matcher to find all the+-- possible matches. There may be 'None', a single 'Unambiguous' match or+-- you may have an 'Ambiguous' match with several possibilities.+--+findMatch :: Match a -> MaybeAmbiguous a+findMatch match = case match of+  NoMatch    _ msgs  -> None msgs+  ExactMatch   _ [x] -> Unambiguous x+  InexactMatch _ [x] -> Unambiguous x+  ExactMatch   _  [] -> error "findMatch: impossible: ExactMatch []"+  InexactMatch _  [] -> error "findMatch: impossible: InexactMatch []"+  ExactMatch   _  xs -> Ambiguous True  xs+  InexactMatch _  xs -> Ambiguous False xs++data MaybeAmbiguous a = None [MatchError] | Unambiguous a | Ambiguous Bool [a]+  deriving Show+++------------------------------+-- Basic matchers+--++-- | A primitive matcher that looks up a value in a finite 'Map'. The+-- value must match exactly.+--+matchExactly :: Ord k => (a -> k) -> [a] -> (k -> Match a)+matchExactly key xs =+    \k -> case Map.lookup k m of+            Nothing -> mzero+            Just ys -> exactMatches ys+  where+    m = Map.fromListWith (++) [ (key x, [x]) | x <- xs ]++-- | A primitive matcher that looks up a value in a finite 'Map'. It checks+-- for an exact or inexact match. We get an inexact match if the match+-- is not exact, but the canonical forms match. It takes a canonicalisation+-- function for this purpose.+--+-- So for example if we used string case fold as the canonicalisation+-- function, then we would get case insensitive matching (but it will still+-- report an exact match when the case matches too).+--+matchInexactly :: (Ord k, Ord k') => (k -> k') -> (a -> k)+               -> [a] -> (k -> Match a)+matchInexactly cannonicalise key xs =+    \k -> case Map.lookup k m of+            Just ys -> exactMatches ys+            Nothing -> case Map.lookup (cannonicalise k) m' of+                         Just ys -> inexactMatches ys+                         Nothing -> mzero+  where+    m = Map.fromListWith (++) [ (key x, [x]) | x <- xs ]++    -- the map of canonicalised keys to groups of inexact matches+    m' = Map.mapKeysWith (++) cannonicalise m+++------------------------------+-- Utils+--++caseFold :: String -> String+caseFold = lowercase+++------------------------------+-- Example inputs+--++{-+ex1pinfo :: [PackageInfo]+ex1pinfo =+  [ addComponent (CExeName (mkUnqualComponentName "foo-exe")) [] ["Data.Foo"] $+    PackageInfo {+      pinfoId          = PackageIdentifier (mkPackageName "foo") (mkVersion [1]),+      pinfoLocation    = LocalUnpackedPackage "/the/foo",+      pinfoDirectory   = Just ("/the/foo", "foo"),+      pinfoPackageFile = Just ("/the/foo/foo.cabal", "foo/foo.cabal"),+      pinfoComponents  = []+    }+  , PackageInfo {+      pinfoId          = PackageIdentifier (mkPackageName "bar") (mkVersion [1]),+      pinfoLocation    = LocalUnpackedPackage "/the/foo",+      pinfoDirectory   = Just ("/the/bar", "bar"),+      pinfoPackageFile = Just ("/the/bar/bar.cabal", "bar/bar.cabal"),+      pinfoComponents  = []+    }+  ]+  where+    addComponent n ds ms p =+      p {+        pinfoComponents =+            ComponentInfo n (componentStringName (pinfoId p) n)+                          p ds (map mkMn ms)+                          [] [] []+          : pinfoComponents p+      }++    mkMn :: String -> ModuleName+    mkMn  = ModuleName.fromString+-}+{-+stargets =+  [ TargetComponent (CExeName "foo")  WholeComponent+  , TargetComponent (CExeName "foo") (ModuleTarget (mkMn "Foo"))+  , TargetComponent (CExeName "tst") (ModuleTarget (mkMn "Foo"))+  ]+    where+    mkMn :: String -> ModuleName+    mkMn  = fromJust . simpleParse++ex_pkgid :: PackageIdentifier+Just ex_pkgid = simpleParse "thelib"+-}++{-+ex_cs :: [ComponentInfo]+ex_cs =+  [ (mkC (CExeName "foo") ["src1", "src1/src2"] ["Foo", "Src2.Bar", "Bar"])+  , (mkC (CExeName "tst") ["src1", "test"]      ["Foo"])+  ]+    where+    mkC n ds ms = ComponentInfo n (componentStringName n) ds (map mkMn ms)+    mkMn :: String -> ModuleName+    mkMn  = fromJust . simpleParse+    pkgid :: PackageIdentifier+    Just pkgid = simpleParse "thelib"+-}
Distribution/Client/Targets.hs view
@@ -1,6 +1,8 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE BangPatterns #-} {-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DeriveFunctor, DeriveFoldable, DeriveTraversable #-}+ ----------------------------------------------------------------------------- -- | -- Module      :  Distribution.Client.Targets@@ -41,28 +43,36 @@   disambiguatePackageName,    -- * User constraints+  UserQualifier(..),+  UserConstraintScope(..),   UserConstraint(..),   userConstraintPackageName,   readUserConstraint,   userToPackageConstraint,-  dispFlagAssignment,-  parseFlagAssignment,    ) where +import Prelude ()+import Distribution.Client.Compat.Prelude+ import Distribution.Package-         ( Package(..), PackageName(..)-         , PackageIdentifier(..), packageName, packageVersion-         , Dependency(Dependency) )+         ( Package(..), PackageName, unPackageName, mkPackageName+         , PackageIdentifier(..), packageName, packageVersion )+import Distribution.Types.Dependency import Distribution.Client.Types-         ( SourcePackage(..), PackageLocation(..), OptionalStanza(..) )-import Distribution.Client.Dependency.Types-         ( PackageConstraint(..), ConstraintSource(..)-         , LabeledPackageConstraint(..) )+         ( PackageLocation(..)+         , ResolvedPkgLoc, UnresolvedSourcePackage ) +import           Distribution.Solver.Types.ConstraintSource+import           Distribution.Solver.Types.LabeledPackageConstraint+import           Distribution.Solver.Types.OptionalStanza+import           Distribution.Solver.Types.PackageConstraint+import           Distribution.Solver.Types.PackagePath+import           Distribution.Solver.Types.PackageIndex (PackageIndex)+import qualified Distribution.Solver.Types.PackageIndex as PackageIndex+import           Distribution.Solver.Types.SourcePackage+ import qualified Distribution.Client.World as World-import Distribution.Client.PackageIndex (PackageIndex)-import qualified Distribution.Client.PackageIndex as PackageIndex import qualified Codec.Archive.Tar       as Tar import qualified Codec.Archive.Tar.Entry as Tar import qualified Distribution.Client.Tar as Tar@@ -72,51 +82,44 @@          ( RepoContext(..) )  import Distribution.PackageDescription-         ( GenericPackageDescription, FlagName(..), FlagAssignment )-import Distribution.PackageDescription.Parse-         ( readPackageDescription, parsePackageDescription, ParseResult(..) )+         ( GenericPackageDescription, parseFlagAssignment ) import Distribution.Version-         ( Version(Version), thisVersion, anyVersion, isAnyVersion-         , VersionRange )+         ( nullVersion, thisVersion, anyVersion, isAnyVersion ) import Distribution.Text          ( Text(..), display ) import Distribution.Verbosity (Verbosity) import Distribution.Simple.Utils-         ( die, warn, intercalate, fromUTF8, lowercase, ignoreBOM )+         ( die', warn, lowercase ) -import Data.List-         ( find, nub )-import Data.Maybe-         ( listToMaybe )+#ifdef CABAL_PARSEC+import Distribution.PackageDescription.Parsec+         ( readGenericPackageDescription, parseGenericPackageDescriptionMaybe )+#else+import Distribution.PackageDescription.Parse+         ( readGenericPackageDescription, parseGenericPackageDescription, ParseResult(..) )+import Distribution.Simple.Utils+         ( fromUTF8, ignoreBOM )+import qualified Data.ByteString.Lazy.Char8 as BS.Char8+#endif++-- import Data.List ( find, nub ) import Data.Either          ( partitionEithers )-#if !MIN_VERSION_base(4,8,0)-import Data.Monoid-         ( Monoid(..) )-#endif import qualified Data.Map as Map import qualified Data.ByteString.Lazy as BS-import qualified Data.ByteString.Lazy.Char8 as BS.Char8 import qualified Distribution.Client.GZipUtils as GZipUtils-import Control.Monad (liftM)+import Control.Monad (mapM) import qualified Distribution.Compat.ReadP as Parse import Distribution.Compat.ReadP          ( (+++), (<++) )-import qualified Distribution.Compat.Semigroup as Semi-         ( Semigroup((<>)) )-import qualified Text.PrettyPrint as Disp-import Text.PrettyPrint-         ( (<>), (<+>) )-import Data.Char-         ( isSpace, isAlphaNum )+import Distribution.ParseUtils+         ( readPToMaybe ) import System.FilePath          ( takeExtension, dropExtension, takeDirectory, splitPath ) import System.Directory          ( doesFileExist, doesDirectoryExist ) import Network.URI          ( URI(..), URIAuth(..), parseAbsoluteURI )-import GHC.Generics (Generic)-import Distribution.Compat.Binary (Binary)  -- ------------------------------------------------------------ -- * User targets@@ -185,10 +188,10 @@       -- | A partially specified reference to a package (either source or      -- installed). It is specified by package name and optionally some-     -- additional constraints. Use a dependency resolver to pick a specific-     -- package satisfying these constraints.+     -- required properties. Use a dependency resolver to pick a specific+     -- package satisfying these properties.      ---     NamedPackage PackageName [PackageConstraint]+     NamedPackage PackageName [PackageProperty]       -- | A fully specified source package.      --@@ -203,24 +206,27 @@  pkgSpecifierConstraints :: Package pkg                         => PackageSpecifier pkg -> [LabeledPackageConstraint]-pkgSpecifierConstraints (NamedPackage _ constraints) = map toLpc constraints+pkgSpecifierConstraints (NamedPackage name props) = map toLpc props   where-    toLpc pc = LabeledPackageConstraint pc ConstraintSourceUserTarget+    toLpc prop = LabeledPackageConstraint+                 (PackageConstraint (scopeToplevel name) prop)+                 ConstraintSourceUserTarget pkgSpecifierConstraints (SpecificSourcePackage pkg)  =     [LabeledPackageConstraint pc ConstraintSourceUserTarget]   where-    pc = PackageConstraintVersion (packageName pkg)-         (thisVersion (packageVersion pkg))+    pc = PackageConstraint+         (scopeToplevel $ packageName pkg)+         (PackagePropertyVersion $ thisVersion (packageVersion pkg))  -- ------------------------------------------------------------ -- * Parsing and checking user targets -- ------------------------------------------------------------  readUserTargets :: Verbosity -> [String] -> IO [UserTarget]-readUserTargets _verbosity targetStrs = do+readUserTargets verbosity targetStrs = do     (problems, targets) <- liftM partitionEithers                                  (mapM readUserTarget targetStrs)-    reportUserTargetProblems problems+    reportUserTargetProblems verbosity problems     return targets  @@ -236,10 +242,12 @@ readUserTarget :: String -> IO (Either UserTargetProblem UserTarget) readUserTarget targetstr =     case testNamedTargets targetstr of-      Just (Dependency (PackageName "world") verrange)-        | verrange == anyVersion -> return (Right UserTargetWorld)-        | otherwise              -> return (Left  UserTargetBadWorldPkg)-      Just dep                   -> return (Right (UserTargetNamed dep))+      Just (Dependency pkgn verrange)+        | pkgn == mkPackageName "world"+          -> return $ if verrange == anyVersion+                      then Right UserTargetWorld+                      else Left  UserTargetBadWorldPkg+      Just dep -> return (Right (UserTargetNamed dep))       Nothing -> do         fileTarget <- testFileTargets targetstr         case fileTarget of@@ -302,19 +310,15 @@       where         pkgidToDependency :: PackageIdentifier -> Dependency         pkgidToDependency p = case packageVersion p of-          Version [] _ -> Dependency (packageName p) anyVersion-          version      -> Dependency (packageName p) (thisVersion version)--readPToMaybe :: Parse.ReadP a a -> String -> Maybe a-readPToMaybe p str = listToMaybe [ r | (r,s) <- Parse.readP_to_S p str-                                     , all isSpace s ]+          v | v == nullVersion -> Dependency (packageName p) anyVersion+            | otherwise        -> Dependency (packageName p) (thisVersion v)  -reportUserTargetProblems :: [UserTargetProblem] -> IO ()-reportUserTargetProblems problems = do+reportUserTargetProblems :: Verbosity -> [UserTargetProblem] -> IO ()+reportUserTargetProblems verbosity problems = do     case [ target | UserTargetUnrecognised target <- problems ] of       []     -> return ()-      target -> die+      target -> die' verbosity               $ unlines                   [ "Unrecognised target '" ++ name ++ "'."                   | name <- target ]@@ -326,18 +330,18 @@      case [ () | UserTargetBadWorldPkg <- problems ] of       [] -> return ()-      _  -> die "The special 'world' target does not take any version."+      _  -> die' verbosity "The special 'world' target does not take any version."      case [ target | UserTargetNonexistantFile target <- problems ] of       []     -> return ()-      target -> die+      target -> die' verbosity               $ unlines                   [ "The file does not exist '" ++ name ++ "'."                   | name <- target ]      case [ target | UserTargetUnexpectedFile target <- problems ] of       []     -> return ()-      target -> die+      target -> die' verbosity               $ unlines                   [ "Unrecognised file target '" ++ name ++ "'."                   | name <- target ]@@ -346,7 +350,7 @@      case [ target | UserTargetUnexpectedUriScheme target <- problems ] of       []     -> return ()-      target -> die+      target -> die' verbosity               $ unlines                   [ "URL target not supported '" ++ name ++ "'."                   | name <- target ]@@ -354,7 +358,7 @@      case [ target | UserTargetUnrecognisedUri target <- problems ] of       []     -> return ()-      target -> die+      target -> die' verbosity               $ unlines                   [ "Unrecognise URL target '" ++ name ++ "'."                   | name <- target ]@@ -374,14 +378,14 @@                    -> FilePath                    -> PackageIndex pkg                    -> [UserTarget]-                   -> IO [PackageSpecifier SourcePackage]+                   -> IO [PackageSpecifier UnresolvedSourcePackage] resolveUserTargets verbosity repoCtxt worldFile available userTargets = do      -- given the user targets, get a list of fully or partially resolved     -- package references     packageTargets <- mapM (readPackageTarget verbosity)                   =<< mapM (fetchPackageTarget verbosity repoCtxt) . concat-                  =<< mapM (expandUserTarget worldFile) userTargets+                  =<< mapM (expandUserTarget verbosity worldFile) userTargets      -- users are allowed to give package names case-insensitively, so we must     -- disambiguate named package references@@ -405,13 +409,13 @@ -- Unlike a 'UserTarget', a 'PackageTarget' refers only to a single package. -- data PackageTarget pkg =-     PackageTargetNamed      PackageName [PackageConstraint] UserTarget+     PackageTargetNamed      PackageName [PackageProperty] UserTarget       -- | A package identified by name, but case insensitively, so it needs      -- to be resolved to the right case-sensitive name.-   | PackageTargetNamedFuzzy PackageName [PackageConstraint] UserTarget+   | PackageTargetNamedFuzzy PackageName [PackageProperty] UserTarget    | PackageTargetLocation pkg-  deriving Show+  deriving (Show, Functor, Foldable, Traversable)   -- ------------------------------------------------------------@@ -421,32 +425,33 @@ -- | Given a user-specified target, expand it to a bunch of package targets -- (each of which refers to only one package). ---expandUserTarget :: FilePath+expandUserTarget :: Verbosity+                 -> FilePath                  -> UserTarget                  -> IO [PackageTarget (PackageLocation ())]-expandUserTarget worldFile userTarget = case userTarget of+expandUserTarget verbosity worldFile userTarget = case userTarget of      UserTargetNamed (Dependency name vrange) ->-      let constraints = [ PackageConstraintVersion name vrange-                        | not (isAnyVersion vrange) ]-      in  return [PackageTargetNamedFuzzy name constraints userTarget]+      let props = [ PackagePropertyVersion vrange+                  | not (isAnyVersion vrange) ]+      in  return [PackageTargetNamedFuzzy name props userTarget]      UserTargetWorld -> do-      worldPkgs <- World.getContents worldFile+      worldPkgs <- World.getContents verbosity worldFile       --TODO: should we warn if there are no world targets?-      return [ PackageTargetNamed name constraints userTarget+      return [ PackageTargetNamed name props userTarget              | World.WorldPkgInfo (Dependency name vrange) flags <- worldPkgs-             , let constraints = [ PackageConstraintVersion name vrange-                                 | not (isAnyVersion vrange) ]-                              ++ [ PackageConstraintFlags name flags-                                 | not (null flags) ] ]+             , let props = [ PackagePropertyVersion vrange+                           | not (isAnyVersion vrange) ]+                        ++ [ PackagePropertyFlags flags+                           | not (null flags) ] ]      UserTargetLocalDir dir ->       return [ PackageTargetLocation (LocalUnpackedPackage dir) ]      UserTargetLocalCabalFile file -> do       let dir = takeDirectory file-      _   <- tryFindPackageDesc dir (localPackageError dir) -- just as a check+      _   <- tryFindPackageDesc verbosity dir (localPackageError dir) -- just as a check       return [ PackageTargetLocation (LocalUnpackedPackage dir) ]      UserTargetLocalTarball tarballFile ->@@ -469,13 +474,9 @@ fetchPackageTarget :: Verbosity                    -> RepoContext                    -> PackageTarget (PackageLocation ())-                   -> IO (PackageTarget (PackageLocation FilePath))-fetchPackageTarget verbosity repoCtxt target = case target of-    PackageTargetNamed      n cs ut -> return (PackageTargetNamed      n cs ut)-    PackageTargetNamedFuzzy n cs ut -> return (PackageTargetNamedFuzzy n cs ut)-    PackageTargetLocation location  -> do-      location' <- fetchPackage verbosity repoCtxt (fmap (const Nothing) location)-      return (PackageTargetLocation location')+                   -> IO (PackageTarget ResolvedPkgLoc)+fetchPackageTarget verbosity repoCtxt = traverse $+  fetchPackage verbosity repoCtxt . fmap (const Nothing)   -- | Given a package target that has been fetched, read the .cabal file.@@ -483,28 +484,21 @@ -- This only affects targets given by location, named targets are unaffected. -- readPackageTarget :: Verbosity-                  -> PackageTarget (PackageLocation FilePath)-                  -> IO (PackageTarget SourcePackage)-readPackageTarget verbosity target = case target of--    PackageTargetNamed pkgname constraints userTarget ->-      return (PackageTargetNamed pkgname constraints userTarget)--    PackageTargetNamedFuzzy pkgname constraints userTarget ->-      return (PackageTargetNamedFuzzy pkgname constraints userTarget)--    PackageTargetLocation location -> case location of+                  -> PackageTarget ResolvedPkgLoc+                  -> IO (PackageTarget UnresolvedSourcePackage)+readPackageTarget verbosity = traverse modifyLocation+  where+    modifyLocation location = case location of        LocalUnpackedPackage dir -> do-        pkg <- tryFindPackageDesc dir (localPackageError dir) >>=-                 readPackageDescription verbosity-        return $ PackageTargetLocation $-                   SourcePackage {-                     packageInfoId        = packageId pkg,-                     packageDescription   = pkg,-                     packageSource        = fmap Just location,-                     packageDescrOverride = Nothing-                   }+        pkg <- tryFindPackageDesc verbosity dir (localPackageError dir) >>=+                 readGenericPackageDescription verbosity+        return $ SourcePackage {+                   packageInfoId        = packageId pkg,+                   packageDescription   = pkg,+                   packageSource        = fmap Just location,+                   packageDescrOverride = Nothing+                 }        LocalTarballPackage tarballFile ->         readTarballPackageTarget location tarballFile tarballFile@@ -516,26 +510,24 @@         error "TODO: readPackageTarget RepoTarballPackage"         -- For repo tarballs this info should be obtained from the index. -  where     readTarballPackageTarget location tarballFile tarballOriginalLoc = do       (filename, content) <- extractTarballPackageCabalFile                                tarballFile tarballOriginalLoc       case parsePackageDescription' content of-        Nothing  -> die $ "Could not parse the cabal file "+        Nothing  -> die' verbosity $ "Could not parse the cabal file "                        ++ filename ++ " in " ++ tarballFile         Just pkg ->-          return $ PackageTargetLocation $-                     SourcePackage {-                       packageInfoId        = packageId pkg,-                       packageDescription   = pkg,-                       packageSource        = fmap Just location,-                       packageDescrOverride = Nothing-                     }+          return $ SourcePackage {+                     packageInfoId        = packageId pkg,+                     packageDescription   = pkg,+                     packageSource        = fmap Just location,+                     packageDescrOverride = Nothing+                   }      extractTarballPackageCabalFile :: FilePath -> String                                    -> IO (FilePath, BS.ByteString)     extractTarballPackageCabalFile tarballFile tarballOriginalLoc =-          either (die . formatErr) return+          either (die' verbosity . formatErr) return         . check         . accumEntryMap         . Tar.filterEntries isCabalFile@@ -566,11 +558,15 @@           _                 -> False      parsePackageDescription' :: BS.ByteString -> Maybe GenericPackageDescription+#ifdef CABAL_PARSEC+    parsePackageDescription' bs = +        parseGenericPackageDescriptionMaybe (BS.toStrict bs)+#else     parsePackageDescription' content =-      case parsePackageDescription . ignoreBOM . fromUTF8 . BS.Char8.unpack $ content of+      case parseGenericPackageDescription . ignoreBOM . fromUTF8 . BS.Char8.unpack $ content of         ParseOk _ pkg -> Just pkg         _             -> Nothing-+#endif  -- ------------------------------------------------------------ -- * Checking package targets@@ -597,20 +593,18 @@     disambiguatePackageTarget packageTarget = case packageTarget of       PackageTargetLocation pkg -> Right (SpecificSourcePackage pkg) -      PackageTargetNamed pkgname constraints userTarget+      PackageTargetNamed pkgname props userTarget         | null (PackageIndex.lookupPackageName availablePkgIndex pkgname)                     -> Left (PackageNameUnknown pkgname userTarget)-        | otherwise -> Right (NamedPackage pkgname constraints)+        | otherwise -> Right (NamedPackage pkgname props) -      PackageTargetNamedFuzzy pkgname constraints userTarget ->+      PackageTargetNamedFuzzy pkgname props userTarget ->         case disambiguatePackageName packageNameEnv pkgname of           None                 -> Left  (PackageNameUnknown                                           pkgname userTarget)           Ambiguous   pkgnames -> Left  (PackageNameAmbiguous                                           pkgname pkgnames userTarget)-          Unambiguous pkgname' -> Right (NamedPackage pkgname' constraints')-            where-              constraints' = map (renamePackageConstraint pkgname') constraints+          Unambiguous pkgname' -> Right (NamedPackage pkgname' props)      -- use any extra specific available packages to help us disambiguate     packageNameEnv :: PackageNameEnv@@ -626,7 +620,7 @@     case [ pkg | PackageNameUnknown pkg originalTarget <- problems                , not (isUserTagetWorld originalTarget) ] of       []    -> return ()-      pkgs  -> die $ unlines+      pkgs  -> die' verbosity $ unlines                        [ "There is no package named '" ++ display name ++ "'. "                        | name <- pkgs ]                   ++ "You may need to run 'cabal update' to get the latest "@@ -634,7 +628,7 @@      case [ (pkg, matches) | PackageNameAmbiguous pkg matches _ <- problems ] of       []          -> return ()-      ambiguities -> die $ unlines+      ambiguities -> die' verbosity $ unlines                              [    "The package name '" ++ display name                                ++ "' is ambiguous. It could be: "                                ++ intercalate ", " (map display matches)@@ -679,66 +673,91 @@  instance Monoid PackageNameEnv where   mempty = PackageNameEnv (const [])-  mappend = (Semi.<>)+  mappend = (<>) -instance Semi.Semigroup PackageNameEnv where+instance Semigroup PackageNameEnv where   PackageNameEnv lookupA <> PackageNameEnv lookupB =     PackageNameEnv (\name -> lookupA name ++ lookupB name)  indexPackageNameEnv :: PackageIndex pkg -> PackageNameEnv indexPackageNameEnv pkgIndex = PackageNameEnv pkgNameLookup   where-    pkgNameLookup (PackageName name) =-      map fst (PackageIndex.searchByName pkgIndex name)+    pkgNameLookup pname =+      map fst (PackageIndex.searchByName pkgIndex $ unPackageName pname)  extraPackageNameEnv :: [PackageName] -> PackageNameEnv extraPackageNameEnv names = PackageNameEnv pkgNameLookup   where-    pkgNameLookup (PackageName name) =-      [ PackageName name'-      | let lname = lowercase name-      , PackageName name' <- names-      , lowercase name' == lname ]+    pkgNameLookup pname =+      [ pname'+      | let lname = lowercase (unPackageName pname)+      , pname' <- names+      , lowercase (unPackageName pname') == lname ]   -- ------------------------------------------------------------ -- * Package constraints -- ------------------------------------------------------------ -data UserConstraint =-     UserConstraintVersion   PackageName VersionRange-   | UserConstraintInstalled PackageName-   | UserConstraintSource    PackageName-   | UserConstraintFlags     PackageName FlagAssignment-   | UserConstraintStanzas   PackageName [OptionalStanza]+-- | Version of 'Qualifier' that a user may specify on the+-- command line.+data UserQualifier =+  -- | Top-level dependency.+  UserQualToplevel++  -- | Setup dependency.+  | UserQualSetup PackageName++  -- | Executable dependency.+  | UserQualExe PackageName PackageName   deriving (Eq, Show, Generic) +instance Binary UserQualifier++-- | Version of 'ConstraintScope' that a user may specify on the+-- command line.+data UserConstraintScope =+  -- | Scope that applies to the package when it has the specified qualifier.+  UserQualified UserQualifier PackageName++  -- | Scope that applies to the package when it has a setup qualifier.+  | UserAnySetupQualifier PackageName++  -- | Scope that applies to the package when it has any qualifier.+  | UserAnyQualifier PackageName+  deriving (Eq, Show, Generic)++instance Binary UserConstraintScope++fromUserQualifier :: UserQualifier -> Qualifier+fromUserQualifier UserQualToplevel = QualToplevel+fromUserQualifier (UserQualSetup name) = QualSetup name+fromUserQualifier (UserQualExe name1 name2) = QualExe name1 name2++fromUserConstraintScope :: UserConstraintScope -> ConstraintScope+fromUserConstraintScope (UserQualified q pn) =+    ScopeQualified (fromUserQualifier q) pn+fromUserConstraintScope (UserAnySetupQualifier pn) = ScopeAnySetupQualifier pn+fromUserConstraintScope (UserAnyQualifier pn) = ScopeAnyQualifier pn++-- | Version of 'PackageConstraint' that the user can specify on+-- the command line.+data UserConstraint =+    UserConstraint UserConstraintScope PackageProperty+  deriving (Eq, Show, Generic)+            instance Binary UserConstraint  userConstraintPackageName :: UserConstraint -> PackageName-userConstraintPackageName uc = case uc of-  UserConstraintVersion   name _ -> name-  UserConstraintInstalled name   -> name-  UserConstraintSource    name   -> name-  UserConstraintFlags     name _ -> name-  UserConstraintStanzas   name _ -> name+userConstraintPackageName (UserConstraint scope _) = scopePN scope+  where+    scopePN (UserQualified _ pn) = pn+    scopePN (UserAnyQualifier pn) = pn+    scopePN (UserAnySetupQualifier pn) = pn  userToPackageConstraint :: UserConstraint -> PackageConstraint--- At the moment, the types happen to be directly equivalent-userToPackageConstraint uc = case uc of-  UserConstraintVersion   name ver   -> PackageConstraintVersion    name ver-  UserConstraintInstalled name       -> PackageConstraintInstalled  name-  UserConstraintSource    name       -> PackageConstraintSource     name-  UserConstraintFlags     name flags -> PackageConstraintFlags      name flags-  UserConstraintStanzas   name stanzas -> PackageConstraintStanzas  name stanzas--renamePackageConstraint :: PackageName -> PackageConstraint -> PackageConstraint-renamePackageConstraint name pc = case pc of-  PackageConstraintVersion   _ ver   -> PackageConstraintVersion    name ver-  PackageConstraintInstalled _       -> PackageConstraintInstalled  name-  PackageConstraintSource    _       -> PackageConstraintSource     name-  PackageConstraintFlags     _ flags -> PackageConstraintFlags      name flags-  PackageConstraintStanzas   _ stanzas -> PackageConstraintStanzas   name stanzas+userToPackageConstraint (UserConstraint scope prop) =+  PackageConstraint (fromUserConstraintScope scope) prop  readUserConstraint :: String -> Either String UserConstraint readUserConstraint str =@@ -747,72 +766,64 @@       Just c  -> Right c   where     msgCannotParse =-         "expected a package name followed by a constraint, which is "-      ++ "either a version range, 'installed', 'source' or flags"+         "expected a (possibly qualified) package name followed by a " +++         "constraint, which is either a version range, 'installed', " +++         "'source', 'test', 'bench', or flags"  instance Text UserConstraint where-  disp (UserConstraintVersion   pkgname verrange) = disp pkgname-                                                    <+> disp verrange-  disp (UserConstraintInstalled pkgname)          = disp pkgname-                                                    <+> Disp.text "installed"-  disp (UserConstraintSource    pkgname)          = disp pkgname-                                                    <+> Disp.text "source"-  disp (UserConstraintFlags     pkgname flags)    = disp pkgname-                                                    <+> dispFlagAssignment flags-  disp (UserConstraintStanzas   pkgname stanzas)  = disp pkgname-                                                    <+> dispStanzas stanzas-    where-      dispStanzas = Disp.hsep . map dispStanza-      dispStanza TestStanzas  = Disp.text "test"-      dispStanza BenchStanzas = Disp.text "bench"--  parse = parse >>= parseConstraint-    where-      parseConstraint pkgname =-            ((parse >>= return . UserConstraintVersion pkgname)-        +++ (do skipSpaces1-                _ <- Parse.string "installed"-                return (UserConstraintInstalled pkgname))-        +++ (do skipSpaces1-                _ <- Parse.string "source"-                return (UserConstraintSource pkgname))-        +++ (do skipSpaces1-                _ <- Parse.string "test"-                return (UserConstraintStanzas pkgname [TestStanzas]))-        +++ (do skipSpaces1-                _ <- Parse.string "bench"-                return (UserConstraintStanzas pkgname [BenchStanzas])))-        <++ (do skipSpaces1-                flags <- parseFlagAssignment-                return (UserConstraintFlags pkgname flags))----TODO: [code cleanup] move these somewhere else-dispFlagAssignment :: FlagAssignment -> Disp.Doc-dispFlagAssignment = Disp.hsep . map dispFlagValue-  where-    dispFlagValue (f, True)   = Disp.char '+' <> dispFlagName f-    dispFlagValue (f, False)  = Disp.char '-' <> dispFlagName f-    dispFlagName (FlagName f) = Disp.text f--parseFlagAssignment :: Parse.ReadP r FlagAssignment-parseFlagAssignment = Parse.sepBy1 parseFlagValue skipSpaces1-  where-    parseFlagValue =-          (do Parse.optional (Parse.char '+')-              f <- parseFlagName-              return (f, True))-      +++ (do _ <- Parse.char '-'-              f <- parseFlagName-              return (f, False))-    parseFlagName = liftM (FlagName . lowercase) ident--    ident :: Parse.ReadP r String-    ident = Parse.munch1 identChar >>= \s -> check s >> return s-      where-        identChar c   = isAlphaNum c || c == '_' || c == '-'-        check ('-':_) = Parse.pfail-        check _       = return ()--skipSpaces1 :: Parse.ReadP r ()-skipSpaces1 = Parse.satisfy isSpace >> Parse.skipSpaces+  disp (UserConstraint scope prop) =+    dispPackageConstraint $ PackageConstraint (fromUserConstraintScope scope) prop+  +  parse =+    let parseConstraintScope :: Parse.ReadP a UserConstraintScope+        parseConstraintScope =+          do+             _ <- Parse.string "any."+             pn <- parse+             return (UserAnyQualifier pn)+          ++++          do+             _ <- Parse.string "setup."+             pn <- parse+             return (UserAnySetupQualifier pn)+          ++++          do+             -- Qualified name+             pn <- parse+             (return (UserQualified UserQualToplevel pn)+              ++++              do _ <- Parse.string ":setup."+                 pn2 <- parse+                 return (UserQualified (UserQualSetup pn) pn2)) +              -- -- TODO: Re-enable parsing of UserQualExe once we decide on a syntax.+              --+              -- ++++              -- do _ <- Parse.string ":"+              --    pn2 <- parse+              --    _ <- Parse.string ":exe."+              --    pn3 <- parse+              --    return (UserQualExe pn pn2, pn3)+    in do+      scope <- parseConstraintScope+                       +      -- Package property+      let keyword str x = Parse.skipSpaces1 >> Parse.string str >> return x+      prop <- ((parse >>= return . PackagePropertyVersion)+               ++++               keyword "installed" PackagePropertyInstalled+               ++++               keyword "source" PackagePropertySource+               ++++               keyword "test" (PackagePropertyStanzas [TestStanzas])+               ++++               keyword "bench" (PackagePropertyStanzas [BenchStanzas]))+              -- Note: the parser is left-biased here so that we+              -- don't get an ambiguous parse from 'installed',+              -- 'source', etc. being regarded as flags.+              <+++              (Parse.skipSpaces1 >> parseFlagAssignment+               >>= return . PackagePropertyFlags)+    +      -- Result+      return (UserConstraint scope prop)
Distribution/Client/Types.hs view
@@ -1,7 +1,9 @@ {-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE BangPatterns #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE TypeFamilies #-} {-# OPTIONS_GHC -fno-warn-orphans #-} ----------------------------------------------------------------------------- -- |@@ -19,30 +21,44 @@ module Distribution.Client.Types where  import Distribution.Package-         ( PackageName, PackageId, Package(..)-         , UnitId(..), mkUnitId-         , HasUnitId(..), PackageInstalled(..) )+         ( Package(..), HasMungedPackageId(..), HasUnitId(..)+         , PackageInstalled(..), newSimpleUnitId ) import Distribution.InstalledPackageInfo-         ( InstalledPackageInfo )+         ( InstalledPackageInfo, installedComponentId, sourceComponentName ) import Distribution.PackageDescription-         ( Benchmark(..), GenericPackageDescription(..), FlagAssignment-         , TestSuite(..) )-import Distribution.PackageDescription.Configuration-         ( mapTreeData )-import Distribution.Client.PackageIndex-         ( PackageIndex )-import Distribution.Client.ComponentDeps-         ( ComponentDeps )-import qualified Distribution.Client.ComponentDeps as CD+         ( FlagAssignment ) import Distribution.Version          ( VersionRange )-import Distribution.Text (display)+import Distribution.Types.ComponentId+         ( ComponentId )+import Distribution.Types.MungedPackageId+         ( computeCompatPackageId )+import Distribution.Types.PackageId+         ( PackageId )+import Distribution.Types.AnnotatedId+import Distribution.Types.UnitId+         ( UnitId )+import Distribution.Types.PackageName+         ( PackageName )+import Distribution.Types.ComponentName+         ( ComponentName(..) ) +import Distribution.Solver.Types.PackageIndex+         ( PackageIndex )+import qualified Distribution.Solver.Types.ComponentDeps as CD+import Distribution.Solver.Types.ComponentDeps+         ( ComponentDeps )+import Distribution.Solver.Types.OptionalStanza+import Distribution.Solver.Types.PackageFixedDeps+import Distribution.Solver.Types.SourcePackage+import Distribution.Compat.Graph (IsNode(..))+import Distribution.Simple.Utils (ordNub)+ import Data.Map (Map) import Network.URI (URI(..), URIAuth(..), nullURI)-import Data.ByteString.Lazy (ByteString) import Control.Exception-         ( SomeException )+         ( Exception, SomeException )+import Data.Typeable (Typeable) import GHC.Generics (Generic) import Distribution.Compat.Binary (Binary(..)) @@ -53,7 +69,7 @@ -- | This is the information we get from a @00-index.tar.gz@ hackage index. -- data SourcePackageDb = SourcePackageDb {-  packageIndex       :: PackageIndex SourcePackage,+  packageIndex       :: PackageIndex UnresolvedSourcePackage,   packagePreferences :: Map PackageName VersionRange }   deriving (Eq, Generic)@@ -75,160 +91,131 @@ -- slightly and we may distinguish these two types and have an explicit -- conversion when we register units with the compiler. ---type InstalledPackageId = UnitId--installedPackageId :: HasUnitId pkg => pkg -> InstalledPackageId-installedPackageId = installedUnitId---- | Subclass of packages that have specific versioned dependencies.------ So for example a not-yet-configured package has dependencies on version--- ranges, not specific versions. A configured or an already installed package--- depends on exact versions. Some operations or data structures (like---  dependency graphs) only make sense on this subclass of package types.----class Package pkg => PackageFixedDeps pkg where-  depends :: pkg -> ComponentDeps [UnitId]--instance PackageFixedDeps InstalledPackageInfo where-  depends = CD.fromInstalled . installedDepends+type InstalledPackageId = ComponentId  --- | In order to reuse the implementation of PackageIndex which relies on--- 'UnitId', we need to be able to synthesize these IDs prior--- to installation.  Eventually, we'll move to a representation of--- 'UnitId' which can be properly computed before compilation--- (of course, it's a bit of a misnomer since the packages are not actually--- installed yet.)  In any case, we'll synthesize temporary installed package--- IDs to use as keys during install planning.  These should never be written--- out!  Additionally, they need to be guaranteed unique within the install--- plan.-fakeUnitId :: PackageId -> UnitId-fakeUnitId = mkUnitId . (".fake."++) . display- -- | A 'ConfiguredPackage' is a not-yet-installed package along with the -- total configuration information. The configuration information is total in -- the sense that it provides all the configuration information and so the -- final configure process will be independent of the environment. ---data ConfiguredPackage = ConfiguredPackage-       SourcePackage       -- package info, including repo-       FlagAssignment      -- complete flag assignment for the package-       [OptionalStanza]    -- list of enabled optional stanzas for the package-       (ComponentDeps [ConfiguredId])-                           -- set of exact dependencies (installed or source).-                           -- These must be consistent with the 'buildDepends'-                           -- in the 'PackageDescription' that you'd get by-                           -- applying the flag assignment and optional stanzas.+-- 'ConfiguredPackage' is assumed to not support Backpack.  Only the+-- @new-build@ codepath supports Backpack.+--+data ConfiguredPackage loc = ConfiguredPackage {+       confPkgId :: InstalledPackageId,+       confPkgSource :: SourcePackage loc, -- package info, including repo+       confPkgFlags :: FlagAssignment,     -- complete flag assignment for the package+       confPkgStanzas :: [OptionalStanza], -- list of enabled optional stanzas for the package+       confPkgDeps :: ComponentDeps [ConfiguredId]+                               -- set of exact dependencies (installed or source).+                               -- These must be consistent with the 'buildDepends'+                               -- in the 'PackageDescription' that you'd get by+                               -- applying the flag assignment and optional stanzas.+    }   deriving (Eq, Show, Generic) -instance Binary ConfiguredPackage+-- | 'HasConfiguredId' indicates data types which have a 'ConfiguredId'.+-- This type class is mostly used to conveniently finesse between+-- 'ElaboratedPackage' and 'ElaboratedComponent'.+--+instance HasConfiguredId (ConfiguredPackage loc) where+    configuredId pkg = ConfiguredId (packageId pkg) (Just CLibName) (confPkgId pkg) +-- 'ConfiguredPackage' is the legacy codepath, we are guaranteed+-- to never have a nontrivial 'UnitId'+instance PackageFixedDeps (ConfiguredPackage loc) where+    depends = fmap (map (newSimpleUnitId . confInstId)) . confPkgDeps++instance IsNode (ConfiguredPackage loc) where+    type Key (ConfiguredPackage loc) = UnitId+    nodeKey       = newSimpleUnitId . confPkgId+    -- TODO: if we update ConfiguredPackage to support order-only+    -- dependencies, need to include those here.+    -- NB: have to deduplicate, otherwise the planner gets confused+    nodeNeighbors = ordNub . CD.flatDeps . depends++instance (Binary loc) => Binary (ConfiguredPackage loc)++ -- | A ConfiguredId is a package ID for a configured package. ----- Once we configure a source package we know it's UnitId--- (at least, in principle, even if we have to fake it currently). It is still+-- Once we configure a source package we know it's UnitId. It is still -- however useful in lots of places to also know the source ID for the package. -- We therefore bundle the two. -- -- An already installed package of course is also "configured" (all it's -- configuration parameters and dependencies have been specified).------ TODO: I wonder if it would make sense to promote this datatype to Cabal--- and use it consistently instead of UnitIds? data ConfiguredId = ConfiguredId {     confSrcId  :: PackageId-  , confInstId :: UnitId+  , confCompName :: Maybe ComponentName+  , confInstId :: ComponentId   }-  deriving (Eq, Generic)+  deriving (Eq, Ord, Generic) +annotatedIdToConfiguredId :: AnnotatedId ComponentId -> ConfiguredId+annotatedIdToConfiguredId aid = ConfiguredId {+        confSrcId    = ann_pid aid,+        confCompName = Just (ann_cname aid),+        confInstId   = ann_id aid+    }+ instance Binary ConfiguredId  instance Show ConfiguredId where-  show = show . confSrcId+  show cid = show (confInstId cid)  instance Package ConfiguredId where   packageId = confSrcId -instance HasUnitId ConfiguredId where-  installedUnitId = confInstId--instance Package ConfiguredPackage where-  packageId (ConfiguredPackage pkg _ _ _) = packageId pkg--instance PackageFixedDeps ConfiguredPackage where-  depends (ConfiguredPackage _ _ _ deps) = fmap (map confInstId) deps--instance HasUnitId ConfiguredPackage where-  installedUnitId = fakeUnitId . packageId---- | Like 'ConfiguredPackage', but with all dependencies guaranteed to be--- installed already, hence itself ready to be installed.-data GenericReadyPackage srcpkg ipkg-   = ReadyPackage-       srcpkg                  -- see 'ConfiguredPackage'.-       (ComponentDeps [ipkg])  -- Installed dependencies.-  deriving (Eq, Show, Generic)--type ReadyPackage = GenericReadyPackage ConfiguredPackage InstalledPackageInfo--instance Package srcpkg => Package (GenericReadyPackage srcpkg ipkg) where-  packageId (ReadyPackage srcpkg _deps) = packageId srcpkg--instance (Package srcpkg, HasUnitId ipkg) =>-         PackageFixedDeps (GenericReadyPackage srcpkg ipkg) where-  depends (ReadyPackage _ deps) = fmap (map installedUnitId) deps--instance HasUnitId srcpkg =>-         HasUnitId (GenericReadyPackage srcpkg ipkg) where-  installedUnitId (ReadyPackage pkg _) = installedUnitId pkg+instance Package (ConfiguredPackage loc) where+  packageId cpkg = packageId (confPkgSource cpkg) -instance (Binary srcpkg, Binary ipkg) => Binary (GenericReadyPackage srcpkg ipkg)+instance HasMungedPackageId (ConfiguredPackage loc) where+  mungedId cpkg = computeCompatPackageId (packageId cpkg) Nothing +-- Never has nontrivial UnitId+instance HasUnitId (ConfiguredPackage loc) where+  installedUnitId = newSimpleUnitId . confPkgId --- | A package description along with the location of the package sources.----data SourcePackage = SourcePackage {-    packageInfoId        :: PackageId,-    packageDescription   :: GenericPackageDescription,-    packageSource        :: PackageLocation (Maybe FilePath),-    packageDescrOverride :: PackageDescriptionOverride-  }-  deriving (Eq, Show, Generic)+instance PackageInstalled (ConfiguredPackage loc) where+  installedDepends = CD.flatDeps . depends -instance Binary SourcePackage+class HasConfiguredId a where+    configuredId :: a -> ConfiguredId --- | We sometimes need to override the .cabal file in the tarball with--- the newer one from the package index.-type PackageDescriptionOverride = Maybe ByteString+-- NB: This instance is slightly dangerous, in that you'll lose+-- information about the specific UnitId you depended on.+instance HasConfiguredId InstalledPackageInfo where+    configuredId ipkg = ConfiguredId (packageId ipkg)+                            (Just (sourceComponentName ipkg))+                            (installedComponentId ipkg) -instance Package SourcePackage where packageId = packageInfoId+-- | Like 'ConfiguredPackage', but with all dependencies guaranteed to be+-- installed already, hence itself ready to be installed.+newtype GenericReadyPackage srcpkg = ReadyPackage srcpkg -- see 'ConfiguredPackage'.+  deriving (Eq, Show, Generic, Package, PackageFixedDeps,+            HasMungedPackageId, HasUnitId, PackageInstalled, Binary) -data OptionalStanza-    = TestStanzas-    | BenchStanzas-  deriving (Eq, Ord, Enum, Bounded, Show, Generic)+-- Can't newtype derive this+instance IsNode srcpkg => IsNode (GenericReadyPackage srcpkg) where+    type Key (GenericReadyPackage srcpkg) = Key srcpkg+    nodeKey (ReadyPackage spkg) = nodeKey spkg+    nodeNeighbors (ReadyPackage spkg) = nodeNeighbors spkg -instance Binary OptionalStanza+type ReadyPackage = GenericReadyPackage (ConfiguredPackage UnresolvedPkgLoc) -enableStanzas-    :: [OptionalStanza]-    -> GenericPackageDescription-    -> GenericPackageDescription-enableStanzas stanzas gpkg = gpkg-    { condBenchmarks = flagBenchmarks $ condBenchmarks gpkg-    , condTestSuites = flagTests $ condTestSuites gpkg-    }-  where-    enableTest t = t { testEnabled = TestStanzas `elem` stanzas }-    enableBenchmark bm = bm { benchmarkEnabled = BenchStanzas `elem` stanzas }-    flagBenchmarks = map (\(n, bm) -> (n, mapTreeData enableBenchmark bm))-    flagTests = map (\(n, t) -> (n, mapTreeData enableTest t))+-- | Convenience alias for 'SourcePackage UnresolvedPkgLoc'.+type UnresolvedSourcePackage = SourcePackage UnresolvedPkgLoc  -- ------------------------------------------------------------ -- * Package locations and repositories -- ------------------------------------------------------------ +type UnresolvedPkgLoc = PackageLocation (Maybe FilePath)++type ResolvedPkgLoc = PackageLocation FilePath+ data PackageLocation local =      -- | An unpacked package in the given dir, or current dir@@ -249,7 +236,7 @@ --TODO: --  * add support for darcs and other SCM style remote repos with a local cache --  | ScmPackage-  deriving (Show, Functor, Eq, Ord, Generic)+  deriving (Show, Functor, Eq, Ord, Generic, Typeable)  instance Binary local => Binary (PackageLocation local) @@ -341,7 +328,14 @@ -- * Build results -- ------------------------------------------------------------ -type BuildResult  = Either BuildFailure BuildSuccess+-- | A summary of the outcome for building a single package.+--+type BuildOutcome = Either BuildFailure BuildResult++-- | A summary of the outcome for building a whole set of packages.+--+type BuildOutcomes = Map UnitId BuildOutcome+ data BuildFailure = PlanningFailed                   | DependentFailed PackageId                   | DownloadFailed  SomeException@@ -350,18 +344,25 @@                   | BuildFailed     SomeException                   | TestsFailed     SomeException                   | InstallFailed   SomeException-  deriving (Show, Generic)-data BuildSuccess = BuildOk         DocsResult TestsResult-                                    (Maybe InstalledPackageInfo)+  deriving (Show, Typeable, Generic)++instance Exception BuildFailure++-- Note that the @Maybe InstalledPackageInfo@ is a slight hack: we only+-- the public library's 'InstalledPackageInfo' is stored here, even if+-- there were 'InstalledPackageInfo' from internal libraries.  This+-- 'InstalledPackageInfo' is not used anyway, so it makes no difference.+data BuildResult = BuildResult DocsResult TestsResult+                               (Maybe InstalledPackageInfo)   deriving (Show, Generic)  data DocsResult  = DocsNotTried  | DocsFailed  | DocsOk-  deriving (Show, Generic)+  deriving (Show, Generic, Typeable) data TestsResult = TestsNotTried | TestsOk-  deriving (Show, Generic)+  deriving (Show, Generic, Typeable)  instance Binary BuildFailure-instance Binary BuildSuccess+instance Binary BuildResult instance Binary DocsResult instance Binary TestsResult 
Distribution/Client/Update.hs view
@@ -47,7 +47,6 @@   warn verbosity $ "No remote package servers have been specified. Usually "                 ++ "you would have one specified in the config file." update verbosity repoCtxt = do-  jobCtrl <- newParallelJobControl   let repos       = repoContextRepos repoCtxt       remoteRepos = catMaybes (map maybeRepoRemote repos)   case remoteRepos of@@ -58,6 +57,7 @@     _ -> notice verbosity . unlines             $ "Downloading the latest package lists from: "             : map (("- " ++) . remoteRepoName) remoteRepos+  jobCtrl <- newParallelJobControl (length repos)   mapM_ (spawnJob jobCtrl . updateRepo verbosity repoCtxt) repos   mapM_ (\_ -> collectJob jobCtrl) repos 
Distribution/Client/Upload.hs view
@@ -1,13 +1,13 @@-module Distribution.Client.Upload (check, upload, uploadDoc, report) where+module Distribution.Client.Upload (upload, uploadDoc, report) where  import Distribution.Client.Types ( Username(..), Password(..)                                  , RemoteRepo(..), maybeRepoRemote ) import Distribution.Client.HttpUtils          ( HttpTransport(..), remoteRepoTryUpgradeToHttps ) import Distribution.Client.Setup-         ( RepoContext(..) )+         ( IsCandidate(..), RepoContext(..) ) -import Distribution.Simple.Utils (notice, warn, info, die)+import Distribution.Simple.Utils (notice, warn, info, die') import Distribution.Verbosity (Verbosity) import Distribution.Text (display) import Distribution.Client.Config@@ -15,69 +15,99 @@ import qualified Distribution.Client.BuildReports.Anonymous as BuildReport import qualified Distribution.Client.BuildReports.Upload as BuildReport -import Network.URI (URI(uriPath), parseURI)+import Network.URI (URI(uriPath)) import Network.HTTP (Header(..), HeaderName(..)) -import System.IO        (hFlush, stdin, stdout, hGetEcho, hSetEcho)+import System.IO        (hFlush, stdout)+import System.IO.Echo   (withoutInputEcho) import System.Exit      (exitFailure)-import Control.Exception (bracket)-import System.FilePath  ((</>), takeExtension, takeFileName)+import System.FilePath  ((</>), takeExtension, takeFileName, dropExtension) import qualified System.FilePath.Posix as FilePath.Posix ((</>)) import System.Directory-import Control.Monad (forM_, when)+import Control.Monad (forM_, when, foldM) import Data.Maybe (catMaybes)+import Data.Char (isSpace)  type Auth = Maybe (String, String) -checkURI :: URI-Just checkURI = parseURI $ "http://hackage.haskell.org/cgi-bin/"-                           ++ "hackage-scripts/check-pkg"+-- > stripExtensions ["tar", "gz"] "foo.tar.gz"+-- Just "foo"+-- > stripExtensions ["tar", "gz"] "foo.gz.tar"+-- Nothing+stripExtensions :: [String] -> FilePath -> Maybe String+stripExtensions exts path = foldM f path (reverse exts)+ where+  f p e+    | takeExtension p == '.':e = Just (dropExtension p)+    | otherwise = Nothing  upload :: Verbosity -> RepoContext-       -> Maybe Username -> Maybe Password -> [FilePath]+       -> Maybe Username -> Maybe Password -> IsCandidate -> [FilePath]        -> IO ()-upload verbosity repoCtxt mUsername mPassword paths = do+upload verbosity repoCtxt mUsername mPassword isCandidate paths = do     let repos = repoContextRepos repoCtxt     transport  <- repoContextGetTransport repoCtxt     targetRepo <-       case [ remoteRepo | Just remoteRepo <- map maybeRepoRemote repos ] of-        [] -> die "Cannot upload. No remote repositories are configured."-        rs -> remoteRepoTryUpgradeToHttps transport (last rs)+        [] -> die' verbosity "Cannot upload. No remote repositories are configured."+        rs -> remoteRepoTryUpgradeToHttps verbosity transport (last rs)     let targetRepoURI = remoteRepoURI targetRepo         rootIfEmpty x = if null x then "/" else x         uploadURI = targetRepoURI {+            uriPath = rootIfEmpty (uriPath targetRepoURI) FilePath.Posix.</>+              case isCandidate of+                IsCandidate -> "packages/candidates"+                IsPublished -> "upload"+        }+        packageURI pkgid = targetRepoURI {             uriPath = rootIfEmpty (uriPath targetRepoURI)-                      FilePath.Posix.</> "upload"+                      FilePath.Posix.</> concat+              [ "package/", pkgid+              , case isCandidate of+                  IsCandidate -> "/candidate"+                  IsPublished -> ""+              ]         }     Username username <- maybe promptUsername return mUsername     Password password <- maybe promptPassword return mPassword     let auth = Just (username,password)     forM_ paths $ \path -> do       notice verbosity $ "Uploading " ++ path ++ "... "-      handlePackage transport verbosity uploadURI auth path+      case fmap takeFileName (stripExtensions ["tar", "gz"] path) of+        Just pkgid -> handlePackage transport verbosity uploadURI+                                    (packageURI pkgid) auth isCandidate path+        -- This case shouldn't really happen, since we check in Main that we+        -- only pass tar.gz files to upload.+        Nothing -> die' verbosity $ "Not a tar.gz file: " ++ path  uploadDoc :: Verbosity -> RepoContext-          -> Maybe Username -> Maybe Password -> FilePath+          -> Maybe Username -> Maybe Password -> IsCandidate -> FilePath           -> IO ()-uploadDoc verbosity repoCtxt mUsername mPassword path = do+uploadDoc verbosity repoCtxt mUsername mPassword isCandidate path = do     let repos = repoContextRepos repoCtxt     transport  <- repoContextGetTransport repoCtxt     targetRepo <-       case [ remoteRepo | Just remoteRepo <- map maybeRepoRemote repos ] of-        [] -> die $ "Cannot upload. No remote repositories are configured."-        rs -> remoteRepoTryUpgradeToHttps transport (last rs)+        [] -> die' verbosity $ "Cannot upload. No remote repositories are configured."+        rs -> remoteRepoTryUpgradeToHttps verbosity transport (last rs)     let targetRepoURI = remoteRepoURI targetRepo         rootIfEmpty x = if null x then "/" else x         uploadURI = targetRepoURI {             uriPath = rootIfEmpty (uriPath targetRepoURI)-                      FilePath.Posix.</> "package/" ++ pkgid ++ "/docs"+                      FilePath.Posix.</> concat+              [ "package/", pkgid+              , case isCandidate of+                IsCandidate -> "/candidate"+                IsPublished -> ""+              , "/docs"+              ]         }         (reverseSuffix, reversePkgid) = break (== '-')                                         (reverse (takeFileName path))         pkgid = reverse $ tail reversePkgid     when (reverse reverseSuffix /= "docs.tar.gz"           || null reversePkgid || head reversePkgid /= '-') $-      die "Expected a file name matching the pattern <pkgid>-docs.tar.gz"+      die' verbosity "Expected a file name matching the pattern <pkgid>-docs.tar.gz"     Username username <- maybe promptUsername return mUsername     Password password <- maybe promptPassword return mPassword @@ -89,7 +119,9 @@     notice verbosity $ "Uploading documentation " ++ path ++ "... "     resp <- putHttpFile transport verbosity uploadURI path auth headers     case resp of-      (200,_)     ->+      -- Hackage responds with 204 No Content when docs are uploaded+      -- successfully.+      (code,_) | code `elem` [200,204] -> do         notice verbosity "Ok"       (code,err)  -> do         notice verbosity $ "Error uploading documentation "@@ -108,10 +140,8 @@ promptPassword = do   putStr "Hackage password: "   hFlush stdout-  -- save/restore the terminal echoing status-  passwd <- bracket (hGetEcho stdin) (hSetEcho stdin) $ \_ -> do-    hSetEcho stdin False  -- no echoing for entering the password-    fmap Password getLine+  -- save/restore the terminal echoing status (no echoing for entering the password)+  passwd <- withoutInputEcho $ fmap Password getLine   putStrLn ""   return passwd @@ -132,7 +162,7 @@            contents <- getDirectoryContents srcDir            forM_ (filter (\c -> takeExtension c ==".log") contents) $ \logFile ->              do inp <- readFile (srcDir </> logFile)-                let (reportStr, buildLog) = read inp :: (String,String)+                let (reportStr, buildLog) = read inp :: (String,String) -- TODO: eradicateNoParse                 case BuildReport.parse reportStr of                   Left errs -> warn verbosity $ "Errors: " ++ errs -- FIXME                   Right report' ->@@ -142,22 +172,32 @@                          (remoteRepoURI remoteRepo) [(report', Just buildLog)]                        return () -check :: Verbosity -> RepoContext -> [FilePath] -> IO ()-check verbosity repoCtxt paths = do-    transport <- repoContextGetTransport repoCtxt-    forM_ paths $ \path -> do-      notice verbosity $ "Checking " ++ path ++ "... "-      handlePackage transport verbosity checkURI Nothing path--handlePackage :: HttpTransport -> Verbosity -> URI -> Auth-              -> FilePath -> IO ()-handlePackage transport verbosity uri auth path =+handlePackage :: HttpTransport -> Verbosity -> URI -> URI -> Auth+              -> IsCandidate -> FilePath -> IO ()+handlePackage transport verbosity uri packageUri auth isCandidate path =   do resp <- postHttpFile transport verbosity uri path auth      case resp of-       (200,_)     ->-          notice verbosity "Ok"+       (code,warnings) | code `elem` [200, 204] ->+          notice verbosity $ okMessage isCandidate +++            if null warnings then "" else "\n" ++ formatWarnings (trim warnings)        (code,err)  -> do           notice verbosity $ "Error uploading " ++ path ++ ": "                           ++ "http code " ++ show code ++ "\n"                           ++ err           exitFailure+ where+  okMessage IsCandidate =+    "Package successfully uploaded as candidate. "+    ++ "You can now preview the result at '" ++ show packageUri+    ++ "'. To publish the candidate, use 'cabal upload --publish'."+  okMessage IsPublished =+    "Package successfully published. You can now view it at '"+    ++ show packageUri ++ "'."++formatWarnings :: String -> String+formatWarnings x = "Warnings:\n" ++ (unlines . map ("- " ++) . lines) x++-- Trim+trim :: String -> String+trim = f . f+      where f = reverse . dropWhile isSpace
Distribution/Client/Utils.hs view
@@ -3,12 +3,14 @@ module Distribution.Client.Utils ( MergeResult(..)                                  , mergeBy, duplicates, duplicatesBy                                  , readMaybe-                                 , inDir, logDirChange+                                 , inDir, withEnv, logDirChange+                                 , withExtraPathEnv                                  , determineNumJobs, numberOfProcessors                                  , removeExistingFile                                  , withTempFileName                                  , makeAbsoluteToCwd                                  , makeRelativeToCwd, makeRelativeToDir+                                 , makeRelativeCanonical                                  , filePathToByteString                                  , byteStringToFilePath, tryCanonicalizePath                                  , canonicalizePathNoThrow@@ -18,36 +20,27 @@                                  , relaxEncodingErrors)        where +import Prelude ()+import Distribution.Client.Compat.Prelude++import Distribution.Compat.Environment import Distribution.Compat.Exception   ( catchIO )-import Distribution.Client.Compat.Time ( getModTime )+import Distribution.Compat.Time ( getModTime ) import Distribution.Simple.Setup       ( Flag(..) )-import Distribution.Simple.Utils       ( die, findPackageDesc )+import Distribution.Verbosity+import Distribution.Simple.Utils       ( die', findPackageDesc ) import qualified Data.ByteString.Lazy as BS-#if !MIN_VERSION_base(4,8,0)-import Control.Applicative-#endif-import Control.Monad-         ( when ) import Data.Bits          ( (.|.), shiftL, shiftR )-import Data.Char-         ( ord, chr )-#if MIN_VERSION_base(4,6,0)-import Text.Read-         ( readMaybe )-#endif+import System.FilePath import Data.List-         ( isPrefixOf, sortBy, groupBy )-import Data.Word-         ( Word8, Word32)+         ( groupBy ) import Foreign.C.Types ( CInt(..) ) import qualified Control.Exception as Exception          ( finally, bracket ) import System.Directory          ( canonicalizePath, doesFileExist, getCurrentDirectory          , removeFile, setCurrentDirectory )-import System.FilePath-         ( (</>), isAbsolute, takeDrive, splitPath, joinPath ) import System.IO          ( Handle, hClose, openTempFile #if MIN_VERSION_base(4,4,0)@@ -64,16 +57,12 @@ #endif  #if defined(mingw32_HOST_OS) || MIN_VERSION_directory(1,2,3)-import Prelude hiding (ioError)-import Control.Monad (liftM2, unless)-import System.Directory (doesDirectoryExist)-import System.IO.Error (ioError, mkIOError, doesNotExistErrorType)+import qualified System.Directory as Dir+import qualified System.IO.Error as IOError #endif  -- | Generic merging utility. For sorted input lists this is a full outer join. ----- * The result list never contains @(Nothing, Nothing)@.--- mergeBy :: (a -> b -> Ordering) -> [a] -> [b] -> [MergeResult a b] mergeBy cmp = merge   where@@ -99,14 +88,6 @@     moreThanOne (_:_:_) = True     moreThanOne _       = False -#if !MIN_VERSION_base(4,6,0)--- | An implementation of readMaybe, for compatability with older base versions.-readMaybe :: Read a => String -> Maybe a-readMaybe s = case reads s of-                [(x,"")] -> Just x-                _        -> Nothing-#endif- -- | Like 'removeFile', but does not throw an exception when the file does not -- exist. removeExistingFile :: FilePath -> IO ()@@ -129,6 +110,9 @@     (\(name, h) -> hClose h >> action name)  -- | Executes the action in the specified directory.+--+-- Warning: This operation is NOT thread-safe, because current+-- working directory is a process-global concept. inDir :: Maybe FilePath -> IO a -> IO a inDir Nothing m = m inDir (Just d) m = do@@ -136,6 +120,36 @@   setCurrentDirectory d   m `Exception.finally` setCurrentDirectory old +-- | Executes the action with an environment variable set to some+-- value.+--+-- Warning: This operation is NOT thread-safe, because current+-- environment is a process-global concept.+withEnv :: String -> String -> IO a -> IO a+withEnv k v m = do+  mb_old <- lookupEnv k+  setEnv k v+  m `Exception.finally` (case mb_old of+    Nothing -> unsetEnv k+    Just old -> setEnv k old)++-- | Executes the action, increasing the PATH environment+-- in some way+--+-- Warning: This operation is NOT thread-safe, because the+-- environment variables are a process-global concept.+withExtraPathEnv :: [FilePath] -> IO a -> IO a+withExtraPathEnv paths m = do+  oldPathSplit <- getSearchPath+  let newPath = mungePath $ intercalate [searchPathSeparator] (paths ++ oldPathSplit)+      oldPath = mungePath $ intercalate [searchPathSeparator] oldPathSplit+      -- TODO: This is a horrible hack to work around the fact that+      -- setEnv can't take empty values as an argument+      mungePath p | p == ""   = "/dev/null"+                  | otherwise = p+  setEnv "PATH" newPath+  m `Exception.finally` setEnv "PATH" oldPath+ -- | Log directory change in 'make' compatible syntax logDirChange :: (String -> IO ()) -> Maybe FilePath -> IO a -> IO a logDirChange _ Nothing m = m@@ -231,9 +245,9 @@ tryCanonicalizePath path = do   ret <- canonicalizePath path #if defined(mingw32_HOST_OS) || MIN_VERSION_directory(1,2,3)-  exists <- liftM2 (||) (doesFileExist ret) (doesDirectoryExist ret)+  exists <- liftM2 (||) (doesFileExist ret) (Dir.doesDirectoryExist ret)   unless exists $-    ioError $ mkIOError doesNotExistErrorType "canonicalizePath"+    IOError.ioError $ IOError.mkIOError IOError.doesNotExistErrorType "canonicalizePath"                         Nothing (Just ret) #endif   return ret@@ -285,17 +299,17 @@       return ()  -- |Like 'tryFindPackageDesc', but with error specific to add-source deps.-tryFindAddSourcePackageDesc :: FilePath -> String -> IO FilePath-tryFindAddSourcePackageDesc depPath err = tryFindPackageDesc depPath $+tryFindAddSourcePackageDesc :: Verbosity -> FilePath -> String -> IO FilePath+tryFindAddSourcePackageDesc verbosity depPath err = tryFindPackageDesc verbosity depPath $     err ++ "\n" ++ "Failed to read cabal file of add-source dependency: "     ++ depPath  -- |Try to find a @.cabal@ file, in directory @depPath@. Fails if one cannot be -- found, with @err@ prefixing the error message. This function simply allows -- us to give a more descriptive error than that provided by @findPackageDesc@.-tryFindPackageDesc :: FilePath -> String -> IO FilePath-tryFindPackageDesc depPath err = do+tryFindPackageDesc :: Verbosity -> FilePath -> String -> IO FilePath+tryFindPackageDesc verbosity depPath err = do     errOrCabalFile <- findPackageDesc depPath     case errOrCabalFile of         Right file -> return file-        Left _ -> die err+        Left _ -> die' verbosity err
+ Distribution/Client/Utils/Assertion.hs view
@@ -0,0 +1,18 @@+{-# LANGUAGE CPP #-}+module Distribution.Client.Utils.Assertion (expensiveAssert) where++#ifdef DEBUG_EXPENSIVE_ASSERTIONS+import Control.Exception (assert)+import Distribution.Compat.Stack+#endif++-- | Like 'assert', but only enabled with -fdebug-expensive-assertions. This+-- function can be used for expensive assertions that should only be turned on+-- during testing or debugging.+#ifdef DEBUG_EXPENSIVE_ASSERTIONS+expensiveAssert :: WithCallStack (Bool -> a -> a)+expensiveAssert = assert+#else+expensiveAssert :: Bool -> a -> a+expensiveAssert _ = id+#endif
− Distribution/Client/Utils/LabeledGraph.hs
@@ -1,116 +0,0 @@--- | Wrapper around Data.Graph with support for edge labels-{-# LANGUAGE ScopedTypeVariables #-}-module Distribution.Client.Utils.LabeledGraph (-    -- * Graphs-    Graph-  , Vertex-    -- ** Building graphs-  , graphFromEdges-  , graphFromEdges'-  , buildG-  , transposeG-    -- ** Graph properties-  , vertices-  , edges-    -- ** Operations on the underlying unlabeled graph-  , forgetLabels-  , topSort-  ) where--import Data.Array-import Data.Graph (Vertex, Bounds)-import Data.List (sortBy)-import Data.Maybe (mapMaybe)-import qualified Data.Graph as G--{--------------------------------------------------------------------------------  Types--------------------------------------------------------------------------------}--type Graph e = Array Vertex [(e, Vertex)]-type Edge  e = (Vertex, e, Vertex)--{--------------------------------------------------------------------------------  Building graphs--------------------------------------------------------------------------------}---- | Construct an edge-labeled graph------ This is a simple adaptation of the definition in Data.Graph-graphFromEdges :: forall key node edge. Ord key-               => [ (node, key, [(edge, key)]) ]-               -> ( Graph edge-                  , Vertex -> (node, key, [(edge, key)])-                  , key -> Maybe Vertex-                  )-graphFromEdges edges0 =-    (graph, \v -> vertex_map ! v, key_vertex)-  where-    max_v        = length edges0 - 1-    bounds0      = (0, max_v) :: (Vertex, Vertex)-    sorted_edges = sortBy lt edges0-    edges1       = zipWith (,) [0..] sorted_edges--    graph        = array bounds0 [(v, (mapMaybe mk_edge ks))-                                 | (v, (_, _, ks)) <- edges1]-    key_map      = array bounds0 [(v, k                    )-                                 | (v, (_, k, _ )) <- edges1]-    vertex_map   = array bounds0 edges1--    (_,k1,_) `lt` (_,k2,_) = k1 `compare` k2--    mk_edge :: (edge, key) -> Maybe (edge, Vertex)-    mk_edge (edge, key) = do v <- key_vertex key ; return (edge, v)--    --  returns Nothing for non-interesting vertices-    key_vertex :: key -> Maybe Vertex-    key_vertex k = findVertex 0 max_v-      where-        findVertex a b-          | a > b     = Nothing-          | otherwise = case compare k (key_map ! mid) of-              LT -> findVertex a (mid-1)-              EQ -> Just mid-              GT -> findVertex (mid+1) b-          where-            mid = a + (b - a) `div` 2--graphFromEdges' :: Ord key-                => [ (node, key, [(edge, key)]) ]-                -> ( Graph edge-                   , Vertex -> (node, key, [(edge, key)])-                   )-graphFromEdges' x = (a,b)-  where-    (a,b,_) = graphFromEdges x--transposeG :: Graph e -> Graph e-transposeG g = buildG (bounds g) (reverseE g)--buildG :: Bounds -> [Edge e] -> Graph e-buildG bounds0 edges0 = accumArray (flip (:)) [] bounds0 (map reassoc edges0)-  where-    reassoc (v, e, w) = (v, (e, w))--reverseE :: Graph e -> [Edge e]-reverseE g = [ (w, e, v) | (v, e, w) <- edges g ]--{--------------------------------------------------------------------------------  Graph properties--------------------------------------------------------------------------------}--vertices :: Graph e -> [Vertex]-vertices = indices--edges :: Graph e -> [Edge e]-edges g = [ (v, e, w) | v <- vertices g, (e, w) <- g!v ]--{--------------------------------------------------------------------------------  Operations on the underlying unlabelled graph--------------------------------------------------------------------------------}--forgetLabels :: Graph e -> G.Graph-forgetLabels = fmap (map snd)--topSort :: Graph e -> [Vertex]-topSort = G.topSort . forgetLabels
Distribution/Client/Win32SelfUpgrade.hs view
@@ -42,7 +42,7 @@     deleteOldExeFile,   ) where -#if mingw32_HOST_OS+#ifdef mingw32_HOST_OS  import qualified System.Win32 as Win32 import System.Win32 (DWORD, BOOL, HANDLE, LPCTSTR)@@ -212,7 +212,7 @@ #else  import Distribution.Verbosity (Verbosity)-import Distribution.Simple.Utils (die)+import Distribution.Simple.Utils (die')  possibleSelfUpgrade :: Verbosity                     -> [FilePath]@@ -220,6 +220,6 @@ possibleSelfUpgrade _ _ action = action  deleteOldExeFile :: Verbosity -> Int -> FilePath -> IO ()-deleteOldExeFile _ _ _ = die "win32selfupgrade not needed except on win32"+deleteOldExeFile verbosity _ _ = die' verbosity "win32selfupgrade not needed except on win32"  #endif
Distribution/Client/World.hs view
@@ -29,14 +29,13 @@     getContents,   ) where -import Distribution.Package-         ( Dependency(..) )+import Distribution.Types.Dependency import Distribution.PackageDescription-         ( FlagAssignment, FlagName(FlagName) )+         ( FlagAssignment, mkFlagName, unFlagName ) import Distribution.Verbosity          ( Verbosity ) import Distribution.Simple.Utils-         ( die, info, chattyTry, writeFileAtomic )+         ( die', info, chattyTry, writeFileAtomic ) import Distribution.Text          ( Text(..), display, simpleParse ) import qualified Distribution.Compat.ReadP as Parse@@ -91,7 +90,7 @@ modifyWorld _ _         _     []   = return () modifyWorld f verbosity world pkgs =   chattyTry "Error while updating world-file. " $ do-    pkgsOldWorld <- getContents world+    pkgsOldWorld <- getContents verbosity world     -- Filter out packages that are not in the world file:     let pkgsNewWorld = nubBy equalUDep $ f pkgs pkgsOldWorld     -- 'Dependency' is not an Ord instance, so we need to check for@@ -107,12 +106,12 @@   -- | Returns the content of the world file as a list-getContents :: FilePath -> IO [WorldPkgInfo]-getContents world = do+getContents :: Verbosity -> FilePath -> IO [WorldPkgInfo]+getContents verbosity world = do   content <- safelyReadFile world   let result = map simpleParse (lines $ B.unpack content)   case sequence result of-    Nothing -> die "Could not parse world file."+    Nothing -> die' verbosity "Could not parse world file."     Just xs -> return xs   where   safelyReadFile :: FilePath -> IO B.ByteString@@ -128,10 +127,10 @@       dispFlags [] = Disp.empty       dispFlags fs = Disp.text "--flags="                   <> Disp.doubleQuotes (flagAssToDoc fs)-      flagAssToDoc = foldr (\(FlagName fname,val) flagAssDoc ->+      flagAssToDoc = foldr (\(fname,val) flagAssDoc ->                              (if not val then Disp.char '-'                                          else Disp.empty)-                             Disp.<> Disp.text fname+                             Disp.<> Disp.text (unFlagName fname)                              Disp.<+> flagAssDoc)                            Disp.empty   parse = do@@ -156,7 +155,7 @@             val <- negative Parse.+++ positive             name <- ident             Parse.skipSpaces-            return (FlagName name,val)+            return (mkFlagName name,val)           negative = do             _ <- Parse.char '-'             return False
+ Distribution/Solver/Modular.hs view
@@ -0,0 +1,60 @@+module Distribution.Solver.Modular+         ( modularResolver, SolverConfig(..)) where++-- Here, we try to map between the external cabal-install solver+-- interface and the internal interface that the solver actually+-- expects. There are a number of type conversions to perform: we+-- have to convert the package indices to the uniform index used+-- by the solver; we also have to convert the initial constraints;+-- and finally, we have to convert back the resulting install+-- plan.++import Data.Map as M+         ( fromListWith )+import Distribution.Compat.Graph+         ( IsNode(..) )+import Distribution.Solver.Modular.Assignment+         ( toCPs )+import Distribution.Solver.Modular.ConfiguredConversion+         ( convCP )+import Distribution.Solver.Modular.IndexConversion+         ( convPIs )+import Distribution.Solver.Modular.Log+         ( logToProgress )+import Distribution.Solver.Modular.Package+         ( PN )+import Distribution.Solver.Modular.Solver+         ( SolverConfig(..), solve )+import Distribution.Solver.Types.LabeledPackageConstraint+import Distribution.Solver.Types.PackageConstraint+import Distribution.Solver.Types.DependencyResolver+import Distribution.System+         ( Platform(..) )+import Distribution.Simple.Utils+         ( ordNubBy )+++-- | Ties the two worlds together: classic cabal-install vs. the modular+-- solver. Performs the necessary translations before and after.+modularResolver :: SolverConfig -> DependencyResolver loc+modularResolver sc (Platform arch os) cinfo iidx sidx pkgConfigDB pprefs pcs pns =+  fmap (uncurry postprocess)                           $ -- convert install plan+  logToProgress (solverVerbosity sc) (maxBackjumps sc) $ -- convert log format into progress format+  solve sc cinfo idx pkgConfigDB pprefs gcs pns+    where+      -- Indices have to be converted into solver-specific uniform index.+      idx    = convPIs os arch cinfo (shadowPkgs sc) (strongFlags sc) (solveExecutables sc) iidx sidx+      -- Constraints have to be converted into a finite map indexed by PN.+      gcs    = M.fromListWith (++) (map pair pcs)+        where+          pair lpc = (pcName $ unlabelPackageConstraint lpc, [lpc])++      -- Results have to be converted into an install plan. 'convCP' removes+      -- package qualifiers, which means that linked packages become duplicates+      -- and can be removed.+      postprocess a rdm = ordNubBy nodeKey $+                          map (convCP iidx sidx) (toCPs a rdm)++      -- Helper function to extract the PN from a constraint.+      pcName :: PackageConstraint -> PN+      pcName (PackageConstraint scope _) = scopeToPackageName scope
+ Distribution/Solver/Modular/Assignment.hs view
@@ -0,0 +1,152 @@+module Distribution.Solver.Modular.Assignment+    ( Assignment(..)+    , FAssignment+    , SAssignment+    , PreAssignment(..)+    , extend+    , toCPs+    ) where++import Control.Applicative+import Control.Monad+import Data.Array as A+import Data.List as L+import Data.Map as M+import Data.Maybe+import Prelude hiding (pi)++import Language.Haskell.Extension (Extension, Language)++import Distribution.PackageDescription (FlagAssignment) -- from Cabal++import Distribution.Solver.Types.ComponentDeps (ComponentDeps, Component)+import qualified Distribution.Solver.Types.ComponentDeps as CD+import Distribution.Solver.Types.OptionalStanza+import Distribution.Solver.Types.PackagePath++import Distribution.Solver.Modular.Configured+import Distribution.Solver.Modular.Dependency+import Distribution.Solver.Modular.Flag+import Distribution.Solver.Modular.LabeledGraph+import Distribution.Solver.Modular.Package+import Distribution.Solver.Modular.Version++-- | A (partial) package assignment. Qualified package names+-- are associated with instances.+type PAssignment    = Map QPN I++-- | A (partial) package preassignment. Qualified package names+-- are associated with constrained instances. Constrained instances+-- record constraints about the instances that can still be chosen,+-- and in the extreme case fix a concrete instance.+type PPreAssignment = Map QPN (CI QPN)+type FAssignment    = Map QFN Bool+type SAssignment    = Map QSN Bool++-- | A (partial) assignment of variables.+data Assignment = A PAssignment FAssignment SAssignment+  deriving (Show, Eq)++-- | A preassignment comprises knowledge about variables, but not+-- necessarily fixed values.+data PreAssignment = PA PPreAssignment FAssignment SAssignment++-- | Extend a package preassignment.+--+-- Takes the variable that causes the new constraints, a current preassignment+-- and a set of new dependency constraints.+--+-- We're trying to extend the preassignment with each dependency one by one.+-- Each dependency is for a particular variable. We check if we already have+-- constraints for that variable in the current preassignment. If so, we're+-- trying to merge the constraints.+--+-- Either returns a witness of the conflict that would arise during the merge,+-- or the successfully extended assignment.+extend :: (Extension -> Bool) -- ^ is a given extension supported+       -> (Language  -> Bool) -- ^ is a given language supported+       -> (PkgconfigName -> VR  -> Bool) -- ^ is a given pkg-config requirement satisfiable+       -> Var QPN+       -> PPreAssignment -> [Dep QPN] -> Either (ConflictSet, [Dep QPN]) PPreAssignment+extend extSupported langSupported pkgPresent var = foldM extendSingle+  where++    extendSingle :: PPreAssignment -> Dep QPN+                 -> Either (ConflictSet, [Dep QPN]) PPreAssignment+    extendSingle a (Ext  ext )  =+      if extSupported  ext  then Right a+                            else Left (varToConflictSet var, [Ext ext])+    extendSingle a (Lang lang)  =+      if langSupported lang then Right a+                            else Left (varToConflictSet var, [Lang lang])+    extendSingle a (Pkg pn vr)  =+      if pkgPresent pn vr then Right a+                          else Left (varToConflictSet var, [Pkg pn vr])+    extendSingle a (Dep is_exe qpn ci) =+      let ci' = M.findWithDefault (Constrained []) qpn a+      in  case (\ x -> M.insert qpn x a) <$> merge ci' ci of+            Left (c, (d, d')) -> Left  (c, L.map (Dep is_exe qpn) (simplify (P qpn) d d'))+            Right x           -> Right x++    -- We're trying to remove trivial elements of the conflict. If we're just+    -- making a choice pkg == instance, and pkg => pkg == instance is a part+    -- of the conflict, then this info is clear from the context and does not+    -- have to be repeated.+    simplify v (Fixed _ var') c | v == var && var' == var = [c]+    simplify v c (Fixed _ var') | v == var && var' == var = [c]+    simplify _ c              d                           = [c, d]++-- | Delivers an ordered list of fully configured packages.+--+-- TODO: This function is (sort of) ok. However, there's an open bug+-- w.r.t. unqualification. There might be several different instances+-- of one package version chosen by the solver, which will lead to+-- clashes.+toCPs :: Assignment -> RevDepMap -> [CP QPN]+toCPs (A pa fa sa) rdm =+  let+    -- get hold of the graph+    g   :: Graph Component+    vm  :: Vertex -> ((), QPN, [(Component, QPN)])+    cvm :: QPN -> Maybe Vertex+    -- Note that the RevDepMap contains duplicate dependencies. Therefore the nub.+    (g, vm, cvm) = graphFromEdges (L.map (\ (x, xs) -> ((), x, nub xs))+                                  (M.toList rdm))+    tg :: Graph Component+    tg = transposeG g+    -- Topsort the dependency graph, yielding a list of pkgs in the right order.+    -- The graph will still contain all the installed packages, and it might+    -- contain duplicates, because several variables might actually resolve to+    -- the same package in the presence of qualified package names.+    ps :: [PI QPN]+    ps = L.map ((\ (_, x, _) -> PI x (pa M.! x)) . vm) $+         topSort g+    -- Determine the flags per package, by walking over and regrouping the+    -- complete flag assignment by package.+    fapp :: Map QPN FlagAssignment+    fapp = M.fromListWith (++) $+           L.map (\ ((FN (PI qpn _) fn), b) -> (qpn, [(fn, b)])) $+           M.toList $+           fa+    -- Stanzas per package.+    sapp :: Map QPN [OptionalStanza]+    sapp = M.fromListWith (++) $+           L.map (\ ((SN (PI qpn _) sn), b) -> (qpn, if b then [sn] else [])) $+           M.toList $+           sa+    -- Dependencies per package.+    depp :: QPN -> [(Component, PI QPN)]+    depp qpn = let v :: Vertex+                   v   = fromJust (cvm qpn)+                   dvs :: [(Component, Vertex)]+                   dvs = tg A.! v+               in L.map (\ (comp, dv) -> case vm dv of (_, x, _) -> (comp, PI x (pa M.! x))) dvs+    -- Translated to PackageDeps+    depp' :: QPN -> ComponentDeps [PI QPN]+    depp' = CD.fromList . L.map (\(comp, d) -> (comp, [d])) . depp+  in+    L.map (\ pi@(PI qpn _) -> CP pi+                                 (M.findWithDefault [] qpn fapp)+                                 (M.findWithDefault [] qpn sapp)+                                 (depp' qpn))+          ps
+ Distribution/Solver/Modular/Builder.hs view
@@ -0,0 +1,273 @@+{-# LANGUAGE ScopedTypeVariables #-}+module Distribution.Solver.Modular.Builder (buildTree) where++-- Building the search tree.+--+-- In this phase, we build a search tree that is too large, i.e, it contains+-- invalid solutions. We keep track of the open goals at each point. We+-- nondeterministically pick an open goal (via a goal choice node), create+-- subtrees according to the index and the available solutions, and extend the+-- set of open goals by superficially looking at the dependencies recorded in+-- the index.+--+-- For each goal, we keep track of all the *reasons* why it is being+-- introduced. These are for debugging and error messages, mainly. A little bit+-- of care has to be taken due to the way we treat flags. If a package has+-- flag-guarded dependencies, we cannot introduce them immediately. Instead, we+-- store the entire dependency.++import Data.List as L+import Data.Map as M+import Prelude hiding (sequence, mapM)++import Distribution.Solver.Modular.Dependency+import Distribution.Solver.Modular.Flag+import Distribution.Solver.Modular.Index+import Distribution.Solver.Modular.Package+import Distribution.Solver.Modular.PSQ (PSQ)+import qualified Distribution.Solver.Modular.PSQ as P+import Distribution.Solver.Modular.Tree+import qualified Distribution.Solver.Modular.WeightedPSQ as W++import Distribution.Solver.Types.ComponentDeps (Component)+import Distribution.Solver.Types.PackagePath+import Distribution.Solver.Types.Settings++-- | All state needed to build and link the search tree. It has a type variable+-- because the linking phase doesn't need to know about the state used to build+-- the tree.+data Linker a = Linker {+  buildState   :: a,+  linkingState :: LinkingState+}++-- | The state needed to build the search tree without creating any linked nodes.+data BuildState = BS {+  index :: Index,                -- ^ information about packages and their dependencies+  rdeps :: RevDepMap,            -- ^ set of all package goals, completed and open, with reverse dependencies+  open  :: PSQ (OpenGoal ()) (), -- ^ set of still open goals (flag and package goals)+  next  :: BuildType,            -- ^ kind of node to generate next+  qualifyOptions :: QualifyOptions -- ^ qualification options+}++-- | Map of available linking targets.+type LinkingState = Map (PN, I) [PackagePath]++-- | Extend the set of open goals with the new goals listed.+--+-- We also adjust the map of overall goals, and keep track of the+-- reverse dependencies of each of the goals.+extendOpen :: QPN -> [OpenGoal Component] -> BuildState -> BuildState+extendOpen qpn' gs s@(BS { rdeps = gs', open = o' }) = go gs' o' gs+  where+    go :: RevDepMap -> PSQ (OpenGoal ()) () -> [OpenGoal Component] -> BuildState+    go g o []                                               = s { rdeps = g, open = o }+    go g o (ng@(OpenGoal (Flagged _ _ _ _)      _gr) : ngs) = go g (cons' ng () o) ngs+      -- Note: for 'Flagged' goals, we always insert, so later additions win.+      -- This is important, because in general, if a goal is inserted twice,+      -- the later addition will have better dependency information.+    go g o (ng@(OpenGoal (Stanza  _   _  )      _gr) : ngs) = go g (cons' ng () o) ngs+    go g o (ng@(OpenGoal (Simple (Dep _ qpn _) c) _gr) : ngs)+      | qpn == qpn'       = go                            g               o  ngs+          -- we ignore self-dependencies at this point; TODO: more care may be needed+      | qpn `M.member` g  = go (M.adjust (addIfAbsent (c, qpn')) qpn g)   o  ngs+      | otherwise         = go (M.insert qpn [(c, qpn')]  g) (cons' ng () o) ngs+          -- code above is correct; insert/adjust have different arg order+    go g o (   (OpenGoal (Simple (Ext _ext ) _) _gr) : ngs) = go g o ngs+    go g o (   (OpenGoal (Simple (Lang _lang)_) _gr) : ngs) = go g o ngs+    go g o (   (OpenGoal (Simple (Pkg _pn _vr)_) _gr) : ngs)= go g o ngs++    cons' = P.cons . forgetCompOpenGoal++    addIfAbsent :: Eq a => a -> [a] -> [a]+    addIfAbsent x xs = if x `elem` xs then xs else x : xs++-- | Given the current scope, qualify all the package names in the given set of+-- dependencies and then extend the set of open goals accordingly.+scopedExtendOpen :: QPN -> I -> QGoalReason -> FlaggedDeps Component PN -> FlagInfo ->+                    BuildState -> BuildState+scopedExtendOpen qpn i gr fdeps fdefs s = extendOpen qpn gs s+  where+    -- Qualify all package names+    qfdeps = qualifyDeps (qualifyOptions s) qpn fdeps+    -- Introduce all package flags+    qfdefs = L.map (\ (fn, b) -> Flagged (FN (PI qpn i) fn) b [] []) $ M.toList fdefs+    -- Combine new package and flag goals+    gs     = L.map (flip OpenGoal gr) (qfdefs ++ qfdeps)+    -- NOTE:+    --+    -- In the expression @qfdefs ++ qfdeps@ above, flags occur potentially+    -- multiple times, both via the flag declaration and via dependencies.+    -- The order is potentially important, because the occurrences via+    -- dependencies may record flag-dependency information. After a number+    -- of bugs involving computing this information incorrectly, however,+    -- we're currently not using carefully computed inter-flag dependencies+    -- anymore, but instead use 'simplifyVar' when computing conflict sets+    -- to map all flags of one package to a single flag for conflict set+    -- purposes, thereby treating them all as interdependent.+    --+    -- If we ever move to a more clever algorithm again, then the line above+    -- needs to be looked at very carefully, and probably be replaced by+    -- more systematically computed flag dependency information.++-- | Datatype that encodes what to build next+data BuildType =+    Goals                                  -- ^ build a goal choice node+  | OneGoal (OpenGoal ())                  -- ^ build a node for this goal+  | Instance QPN I PInfo QGoalReason  -- ^ build a tree for a concrete instance+  deriving Show++build :: Linker BuildState -> Tree () QGoalReason+build = ana go+  where+    go :: Linker BuildState -> TreeF () QGoalReason (Linker BuildState)+    go s = addLinking (linkingState s) $ addChildren (buildState s)++addChildren :: BuildState -> TreeF () QGoalReason BuildState++-- If we have a choice between many goals, we just record the choice in+-- the tree. We select each open goal in turn, and before we descend, remove+-- it from the queue of open goals.+addChildren bs@(BS { rdeps = rdm, open = gs, next = Goals })+  | P.null gs = DoneF rdm ()+  | otherwise = GoalChoiceF rdm $ P.mapKeys close+                                $ P.mapWithKey (\ g (_sc, gs') -> bs { next = OneGoal g, open = gs' })+                                $ P.splits gs++-- If we have already picked a goal, then the choice depends on the kind+-- of goal.+--+-- For a package, we look up the instances available in the global info,+-- and then handle each instance in turn.+addChildren    (BS { index = _  , next = OneGoal (OpenGoal (Simple (Ext _             ) _) _ ) }) =+  error "Distribution.Solver.Modular.Builder: addChildren called with Ext goal"+addChildren    (BS { index = _  , next = OneGoal (OpenGoal (Simple (Lang _            ) _) _ ) }) =+  error "Distribution.Solver.Modular.Builder: addChildren called with Lang goal"+addChildren    (BS { index = _  , next = OneGoal (OpenGoal (Simple (Pkg _ _          ) _) _ ) }) =+  error "Distribution.Solver.Modular.Builder: addChildren called with Pkg goal"+addChildren bs@(BS { rdeps = rdm, index = idx, next = OneGoal (OpenGoal (Simple (Dep _ qpn@(Q _ pn) _) _) gr) }) =+  -- If the package does not exist in the index, we construct an emty PChoiceF node for it+  -- After all, we have no choices here. Alternatively, we could immediately construct+  -- a Fail node here, but that would complicate the construction of conflict sets.+  -- We will probably want to give this case special treatment when generating error+  -- messages though.+  case M.lookup pn idx of+    Nothing  -> PChoiceF qpn rdm gr (W.fromList [])+    Just pis -> PChoiceF qpn rdm gr (W.fromList (L.map (\ (i, info) ->+                                                       ([], POption i Nothing, bs { next = Instance qpn i info gr }))+                                                     (M.toList pis)))+      -- TODO: data structure conversion is rather ugly here++-- For a flag, we create only two subtrees, and we create them in the order+-- that is indicated by the flag default.+addChildren bs@(BS { rdeps = rdm, next = OneGoal (OpenGoal (Flagged qfn@(FN (PI qpn _) _) (FInfo b m w) t f) gr) }) =+  FChoiceF qfn rdm gr weak m b (W.fromList+    [([if b then 0 else 1], True,  (extendOpen qpn (L.map (flip OpenGoal (FDependency qfn True )) t) bs) { next = Goals }),+     ([if b then 1 else 0], False, (extendOpen qpn (L.map (flip OpenGoal (FDependency qfn False)) f) bs) { next = Goals })])+  where+    trivial = L.null t && L.null f+    weak = WeakOrTrivial $ unWeakOrTrivial w || trivial++-- For a stanza, we also create only two subtrees. The order is initially+-- False, True. This can be changed later by constraints (force enabling+-- the stanza by replacing the False branch with failure) or preferences+-- (try enabling the stanza if possible by moving the True branch first).++addChildren bs@(BS { rdeps = rdm, next = OneGoal (OpenGoal (Stanza qsn@(SN (PI qpn _) _) t) gr) }) =+  SChoiceF qsn rdm gr trivial (W.fromList+    [([0], False,                                                             bs  { next = Goals }),+     ([1], True,  (extendOpen qpn (L.map (flip OpenGoal (SDependency qsn)) t) bs) { next = Goals })])+  where+    trivial = WeakOrTrivial (L.null t)++-- For a particular instance, we change the state: we update the scope,+-- and furthermore we update the set of goals.+--+-- TODO: We could inline this above.+addChildren bs@(BS { next = Instance qpn i (PInfo fdeps fdefs _) _gr }) =+  addChildren ((scopedExtendOpen qpn i (PDependency (PI qpn i)) fdeps fdefs bs)+         { next = Goals })++{-------------------------------------------------------------------------------+  Add linking+-------------------------------------------------------------------------------}++-- | Introduce link nodes into the tree+--+-- Linking is a phase that adapts package choice nodes and adds the option to+-- link wherever appropriate: Package goals are called "related" if they are for+-- the same instance of the same package (but have different prefixes). A link+-- option is available in a package choice node whenever we can choose an+-- instance that has already been chosen for a related goal at a higher position+-- in the tree. We only create link options for related goals that are not+-- themselves linked, because the choice to link to a linked goal is the same as+-- the choice to link to the target of that goal's linking.+--+-- The code here proceeds by maintaining a finite map recording choices that+-- have been made at higher positions in the tree. For each pair of package name+-- and instance, it stores the prefixes at which we have made a choice for this+-- package instance. Whenever we make an unlinked choice, we extend the map.+-- Whenever we find a choice, we look into the map in order to find out what+-- link options we have to add.+--+-- A separate tree traversal would be simpler. However, 'addLinking' creates+-- linked nodes from existing unlinked nodes, which leads to sharing between the+-- nodes. If we copied the nodes when they were full trees of type+-- 'Tree () QGoalReason', then the sharing would cause a space leak during+-- exploration of the tree. Instead, we only copy the 'BuildState', which is+-- relatively small, while the tree is being constructed. See+-- https://github.com/haskell/cabal/issues/2899+addLinking :: LinkingState -> TreeF () c a -> TreeF () c (Linker a)+-- The only nodes of interest are package nodes+addLinking ls (PChoiceF qpn@(Q pp pn) rdm gr cs) =+  let linkedCs = fmap (\bs -> Linker bs ls) $+                 W.fromList $ concatMap (linkChoices ls qpn) (W.toList cs)+      unlinkedCs = W.mapWithKey goP cs+      allCs = unlinkedCs `W.union` linkedCs++      -- Recurse underneath package choices. Here we just need to make sure+      -- that we record the package choice so that it is available below+      goP :: POption -> a -> Linker a+      goP (POption i Nothing) bs = Linker bs $ M.insertWith (++) (pn, i) [pp] ls+      goP _                   _  = alreadyLinked+  in PChoiceF qpn rdm gr allCs+addLinking ls t = fmap (\bs -> Linker bs ls) t++linkChoices :: forall a w . LinkingState+            -> QPN+            -> (w, POption, a)+            -> [(w, POption, a)]+linkChoices related (Q _pp pn) (weight, POption i Nothing, subtree) =+    L.map aux (M.findWithDefault [] (pn, i) related)+  where+    aux :: PackagePath -> (w, POption, a)+    aux pp = (weight, POption i (Just pp), subtree)+linkChoices _ _ (_, POption _ (Just _), _) =+    alreadyLinked++alreadyLinked :: a+alreadyLinked = error "addLinking called on tree that already contains linked nodes"++-------------------------------------------------------------------------------++-- | Interface to the tree builder. Just takes an index and a list of package names,+-- and computes the initial state and then the tree from there.+buildTree :: Index -> IndependentGoals -> [PN] -> Tree () QGoalReason+buildTree idx (IndependentGoals ind) igs =+    build Linker {+        buildState = BS {+            index = idx+          , rdeps = M.fromList (L.map (\ qpn -> (qpn, []))              qpns)+          , open  = P.fromList (L.map (\ qpn -> (topLevelGoal qpn, ())) qpns)+          , next  = Goals+          , qualifyOptions = defaultQualifyOptions idx+          }+      , linkingState = M.empty+      }+  where+    -- Should a top-level goal allowed to be an executable style+    -- dependency? Well, I don't think it would make much difference+    topLevelGoal qpn = OpenGoal (Simple (Dep False {- not exe -} qpn (Constrained [])) ()) UserGoal++    qpns | ind       = makeIndependent igs+         | otherwise = L.map (Q (PackagePath DefaultNamespace QualToplevel)) igs
+ Distribution/Solver/Modular/Configured.hs view
@@ -0,0 +1,13 @@+module Distribution.Solver.Modular.Configured+    ( CP(..)+    ) where++import Distribution.PackageDescription (FlagAssignment)++import Distribution.Solver.Modular.Package+import Distribution.Solver.Types.ComponentDeps (ComponentDeps)+import Distribution.Solver.Types.OptionalStanza++-- | A configured package is a package instance together with+-- a flag assignment and complete dependencies.+data CP qpn = CP (PI qpn) FlagAssignment [OptionalStanza] (ComponentDeps [PI qpn])
+ Distribution/Solver/Modular/ConfiguredConversion.hs view
@@ -0,0 +1,72 @@+module Distribution.Solver.Modular.ConfiguredConversion+    ( convCP+    ) where++import Data.Maybe+import Prelude hiding (pi)+import Data.Either (partitionEithers)++import Distribution.Package (UnitId, packageId)++import qualified Distribution.Simple.PackageIndex as SI++import Distribution.Solver.Modular.Configured+import Distribution.Solver.Modular.Package++import           Distribution.Solver.Types.ComponentDeps (ComponentDeps)+import qualified Distribution.Solver.Types.PackageIndex as CI+import           Distribution.Solver.Types.PackagePath+import           Distribution.Solver.Types.ResolverPackage+import           Distribution.Solver.Types.SolverId+import           Distribution.Solver.Types.SolverPackage+import           Distribution.Solver.Types.InstSolverPackage+import           Distribution.Solver.Types.SourcePackage++-- | Converts from the solver specific result @CP QPN@ into+-- a 'ResolverPackage', which can then be converted into+-- the install plan.+convCP :: SI.InstalledPackageIndex ->+          CI.PackageIndex (SourcePackage loc) ->+          CP QPN -> ResolverPackage loc+convCP iidx sidx (CP qpi fa es ds) =+  case convPI qpi of+    Left  pi -> PreExisting $+                  InstSolverPackage {+                    instSolverPkgIPI = fromJust $ SI.lookupUnitId iidx pi,+                    instSolverPkgLibDeps = fmap fst ds',+                    instSolverPkgExeDeps = fmap snd ds'+                  }+    Right pi -> Configured $+                  SolverPackage {+                      solverPkgSource = srcpkg,+                      solverPkgFlags = fa,+                      solverPkgStanzas = es,+                      solverPkgLibDeps = fmap fst ds',+                      solverPkgExeDeps = fmap snd ds'+                    }+      where+        Just srcpkg = CI.lookupPackageId sidx pi+  where+    ds' :: ComponentDeps ([SolverId] {- lib -}, [SolverId] {- exe -})+    ds' = fmap (partitionEithers . map convConfId) ds++convPI :: PI QPN -> Either UnitId PackageId+convPI (PI _ (I _ (Inst pi))) = Left pi+convPI pi                     = Right (packageId (either id id (convConfId pi)))++convConfId :: PI QPN -> Either SolverId {- is lib -} SolverId {- is exe -}+convConfId (PI (Q (PackagePath _ q) pn) (I v loc)) =+    case loc of+        Inst pi -> Left (PreExistingId sourceId pi)+        _otherwise+          | QualExe _ pn' <- q+          -- NB: the dependencies of the executable are also+          -- qualified.  So the way to tell if this is an executable+          -- dependency is to make sure the qualifier is pointing+          -- at the actual thing.  Fortunately for us, I was+          -- silly and didn't allow arbitrarily nested build-tools+          -- dependencies, so a shallow check works.+          , pn == pn' -> Right (PlannedId sourceId)+          | otherwise    -> Left  (PlannedId sourceId)+  where+    sourceId    = PackageIdentifier pn v
+ Distribution/Solver/Modular/ConflictSet.hs view
@@ -0,0 +1,192 @@+{-# LANGUAGE CPP #-}+#ifdef DEBUG_CONFLICT_SETS+{-# LANGUAGE ImplicitParams #-}+#endif+-- | Conflict sets+--+-- Intended for double import+--+-- > import Distribution.Solver.Modular.ConflictSet (ConflictSet)+-- > import qualified Distribution.Solver.Modular.ConflictSet as CS+module Distribution.Solver.Modular.ConflictSet (+    ConflictSet -- opaque+  , ConflictMap+#ifdef DEBUG_CONFLICT_SETS+  , conflictSetOrigin+#endif+  , showConflictSet+  , showCSSortedByFrequency+  , showCSWithFrequency+    -- Set-like operations+  , toList+  , union+  , unions+  , insert+  , empty+  , singleton+  , member+  , filter+  , fromList+  ) where++import Prelude hiding (filter)+import Data.List (intercalate, sortBy)+import Data.Map (Map)+import Data.Set (Set)+import Data.Function (on)+import qualified Data.Set as S+import qualified Data.Map as M++#ifdef DEBUG_CONFLICT_SETS+import Data.Tree+import GHC.Stack+#endif++import Distribution.Solver.Modular.Var+import Distribution.Solver.Types.PackagePath++-- | The set of variables involved in a solver conflict+--+-- Since these variables should be preprocessed in some way, this type is+-- kept abstract.+data ConflictSet = CS {+    -- | The set of variables involved on the conflict+    conflictSetToSet :: Set (Var QPN)++#ifdef DEBUG_CONFLICT_SETS+    -- | The origin of the conflict set+    --+    -- When @DEBUG_CONFLICT_SETS@ is defined @(-f debug-conflict-sets)@,+    -- we record the origin of every conflict set. For new conflict sets+    -- ('empty', 'fromVars', ..) we just record the 'CallStack'; for operations+    -- that construct new conflict sets from existing conflict sets ('union',+    -- 'filter', ..)  we record the 'CallStack' to the call to the combinator+    -- as well as the 'CallStack's of the input conflict sets.+    --+    -- Requires @GHC >= 7.10@.+  , conflictSetOrigin :: Tree CallStack+#endif+  }+  deriving (Show)++instance Eq ConflictSet where+  (==) = (==) `on` conflictSetToSet++instance Ord ConflictSet where+  compare = compare `on` conflictSetToSet++showConflictSet :: ConflictSet -> String+showConflictSet = intercalate ", " . map showVar . toList++showCSSortedByFrequency :: ConflictMap -> ConflictSet -> String+showCSSortedByFrequency = showCS False++showCSWithFrequency :: ConflictMap -> ConflictSet -> String+showCSWithFrequency = showCS True++showCS :: Bool -> ConflictMap -> ConflictSet -> String+showCS showCount cm =+    intercalate ", " . map showWithFrequency . indexByFrequency+  where+    indexByFrequency = sortBy (flip compare `on` snd) . map (\c -> (c, M.lookup c cm)) . toList+    showWithFrequency (conflict, maybeFrequency) = case maybeFrequency of+      Just frequency+        | showCount -> showVar conflict ++ " (" ++ show frequency ++ ")"+      _             -> showVar conflict++{-------------------------------------------------------------------------------+  Set-like operations+-------------------------------------------------------------------------------}++toList :: ConflictSet -> [Var QPN]+toList = S.toList . conflictSetToSet++union ::+#ifdef DEBUG_CONFLICT_SETS+  (?loc :: CallStack) =>+#endif+  ConflictSet -> ConflictSet -> ConflictSet+union cs cs' = CS {+      conflictSetToSet = S.union (conflictSetToSet cs) (conflictSetToSet cs')+#ifdef DEBUG_CONFLICT_SETS+    , conflictSetOrigin = Node ?loc (map conflictSetOrigin [cs, cs'])+#endif+    }++unions ::+#ifdef DEBUG_CONFLICT_SETS+  (?loc :: CallStack) =>+#endif+  [ConflictSet] -> ConflictSet+unions css = CS {+      conflictSetToSet = S.unions (map conflictSetToSet css)+#ifdef DEBUG_CONFLICT_SETS+    , conflictSetOrigin = Node ?loc (map conflictSetOrigin css)+#endif+    }++insert ::+#ifdef DEBUG_CONFLICT_SETS+  (?loc :: CallStack) =>+#endif+  Var QPN -> ConflictSet -> ConflictSet+insert var cs = CS {+      conflictSetToSet = S.insert (simplifyVar var) (conflictSetToSet cs)+#ifdef DEBUG_CONFLICT_SETS+    , conflictSetOrigin = Node ?loc [conflictSetOrigin cs]+#endif+    }++empty ::+#ifdef DEBUG_CONFLICT_SETS+  (?loc :: CallStack) =>+#endif+  ConflictSet+empty = CS {+      conflictSetToSet = S.empty+#ifdef DEBUG_CONFLICT_SETS+    , conflictSetOrigin = Node ?loc []+#endif+    }++singleton ::+#ifdef DEBUG_CONFLICT_SETS+  (?loc :: CallStack) =>+#endif+  Var QPN -> ConflictSet+singleton var = CS {+      conflictSetToSet = S.singleton (simplifyVar var)+#ifdef DEBUG_CONFLICT_SETS+    , conflictSetOrigin = Node ?loc []+#endif+    }++member :: Var QPN -> ConflictSet -> Bool+member var = S.member (simplifyVar var) . conflictSetToSet++filter ::+#ifdef DEBUG_CONFLICT_SETS+  (?loc :: CallStack) =>+#endif+  (Var QPN -> Bool) -> ConflictSet -> ConflictSet+filter p cs = CS {+      conflictSetToSet = S.filter p (conflictSetToSet cs)+#ifdef DEBUG_CONFLICT_SETS+    , conflictSetOrigin = Node ?loc [conflictSetOrigin cs]+#endif+    }++fromList ::+#ifdef DEBUG_CONFLICT_SETS+  (?loc :: CallStack) =>+#endif+  [Var QPN] -> ConflictSet+fromList vars = CS {+      conflictSetToSet = S.fromList (map simplifyVar vars)+#ifdef DEBUG_CONFLICT_SETS+    , conflictSetOrigin = Node ?loc []+#endif+    }++type ConflictMap = Map (Var QPN) Int+
+ Distribution/Solver/Modular/Cycles.hs view
@@ -0,0 +1,119 @@+{-# LANGUAGE TypeFamilies #-}+module Distribution.Solver.Modular.Cycles (+    detectCyclesPhase+  ) where++import Prelude hiding (cycle)+import qualified Data.Map as M+import qualified Data.Set as S++import qualified Distribution.Compat.Graph as G+import Distribution.Simple.Utils (ordNub)+import Distribution.Solver.Modular.Dependency+import Distribution.Solver.Modular.Flag+import Distribution.Solver.Modular.Package+import Distribution.Solver.Modular.Tree+import qualified Distribution.Solver.Modular.ConflictSet as CS+import Distribution.Solver.Types.ComponentDeps (Component)+import Distribution.Solver.Types.PackagePath++-- | Find and reject any nodes with cyclic dependencies+detectCyclesPhase :: Tree d c -> Tree d c+detectCyclesPhase = cata go+  where+    -- Only check children of choice nodes.+    go :: TreeF d c (Tree d c) -> Tree d c+    go (PChoiceF qpn rdm gr                         cs) =+        PChoice qpn rdm gr     $ fmap (checkChild qpn)   cs+    go (FChoiceF qfn@(FN (PI qpn _) _) rdm gr w m d cs) =+        FChoice qfn rdm gr w m d $ fmap (checkChild qpn) cs+    go (SChoiceF qsn@(SN (PI qpn _) _) rdm gr w     cs) =+        SChoice qsn rdm gr w   $ fmap (checkChild qpn)   cs+    go x                                                = inn x++    checkChild :: QPN -> Tree d c -> Tree d c+    checkChild qpn x@(PChoice _  rdm _       _) = failIfCycle qpn rdm x+    checkChild qpn x@(FChoice _  rdm _ _ _ _ _) = failIfCycle qpn rdm x+    checkChild qpn x@(SChoice _  rdm _ _     _) = failIfCycle qpn rdm x+    checkChild qpn x@(GoalChoice rdm         _) = failIfCycle qpn rdm x+    checkChild _   x@(Fail _ _)                 = x+    checkChild qpn x@(Done       rdm _)         = failIfCycle qpn rdm x++    failIfCycle :: QPN -> RevDepMap -> Tree d c -> Tree d c+    failIfCycle qpn rdm x =+      case findCycles qpn rdm of+        Nothing     -> x+        Just relSet -> Fail relSet CyclicDependencies++-- | Given the reverse dependency map from a node in the tree, check+-- if the solution is cyclic. If it is, return the conflict set containing+-- all decisions that could potentially break the cycle.+--+-- TODO: The conflict set should also contain flag and stanza variables.+findCycles :: QPN -> RevDepMap -> Maybe ConflictSet+findCycles pkg rdm =+    -- This function has two parts: a faster cycle check that is called at every+    -- step and a slower calculation of the conflict set.+    --+    -- 'hasCycle' checks for cycles incrementally by only looking for cycles+    -- containing the current package, 'pkg'. It searches for cycles in the+    -- 'RevDepMap', which is the data structure used to store reverse+    -- dependencies in the search tree. We store the reverse dependencies in a+    -- map, because Data.Map is smaller and/or has better sharing than+    -- Distribution.Compat.Graph.+    --+    -- If there is a cycle, we call G.cycles to find a strongly connected+    -- component. Then we choose one cycle from the component to use for the+    -- conflict set. Choosing only one cycle can lead to a smaller conflict set,+    -- such as when a choice to enable testing introduces many cycles at once.+    -- In that case, all cycles contain the current package and are in one large+    -- strongly connected component.+    --+    if hasCycle+    then let scc :: G.Graph RevDepMapNode+             scc = case G.cycles $ revDepMapToGraph rdm of+                     []    -> findCyclesError "cannot find a strongly connected component"+                     c : _ -> G.fromDistinctList c++             next :: QPN -> QPN+             next p = case G.neighbors scc p of+                        Just (n : _) -> G.nodeKey n+                        _            -> findCyclesError "cannot find next node in the cycle"++             -- This function also assumes that all cycles contain 'pkg'.+             oneCycle :: [QPN]+             oneCycle = case iterate next pkg of+                          []     -> findCyclesError "empty cycle"+                          x : xs -> x : takeWhile (/= x) xs+         in Just $ CS.fromList $ map P oneCycle+    else Nothing+  where+    hasCycle :: Bool+    hasCycle = pkg `S.member` closure (neighbors pkg)++    closure :: [QPN] -> S.Set QPN+    closure = foldl go S.empty+      where+        go :: S.Set QPN -> QPN -> S.Set QPN+        go s x =+            if x `S.member` s+            then s+            else foldl go (S.insert x s) $ neighbors x++    neighbors :: QPN -> [QPN]+    neighbors x = case x `M.lookup` rdm of+                    Nothing -> findCyclesError "cannot find node"+                    Just xs -> map snd xs++    findCyclesError = error . ("Distribution.Solver.Modular.Cycles.findCycles: " ++)++data RevDepMapNode = RevDepMapNode QPN [(Component, QPN)]++instance G.IsNode RevDepMapNode where+  type Key RevDepMapNode = QPN+  nodeKey (RevDepMapNode qpn _) = qpn+  nodeNeighbors (RevDepMapNode _ ns) = ordNub $ map snd ns++revDepMapToGraph :: RevDepMap -> G.Graph RevDepMapNode+revDepMapToGraph rdm = G.fromDistinctList+                       [RevDepMapNode qpn ns | (qpn, ns) <- M.toList rdm]
+ Distribution/Solver/Modular/Dependency.hs view
@@ -0,0 +1,431 @@+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE CPP #-}+#ifdef DEBUG_CONFLICT_SETS+{-# LANGUAGE ImplicitParams #-}+#endif+module Distribution.Solver.Modular.Dependency (+    -- * Variables+    Var(..)+  , simplifyVar+  , varPI+  , showVar+    -- * Conflict sets+  , ConflictSet+  , ConflictMap+  , CS.showConflictSet+    -- * Constrained instances+  , CI(..)+  , merge+    -- * Flagged dependencies+  , FlaggedDeps+  , FlaggedDep(..)+  , Dep(..)+  , showDep+  , flattenFlaggedDeps+  , QualifyOptions(..)+  , qualifyDeps+  , unqualifyDeps+    -- ** Setting/forgetting components+  , forgetCompOpenGoal+  , setCompFlaggedDeps+    -- * Reverse dependency map+  , RevDepMap+    -- * Goals+  , Goal(..)+  , GoalReason(..)+  , QGoalReason+  , ResetVar(..)+  , goalToVar+  , goalVarToConflictSet+  , varToConflictSet+  , goalReasonToVars+    -- * Open goals+  , OpenGoal(..)+  , close+  ) where++import Prelude hiding (pi)++import Data.Map (Map)+import qualified Data.List as L++import Language.Haskell.Extension (Extension(..), Language(..))++import Distribution.Text++import Distribution.Solver.Modular.ConflictSet (ConflictSet, ConflictMap)+import Distribution.Solver.Modular.Flag+import Distribution.Solver.Modular.Package+import Distribution.Solver.Modular.Var+import Distribution.Solver.Modular.Version+import qualified Distribution.Solver.Modular.ConflictSet as CS++import Distribution.Solver.Types.ComponentDeps (Component(..))+import Distribution.Solver.Types.PackagePath++#ifdef DEBUG_CONFLICT_SETS+import GHC.Stack (CallStack)+#endif++{-------------------------------------------------------------------------------+  Constrained instances+-------------------------------------------------------------------------------}++-- | Constrained instance. If the choice has already been made, this is+-- a fixed instance, and we record the package name for which the choice+-- is for convenience. Otherwise, it is a list of version ranges paired with+-- the goals / variables that introduced them.+data CI qpn = Fixed I (Var qpn) | Constrained [VROrigin qpn]+  deriving (Eq, Show, Functor)++showCI :: CI QPN -> String+showCI (Fixed i _)      = "==" ++ showI i+showCI (Constrained vr) = showVR (collapse vr)++-- | Merge constrained instances. We currently adopt a lazy strategy for+-- merging, i.e., we only perform actual checking if one of the two choices+-- is fixed. If the merge fails, we return a conflict set indicating the+-- variables responsible for the failure, as well as the two conflicting+-- fragments.+--+-- Note that while there may be more than one conflicting pair of version+-- ranges, we only return the first we find.+--+-- TODO: Different pairs might have different conflict sets. We're+-- obviously interested to return a conflict that has a "better" conflict+-- set in the sense the it contains variables that allow us to backjump+-- further. We might apply some heuristics here, such as to change the+-- order in which we check the constraints.+merge ::+#ifdef DEBUG_CONFLICT_SETS+  (?loc :: CallStack) =>+#endif+  CI QPN -> CI QPN -> Either (ConflictSet, (CI QPN, CI QPN)) (CI QPN)+merge c@(Fixed i g1)       d@(Fixed j g2)+  | i == j                                    = Right c+  | otherwise                                 = Left (CS.union (varToConflictSet g1) (varToConflictSet g2), (c, d))+merge c@(Fixed (I v _) g1)   (Constrained rs) = go rs -- I tried "reverse rs" here, but it seems to slow things down ...+  where+    go []              = Right c+    go (d@(vr, g2) : vrs)+      | checkVR vr v   = go vrs+      | otherwise      = Left (CS.union (varToConflictSet g1) (varToConflictSet g2), (c, Constrained [d]))+merge c@(Constrained _)    d@(Fixed _ _)      = merge d c+merge   (Constrained rs)     (Constrained ss) = Right (Constrained (rs ++ ss))++{-------------------------------------------------------------------------------+  Flagged dependencies+-------------------------------------------------------------------------------}++-- | Flagged dependencies+--+-- 'FlaggedDeps' is the modular solver's view of a packages dependencies:+-- rather than having the dependencies indexed by component, each dependency+-- defines what component it is in.+--+-- However, top-level goals are also modelled as dependencies, but of course+-- these don't actually belong in any component of any package. Therefore, we+-- parameterize 'FlaggedDeps' and derived datatypes with a type argument that+-- specifies whether or not we have a component: we only ever instantiate this+-- type argument with @()@ for top-level goals, or 'Component' for everything+-- else (we could express this as a kind at the type-level, but that would+-- require a very recent GHC).+--+-- Note however, crucially, that independent of the type parameters, the list+-- of dependencies underneath a flag choice or stanza choices _always_ uses+-- Component as the type argument. This is important: when we pick a value for+-- a flag, we _must_ know what component the new dependencies belong to, or+-- else we don't be able to construct fine-grained reverse dependencies.+type FlaggedDeps comp qpn = [FlaggedDep comp qpn]++-- | Flagged dependencies can either be plain dependency constraints,+-- or flag-dependent dependency trees.+data FlaggedDep comp qpn =+    -- | Dependencies which are conditional on a flag choice.+    Flagged (FN qpn) FInfo (TrueFlaggedDeps qpn) (FalseFlaggedDeps qpn)+    -- | Dependencies which are conditional on whether or not a stanza+    -- (e.g., a test suite or benchmark) is enabled.+  | Stanza  (SN qpn)       (TrueFlaggedDeps qpn)+    -- | Dependencies for which are always enabled, for the component+    -- 'comp' (or requested for the user, if comp is @()@).+  | Simple (Dep qpn) comp+  deriving (Eq, Show)++-- | Conversatively flatten out flagged dependencies+--+-- NOTE: We do not filter out duplicates.+flattenFlaggedDeps :: FlaggedDeps Component qpn -> [(Dep qpn, Component)]+flattenFlaggedDeps = concatMap aux+  where+    aux :: FlaggedDep Component qpn -> [(Dep qpn, Component)]+    aux (Flagged _ _ t f) = flattenFlaggedDeps t ++ flattenFlaggedDeps f+    aux (Stanza  _   t)   = flattenFlaggedDeps t+    aux (Simple d c)      = [(d, c)]++type TrueFlaggedDeps  qpn = FlaggedDeps Component qpn+type FalseFlaggedDeps qpn = FlaggedDeps Component qpn++-- | Is this dependency on an executable+type IsExe = Bool++-- | A dependency (constraint) associates a package name with a+-- constrained instance.+--+-- 'Dep' intentionally has no 'Functor' instance because the type variable+-- is used both to record the dependencies as well as who's doing the+-- depending; having a 'Functor' instance makes bugs where we don't distinguish+-- these two far too likely. (By rights 'Dep' ought to have two type variables.)+data Dep qpn = Dep IsExe qpn (CI qpn)  -- ^ dependency on a package (possibly for executable+             | Ext  Extension          -- ^ dependency on a language extension+             | Lang Language           -- ^ dependency on a language version+             | Pkg  PkgconfigName VR   -- ^ dependency on a pkg-config package+  deriving (Eq, Show)++showDep :: Dep QPN -> String+showDep (Dep is_exe qpn (Fixed i v)            ) =+  (if P qpn /= v then showVar v ++ " => " else "") +++  showQPN qpn +++  (if is_exe then " (exe) " else "") ++ "==" ++ showI i+showDep (Dep is_exe qpn (Constrained [(vr, v)])) =+  showVar v ++ " => " ++ showQPN qpn +++  (if is_exe then " (exe) " else "") ++ showVR vr+showDep (Dep is_exe qpn ci                     ) =+  showQPN qpn ++ (if is_exe then " (exe) " else "") ++ showCI ci+showDep (Ext ext)   = "requires " ++ display ext+showDep (Lang lang) = "requires " ++ display lang+showDep (Pkg pn vr) = "requires pkg-config package "+                      ++ display pn ++ display vr+                      ++ ", not found in the pkg-config database"++-- | Options for goal qualification (used in 'qualifyDeps')+--+-- See also 'defaultQualifyOptions'+data QualifyOptions = QO {+    -- | Do we have a version of base relying on another version of base?+    qoBaseShim :: Bool++    -- Should dependencies of the setup script be treated as independent?+  , qoSetupIndependent :: Bool+  }+  deriving Show++-- | Apply built-in rules for package qualifiers+--+-- Although the behaviour of 'qualifyDeps' depends on the 'QualifyOptions',+-- it is important that these 'QualifyOptions' are _static_. Qualification+-- does NOT depend on flag assignment; in other words, it behaves the same no+-- matter which choices the solver makes (modulo the global 'QualifyOptions');+-- we rely on this in 'linkDeps' (see comment there).+--+-- NOTE: It's the _dependencies_ of a package that may or may not be independent+-- from the package itself. Package flag choices must of course be consistent.+qualifyDeps :: QualifyOptions -> QPN -> FlaggedDeps Component PN -> FlaggedDeps Component QPN+qualifyDeps QO{..} (Q pp@(PackagePath ns q) pn) = go+  where+    go :: FlaggedDeps Component PN -> FlaggedDeps Component QPN+    go = map go1++    go1 :: FlaggedDep Component PN -> FlaggedDep Component QPN+    go1 (Flagged fn nfo t f) = Flagged (fmap (Q pp) fn) nfo (go t) (go f)+    go1 (Stanza  sn     t)   = Stanza  (fmap (Q pp) sn)     (go t)+    go1 (Simple dep comp)    = Simple (goD dep comp) comp++    -- Suppose package B has a setup dependency on package A.+    -- This will be recorded as something like+    --+    -- > Dep "A" (Constrained [(AnyVersion, Goal (P "B") reason])+    --+    -- Observe that when we qualify this dependency, we need to turn that+    -- @"A"@ into @"B-setup.A"@, but we should not apply that same qualifier+    -- to the goal or the goal reason chain.+    goD :: Dep PN -> Component -> Dep QPN+    goD (Ext  ext)    _    = Ext  ext+    goD (Lang lang)   _    = Lang lang+    goD (Pkg pkn vr)  _    = Pkg pkn vr+    goD (Dep is_exe dep ci) comp+      | is_exe      = Dep is_exe (Q (PackagePath ns (QualExe pn dep)) dep) (fmap (Q pp) ci)+      | qBase  dep  = Dep is_exe (Q (PackagePath ns (QualBase  pn)) dep) (fmap (Q pp) ci)+      | qSetup comp = Dep is_exe (Q (PackagePath ns (QualSetup pn)) dep) (fmap (Q pp) ci)+      | otherwise   = Dep is_exe (Q (PackagePath ns inheritedQ) dep) (fmap (Q pp) ci)++    -- If P has a setup dependency on Q, and Q has a regular dependency on R, then+    -- we say that the 'Setup' qualifier is inherited: P has an (indirect) setup+    -- dependency on R. We do not do this for the base qualifier however.+    --+    -- The inherited qualifier is only used for regular dependencies; for setup+    -- and base deppendencies we override the existing qualifier. See #3160 for+    -- a detailed discussion.+    inheritedQ :: Qualifier+    inheritedQ = case q of+                   QualSetup _  -> q+                   QualExe _ _  -> q+                   QualToplevel -> q+                   QualBase _   -> QualToplevel++    -- Should we qualify this goal with the 'Base' package path?+    qBase :: PN -> Bool+    qBase dep = qoBaseShim && unPackageName dep == "base"++    -- Should we qualify this goal with the 'Setup' package path?+    qSetup :: Component -> Bool+    qSetup comp = qoSetupIndependent && comp == ComponentSetup++-- | Remove qualifiers from set of dependencies+--+-- This is used during link validation: when we link package @Q.A@ to @Q'.A@,+-- then all dependencies @Q.B@ need to be linked to @Q'.B@. In order to compute+-- what to link these dependencies to, we need to requalify @Q.B@ to become+-- @Q'.B@; we do this by first removing all qualifiers and then calling+-- 'qualifyDeps' again.+unqualifyDeps :: FlaggedDeps comp QPN -> FlaggedDeps comp PN+unqualifyDeps = go+  where+    go :: FlaggedDeps comp QPN -> FlaggedDeps comp PN+    go = map go1++    go1 :: FlaggedDep comp QPN -> FlaggedDep comp PN+    go1 (Flagged fn nfo t f) = Flagged (fmap unq fn) nfo (go t) (go f)+    go1 (Stanza  sn     t)   = Stanza  (fmap unq sn)     (go t)+    go1 (Simple dep comp)    = Simple (goD dep) comp++    goD :: Dep QPN -> Dep PN+    goD (Dep is_exe qpn ci) = Dep is_exe (unq qpn) (fmap unq ci)+    goD (Ext  ext)   = Ext ext+    goD (Lang lang)  = Lang lang+    goD (Pkg pn vr)  = Pkg pn vr++    unq :: QPN -> PN+    unq (Q _ pn) = pn++{-------------------------------------------------------------------------------+  Setting/forgetting the Component+-------------------------------------------------------------------------------}++forgetCompOpenGoal :: OpenGoal Component -> OpenGoal ()+forgetCompOpenGoal = mapCompOpenGoal $ const ()++setCompFlaggedDeps :: Component -> FlaggedDeps () qpn -> FlaggedDeps Component qpn+setCompFlaggedDeps = mapCompFlaggedDeps . const++{-------------------------------------------------------------------------------+  Auxiliary: Mapping over the Component goal++  We don't export these, because the only type instantiations for 'a' and 'b'+  here should be () or Component. (We could express this at the type level+  if we relied on newer versions of GHC.)+-------------------------------------------------------------------------------}++mapCompOpenGoal :: (a -> b) -> OpenGoal a -> OpenGoal b+mapCompOpenGoal g (OpenGoal d gr) = OpenGoal (mapCompFlaggedDep g d) gr++mapCompFlaggedDeps :: (a -> b) -> FlaggedDeps a qpn -> FlaggedDeps b qpn+mapCompFlaggedDeps = L.map . mapCompFlaggedDep++mapCompFlaggedDep :: (a -> b) -> FlaggedDep a qpn -> FlaggedDep b qpn+mapCompFlaggedDep _ (Flagged fn nfo t f) = Flagged fn nfo   t f+mapCompFlaggedDep _ (Stanza  sn     t  ) = Stanza  sn       t+mapCompFlaggedDep g (Simple  pn a      ) = Simple  pn (g a)++{-------------------------------------------------------------------------------+  Reverse dependency map+-------------------------------------------------------------------------------}++-- | A map containing reverse dependencies between qualified+-- package names.+type RevDepMap = Map QPN [(Component, QPN)]++{-------------------------------------------------------------------------------+  Goals+-------------------------------------------------------------------------------}++-- | A goal is just a solver variable paired with a reason.+-- The reason is only used for tracing.+data Goal qpn = Goal (Var qpn) (GoalReason qpn)+  deriving (Eq, Show, Functor)++-- | Reason why a goal is being added to a goal set.+data GoalReason qpn =+    UserGoal+  | PDependency (PI qpn)+  | FDependency (FN qpn) Bool+  | SDependency (SN qpn)+  deriving (Eq, Show, Functor)++type QGoalReason = GoalReason QPN++class ResetVar f where+  resetVar :: Var qpn -> f qpn -> f qpn++instance ResetVar CI where+  resetVar v (Fixed i _)       = Fixed i v+  resetVar v (Constrained vrs) = Constrained (L.map (\ (x, y) -> (x, resetVar v y)) vrs)++instance ResetVar Dep where+  resetVar v (Dep is_exe qpn ci) = Dep is_exe qpn (resetVar v ci)+  resetVar _ (Ext ext)    = Ext ext+  resetVar _ (Lang lang)  = Lang lang+  resetVar _ (Pkg pn vr)  = Pkg pn vr++instance ResetVar Var where+  resetVar = const++goalToVar :: Goal a -> Var a+goalToVar (Goal v _) = v++-- | Compute a singleton conflict set from a goal, containing just+-- the goal variable.+--+-- NOTE: This is just a call to 'varToConflictSet' under the hood;+-- the 'GoalReason' is ignored.+goalVarToConflictSet :: Goal QPN -> ConflictSet+goalVarToConflictSet (Goal g _gr) = varToConflictSet g++-- | Compute a singleton conflict set from a 'Var'+varToConflictSet :: Var QPN -> ConflictSet+varToConflictSet = CS.singleton++-- | A goal reason is mostly just a variable paired with the+-- decision we made for that variable (except for user goals,+-- where we cannot really point to a solver variable). This+-- function drops the decision and recovers the list of+-- variables (which will be empty or contain one element).+--+goalReasonToVars :: GoalReason qpn -> [Var qpn]+goalReasonToVars UserGoal                 = []+goalReasonToVars (PDependency (PI qpn _)) = [P qpn]+goalReasonToVars (FDependency qfn _)      = [F qfn]+goalReasonToVars (SDependency qsn)        = [S qsn]++{-------------------------------------------------------------------------------+  Open goals+-------------------------------------------------------------------------------}++-- | For open goals as they occur during the build phase, we need to store+-- additional information about flags.+data OpenGoal comp = OpenGoal (FlaggedDep comp QPN) QGoalReason+  deriving (Eq, Show)++-- | Closes a goal, i.e., removes all the extraneous information that we+-- need only during the build phase.+close :: OpenGoal comp -> Goal QPN+close (OpenGoal (Simple (Dep _ qpn _) _) gr) = Goal (P qpn) gr+close (OpenGoal (Simple (Ext     _) _) _ ) =+  error "Distribution.Solver.Modular.Dependency.close: called on Ext goal"+close (OpenGoal (Simple (Lang    _) _) _ ) =+  error "Distribution.Solver.Modular.Dependency.close: called on Lang goal"+close (OpenGoal (Simple (Pkg   _ _) _) _ ) =+  error "Distribution.Solver.Modular.Dependency.close: called on Pkg goal"+close (OpenGoal (Flagged qfn _ _ _ )   gr) = Goal (F qfn) gr+close (OpenGoal (Stanza  qsn _)        gr) = Goal (S qsn) gr++{-------------------------------------------------------------------------------+  Version ranges paired with origins+-------------------------------------------------------------------------------}++type VROrigin qpn = (VR, Var qpn)++-- | Helper function to collapse a list of version ranges with origins into+-- a single, simplified, version range.+collapse :: [VROrigin qpn] -> VR+collapse = simplifyVR . L.foldr ((.&&.) . fst) anyVR
+ Distribution/Solver/Modular/Explore.hs view
@@ -0,0 +1,175 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE ScopedTypeVariables #-}+module Distribution.Solver.Modular.Explore+    ( backjump+    , backjumpAndExplore+    ) where++import Data.Foldable as F+import Data.List as L (foldl')+import Data.Map as M++import Distribution.Solver.Modular.Assignment+import Distribution.Solver.Modular.Dependency+import Distribution.Solver.Modular.Log+import Distribution.Solver.Modular.Message+import qualified Distribution.Solver.Modular.PSQ as P+import qualified Distribution.Solver.Modular.ConflictSet as CS+import Distribution.Solver.Modular.RetryLog+import Distribution.Solver.Modular.Tree+import qualified Distribution.Solver.Modular.WeightedPSQ as W+import Distribution.Solver.Types.PackagePath+import Distribution.Solver.Types.Settings (EnableBackjumping(..), CountConflicts(..))++-- | This function takes the variable we're currently considering, an+-- initial conflict set and a+-- list of children's logs. Each log yields either a solution or a+-- conflict set. The result is a combined log for the parent node that+-- has explored a prefix of the children.+--+-- We can stop traversing the children's logs if we find an individual+-- conflict set that does not contain the current variable. In this+-- case, we can just lift the conflict set to the current level,+-- because the current level cannot possibly have contributed to this+-- conflict, so no other choice at the current level would avoid the+-- conflict.+--+-- If any of the children might contain a successful solution, we can+-- return it immediately. If all children contain conflict sets, we can+-- take the union as the combined conflict set.+--+-- The initial conflict set corresponds to the justification that we+-- have to choose this goal at all. There is a reason why we have+-- introduced the goal in the first place, and this reason is in conflict+-- with the (virtual) option not to choose anything for the current+-- variable. See also the comments for 'avoidSet'.+--+backjump :: EnableBackjumping -> Var QPN+         -> ConflictSet -> W.WeightedPSQ w k (ConflictMap -> ConflictSetLog a)+         -> ConflictMap -> ConflictSetLog a+backjump (EnableBackjumping enableBj) var initial xs =+    F.foldr combine logBackjump xs initial+  where+    combine :: forall a . (ConflictMap -> ConflictSetLog a)+            -> (ConflictSet -> ConflictMap -> ConflictSetLog a)+            ->  ConflictSet -> ConflictMap -> ConflictSetLog a+    combine x f csAcc cm = retry (x cm) next+      where+        next :: (ConflictSet, ConflictMap) -> ConflictSetLog a+        next (cs, cm')+          | enableBj && not (var `CS.member` cs) = logBackjump cs cm'+          | otherwise                            = f (csAcc `CS.union` cs) cm'++    logBackjump :: ConflictSet -> ConflictMap -> ConflictSetLog a+    logBackjump cs !cm = failWith (Failure cs Backjump) (cs, updateCM initial cm)+                                   -- 'intial' instead of 'cs' here ---^+                                   -- since we do not want to double-count the+                                   -- additionally accumulated conflicts.++type ConflictSetLog = RetryLog Message (ConflictSet, ConflictMap)++getBestGoal :: ConflictMap -> P.PSQ (Goal QPN) a -> (Goal QPN, a)+getBestGoal cm =+  P.maximumBy+    ( flip (M.findWithDefault 0) cm+    . (\ (Goal v _) -> v)+    )++getFirstGoal :: P.PSQ (Goal QPN) a -> (Goal QPN, a)+getFirstGoal ts =+  P.casePSQ ts+    (error "getFirstGoal: empty goal choice") -- empty goal choice is an internal error+    (\ k v _xs -> (k, v))  -- commit to the first goal choice++updateCM :: ConflictSet -> ConflictMap -> ConflictMap+updateCM cs cm =+  L.foldl' (\ cmc k -> M.alter inc k cmc) cm (CS.toList cs)+  where+    inc Nothing  = Just 1+    inc (Just n) = Just $! n + 1++-- | Record complete assignments on 'Done' nodes.+assign :: Tree d c -> Tree Assignment c+assign tree = cata go tree $ A M.empty M.empty M.empty+  where+    go :: TreeF d c (Assignment -> Tree Assignment c)+                 -> (Assignment -> Tree Assignment c)+    go (FailF c fr)            _                  = Fail c fr+    go (DoneF rdm _)           a                  = Done rdm a+    go (PChoiceF qpn rdm y       ts) (A pa fa sa) = PChoice qpn rdm y       $ W.mapWithKey f ts+        where f (POption k _) r = r (A (M.insert qpn k pa) fa sa)+    go (FChoiceF qfn rdm y t m d ts) (A pa fa sa) = FChoice qfn rdm y t m d $ W.mapWithKey f ts+        where f k             r = r (A pa (M.insert qfn k fa) sa)+    go (SChoiceF qsn rdm y t     ts) (A pa fa sa) = SChoice qsn rdm y t     $ W.mapWithKey f ts+        where f k             r = r (A pa fa (M.insert qsn k sa))+    go (GoalChoiceF  rdm         ts) a            = GoalChoice  rdm         $ fmap ($ a) ts++-- | A tree traversal that simultaneously propagates conflict sets up+-- the tree from the leaves and creates a log.+exploreLog :: EnableBackjumping -> CountConflicts -> Tree Assignment QGoalReason+           -> ConflictSetLog (Assignment, RevDepMap)+exploreLog enableBj (CountConflicts countConflicts) t = cata go t M.empty+  where+    getBestGoal' :: P.PSQ (Goal QPN) a -> ConflictMap -> (Goal QPN, a)+    getBestGoal'+      | countConflicts = \ ts cm -> getBestGoal cm ts+      | otherwise      = \ ts _  -> getFirstGoal ts++    go :: TreeF Assignment QGoalReason (ConflictMap -> ConflictSetLog (Assignment, RevDepMap))+                                    -> (ConflictMap -> ConflictSetLog (Assignment, RevDepMap))+    go (FailF c fr)                            = \ !cm -> failWith (Failure c fr)+                                                                 (c, updateCM c cm)+    go (DoneF rdm a)                           = \ _   -> succeedWith Success (a, rdm)+    go (PChoiceF qpn _ gr       ts)            =+      backjump enableBj (P qpn) (avoidSet (P qpn) gr) $ -- try children in order,+        W.mapWithKey                                -- when descending ...+          (\ k r cm -> tryWith (TryP qpn k) (r cm))+          ts+    go (FChoiceF qfn _ gr _ _ _ ts)            =+      backjump enableBj (F qfn) (avoidSet (F qfn) gr) $ -- try children in order,+        W.mapWithKey                                -- when descending ...+          (\ k r cm -> tryWith (TryF qfn k) (r cm))+          ts+    go (SChoiceF qsn _ gr _     ts)            =+      backjump enableBj (S qsn) (avoidSet (S qsn) gr) $ -- try children in order,+        W.mapWithKey                                -- when descending ...+          (\ k r cm -> tryWith (TryS qsn k) (r cm))+          ts+    go (GoalChoiceF _           ts)            = \ cm ->+      let (k, v) = getBestGoal' ts cm+      in continueWith (Next k) (v cm)++-- | Build a conflict set corresponding to the (virtual) option not to+-- choose a solution for a goal at all.+--+-- In the solver, the set of goals is not statically determined, but depends+-- on the choices we make. Therefore, when dealing with conflict sets, we+-- always have to consider that we could perhaps make choices that would+-- avoid the existence of the goal completely.+--+-- Whenever we actual introduce a choice in the tree, we have already established+-- that the goal cannot be avoided. This is tracked in the "goal reason".+-- The choice to avoid the goal therefore is a conflict between the goal itself+-- and its goal reason. We build this set here, and pass it to the 'backjump'+-- function as the initial conflict set.+--+-- This has two effects:+--+-- - In a situation where there are no choices available at all (this happens+-- if an unknown package is requested), the initial conflict set becomes the+-- actual conflict set.+--+-- - In a situation where all of the children's conflict sets contain the+-- current variable, the goal reason of the current node will be added to the+-- conflict set.+--+avoidSet :: Var QPN -> QGoalReason -> ConflictSet+avoidSet var gr =+  CS.fromList (var : goalReasonToVars gr)++-- | Interface.+backjumpAndExplore :: EnableBackjumping+                   -> CountConflicts+                   -> Tree d QGoalReason -> Log Message (Assignment, RevDepMap)+backjumpAndExplore enableBj countConflicts =+    toProgress . exploreLog enableBj countConflicts . assign
+ Distribution/Solver/Modular/Flag.hs view
@@ -0,0 +1,92 @@+{-# LANGUAGE DeriveFunctor #-}+module Distribution.Solver.Modular.Flag+    ( FInfo(..)+    , Flag+    , FlagInfo+    , FN(..)+    , QFN+    , QSN+    , SN(..)+    , WeakOrTrivial(..)+    , mkFlag+    , showFBool+    , showQFN+    , showQFNBool+    , showQSN+    , showQSNBool+    ) where++import Data.Map as M+import Prelude hiding (pi)++import Distribution.PackageDescription hiding (Flag) -- from Cabal++import Distribution.Solver.Modular.Package+import Distribution.Solver.Types.Flag+import Distribution.Solver.Types.OptionalStanza+import Distribution.Solver.Types.PackagePath++-- | Flag name. Consists of a package instance and the flag identifier itself.+data FN qpn = FN (PI qpn) Flag+  deriving (Eq, Ord, Show, Functor)++-- | Flag identifier. Just a string.+type Flag = FlagName++unFlag :: Flag -> String+unFlag = unFlagName++mkFlag :: String -> Flag+mkFlag = mkFlagName++-- | Flag info. Default value, whether the flag is manual, and+-- whether the flag is weak. Manual flags can only be set explicitly.+-- Weak flags are typically deferred by the solver.+data FInfo = FInfo { fdefault :: Bool, fmanual :: FlagType, fweak :: WeakOrTrivial }+  deriving (Eq, Show)++-- | Flag defaults.+type FlagInfo = Map Flag FInfo++-- | Qualified flag name.+type QFN = FN QPN++-- | Stanza name. Paired with a package name, much like a flag.+data SN qpn = SN (PI qpn) OptionalStanza+  deriving (Eq, Ord, Show, Functor)++-- | Qualified stanza name.+type QSN = SN QPN++-- | A property of flag and stanza choices that determines whether the+-- choice should be deferred in the solving process.+--+-- A choice is called weak if we do want to defer it. This is the+-- case for flags that should be implied by what's currently installed on+-- the system, as opposed to flags that are used to explicitly enable or+-- disable some functionality.+--+-- A choice is called trivial if it clearly does not matter. The+-- special case of triviality we actually consider is if there are no new+-- dependencies introduced by the choice.+newtype WeakOrTrivial = WeakOrTrivial { unWeakOrTrivial :: Bool }+  deriving (Eq, Ord, Show)++showQFNBool :: QFN -> Bool -> String+showQFNBool qfn@(FN pi _f) b = showPI pi ++ ":" ++ showFBool qfn b++showQSNBool :: QSN -> Bool -> String+showQSNBool qsn@(SN pi _f) b = showPI pi ++ ":" ++ showSBool qsn b++showFBool :: FN qpn -> Bool -> String+showFBool (FN _ f) v = showFlagValue (f, v)++showSBool :: SN qpn -> Bool -> String+showSBool (SN _ s) True  = "*" ++ showStanza s+showSBool (SN _ s) False = "!" ++ showStanza s++showQFN :: QFN -> String+showQFN (FN pi f) = showPI pi ++ ":" ++ unFlag f++showQSN :: QSN -> String+showQSN (SN pi s) = showPI pi ++ ":" ++ showStanza s
+ Distribution/Solver/Modular/Index.hs view
@@ -0,0 +1,52 @@+module Distribution.Solver.Modular.Index+    ( Index+    , PInfo(..)+    , defaultQualifyOptions+    , mkIndex+    ) where++import Data.List as L+import Data.Map as M+import Prelude hiding (pi)++import Distribution.Solver.Modular.Dependency+import Distribution.Solver.Modular.Flag+import Distribution.Solver.Modular.Package+import Distribution.Solver.Modular.Tree++import Distribution.Solver.Types.ComponentDeps (Component)++-- | An index contains information about package instances. This is a nested+-- dictionary. Package names are mapped to instances, which in turn is mapped+-- to info.+type Index = Map PN (Map I PInfo)++-- | Info associated with a package instance.+-- Currently, dependencies, flags and failure reasons.+-- Packages that have a failure reason recorded for them are disabled+-- globally, for reasons external to the solver. We currently use this+-- for shadowing which essentially is a GHC limitation, and for+-- installed packages that are broken.+data PInfo = PInfo (FlaggedDeps Component PN) FlagInfo (Maybe FailReason)+  deriving (Show)++mkIndex :: [(PN, I, PInfo)] -> Index+mkIndex xs = M.map M.fromList (groupMap (L.map (\ (pn, i, pi) -> (pn, (i, pi))) xs))++groupMap :: Ord a => [(a, b)] -> Map a [b]+groupMap xs = M.fromListWith (flip (++)) (L.map (\ (x, y) -> (x, [y])) xs)++defaultQualifyOptions :: Index -> QualifyOptions+defaultQualifyOptions idx = QO {+      qoBaseShim         = or [ dep == base+                              | -- Find all versions of base ..+                                Just is <- [M.lookup base idx]+                                -- .. which are installed ..+                              , (I _ver (Inst _), PInfo deps _flagNfo _fr) <- M.toList is+                                -- .. and flatten all their dependencies ..+                              , (Dep _is_exe dep _ci, _comp) <- flattenFlaggedDeps deps+                              ]+    , qoSetupIndependent = True+    }+  where+    base = mkPackageName "base"
+ Distribution/Solver/Modular/IndexConversion.hs view
@@ -0,0 +1,350 @@+module Distribution.Solver.Modular.IndexConversion+    ( convPIs+    ) where++import Data.List as L+import Data.Map as M+import Data.Maybe+import Data.Monoid as Mon+import Data.Set as S+import Prelude hiding (pi)++import Distribution.Compiler+import Distribution.InstalledPackageInfo as IPI+import Distribution.Package                          -- from Cabal+import Distribution.Simple.BuildToolDepends          -- from Cabal+import Distribution.Types.ExeDependency              -- from Cabal+import Distribution.Types.PkgconfigDependency        -- from Cabal+import Distribution.Types.ComponentName              -- from Cabal+import Distribution.Types.UnqualComponentName        -- from Cabal+import Distribution.Types.CondTree                   -- from Cabal+import Distribution.Types.MungedPackageId            -- from Cabal+import Distribution.Types.MungedPackageName          -- from Cabal+import Distribution.PackageDescription as PD         -- from Cabal+import Distribution.PackageDescription.Configuration as PDC+import qualified Distribution.Simple.PackageIndex as SI+import Distribution.System+import Distribution.Types.ForeignLib++import           Distribution.Solver.Types.ComponentDeps+                   ( Component(..), componentNameToComponent )+import           Distribution.Solver.Types.Flag+import           Distribution.Solver.Types.OptionalStanza+import qualified Distribution.Solver.Types.PackageIndex as CI+import           Distribution.Solver.Types.Settings+import           Distribution.Solver.Types.SourcePackage++import Distribution.Solver.Modular.Dependency as D+import Distribution.Solver.Modular.Flag as F+import Distribution.Solver.Modular.Index+import Distribution.Solver.Modular.Package+import Distribution.Solver.Modular.Tree+import Distribution.Solver.Modular.Version++-- | Convert both the installed package index and the source package+-- index into one uniform solver index.+--+-- We use 'allPackagesBySourcePackageId' for the installed package index+-- because that returns us several instances of the same package and version+-- in order of preference. This allows us in principle to \"shadow\"+-- packages if there are several installed packages of the same version.+-- There are currently some shortcomings in both GHC and Cabal in+-- resolving these situations. However, the right thing to do is to+-- fix the problem there, so for now, shadowing is only activated if+-- explicitly requested.+convPIs :: OS -> Arch -> CompilerInfo -> ShadowPkgs -> StrongFlags -> SolveExecutables ->+           SI.InstalledPackageIndex -> CI.PackageIndex (SourcePackage loc) -> Index+convPIs os arch comp sip strfl sexes iidx sidx =+  mkIndex (convIPI' sip iidx ++ convSPI' os arch comp strfl sexes sidx)++-- | Convert a Cabal installed package index to the simpler,+-- more uniform index format of the solver.+convIPI' :: ShadowPkgs -> SI.InstalledPackageIndex -> [(PN, I, PInfo)]+convIPI' (ShadowPkgs sip) idx =+    -- apply shadowing whenever there are multiple installed packages with+    -- the same version+    [ maybeShadow (convIP idx pkg)+    -- IMPORTANT to get internal libraries. See+    -- Note [Index conversion with internal libraries]+    | (_, pkgs) <- SI.allPackagesBySourcePackageIdAndLibName idx+    , (maybeShadow, pkg) <- zip (id : repeat shadow) pkgs ]+  where++    -- shadowing is recorded in the package info+    shadow (pn, i, PInfo fdeps fds _) | sip = (pn, i, PInfo fdeps fds (Just Shadowed))+    shadow x                                = x++-- | Extract/recover the the package ID from an installed package info, and convert it to a solver's I.+convId :: InstalledPackageInfo -> (PN, I)+convId ipi = (pn, I ver $ Inst $ IPI.installedUnitId ipi)+  where MungedPackageId mpn ver = mungedId ipi+        -- HACK. See Note [Index conversion with internal libraries]+        pn = mkPackageName (unMungedPackageName mpn)++-- | Convert a single installed package into the solver-specific format.+convIP :: SI.InstalledPackageIndex -> InstalledPackageInfo -> (PN, I, PInfo)+convIP idx ipi =+  case mapM (convIPId pn idx) (IPI.depends ipi) of+        Nothing  -> (pn, i, PInfo []            M.empty (Just Broken))+        Just fds -> (pn, i, PInfo (setComp fds) M.empty Nothing)+ where+  (pn, i) = convId ipi+  -- 'sourceLibName' is unreliable, but for now we only really use this for+  -- primary libs anyways+  setComp = setCompFlaggedDeps $ componentNameToComponent+    $ libraryComponentName $ sourceLibName ipi+-- TODO: Installed packages should also store their encapsulations!++-- Note [Index conversion with internal libraries]+-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+-- Something very interesting happens when we have internal libraries+-- in our index.  In this case, we maybe have p-0.1, which itself+-- depends on the internal library p-internal ALSO from p-0.1.+-- Here's the danger:+--+--      - If we treat both of these packages as having PN "p",+--        then the solver will try to pick one or the other,+--        but never both.+--+--      - If we drop the internal packages, now p-0.1 has a+--        dangling dependency on an "installed" package we know+--        nothing about. Oops.+--+-- An expedient hack is to put p-internal into cabal-install's+-- index as a MUNGED package name, so that it doesn't conflict+-- with anyone else (except other instances of itself).  But+-- yet, we ought NOT to say that PNs in the solver are munged+-- package names, because they're not; for source packages,+-- we really will never see munged package names.+--+-- The tension here is that the installed package index is actually+-- per library, but the solver is per package.  We need to smooth+-- it over, and munging the package names is a pretty good way to+-- do it.++-- | Convert dependencies specified by an installed package id into+-- flagged dependencies of the solver.+--+-- May return Nothing if the package can't be found in the index. That+-- indicates that the original package having this dependency is broken+-- and should be ignored.+convIPId :: PN -> SI.InstalledPackageIndex -> UnitId -> Maybe (FlaggedDep () PN)+convIPId pn' idx ipid =+  case SI.lookupUnitId idx ipid of+    Nothing  -> Nothing+    Just ipi -> let (pn, i) = convId ipi+                in  Just (D.Simple (Dep False pn (Fixed i (P pn'))) ())+                -- NB: something we pick up from the+                -- InstalledPackageIndex is NEVER an executable++-- | Convert a cabal-install source package index to the simpler,+-- more uniform index format of the solver.+convSPI' :: OS -> Arch -> CompilerInfo -> StrongFlags -> SolveExecutables ->+            CI.PackageIndex (SourcePackage loc) -> [(PN, I, PInfo)]+convSPI' os arch cinfo strfl sexes = L.map (convSP os arch cinfo strfl sexes) . CI.allPackages++-- | Convert a single source package into the solver-specific format.+convSP :: OS -> Arch -> CompilerInfo -> StrongFlags -> SolveExecutables -> SourcePackage loc -> (PN, I, PInfo)+convSP os arch cinfo strfl sexes (SourcePackage (PackageIdentifier pn pv) gpd _ _pl) =+  let i = I pv InRepo+  in  (pn, i, convGPD os arch cinfo strfl sexes (PI pn i) gpd)++-- We do not use 'flattenPackageDescription' or 'finalizePD'+-- from 'Distribution.PackageDescription.Configuration' here, because we+-- want to keep the condition tree, but simplify much of the test.++-- | Convert a generic package description to a solver-specific 'PInfo'.+convGPD :: OS -> Arch -> CompilerInfo -> StrongFlags -> SolveExecutables ->+           PI PN -> GenericPackageDescription -> PInfo+convGPD os arch cinfo strfl sexes pi+        (GenericPackageDescription pkg flags mlib sub_libs flibs exes tests benchs) =+  let+    fds  = flagInfo strfl flags++    -- | We have to be careful to filter out dependencies on+    -- internal libraries, since they don't refer to real packages+    -- and thus cannot actually be solved over.  We'll do this+    -- by creating a set of package names which are "internal"+    -- and dropping them as we convert.++    ipns = S.fromList $ [ unqualComponentNameToPackageName nm+                        | (nm, _) <- sub_libs ]++    conv :: Mon.Monoid a => Component -> (a -> BuildInfo) ->+            CondTree ConfVar [Dependency] a -> FlaggedDeps Component PN+    conv comp getInfo = convCondTree pkg os arch cinfo pi fds comp getInfo ipns sexes .+                        PDC.addBuildableCondition getInfo++    flagged_deps+        = concatMap (\ds -> conv ComponentLib libBuildInfo ds) (maybeToList mlib)+       ++ concatMap (\(nm, ds) -> conv (ComponentSubLib nm)   libBuildInfo       ds) sub_libs+       ++ concatMap (\(nm, ds) -> conv (ComponentFLib nm)  foreignLibBuildInfo ds) flibs+       ++ concatMap (\(nm, ds) -> conv (ComponentExe nm)   buildInfo          ds) exes+       ++ prefix (Stanza (SN pi TestStanzas))+            (L.map  (\(nm, ds) -> conv (ComponentTest nm)  testBuildInfo      ds) tests)+       ++ prefix (Stanza (SN pi BenchStanzas))+            (L.map  (\(nm, ds) -> conv (ComponentBench nm) benchmarkBuildInfo ds) benchs)+       ++ maybe []    (convSetupBuildInfo pi)    (setupBuildInfo pkg)++  in+    PInfo flagged_deps fds Nothing++-- | Create a flagged dependency tree from a list @fds@ of flagged+-- dependencies, using @f@ to form the tree node (@f@ will be+-- something like @Stanza sn@).+prefix :: (FlaggedDeps comp qpn -> FlaggedDep comp' qpn)+       -> [FlaggedDeps comp qpn] -> FlaggedDeps comp' qpn+prefix _ []  = []+prefix f fds = [f (concat fds)]++-- | Convert flag information. Automatic flags are now considered weak+-- unless strong flags have been selected explicitly.+flagInfo :: StrongFlags -> [PD.Flag] -> FlagInfo+flagInfo (StrongFlags strfl) =+    M.fromList . L.map (\ (MkFlag fn _ b m) -> (fn, FInfo b (flagType m) (weak m)))+  where+    weak m = WeakOrTrivial $ not (strfl || m)+    flagType m = if m then Manual else Automatic++-- | Internal package names, which should not be interpreted as true+-- dependencies.+type IPNs = Set PN++-- | Convenience function to delete a 'FlaggedDep' if it's+-- for a 'PN' that isn't actually real.+filterIPNs :: IPNs -> Dependency -> FlaggedDep Component PN -> FlaggedDeps Component PN+filterIPNs ipns (Dependency pn _) fd+    | S.notMember pn ipns = [fd]+    | otherwise           = []++-- | Convert condition trees to flagged dependencies.  Mutually+-- recursive with 'convBranch'.  See 'convBranch' for an explanation+-- of all arguments preceeding the input 'CondTree'.+convCondTree :: PackageDescription -> OS -> Arch -> CompilerInfo -> PI PN -> FlagInfo ->+                Component ->+                (a -> BuildInfo) ->+                IPNs ->+                SolveExecutables ->+                CondTree ConfVar [Dependency] a -> FlaggedDeps Component PN+convCondTree pkg os arch cinfo pi@(PI pn _) fds comp getInfo ipns sexes@(SolveExecutables sexes') (CondNode info ds branches) =+                 concatMap+                    (\d -> filterIPNs ipns d (D.Simple (convLibDep pn d) comp))+                    ds  -- unconditional package dependencies+              ++ L.map (\e -> D.Simple (Ext  e) comp) (PD.allExtensions bi) -- unconditional extension dependencies+              ++ L.map (\l -> D.Simple (Lang l) comp) (PD.allLanguages  bi) -- unconditional language dependencies+              ++ L.map (\(PkgconfigDependency pkn vr) -> D.Simple (Pkg pkn vr) comp) (PD.pkgconfigDepends bi) -- unconditional pkg-config dependencies+              ++ concatMap (convBranch pkg os arch cinfo pi fds comp getInfo ipns sexes) branches+              -- build-tools dependencies+              -- NB: Only include these dependencies if SolveExecutables+              -- is True.  It might be false in the legacy solver+              -- codepath, in which case there won't be any record of+              -- an executable we need.+              ++ [ D.Simple (convExeDep pn exeDep) comp+                 | sexes'+                 , exeDep <- getAllToolDependencies pkg bi+                 , not $ isInternal pkg exeDep+                 ]+  where+    bi = getInfo info++-- | Branch interpreter.  Mutually recursive with 'convCondTree'.+--+-- Here, we try to simplify one of Cabal's condition tree branches into the+-- solver's flagged dependency format, which is weaker. Condition trees can+-- contain complex logical expression composed from flag choices and special+-- flags (such as architecture, or compiler flavour). We try to evaluate the+-- special flags and subsequently simplify to a tree that only depends on+-- simple flag choices.+--+-- This function takes a number of arguments:+--+--      1. Some pre dependency-solving known information ('OS', 'Arch',+--         'CompilerInfo') for @os()@, @arch()@ and @impl()@ variables,+--+--      2. The package instance @'PI' 'PN'@ which this condition tree+--         came from, so that we can correctly associate @flag()@+--         variables with the correct package name qualifier,+--+--      3. The flag defaults 'FlagInfo' so that we can populate+--         'Flagged' dependencies with 'FInfo',+--+--      4. The name of the component 'Component' so we can record where+--         the fine-grained information about where the component came+--         from (see 'convCondTree'), and+--+--      5. A selector to extract the 'BuildInfo' from the leaves of+--         the 'CondTree' (which actually contains the needed+--         dependency information.)+--+--      6. The set of package names which should be considered internal+--         dependencies, and thus not handled as dependencies.+convBranch :: PackageDescription -> OS -> Arch -> CompilerInfo ->+              PI PN -> FlagInfo ->+              Component ->+              (a -> BuildInfo) ->+              IPNs ->+              SolveExecutables ->+              CondBranch ConfVar [Dependency] a ->+              FlaggedDeps Component PN+convBranch pkg os arch cinfo pi@(PI pn _) fds comp getInfo ipns sexes (CondBranch c' t' mf') =+  go c' (          convCondTree pkg os arch cinfo pi fds comp getInfo ipns sexes  t')+        (maybe [] (convCondTree pkg os arch cinfo pi fds comp getInfo ipns sexes) mf')+  where+    go :: Condition ConfVar ->+          FlaggedDeps Component PN -> FlaggedDeps Component PN -> FlaggedDeps Component PN+    go (Lit True)  t _ = t+    go (Lit False) _ f = f+    go (CNot c)    t f = go c f t+    go (CAnd c d)  t f = go c (go d t f) f+    go (COr  c d)  t f = go c t (go d t f)+    go (Var (Flag fn)) t f = extractCommon t f ++ [Flagged (FN pi fn) (fds ! fn) t f]+    go (Var (OS os')) t f+      | os == os'      = t+      | otherwise      = f+    go (Var (Arch arch')) t f+      | arch == arch'  = t+      | otherwise      = f+    go (Var (Impl cf cvr)) t f+      | matchImpl (compilerInfoId cinfo) ||+            -- fixme: Nothing should be treated as unknown, rather than empty+            --        list. This code should eventually be changed to either+            --        support partial resolution of compiler flags or to+            --        complain about incompletely configured compilers.+        any matchImpl (fromMaybe [] $ compilerInfoCompat cinfo) = t+      | otherwise      = f+      where+        matchImpl (CompilerId cf' cv) = cf == cf' && checkVR cvr cv++    -- If both branches contain the same package as a simple dep, we lift it to+    -- the next higher-level, but without constraints. This heuristic together+    -- with deferring flag choices will then usually first resolve this package,+    -- and try an already installed version before imposing a default flag choice+    -- that might not be what we want.+    --+    -- Note that we make assumptions here on the form of the dependencies that+    -- can occur at this point. In particular, no occurrences of Fixed, and no+    -- occurrences of multiple version ranges, as all dependencies below this+    -- point have been generated using 'convLibDep'.+    --+    -- WARNING: This is quadratic!+    extractCommon :: FlaggedDeps Component PN -> FlaggedDeps Component PN -> FlaggedDeps Component PN+    extractCommon ps ps' = [ D.Simple (Dep is_exe1 pn1 (Constrained [(vr1 .||. vr2, P pn)])) comp+                           | D.Simple (Dep is_exe1 pn1 (Constrained [(vr1, _)])) _ <- ps+                           , D.Simple (Dep is_exe2 pn2 (Constrained [(vr2, _)])) _ <- ps'+                           , pn1 == pn2+                           , is_exe1 == is_exe2+                           ]++-- | Convert a Cabal dependency on a library to a solver-specific dependency.+convLibDep :: PN -> Dependency -> Dep PN+convLibDep pn' (Dependency pn vr) = Dep False {- not exe -} pn (Constrained [(vr, P pn')])++-- | Convert a Cabal dependency on a executable (build-tools) to a solver-specific dependency.+-- TODO do something about the name of the exe component itself+convExeDep :: PN -> ExeDependency -> Dep PN+convExeDep pn' (ExeDependency pn _ vr) = Dep True pn (Constrained [(vr, P pn')])++-- | Convert setup dependencies+convSetupBuildInfo :: PI PN -> SetupBuildInfo -> FlaggedDeps Component PN+convSetupBuildInfo (PI pn _i) nfo =+    L.map (\d -> D.Simple (convLibDep pn d) ComponentSetup) (PD.setupDepends nfo)
+ Distribution/Solver/Modular/LabeledGraph.hs view
@@ -0,0 +1,116 @@+-- | Wrapper around Data.Graph with support for edge labels+{-# LANGUAGE ScopedTypeVariables #-}+module Distribution.Solver.Modular.LabeledGraph (+    -- * Graphs+    Graph+  , Vertex+    -- ** Building graphs+  , graphFromEdges+  , graphFromEdges'+  , buildG+  , transposeG+    -- ** Graph properties+  , vertices+  , edges+    -- ** Operations on the underlying unlabeled graph+  , forgetLabels+  , topSort+  ) where++import Data.Array+import Data.Graph (Vertex, Bounds)+import Data.List (sortBy)+import Data.Maybe (mapMaybe)+import qualified Data.Graph as G++{-------------------------------------------------------------------------------+  Types+-------------------------------------------------------------------------------}++type Graph e = Array Vertex [(e, Vertex)]+type Edge  e = (Vertex, e, Vertex)++{-------------------------------------------------------------------------------+  Building graphs+-------------------------------------------------------------------------------}++-- | Construct an edge-labeled graph+--+-- This is a simple adaptation of the definition in Data.Graph+graphFromEdges :: forall key node edge. Ord key+               => [ (node, key, [(edge, key)]) ]+               -> ( Graph edge+                  , Vertex -> (node, key, [(edge, key)])+                  , key -> Maybe Vertex+                  )+graphFromEdges edges0 =+    (graph, \v -> vertex_map ! v, key_vertex)+  where+    max_v        = length edges0 - 1+    bounds0      = (0, max_v) :: (Vertex, Vertex)+    sorted_edges = sortBy lt edges0+    edges1       = zipWith (,) [0..] sorted_edges++    graph        = array bounds0 [(v, (mapMaybe mk_edge ks))+                                 | (v, (_, _, ks)) <- edges1]+    key_map      = array bounds0 [(v, k                    )+                                 | (v, (_, k, _ )) <- edges1]+    vertex_map   = array bounds0 edges1++    (_,k1,_) `lt` (_,k2,_) = k1 `compare` k2++    mk_edge :: (edge, key) -> Maybe (edge, Vertex)+    mk_edge (edge, key) = do v <- key_vertex key ; return (edge, v)++    --  returns Nothing for non-interesting vertices+    key_vertex :: key -> Maybe Vertex+    key_vertex k = findVertex 0 max_v+      where+        findVertex a b+          | a > b     = Nothing+          | otherwise = case compare k (key_map ! mid) of+              LT -> findVertex a (mid-1)+              EQ -> Just mid+              GT -> findVertex (mid+1) b+          where+            mid = a + (b - a) `div` 2++graphFromEdges' :: Ord key+                => [ (node, key, [(edge, key)]) ]+                -> ( Graph edge+                   , Vertex -> (node, key, [(edge, key)])+                   )+graphFromEdges' x = (a,b)+  where+    (a,b,_) = graphFromEdges x++transposeG :: Graph e -> Graph e+transposeG g = buildG (bounds g) (reverseE g)++buildG :: Bounds -> [Edge e] -> Graph e+buildG bounds0 edges0 = accumArray (flip (:)) [] bounds0 (map reassoc edges0)+  where+    reassoc (v, e, w) = (v, (e, w))++reverseE :: Graph e -> [Edge e]+reverseE g = [ (w, e, v) | (v, e, w) <- edges g ]++{-------------------------------------------------------------------------------+  Graph properties+-------------------------------------------------------------------------------}++vertices :: Graph e -> [Vertex]+vertices = indices++edges :: Graph e -> [Edge e]+edges g = [ (v, e, w) | v <- vertices g, (e, w) <- g!v ]++{-------------------------------------------------------------------------------+  Operations on the underlying unlabelled graph+-------------------------------------------------------------------------------}++forgetLabels :: Graph e -> G.Graph+forgetLabels = fmap (map snd)++topSort :: Graph e -> [Vertex]+topSort = G.topSort . forgetLabels
+ Distribution/Solver/Modular/Linking.hs view
@@ -0,0 +1,517 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE MultiParamTypeClasses #-}+module Distribution.Solver.Modular.Linking (+    validateLinking+  ) where++import Prelude ()+import Distribution.Client.Compat.Prelude hiding (get,put)++import Control.Exception (assert)+import Control.Monad.Reader+import Control.Monad.State+import Data.Map ((!))+import Data.Set (Set)+import qualified Data.Map         as M+import qualified Data.Set         as S+import qualified Data.Traversable as T++import Distribution.Client.Utils.Assertion+import Distribution.Solver.Modular.Assignment+import Distribution.Solver.Modular.Dependency+import Distribution.Solver.Modular.Flag+import Distribution.Solver.Modular.Index+import Distribution.Solver.Modular.Package+import Distribution.Solver.Modular.Tree+import qualified Distribution.Solver.Modular.ConflictSet as CS+import qualified Distribution.Solver.Modular.WeightedPSQ as W++import Distribution.Solver.Types.OptionalStanza+import Distribution.Solver.Types.PackagePath+import Distribution.Solver.Types.ComponentDeps (Component)+import Distribution.Types.GenericPackageDescription (unFlagName)++{-------------------------------------------------------------------------------+  Validation++  Validation of links is a separate pass that's performed after normal+  validation. Validation of links checks that if the tree indicates that a+  package is linked, then everything underneath that choice really matches the+  package we have linked to.++  This is interesting because it isn't unidirectional. Consider that we've+  chosen a.foo to be version 1 and later decide that b.foo should link to a.foo.+  Now foo depends on bar. Because a.foo and b.foo are linked, it's required that+  a.bar and b.bar are also linked. However, it's not required that we actually+  choose a.bar before b.bar. Goal choice order is relatively free. It's possible+  that we choose a.bar first, but also possible that we choose b.bar first. In+  both cases, we have to recognize that we have freedom of choice for the first+  of the two, but no freedom of choice for the second.++  This is what LinkGroups are all about. Using LinkGroup, we can record (in the+  situation above) that a.bar and b.bar need to be linked even if we haven't+  chosen either of them yet.+-------------------------------------------------------------------------------}++data ValidateState = VS {+      vsIndex    :: Index+    , vsLinks    :: Map QPN LinkGroup+    , vsFlags    :: FAssignment+    , vsStanzas  :: SAssignment+    , vsQualifyOptions :: QualifyOptions+    }+    deriving Show++type Validate = Reader ValidateState++-- | Validate linked packages+--+-- Verify that linked packages have+--+-- * Linked dependencies,+-- * Equal flag assignments+-- * Equal stanza assignments+validateLinking :: Index -> Tree d c -> Tree d c+validateLinking index = (`runReader` initVS) . cata go+  where+    go :: TreeF d c (Validate (Tree d c)) -> Validate (Tree d c)++    go (PChoiceF qpn rdm gr       cs) =+      PChoice qpn rdm gr       <$> T.sequence (W.mapWithKey (goP qpn) cs)+    go (FChoiceF qfn rdm gr t m d cs) =+      FChoice qfn rdm gr t m d <$> T.sequence (W.mapWithKey (goF qfn) cs)+    go (SChoiceF qsn rdm gr t     cs) =+      SChoice qsn rdm gr t     <$> T.sequence (W.mapWithKey (goS qsn) cs)++    -- For the other nodes we just recurse+    go (GoalChoiceF rdm           cs) = GoalChoice rdm <$> T.sequence cs+    go (DoneF revDepMap s)            = return $ Done revDepMap s+    go (FailF conflictSet failReason) = return $ Fail conflictSet failReason++    -- Package choices+    goP :: QPN -> POption -> Validate (Tree d c) -> Validate (Tree d c)+    goP qpn@(Q _pp pn) opt@(POption i _) r = do+      vs <- ask+      let PInfo deps _ _ = vsIndex vs ! pn ! i+          qdeps          = qualifyDeps (vsQualifyOptions vs) qpn deps+      case execUpdateState (pickPOption qpn opt qdeps) vs of+        Left  (cs, err) -> return $ Fail cs (DependenciesNotLinked err)+        Right vs'       -> local (const vs') r++    -- Flag choices+    goF :: QFN -> Bool -> Validate (Tree d c) -> Validate (Tree d c)+    goF qfn b r = do+      vs <- ask+      case execUpdateState (pickFlag qfn b) vs of+        Left  (cs, err) -> return $ Fail cs (DependenciesNotLinked err)+        Right vs'       -> local (const vs') r++    -- Stanza choices (much the same as flag choices)+    goS :: QSN -> Bool -> Validate (Tree d c) -> Validate (Tree d c)+    goS qsn b r = do+      vs <- ask+      case execUpdateState (pickStanza qsn b) vs of+        Left  (cs, err) -> return $ Fail cs (DependenciesNotLinked err)+        Right vs'       -> local (const vs') r++    initVS :: ValidateState+    initVS = VS {+        vsIndex   = index+      , vsLinks   = M.empty+      , vsFlags   = M.empty+      , vsStanzas = M.empty+      , vsQualifyOptions = defaultQualifyOptions index+      }++{-------------------------------------------------------------------------------+  Updating the validation state+-------------------------------------------------------------------------------}++type Conflict = (ConflictSet, String)++newtype UpdateState a = UpdateState {+    unUpdateState :: StateT ValidateState (Either Conflict) a+  }+  deriving (Functor, Applicative, Monad)++instance MonadState ValidateState UpdateState where+  get    = UpdateState $ get+  put st = UpdateState $ do+             expensiveAssert (lgInvariant $ vsLinks st) $ return ()+             put st++lift' :: Either Conflict a -> UpdateState a+lift' = UpdateState . lift++conflict :: Conflict -> UpdateState a+conflict = lift' . Left++execUpdateState :: UpdateState () -> ValidateState -> Either Conflict ValidateState+execUpdateState = execStateT . unUpdateState++pickPOption :: QPN -> POption -> FlaggedDeps Component QPN -> UpdateState ()+pickPOption qpn (POption i Nothing)    _deps = pickConcrete qpn i+pickPOption qpn (POption i (Just pp'))  deps = pickLink     qpn i pp' deps++pickConcrete :: QPN -> I -> UpdateState ()+pickConcrete qpn@(Q pp _) i = do+    vs <- get+    case M.lookup qpn (vsLinks vs) of+      -- Package is not yet in a LinkGroup. Create a new singleton link group.+      Nothing -> do+        let lg = lgSingleton qpn (Just $ PI pp i)+        updateLinkGroup lg++      -- Package is already in a link group. Since we are picking a concrete+      -- instance here, it must by definition be the canonical package.+      Just lg ->+        makeCanonical lg qpn i++pickLink :: QPN -> I -> PackagePath -> FlaggedDeps Component QPN -> UpdateState ()+pickLink qpn@(Q _pp pn) i pp' deps = do+    vs <- get++    -- The package might already be in a link group+    -- (because one of its reverse dependencies is)+    let lgSource = case M.lookup qpn (vsLinks vs) of+                     Nothing -> lgSingleton qpn Nothing+                     Just lg -> lg++    -- Find the link group for the package we are linking to+    --+    -- Since the builder never links to a package without having first picked a+    -- concrete instance for that package, and since we create singleton link+    -- groups for concrete instances, this link group must exist (and must+    -- in fact already have a canonical member).+    let target   = Q pp' pn+        lgTarget = vsLinks vs ! target++    -- Verify here that the member we add is in fact for the same package and+    -- matches the version of the canonical instance. However, violations of+    -- these checks would indicate a bug in the linker, not a true conflict.+    let sanityCheck :: Maybe (PI PackagePath) -> Bool+        sanityCheck Nothing              = False+        sanityCheck (Just (PI _ canonI)) = pn == lgPackage lgTarget && i == canonI+    assert (sanityCheck (lgCanon lgTarget)) $ return ()++    -- Merge the two link groups (updateLinkGroup will propagate the change)+    lgTarget' <- lift' $ lgMerge [] lgSource lgTarget+    updateLinkGroup lgTarget'++    -- Make sure all dependencies are linked as well+    linkDeps target [P qpn] deps++makeCanonical :: LinkGroup -> QPN -> I -> UpdateState ()+makeCanonical lg qpn@(Q pp _) i =+    case lgCanon lg of+      -- There is already a canonical member. Fail.+      Just _ ->+        conflict ( CS.insert (P qpn) (lgConflictSet lg)+                 ,    "cannot make " ++ showQPN qpn+                   ++ " canonical member of " ++ showLinkGroup lg+                 )+      Nothing -> do+        let lg' = lg { lgCanon = Just (PI pp i) }+        updateLinkGroup lg'++-- | Link the dependencies of linked parents.+--+-- When we decide to link one package against another we walk through the+-- package's direct depedencies and make sure that they're all linked to each+-- other by merging their link groups (or creating new singleton link groups if+-- they don't have link groups yet). We do not need to do this recursively,+-- because having the direct dependencies in a link group means that we must+-- have already made or will make sooner or later a link choice for one of these+-- as well, and cover their dependencies at that point.+linkDeps :: QPN -> [Var QPN] -> FlaggedDeps Component QPN -> UpdateState ()+linkDeps target = \blame deps -> do+    -- linkDeps is called in two places: when we first link one package to+    -- another, and when we discover more dependencies of an already linked+    -- package after doing some flag assignment. It is therefore important that+    -- flag assignments cannot influence _how_ dependencies are qualified;+    -- fortunately this is a documented property of 'qualifyDeps'.+    rdeps <- requalify deps+    go blame deps rdeps+  where+    go :: [Var QPN] -> FlaggedDeps Component QPN -> FlaggedDeps Component QPN -> UpdateState ()+    go = zipWithM_ . go1++    go1 :: [Var QPN] -> FlaggedDep Component QPN -> FlaggedDep Component QPN -> UpdateState ()+    go1 blame dep rdep = case (dep, rdep) of+      (Simple (Dep _ qpn _) _, ~(Simple (Dep _ qpn' _) _)) -> do+        vs <- get+        let lg   = M.findWithDefault (lgSingleton qpn  Nothing) qpn  $ vsLinks vs+            lg'  = M.findWithDefault (lgSingleton qpn' Nothing) qpn' $ vsLinks vs+        lg'' <- lift' $ lgMerge blame lg lg'+        updateLinkGroup lg''+      (Flagged fn _ t f, ~(Flagged _ _ t' f')) -> do+        vs <- get+        case M.lookup fn (vsFlags vs) of+          Nothing    -> return () -- flag assignment not yet known+          Just True  -> go (F fn:blame) t t'+          Just False -> go (F fn:blame) f f'+      (Stanza sn t, ~(Stanza _ t')) -> do+        vs <- get+        case M.lookup sn (vsStanzas vs) of+          Nothing    -> return () -- stanza assignment not yet known+          Just True  -> go (S sn:blame) t t'+          Just False -> return () -- stanza not enabled; no new deps+    -- For extensions and language dependencies, there is nothing to do.+    -- No choice is involved, just checking, so there is nothing to link.+    -- The same goes for for pkg-config constraints.+      (Simple (Ext  _)   _, _) -> return ()+      (Simple (Lang _)   _, _) -> return ()+      (Simple (Pkg  _ _) _, _) -> return ()++    requalify :: FlaggedDeps Component QPN -> UpdateState (FlaggedDeps Component QPN)+    requalify deps = do+      vs <- get+      return $ qualifyDeps (vsQualifyOptions vs) target (unqualifyDeps deps)++pickFlag :: QFN -> Bool -> UpdateState ()+pickFlag qfn b = do+    modify $ \vs -> vs { vsFlags = M.insert qfn b (vsFlags vs) }+    verifyFlag qfn+    linkNewDeps (F qfn) b++pickStanza :: QSN -> Bool -> UpdateState ()+pickStanza qsn b = do+    modify $ \vs -> vs { vsStanzas = M.insert qsn b (vsStanzas vs) }+    verifyStanza qsn+    linkNewDeps (S qsn) b++-- | Link dependencies that we discover after making a flag choice.+--+-- When we make a flag choice for a package, then new dependencies for that+-- package might become available. If the package under consideration is in a+-- non-trivial link group, then these new dependencies have to be linked as+-- well. In linkNewDeps, we compute such new dependencies and make sure they are+-- linked.+linkNewDeps :: Var QPN -> Bool -> UpdateState ()+linkNewDeps var b = do+    vs <- get+    let (qpn@(Q pp pn), Just i) = varPI var+        PInfo deps _ _          = vsIndex vs ! pn ! i+        qdeps                   = qualifyDeps (vsQualifyOptions vs) qpn deps+        lg                      = vsLinks vs ! qpn+        (parents, newDeps)      = findNewDeps vs qdeps+        linkedTo                = S.delete pp (lgMembers lg)+    forM_ (S.toList linkedTo) $ \pp' -> linkDeps (Q pp' pn) (P qpn : parents) newDeps+  where+    findNewDeps :: ValidateState -> FlaggedDeps comp QPN -> ([Var QPN], FlaggedDeps Component QPN)+    findNewDeps vs = concatMapUnzip (findNewDeps' vs)++    findNewDeps' :: ValidateState -> FlaggedDep comp QPN -> ([Var QPN], FlaggedDeps Component QPN)+    findNewDeps' _  (Simple _ _)        = ([], [])+    findNewDeps' vs (Flagged qfn _ t f) =+      case (F qfn == var, M.lookup qfn (vsFlags vs)) of+        (True, _)    -> ([F qfn], if b then t else f)+        (_, Nothing) -> ([], []) -- not yet known+        (_, Just b') -> let (parents, deps) = findNewDeps vs (if b' then t else f)+                        in (F qfn:parents, deps)+    findNewDeps' vs (Stanza qsn t) =+      case (S qsn == var, M.lookup qsn (vsStanzas vs)) of+        (True, _)    -> ([S qsn], if b then t else [])+        (_, Nothing) -> ([], []) -- not yet known+        (_, Just b') -> let (parents, deps) = findNewDeps vs (if b' then t else [])+                        in (S qsn:parents, deps)++updateLinkGroup :: LinkGroup -> UpdateState ()+updateLinkGroup lg = do+    verifyLinkGroup lg+    modify $ \vs -> vs {+        vsLinks =           M.fromList (map aux (S.toList (lgMembers lg)))+                  `M.union` vsLinks vs+      }+  where+    aux pp = (Q pp (lgPackage lg), lg)++{-------------------------------------------------------------------------------+  Verification+-------------------------------------------------------------------------------}++verifyLinkGroup :: LinkGroup -> UpdateState ()+verifyLinkGroup lg =+    case lgInstance lg of+      -- No instance picked yet. Nothing to verify+      Nothing ->+        return ()++      -- We picked an instance. Verify flags and stanzas+      -- TODO: The enumeration of OptionalStanza names is very brittle;+      -- if a constructor is added to the datatype we won't notice it here+      Just i -> do+        vs <- get+        let PInfo _deps finfo _ = vsIndex vs ! lgPackage lg ! i+            flags   = M.keys finfo+            stanzas = [TestStanzas, BenchStanzas]+        forM_ flags $ \fn -> do+          let flag = FN (PI (lgPackage lg) i) fn+          verifyFlag' flag lg+        forM_ stanzas $ \sn -> do+          let stanza = SN (PI (lgPackage lg) i) sn+          verifyStanza' stanza lg++verifyFlag :: QFN -> UpdateState ()+verifyFlag (FN (PI qpn@(Q _pp pn) i) fn) = do+    vs <- get+    -- We can only pick a flag after picking an instance; link group must exist+    verifyFlag' (FN (PI pn i) fn) (vsLinks vs ! qpn)++verifyStanza :: QSN -> UpdateState ()+verifyStanza (SN (PI qpn@(Q _pp pn) i) sn) = do+    vs <- get+    -- We can only pick a stanza after picking an instance; link group must exist+    verifyStanza' (SN (PI pn i) sn) (vsLinks vs ! qpn)++-- | Verify that all packages in the link group agree on flag assignments+--+-- For the given flag and the link group, obtain all assignments for the flag+-- that have already been made for link group members, and check that they are+-- equal.+verifyFlag' :: FN PN -> LinkGroup -> UpdateState ()+verifyFlag' (FN (PI pn i) fn) lg = do+    vs <- get+    let flags = map (\pp' -> FN (PI (Q pp' pn) i) fn) (S.toList (lgMembers lg))+        vals  = map (`M.lookup` vsFlags vs) flags+    if allEqual (catMaybes vals) -- We ignore not-yet assigned flags+      then return ()+      else conflict ( CS.fromList (map F flags) `CS.union` lgConflictSet lg+                    , "flag \"" ++ unFlagName fn ++ "\" incompatible"+                    )++-- | Verify that all packages in the link group agree on stanza assignments+--+-- For the given stanza and the link group, obtain all assignments for the+-- stanza that have already been made for link group members, and check that+-- they are equal.+--+-- This function closely mirrors 'verifyFlag''.+verifyStanza' :: SN PN -> LinkGroup -> UpdateState ()+verifyStanza' (SN (PI pn i) sn) lg = do+    vs <- get+    let stanzas = map (\pp' -> SN (PI (Q pp' pn) i) sn) (S.toList (lgMembers lg))+        vals    = map (`M.lookup` vsStanzas vs) stanzas+    if allEqual (catMaybes vals) -- We ignore not-yet assigned stanzas+      then return ()+      else conflict ( CS.fromList (map S stanzas) `CS.union` lgConflictSet lg+                    , "stanza \"" ++ showStanza sn ++ "\" incompatible"+                    )++{-------------------------------------------------------------------------------+  Link groups+-------------------------------------------------------------------------------}++-- | Set of packages that must be linked together+--+-- A LinkGroup is between several qualified package names. In the validation+-- state, we maintain a map vsLinks from qualified package names to link groups.+-- There is an invariant that for all members of a link group, vsLinks must map+-- to the same link group. The function updateLinkGroup can be used to+-- re-establish this invariant after creating or expanding a LinkGroup.+data LinkGroup = LinkGroup {+      -- | The name of the package of this link group+      lgPackage :: PN++      -- | The canonical member of this link group (the one where we picked+      -- a concrete instance). Once we have picked a canonical member, all+      -- other packages must link to this one.+      --+      -- We may not know this yet (if we are constructing link groups+      -- for dependencies)+    , lgCanon :: Maybe (PI PackagePath)++      -- | The members of the link group+    , lgMembers :: Set PackagePath++      -- | The set of variables that should be added to the conflict set if+      -- something goes wrong with this link set (in addition to the members+      -- of the link group itself)+    , lgBlame :: ConflictSet+    }+    deriving (Show, Eq)++-- | Invariant for the set of link groups: every element in the link group+-- must be pointing to the /same/ link group+lgInvariant :: Map QPN LinkGroup -> Bool+lgInvariant links = all invGroup (M.elems links)+  where+    invGroup :: LinkGroup -> Bool+    invGroup lg = allEqual $ map (`M.lookup` links) members+      where+        members :: [QPN]+        members = map (`Q` lgPackage lg) $ S.toList (lgMembers lg)++-- | Package version of this group+--+-- This is only known once we have picked a canonical element.+lgInstance :: LinkGroup -> Maybe I+lgInstance = fmap (\(PI _ i) -> i) . lgCanon++showLinkGroup :: LinkGroup -> String+showLinkGroup lg =+    "{" ++ intercalate "," (map showMember (S.toList (lgMembers lg))) ++ "}"+  where+    showMember :: PackagePath -> String+    showMember pp = case lgCanon lg of+                      Just (PI pp' _i) | pp == pp' -> "*"+                      _otherwise                   -> ""+                 ++ case lgInstance lg of+                      Nothing -> showQPN (qpn pp)+                      Just i  -> showPI (PI (qpn pp) i)++    qpn :: PackagePath -> QPN+    qpn pp = Q pp (lgPackage lg)++-- | Creates a link group that contains a single member.+lgSingleton :: QPN -> Maybe (PI PackagePath) -> LinkGroup+lgSingleton (Q pp pn) canon = LinkGroup {+      lgPackage = pn+    , lgCanon   = canon+    , lgMembers = S.singleton pp+    , lgBlame   = CS.empty+    }++lgMerge :: [Var QPN] -> LinkGroup -> LinkGroup -> Either Conflict LinkGroup+lgMerge blame lg lg' = do+    canon <- pick (lgCanon lg) (lgCanon lg')+    return LinkGroup {+        lgPackage = lgPackage lg+      , lgCanon   = canon+      , lgMembers = lgMembers lg `S.union` lgMembers lg'+      , lgBlame   = CS.unions [CS.fromList blame, lgBlame lg, lgBlame lg']+      }+  where+    pick :: Eq a => Maybe a -> Maybe a -> Either Conflict (Maybe a)+    pick Nothing  Nothing  = Right Nothing+    pick (Just x) Nothing  = Right $ Just x+    pick Nothing  (Just y) = Right $ Just y+    pick (Just x) (Just y) =+      if x == y then Right $ Just x+                else Left ( CS.unions [+                               CS.fromList blame+                             , lgConflictSet lg+                             , lgConflictSet lg'+                             ]+                          ,    "cannot merge " ++ showLinkGroup lg+                            ++ " and " ++ showLinkGroup lg'+                          )++lgConflictSet :: LinkGroup -> ConflictSet+lgConflictSet lg =+               CS.fromList (map aux (S.toList (lgMembers lg)))+    `CS.union` lgBlame lg+  where+    aux pp = P (Q pp (lgPackage lg))++{-------------------------------------------------------------------------------+  Auxiliary+-------------------------------------------------------------------------------}++allEqual :: Eq a => [a] -> Bool+allEqual []       = True+allEqual [_]      = True+allEqual (x:y:ys) = x == y && allEqual (y:ys)++concatMapUnzip :: (a -> ([b], [c])) -> [a] -> ([b], [c])+concatMapUnzip f = (\(xs, ys) -> (concat xs, concat ys)) . unzip . map f
+ Distribution/Solver/Modular/Log.hs view
@@ -0,0 +1,92 @@+module Distribution.Solver.Modular.Log+    ( Log+    , logToProgress+    ) where++import Prelude ()+import Distribution.Client.Compat.Prelude++import Data.List as L++import Distribution.Solver.Types.Progress++import Distribution.Solver.Modular.Dependency+import Distribution.Solver.Modular.Message+import Distribution.Solver.Modular.Tree (FailReason(..))+import qualified Distribution.Solver.Modular.ConflictSet as CS+import Distribution.Verbosity++-- | The 'Log' datatype.+--+-- Represents the progress of a computation lazily.+--+-- Parameterized over the type of actual messages and the final result.+type Log m a = Progress m (ConflictSet, ConflictMap) a++messages :: Progress step fail done -> [step]+messages = foldProgress (:) (const []) (const [])++data Exhaustiveness = Exhaustive | BackjumpLimitReached++-- | Postprocesses a log file. Takes as an argument a limit on allowed backjumps.+-- If the limit is 'Nothing', then infinitely many backjumps are allowed. If the+-- limit is 'Just 0', backtracking is completely disabled.+logToProgress :: Verbosity -> Maybe Int -> Log Message a -> Progress String String a+logToProgress verbosity mbj l =+    let es = proc (Just 0) l -- catch first error (always)+        ms = proc mbj l+    in go es es -- trace for first error+          (showMessages (const True) True ms) -- run with backjump limit applied+  where+    -- Proc takes the allowed number of backjumps and a 'Progress' and explores the+    -- messages until the maximum number of backjumps has been reached. It filters out+    -- and ignores repeated backjumps. If proc reaches the backjump limit, it truncates+    -- the 'Progress' and ends it with the last conflict set. Otherwise, it leaves the+    -- original result.+    proc :: Maybe Int -> Log Message b -> Progress Message (Exhaustiveness, ConflictSet, ConflictMap) b+    proc _        (Done x)                          = Done x+    proc _        (Fail (cs, cm))                   = Fail (Exhaustive, cs, cm)+    proc mbj'     (Step x@(Failure cs Backjump) xs@(Step Leave (Step (Failure cs' Backjump) _)))+      | cs == cs'                                   = Step x (proc mbj'           xs) -- repeated backjumps count as one+    proc (Just 0) (Step   (Failure cs Backjump)  _) = Fail (BackjumpLimitReached, cs, mempty) -- No final conflict map available+    proc (Just n) (Step x@(Failure _  Backjump) xs) = Step x (proc (Just (n - 1)) xs)+    proc mbj'     (Step x                       xs) = Step x (proc mbj'           xs)++    -- The first two arguments are both supposed to be the log up to the first error.+    -- That's the error that will always be printed in case we do not find a solution.+    -- We pass this log twice, because we evaluate it in parallel with the full log,+    -- but we also want to retain the reference to its beginning for when we print it.+    -- This trick prevents a space leak!+    --+    -- The third argument is the full log, ending with either the solution or the+    -- exhaustiveness and final conflict set.+    go :: Progress Message (Exhaustiveness, ConflictSet, ConflictMap) b+       -> Progress Message (Exhaustiveness, ConflictSet, ConflictMap) b+       -> Progress String  (Exhaustiveness, ConflictSet, ConflictMap) b+       -> Progress String String b+    go ms (Step _ ns)        (Step x xs)           = Step x (go ms ns xs)+    go ms r                  (Step x xs)           = Step x (go ms r  xs)+    go ms (Step _ ns)        r                     = go ms ns r+    go ms (Fail (_, cs', _)) (Fail (exh, cs, cm))  = Fail $+        "Could not resolve dependencies:\n" +++        unlines (messages $ showMessages (L.foldr (\ v _ -> v `CS.member` cs') True) False ms) +++        case exh of+            Exhaustive ->+                "After searching the rest of the dependency tree exhaustively, "+                ++ "these were the goals I've had most trouble fulfilling: "+                ++ showCS cm cs+              where+                showCS = if verbosity > normal+                         then CS.showCSWithFrequency+                         else CS.showCSSortedByFrequency+            BackjumpLimitReached ->+                "Backjump limit reached (" ++ currlimit mbj +++                "change with --max-backjumps or try to run with --reorder-goals).\n"+              where currlimit (Just n) = "currently " ++ show n ++ ", "+                    currlimit Nothing  = ""+    go _  _                  (Done s)              = Done s+    go _  (Done _)           (Fail _)              = Fail $+        -- Should not happen: Second argument is the log up to first error,+        -- third one is the entire log. Therefore it should never happen that+        -- the second log finishes with 'Done' and the third log with 'Fail'.+        "Could not resolve dependencies; something strange happened."
+ Distribution/Solver/Modular/Message.hs view
@@ -0,0 +1,155 @@+{-# LANGUAGE BangPatterns #-}++module Distribution.Solver.Modular.Message (+    Message(..),+    showMessages+  ) where++import qualified Data.List as L+import Prelude hiding (pi)++import Distribution.Text -- from Cabal++import Distribution.Solver.Modular.Dependency+import Distribution.Solver.Modular.Flag+import Distribution.Solver.Modular.Package+import Distribution.Solver.Modular.Tree+         ( FailReason(..), POption(..) )+import Distribution.Solver.Types.ConstraintSource+import Distribution.Solver.Types.PackagePath+import Distribution.Solver.Types.Progress++data Message =+    Enter           -- ^ increase indentation level+  | Leave           -- ^ decrease indentation level+  | TryP QPN POption+  | TryF QFN Bool+  | TryS QSN Bool+  | Next (Goal QPN)+  | Success+  | Failure ConflictSet FailReason++-- | Transforms the structured message type to actual messages (strings).+--+-- Takes an additional relevance predicate. The predicate gets a stack of goal+-- variables and can decide whether messages regarding these goals are relevant.+-- You can plug in 'const True' if you're interested in a full trace. If you+-- want a slice of the trace concerning a particular conflict set, then plug in+-- a predicate returning 'True' on the empty stack and if the head is in the+-- conflict set.+--+-- The second argument indicates if the level numbers should be shown. This is+-- recommended for any trace that involves backtracking, because only the level+-- numbers will allow to keep track of backjumps.+showMessages :: ([Var QPN] -> Bool) -> Bool -> Progress Message a b -> Progress String a b+showMessages p sl = go [] 0+  where+    -- The stack 'v' represents variables that are currently assigned by the+    -- solver.  'go' pushes a variable for a recursive call when it encounters+    -- 'TryP', 'TryF', or 'TryS' and pops a variable when it encounters 'Leave'.+    -- When 'go' processes a package goal, or a package goal followed by a+    -- 'Failure', it calls 'atLevel' with the goal variable at the head of the+    -- stack so that the predicate can also select messages relating to package+    -- goal choices.+    go :: [Var QPN] -> Int -> Progress Message a b -> Progress String a b+    go !_ !_ (Done x)                           = Done x+    go !_ !_ (Fail x)                           = Fail x+    -- complex patterns+    go !v !l (Step (TryP qpn i) (Step Enter (Step (Failure c fr) (Step Leave ms)))) =+        goPReject v l qpn [i] c fr ms+    go !v !l (Step (TryF qfn b) (Step Enter (Step (Failure c fr) (Step Leave ms)))) =+        (atLevel (add (F qfn) v) l $ "rejecting: " ++ showQFNBool qfn b ++ showFR c fr) (go v l ms)+    go !v !l (Step (TryS qsn b) (Step Enter (Step (Failure c fr) (Step Leave ms)))) =+        (atLevel (add (S qsn) v) l $ "rejecting: " ++ showQSNBool qsn b ++ showFR c fr) (go v l ms)+    go !v !l (Step (Next (Goal (P qpn) gr)) (Step (TryP qpn' i) ms@(Step Enter (Step (Next _) _)))) =+        (atLevel (add (P qpn) v) l $ "trying: " ++ showQPNPOpt qpn' i ++ showGR gr) (go (add (P qpn) v) l ms)+    go !v !l (Step (Next (Goal (P qpn) gr)) ms@(Fail _)) =+        (atLevel (add (P qpn) v) l $ "unknown package: " ++ showQPN qpn ++ showGR gr) $ go v l ms+        -- the previous case potentially arises in the error output, because we remove the backjump itself+        -- if we cut the log after the first error+    go !v !l (Step (Next (Goal (P qpn) gr)) ms@(Step (Failure _c Backjump) _)) =+        (atLevel (add (P qpn) v) l $ "unknown package: " ++ showQPN qpn ++ showGR gr) $ go v l ms+    go !v !l (Step (Next (Goal (P qpn) gr)) (Step (Failure c fr) ms)) =+        let v' = add (P qpn) v+        in (atLevel v' l $ showPackageGoal qpn gr) $ (atLevel v' l $ showFailure c fr) (go v l ms)+    go !v !l (Step (Failure c Backjump) ms@(Step Leave (Step (Failure c' Backjump) _)))+        | c == c' = go v l ms+    -- standard display+    go !v !l (Step Enter                    ms) = go v          (l+1) ms+    go !v !l (Step Leave                    ms) = go (drop 1 v) (l-1) ms+    go !v !l (Step (TryP qpn i)             ms) = (atLevel (add (P qpn) v) l $ "trying: " ++ showQPNPOpt qpn i) (go (add (P qpn) v) l ms)+    go !v !l (Step (TryF qfn b)             ms) = (atLevel (add (F qfn) v) l $ "trying: " ++ showQFNBool qfn b) (go (add (F qfn) v) l ms)+    go !v !l (Step (TryS qsn b)             ms) = (atLevel (add (S qsn) v) l $ "trying: " ++ showQSNBool qsn b) (go (add (S qsn) v) l ms)+    go !v !l (Step (Next (Goal (P qpn) gr)) ms) = (atLevel (add (P qpn) v) l $ showPackageGoal qpn gr) (go v l ms)+    go !v !l (Step (Next _)                 ms) = go v l ms -- ignore flag goals in the log+    go !v !l (Step (Success)                ms) = (atLevel v l $ "done") (go v l ms)+    go !v !l (Step (Failure c fr)           ms) = (atLevel v l $ showFailure c fr) (go v l ms)++    showPackageGoal :: QPN -> QGoalReason -> String+    showPackageGoal qpn gr = "next goal: " ++ showQPN qpn ++ showGR gr++    showFailure :: ConflictSet -> FailReason -> String+    showFailure c fr = "fail" ++ showFR c fr++    add :: Var QPN -> [Var QPN] -> [Var QPN]+    add v vs = simplifyVar v : vs++    -- special handler for many subsequent package rejections+    goPReject :: [Var QPN]+              -> Int+              -> QPN+              -> [POption]+              -> ConflictSet+              -> FailReason+              -> Progress Message a b+              -> Progress String a b+    goPReject v l qpn is c fr (Step (TryP qpn' i) (Step Enter (Step (Failure _ fr') (Step Leave ms))))+      | qpn == qpn' && fr == fr' = goPReject v l qpn (i : is) c fr ms+    goPReject v l qpn is c fr ms =+        (atLevel (P qpn : v) l $ "rejecting: " ++ L.intercalate ", " (map (showQPNPOpt qpn) (reverse is)) ++ showFR c fr) (go v l ms)++    -- write a message, but only if it's relevant; we can also enable or disable the display of the current level+    atLevel :: [Var QPN] -> Int -> String -> Progress String a b -> Progress String a b+    atLevel v l x xs+      | sl && p v = let s = show l+                    in  Step ("[" ++ replicate (3 - length s) '_' ++ s ++ "] " ++ x) xs+      | p v       = Step x xs+      | otherwise = xs++showQPNPOpt :: QPN -> POption -> String+showQPNPOpt qpn@(Q _pp pn) (POption i linkedTo) =+  case linkedTo of+    Nothing  -> showPI (PI qpn i) -- Consistent with prior to POption+    Just pp' -> showQPN qpn ++ "~>" ++ showPI (PI (Q pp' pn) i)++showGR :: QGoalReason -> String+showGR UserGoal            = " (user goal)"+showGR (PDependency pi)    = " (dependency of " ++ showPI pi            ++ ")"+showGR (FDependency qfn b) = " (dependency of " ++ showQFNBool qfn b    ++ ")"+showGR (SDependency qsn)   = " (dependency of " ++ showQSNBool qsn True ++ ")"++showFR :: ConflictSet -> FailReason -> String+showFR _ InconsistentInitialConstraints   = " (inconsistent initial constraints)"+showFR _ (Conflicting ds)                 = " (conflict: " ++ L.intercalate ", " (map showDep ds) ++ ")"+showFR _ CannotInstall                    = " (only already installed instances can be used)"+showFR _ CannotReinstall                  = " (avoiding to reinstall a package with same version but new dependencies)"+showFR _ Shadowed                         = " (shadowed by another installed package with same version)"+showFR _ Broken                           = " (package is broken)"+showFR _ (GlobalConstraintVersion vr src) = " (" ++ constraintSource src ++ " requires " ++ display vr ++ ")"+showFR _ (GlobalConstraintInstalled src)  = " (" ++ constraintSource src ++ " requires installed instance)"+showFR _ (GlobalConstraintSource src)     = " (" ++ constraintSource src ++ " requires source instance)"+showFR _ (GlobalConstraintFlag src)       = " (" ++ constraintSource src ++ " requires opposite flag selection)"+showFR _ ManualFlag                       = " (manual flag can only be changed explicitly)"+showFR c Backjump                         = " (backjumping, conflict set: " ++ showConflictSet c ++ ")"+showFR _ MultipleInstances                = " (multiple instances)"+showFR c (DependenciesNotLinked msg)      = " (dependencies not linked: " ++ msg ++ "; conflict set: " ++ showConflictSet c ++ ")"+showFR c CyclicDependencies               = " (cyclic dependencies; conflict set: " ++ showConflictSet c ++ ")"+-- The following are internal failures. They should not occur. In the+-- interest of not crashing unnecessarily, we still just print an error+-- message though.+showFR _ (MalformedFlagChoice qfn)        = " (INTERNAL ERROR: MALFORMED FLAG CHOICE: " ++ showQFN qfn ++ ")"+showFR _ (MalformedStanzaChoice qsn)      = " (INTERNAL ERROR: MALFORMED STANZA CHOICE: " ++ showQSN qsn ++ ")"+showFR _ EmptyGoalChoice                  = " (INTERNAL ERROR: EMPTY GOAL CHOICE)"++constraintSource :: ConstraintSource -> String+constraintSource src = "constraint from " ++ showConstraintSource src
+ Distribution/Solver/Modular/PSQ.hs view
@@ -0,0 +1,157 @@+{-# LANGUAGE DeriveFunctor, DeriveFoldable, DeriveTraversable #-}+module Distribution.Solver.Modular.PSQ+    ( PSQ(..)  -- Unit test needs constructor access+    , casePSQ+    , cons+    , length+    , lookup+    , filter+    , filterIfAny+    , filterIfAnyByKeys+    , filterKeys+    , firstOnly+    , fromList+    , isZeroOrOne+    , keys+    , map+    , mapKeys+    , mapWithKey+    , maximumBy+    , minimumBy+    , null+    , prefer+    , preferByKeys+    , snoc+    , sortBy+    , sortByKeys+    , splits+    , toList+    , union+    ) where++-- Priority search queues.+--+-- I am not yet sure what exactly is needed. But we need a data structure with+-- key-based lookup that can be sorted. We're using a sequence right now with+-- (inefficiently implemented) lookup, because I think that queue-based+-- operations and sorting turn out to be more efficiency-critical in practice.++import Control.Arrow (first, second)++import qualified Data.Foldable as F+import Data.Function+import qualified Data.List as S+import Data.Ord (comparing)+import Data.Traversable+import Prelude hiding (foldr, length, lookup, filter, null, map)++newtype PSQ k v = PSQ [(k, v)]+  deriving (Eq, Show, Functor, F.Foldable, Traversable) -- Qualified Foldable to avoid issues with FTP++keys :: PSQ k v -> [k]+keys (PSQ xs) = fmap fst xs++lookup :: Eq k => k -> PSQ k v -> Maybe v+lookup k (PSQ xs) = S.lookup k xs++map :: (v1 -> v2) -> PSQ k v1 -> PSQ k v2+map f (PSQ xs) = PSQ (fmap (second f) xs)++mapKeys :: (k1 -> k2) -> PSQ k1 v -> PSQ k2 v+mapKeys f (PSQ xs) = PSQ (fmap (first f) xs)++mapWithKey :: (k -> a -> b) -> PSQ k a -> PSQ k b+mapWithKey f (PSQ xs) = PSQ (fmap (\ (k, v) -> (k, f k v)) xs)++fromList :: [(k, a)] -> PSQ k a+fromList = PSQ++cons :: k -> a -> PSQ k a -> PSQ k a+cons k x (PSQ xs) = PSQ ((k, x) : xs)++snoc :: PSQ k a -> k -> a -> PSQ k a+snoc (PSQ xs) k x = PSQ (xs ++ [(k, x)])++casePSQ :: PSQ k a -> r -> (k -> a -> PSQ k a -> r) -> r+casePSQ (PSQ xs) n c =+  case xs of+    []          -> n+    (k, v) : ys -> c k v (PSQ ys)++splits :: PSQ k a -> PSQ k (a, PSQ k a)+splits = go id+  where+    go f xs = casePSQ xs+        (PSQ [])+        (\ k v ys -> cons k (v, f ys) (go (f . cons k v) ys))++sortBy :: (a -> a -> Ordering) -> PSQ k a -> PSQ k a+sortBy cmp (PSQ xs) = PSQ (S.sortBy (cmp `on` snd) xs)++sortByKeys :: (k -> k -> Ordering) -> PSQ k a -> PSQ k a+sortByKeys cmp (PSQ xs) = PSQ (S.sortBy (cmp `on` fst) xs)++maximumBy :: (k -> Int) -> PSQ k a -> (k, a)+maximumBy sel (PSQ xs) =+  S.minimumBy (flip (comparing (sel . fst))) xs++minimumBy :: (a -> Int) -> PSQ k a -> PSQ k a+minimumBy sel (PSQ xs) =+  PSQ [snd (S.minimumBy (comparing fst) (S.map (\ x -> (sel (snd x), x)) xs))]++-- | Sort the list so that values satisfying the predicate are first.+prefer :: (a -> Bool) -> PSQ k a -> PSQ k a+prefer p = sortBy $ flip (comparing p)++-- | Sort the list so that keys satisfying the predicate are first.+preferByKeys :: (k -> Bool) -> PSQ k a -> PSQ k a+preferByKeys p = sortByKeys $ flip (comparing p)++-- | Will partition the list according to the predicate. If+-- there is any element that satisfies the precidate, then only+-- the elements satisfying the predicate are returned.+-- Otherwise, the rest is returned.+--+filterIfAny :: (a -> Bool) -> PSQ k a -> PSQ k a+filterIfAny p (PSQ xs) =+  let+    (pro, con) = S.partition (p . snd) xs+  in+    if S.null pro then PSQ con else PSQ pro++-- | Variant of 'filterIfAny' that takes a predicate on the keys+-- rather than on the values.+--+filterIfAnyByKeys :: (k -> Bool) -> PSQ k a -> PSQ k a+filterIfAnyByKeys p (PSQ xs) =+  let+    (pro, con) = S.partition (p . fst) xs+  in+    if S.null pro then PSQ con else PSQ pro++filterKeys :: (k -> Bool) -> PSQ k a -> PSQ k a+filterKeys p (PSQ xs) = PSQ (S.filter (p . fst) xs)++filter :: (a -> Bool) -> PSQ k a -> PSQ k a+filter p (PSQ xs) = PSQ (S.filter (p . snd) xs)++length :: PSQ k a -> Int+length (PSQ xs) = S.length xs++null :: PSQ k a -> Bool+null (PSQ xs) = S.null xs++isZeroOrOne :: PSQ k a -> Bool+isZeroOrOne (PSQ [])  = True+isZeroOrOne (PSQ [_]) = True+isZeroOrOne _         = False++firstOnly :: PSQ k a -> PSQ k a+firstOnly (PSQ [])      = PSQ []+firstOnly (PSQ (x : _)) = PSQ [x]++toList :: PSQ k a -> [(k, a)]+toList (PSQ xs) = xs++union :: PSQ k a -> PSQ k a -> PSQ k a+union (PSQ xs) (PSQ ys) = PSQ (xs ++ ys)
+ Distribution/Solver/Modular/Package.hs view
@@ -0,0 +1,109 @@+{-# LANGUAGE DeriveFunctor #-}+module Distribution.Solver.Modular.Package+  ( I(..)+  , Loc(..)+  , PackageId+  , PackageIdentifier(..)+  , PackageName, mkPackageName, unPackageName+  , PkgconfigName, mkPkgconfigName, unPkgconfigName+  , PI(..)+  , PN+  , QPV+  , instI+  , makeIndependent+  , primaryPP+  , setupPP+  , showI+  , showPI+  , unPN+  ) where++import Data.List as L++import Distribution.Package -- from Cabal+import Distribution.Text (display)++import Distribution.Solver.Modular.Version+import Distribution.Solver.Types.PackagePath++-- | A package name.+type PN = PackageName++-- | Unpacking a package name.+unPN :: PN -> String+unPN = unPackageName++-- | Package version. A package name plus a version number.+type PV = PackageId++-- | Qualified package version.+type QPV = Qualified PV++-- | Package id. Currently just a black-box string.+type PId = UnitId++-- | Location. Info about whether a package is installed or not, and where+-- exactly it is located. For installed packages, uniquely identifies the+-- package instance via its 'PId'.+--+-- TODO: More information is needed about the repo.+data Loc = Inst PId | InRepo+  deriving (Eq, Ord, Show)++-- | Instance. A version number and a location.+data I = I Ver Loc+  deriving (Eq, Ord, Show)++-- | String representation of an instance.+showI :: I -> String+showI (I v InRepo)   = showVer v+showI (I v (Inst uid)) = showVer v ++ "/installed" ++ shortId uid+  where+    -- A hack to extract the beginning of the package ABI hash+    shortId = snip (splitAt 4) (++ "...")+            . snip ((\ (x, y) -> (reverse x, y)) . break (=='-') . reverse) ('-':)+            . display+    snip p f xs = case p xs of+                    (ys, zs) -> (if L.null zs then id else f) ys++-- | Package instance. A package name and an instance.+data PI qpn = PI qpn I+  deriving (Eq, Ord, Show, Functor)++-- | String representation of a package instance.+showPI :: PI QPN -> String+showPI (PI qpn i) = showQPN qpn ++ "-" ++ showI i++instI :: I -> Bool+instI (I _ (Inst _)) = True+instI _              = False++-- | Is the package in the primary group of packages.  This is used to+-- determine (1) if we should try to establish stanza preferences+-- for this goal, and (2) whether or not a user specified @--constraint@+-- should apply to this dependency (grep 'primaryPP' to see the+-- use sites).  In particular this does not include packages pulled in+-- as setup deps.+--+primaryPP :: PackagePath -> Bool+primaryPP (PackagePath _ns q) = go q+  where+    go QualToplevel    = True+    go (QualBase  _)   = True+    go (QualSetup _)   = False+    go (QualExe _ _)   = False++-- | Is the package a dependency of a setup script.  This is used to+-- establish whether or not certain constraints should apply to this+-- dependency (grep 'setupPP' to see the use sites).+--+setupPP :: PackagePath -> Bool+setupPP (PackagePath _ns (QualSetup _)) = True+setupPP (PackagePath _ns _)         = False++-- | Create artificial parents for each of the package names, making+-- them all independent.+makeIndependent :: [PN] -> [QPN]+makeIndependent ps = [ Q pp pn | (pn, i) <- zip ps [0::Int ..]+                               , let pp = PackagePath (Independent i) QualToplevel+                     ]
+ Distribution/Solver/Modular/Preference.hs view
@@ -0,0 +1,455 @@+{-# LANGUAGE ScopedTypeVariables #-}+-- | Reordering or pruning the tree in order to prefer or make certain choices.+module Distribution.Solver.Modular.Preference+    ( avoidReinstalls+    , deferSetupChoices+    , deferWeakFlagChoices+    , enforceManualFlags+    , enforcePackageConstraints+    , enforceSingleInstanceRestriction+    , firstGoal+    , preferBaseGoalChoice+    , preferLinked+    , preferPackagePreferences+    , preferReallyEasyGoalChoices+    , requireInstalled+    , sortGoals+    ) where++import Prelude ()+import Distribution.Client.Compat.Prelude++import Data.Function (on)+import qualified Data.List as L+import qualified Data.Map as M+import Control.Monad.Reader hiding (sequence)+import Data.Traversable (sequence)++import Distribution.Solver.Types.Flag+import Distribution.Solver.Types.InstalledPreference+import Distribution.Solver.Types.LabeledPackageConstraint+import Distribution.Solver.Types.OptionalStanza+import Distribution.Solver.Types.PackageConstraint+import Distribution.Solver.Types.PackagePath+import Distribution.Solver.Types.PackagePreferences+import Distribution.Solver.Types.Variable++import Distribution.Solver.Modular.Dependency+import Distribution.Solver.Modular.Flag+import Distribution.Solver.Modular.Package+import qualified Distribution.Solver.Modular.PSQ as P+import Distribution.Solver.Modular.Tree+import Distribution.Solver.Modular.Version+import qualified Distribution.Solver.Modular.ConflictSet as CS+import qualified Distribution.Solver.Modular.WeightedPSQ as W++-- | Update the weights of children under 'PChoice' nodes. 'addWeights' takes a+-- list of weight-calculating functions in order to avoid sorting the package+-- choices multiple times. Each function takes the package name, sorted list of+-- children's versions, and package option. 'addWeights' prepends the new+-- weights to the existing weights, which gives precedence to preferences that+-- are applied later.+addWeights :: [PN -> [Ver] -> POption -> Weight] -> Tree d c -> Tree d c+addWeights fs = trav go+  where+    go :: TreeF d c (Tree d c) -> TreeF d c (Tree d c)+    go (PChoiceF qpn@(Q _ pn) rdm x cs) =+      let sortedVersions = L.sortBy (flip compare) $ L.map version (W.keys cs)+          weights k = [f pn sortedVersions k | f <- fs]++          elemsToWhnf :: [a] -> ()+          elemsToWhnf = foldr seq ()+      in  PChoiceF qpn rdm x+          -- Evaluate the children's versions before evaluating any of the+          -- subtrees, so that 'sortedVersions' doesn't hold onto all of the+          -- subtrees (referenced by cs) and cause a space leak.+          (elemsToWhnf sortedVersions `seq`+             W.mapWeightsWithKey (\k w -> weights k ++ w) cs)+    go x                            = x++addWeight :: (PN -> [Ver] -> POption -> Weight) -> Tree d c -> Tree d c+addWeight f = addWeights [f]++version :: POption -> Ver+version (POption (I v _) _) = v++-- | Prefer to link packages whenever possible.+preferLinked :: Tree d c -> Tree d c+preferLinked = addWeight (const (const linked))+  where+    linked (POption _ Nothing)  = 1+    linked (POption _ (Just _)) = 0++-- Works by setting weights on choice nodes. Also applies stanza preferences.+preferPackagePreferences :: (PN -> PackagePreferences) -> Tree d c -> Tree d c+preferPackagePreferences pcs =+    preferPackageStanzaPreferences pcs .+    addWeights [+          \pn _  opt -> preferred pn opt++        -- Note that we always rank installed before uninstalled, and later+        -- versions before earlier, but we can change the priority of the+        -- two orderings.+        , \pn vs opt -> case preference pn of+                          PreferInstalled -> installed opt+                          PreferLatest    -> latest vs opt+        , \pn vs opt -> case preference pn of+                          PreferInstalled -> latest vs opt+                          PreferLatest    -> installed opt+        ]+  where+    -- Prefer packages with higher version numbers over packages with+    -- lower version numbers.+    latest :: [Ver] -> POption -> Weight+    latest sortedVersions opt =+      let l = length sortedVersions+          index = fromMaybe l $ L.findIndex (<= version opt) sortedVersions+      in  fromIntegral index / fromIntegral l++    preference :: PN -> InstalledPreference+    preference pn =+      let PackagePreferences _ ipref _ = pcs pn+      in  ipref++    -- | Prefer versions satisfying more preferred version ranges.+    preferred :: PN -> POption -> Weight+    preferred pn opt =+      let PackagePreferences vrs _ _ = pcs pn+      in fromIntegral . negate . L.length $+         L.filter (flip checkVR (version opt)) vrs++    -- Prefer installed packages over non-installed packages.+    installed :: POption -> Weight+    installed (POption (I _ (Inst _)) _) = 0+    installed _                          = 1++-- | Traversal that tries to establish package stanza enable\/disable+-- preferences. Works by reordering the branches of stanza choices.+preferPackageStanzaPreferences :: (PN -> PackagePreferences) -> Tree d c -> Tree d c+preferPackageStanzaPreferences pcs = trav go+  where+    go (SChoiceF qsn@(SN (PI (Q pp pn) _) s) rdm gr _tr ts)+      | primaryPP pp && enableStanzaPref pn s =+          -- move True case first to try enabling the stanza+          let ts' = W.mapWeightsWithKey (\k w -> weight k : w) ts+              weight k = if k then 0 else 1+          -- defer the choice by setting it to weak+          in  SChoiceF qsn rdm gr (WeakOrTrivial True) ts'+    go x = x++    enableStanzaPref :: PN -> OptionalStanza -> Bool+    enableStanzaPref pn s =+      let PackagePreferences _ _ spref = pcs pn+      in  s `elem` spref++-- | Helper function that tries to enforce a single package constraint on a+-- given instance for a P-node. Translates the constraint into a+-- tree-transformer that either leaves the subtree untouched, or replaces it+-- with an appropriate failure node.+processPackageConstraintP :: forall d c. QPN+                          -> ConflictSet+                          -> I+                          -> LabeledPackageConstraint+                          -> Tree d c+                          -> Tree d c+processPackageConstraintP qpn c i (LabeledPackageConstraint (PackageConstraint scope prop) src) r =+    if constraintScopeMatches scope qpn+    then go i prop+    else r+  where+    go :: I -> PackageProperty -> Tree d c+    go (I v _) (PackagePropertyVersion vr)+        | checkVR vr v  = r+        | otherwise     = Fail c (GlobalConstraintVersion vr src)+    go _       PackagePropertyInstalled+        | instI i       = r+        | otherwise     = Fail c (GlobalConstraintInstalled src)+    go _       PackagePropertySource+        | not (instI i) = r+        | otherwise     = Fail c (GlobalConstraintSource src)+    go _       _        = r++-- | Helper function that tries to enforce a single package constraint on a+-- given flag setting for an F-node. Translates the constraint into a+-- tree-transformer that either leaves the subtree untouched, or replaces it+-- with an appropriate failure node.+processPackageConstraintF :: forall d c. QPN+                          -> Flag+                          -> ConflictSet+                          -> Bool+                          -> LabeledPackageConstraint+                          -> Tree d c+                          -> Tree d c+processPackageConstraintF qpn f c b' (LabeledPackageConstraint (PackageConstraint scope prop) src) r =+    if constraintScopeMatches scope qpn+    then go prop+    else r+  where+    go :: PackageProperty -> Tree d c+    go (PackagePropertyFlags fa) =+        case L.lookup f fa of+          Nothing            -> r+          Just b | b == b'   -> r+                 | otherwise -> Fail c (GlobalConstraintFlag src)+    go _                             = r++-- | Helper function that tries to enforce a single package constraint on a+-- given flag setting for an F-node. Translates the constraint into a+-- tree-transformer that either leaves the subtree untouched, or replaces it+-- with an appropriate failure node.+processPackageConstraintS :: forall d c. QPN+                          -> OptionalStanza+                          -> ConflictSet+                          -> Bool+                          -> LabeledPackageConstraint+                          -> Tree d c+                          -> Tree d c+processPackageConstraintS qpn s c b' (LabeledPackageConstraint (PackageConstraint scope prop) src) r =+    if constraintScopeMatches scope qpn+    then go prop+    else r+  where+    go :: PackageProperty -> Tree d c+    go (PackagePropertyStanzas ss) =+        if not b' && s `elem` ss then Fail c (GlobalConstraintFlag src)+                                 else r+    go _                               = r++-- | Traversal that tries to establish various kinds of user constraints. Works+-- by selectively disabling choices that have been ruled out by global user+-- constraints.+enforcePackageConstraints :: M.Map PN [LabeledPackageConstraint]+                          -> Tree d c+                          -> Tree d c+enforcePackageConstraints pcs = trav go+  where+    go (PChoiceF qpn@(Q _ pn) rdm gr                    ts) =+      let c = varToConflictSet (P qpn)+          -- compose the transformation functions for each of the relevant constraint+          g = \ (POption i _) -> foldl (\ h pc -> h . processPackageConstraintP qpn c i pc)+                                       id+                                       (M.findWithDefault [] pn pcs)+      in PChoiceF qpn rdm gr        (W.mapWithKey g ts)+    go (FChoiceF qfn@(FN (PI qpn@(Q _ pn) _) f) rdm gr tr m d ts) =+      let c = varToConflictSet (F qfn)+          -- compose the transformation functions for each of the relevant constraint+          g = \ b -> foldl (\ h pc -> h . processPackageConstraintF qpn f c b pc)+                           id+                           (M.findWithDefault [] pn pcs)+      in FChoiceF qfn rdm gr tr m d (W.mapWithKey g ts)+    go (SChoiceF qsn@(SN (PI qpn@(Q _ pn) _) f) rdm gr tr   ts) =+      let c = varToConflictSet (S qsn)+          -- compose the transformation functions for each of the relevant constraint+          g = \ b -> foldl (\ h pc -> h . processPackageConstraintS qpn f c b pc)+                           id+                           (M.findWithDefault [] pn pcs)+      in SChoiceF qsn rdm gr tr     (W.mapWithKey g ts)+    go x = x++-- | Transformation that tries to enforce the rule that manual flags can only be+-- set by the user.+--+-- If there are no constraints on a manual flag, this function prunes all but+-- the default value. If there are constraints, then the flag is allowed to have+-- the values specified by the constraints. Note that the type used for flag+-- values doesn't need to be Bool.+--+-- This function makes an exception for the case where there are multiple goals+-- for a single package (with different qualifiers), and flag constraints for+-- manual flag x only apply to some of those goals. In that case, we allow the+-- unconstrained goals to use the default value for x OR any of the values in+-- the constraints on x (even though the constraints don't apply), in order to+-- allow the unconstrained goals to be linked to the constrained goals. See+-- https://github.com/haskell/cabal/issues/4299.+--+-- This function does not enforce any of the constraints, since that is done by+-- 'enforcePackageConstraints'.+enforceManualFlags :: M.Map PN [LabeledPackageConstraint] -> Tree d c -> Tree d c+enforceManualFlags pcs = trav go+  where+    go (FChoiceF qfn@(FN (PI (Q _ pn) _) fn) rdm gr tr Manual d ts) =+        FChoiceF qfn rdm gr tr Manual d $+          let -- A list of all values specified by constraints on 'fn',+              -- regardless of scope.+              flagConstraintValues :: [Bool]+              flagConstraintValues =+                  [ flagVal+                  | let lpcs = M.findWithDefault [] pn pcs+                  , (LabeledPackageConstraint (PackageConstraint _ (PackagePropertyFlags fa)) _) <- lpcs+                  , (fn', flagVal) <- fa+                  , fn' == fn ]++              -- Prune flag values that are not the default and do not match any+              -- of the constraints.+              restrictToggling :: Eq a => a -> [a] -> a -> Tree d c -> Tree d c+              restrictToggling flagDefault constraintVals flagVal r =+                  if flagVal `elem` constraintVals || flagVal == flagDefault+                  then r+                  else Fail (varToConflictSet (F qfn)) ManualFlag++      in W.mapWithKey (restrictToggling d flagConstraintValues) ts+    go x                                                            = x++-- | Require installed packages.+requireInstalled :: (PN -> Bool) -> Tree d c -> Tree d c+requireInstalled p = trav go+  where+    go (PChoiceF v@(Q _ pn) rdm gr cs)+      | p pn      = PChoiceF v rdm gr (W.mapWithKey installed cs)+      | otherwise = PChoiceF v rdm gr                         cs+      where+        installed (POption (I _ (Inst _)) _) x = x+        installed _ _ = Fail (varToConflictSet (P v)) CannotInstall+    go x          = x++-- | Avoid reinstalls.+--+-- This is a tricky strategy. If a package version is installed already and the+-- same version is available from a repo, the repo version will never be chosen.+-- This would result in a reinstall (either destructively, or potentially,+-- shadowing). The old instance won't be visible or even present anymore, but+-- other packages might have depended on it.+--+-- TODO: It would be better to actually check the reverse dependencies of installed+-- packages. If they're not depended on, then reinstalling should be fine. Even if+-- they are, perhaps this should just result in trying to reinstall those other+-- packages as well. However, doing this all neatly in one pass would require to+-- change the builder, or at least to change the goal set after building.+avoidReinstalls :: (PN -> Bool) -> Tree d c -> Tree d c+avoidReinstalls p = trav go+  where+    go (PChoiceF qpn@(Q _ pn) rdm gr cs)+      | p pn      = PChoiceF qpn rdm gr disableReinstalls+      | otherwise = PChoiceF qpn rdm gr cs+      where+        disableReinstalls =+          let installed = [ v | (_, POption (I v (Inst _)) _, _) <- W.toList cs ]+          in  W.mapWithKey (notReinstall installed) cs++        notReinstall vs (POption (I v InRepo) _) _ | v `elem` vs =+          Fail (varToConflictSet (P qpn)) CannotReinstall+        notReinstall _ _ x =+          x+    go x          = x++-- | Sort all goals using the provided function.+sortGoals :: (Variable QPN -> Variable QPN -> Ordering) -> Tree d c -> Tree d c+sortGoals variableOrder = trav go+  where+    go (GoalChoiceF rdm xs) = GoalChoiceF rdm (P.sortByKeys goalOrder xs)+    go x                    = x++    goalOrder :: Goal QPN -> Goal QPN -> Ordering+    goalOrder = variableOrder `on` (varToVariable . goalToVar)++    varToVariable :: Var QPN -> Variable QPN+    varToVariable (P qpn)                    = PackageVar qpn+    varToVariable (F (FN (PI qpn _) fn))     = FlagVar qpn fn+    varToVariable (S (SN (PI qpn _) stanza)) = StanzaVar qpn stanza++-- | Always choose the first goal in the list next, abandoning all+-- other choices.+--+-- This is unnecessary for the default search strategy, because+-- it descends only into the first goal choice anyway,+-- but may still make sense to just reduce the tree size a bit.+firstGoal :: Tree d c -> Tree d c+firstGoal = trav go+  where+    go (GoalChoiceF rdm xs) = GoalChoiceF rdm (P.firstOnly xs)+    go x                    = x+    -- Note that we keep empty choice nodes, because they mean success.++-- | Transformation that tries to make a decision on base as early as+-- possible by pruning all other goals when base is available. In nearly+-- all cases, there's a single choice for the base package. Also, fixing+-- base early should lead to better error messages.+preferBaseGoalChoice :: Tree d c -> Tree d c+preferBaseGoalChoice = trav go+  where+    go (GoalChoiceF rdm xs) = GoalChoiceF rdm (P.filterIfAnyByKeys isBase xs)+    go x                    = x++    isBase :: Goal QPN -> Bool+    isBase (Goal (P (Q _pp pn)) _) = unPN pn == "base"+    isBase _                       = False++-- | Deal with setup dependencies after regular dependencies, so that we can+-- will link setup dependencies against package dependencies when possible+deferSetupChoices :: Tree d c -> Tree d c+deferSetupChoices = trav go+  where+    go (GoalChoiceF rdm xs) = GoalChoiceF rdm (P.preferByKeys noSetup xs)+    go x                    = x++    noSetup :: Goal QPN -> Bool+    noSetup (Goal (P (Q (PackagePath _ns (QualSetup _)) _)) _) = False+    noSetup _                                                  = True++-- | Transformation that tries to avoid making weak flag choices early.+-- Weak flags are trivial flags (not influencing dependencies) or such+-- flags that are explicitly declared to be weak in the index.+deferWeakFlagChoices :: Tree d c -> Tree d c+deferWeakFlagChoices = trav go+  where+    go (GoalChoiceF rdm xs) = GoalChoiceF rdm (P.prefer noWeakFlag (P.prefer noWeakStanza xs))+    go x                    = x++    noWeakStanza :: Tree d c -> Bool+    noWeakStanza (SChoice _ _ _ (WeakOrTrivial True)   _) = False+    noWeakStanza _                                        = True++    noWeakFlag :: Tree d c -> Bool+    noWeakFlag (FChoice _ _ _ (WeakOrTrivial True) _ _ _) = False+    noWeakFlag _                                          = True++-- | Transformation that prefers goals with lower branching degrees.+--+-- When a goal choice node has at least one goal with zero or one children, this+-- function prunes all other goals. This transformation can help the solver find+-- a solution in fewer steps by allowing it to backtrack sooner when it is+-- exploring a subtree with no solutions. However, each step is more expensive.+preferReallyEasyGoalChoices :: Tree d c -> Tree d c+preferReallyEasyGoalChoices = trav go+  where+    go (GoalChoiceF rdm xs) = GoalChoiceF rdm (P.filterIfAny zeroOrOneChoices xs)+    go x                    = x++-- | Monad used internally in enforceSingleInstanceRestriction+--+-- For each package instance we record the goal for which we picked a concrete+-- instance. The SIR means that for any package instance there can only be one.+type EnforceSIR = Reader (Map (PI PN) QPN)++-- | Enforce ghc's single instance restriction+--+-- From the solver's perspective, this means that for any package instance+-- (that is, package name + package version) there can be at most one qualified+-- goal resolving to that instance (there may be other goals _linking_ to that+-- instance however).+enforceSingleInstanceRestriction :: Tree d c -> Tree d c+enforceSingleInstanceRestriction = (`runReader` M.empty) . cata go+  where+    go :: TreeF d c (EnforceSIR (Tree d c)) -> EnforceSIR (Tree d c)++    -- We just verify package choices.+    go (PChoiceF qpn rdm gr cs) =+      PChoice qpn rdm gr <$> sequence (W.mapWithKey (goP qpn) cs)+    go _otherwise =+      innM _otherwise++    -- The check proper+    goP :: QPN -> POption -> EnforceSIR (Tree d c) -> EnforceSIR (Tree d c)+    goP qpn@(Q _ pn) (POption i linkedTo) r = do+      let inst = PI pn i+      env <- ask+      case (linkedTo, M.lookup inst env) of+        (Just _, _) ->+          -- For linked nodes we don't check anything+          r+        (Nothing, Nothing) ->+          -- Not linked, not already used+          local (M.insert inst qpn) r+        (Nothing, Just qpn') -> do+          -- Not linked, already used. This is an error+          return $ Fail (CS.union (varToConflictSet (P qpn)) (varToConflictSet (P qpn'))) MultipleInstances
+ Distribution/Solver/Modular/RetryLog.hs view
@@ -0,0 +1,69 @@+{-# LANGUAGE Rank2Types #-}+module Distribution.Solver.Modular.RetryLog+    ( RetryLog+    , toProgress+    , fromProgress+    , mapFailure+    , retry+    , failWith+    , succeedWith+    , continueWith+    , tryWith+    ) where++import Distribution.Solver.Modular.Message+import Distribution.Solver.Types.Progress++-- | 'Progress' as a difference list that allows efficient appends at failures.+newtype RetryLog step fail done = RetryLog {+    unRetryLog :: forall fail2 . (fail -> Progress step fail2 done)+               -> Progress step fail2 done+  }++-- | /O(1)/. Convert a 'RetryLog' to a 'Progress'.+toProgress :: RetryLog step fail done -> Progress step fail done+toProgress (RetryLog f) = f Fail++-- | /O(N)/. Convert a 'Progress' to a 'RetryLog'.+fromProgress :: Progress step fail done -> RetryLog step fail done+fromProgress l = RetryLog $ \f -> go f l+  where+    go :: (fail1 -> Progress step fail2 done)+       -> Progress step fail1 done+       -> Progress step fail2 done+    go _ (Done d) = Done d+    go f (Fail failure) = f failure+    go f (Step m ms) = Step m (go f ms)++-- | /O(1)/. Apply a function to the failure value in a log.+mapFailure :: (fail1 -> fail2)+           -> RetryLog step fail1 done+           -> RetryLog step fail2 done+mapFailure f l = retry l $ \failure -> RetryLog $ \g -> g (f failure)++-- | /O(1)/. If the first log leads to failure, continue with the second.+retry :: RetryLog step fail1 done+      -> (fail1 -> RetryLog step fail2 done)+      -> RetryLog step fail2 done+retry (RetryLog f) g =+    RetryLog $ \extendLog -> f $ \failure -> unRetryLog (g failure) extendLog++-- | /O(1)/. Create a log with one message before a failure.+failWith :: step -> fail -> RetryLog step fail done+failWith m failure = RetryLog $ \f -> Step m (f failure)++-- | /O(1)/. Create a log with one message before a success.+succeedWith :: step -> done -> RetryLog step fail done+succeedWith m d = RetryLog $ const $ Step m (Done d)++-- | /O(1)/. Prepend a message to a log.+continueWith :: step+             -> RetryLog step fail done+             -> RetryLog step fail done+continueWith m (RetryLog f) = RetryLog $ Step m . f++-- | /O(1)/. Prepend the given message and 'Enter' to the log, and insert+-- 'Leave' before the failure if the log fails.+tryWith :: Message -> RetryLog Message fail done -> RetryLog Message fail done+tryWith m f =+  RetryLog $ Step m . Step Enter . unRetryLog (retry f (failWith Leave))
+ Distribution/Solver/Modular/Solver.hs view
@@ -0,0 +1,237 @@+{-# LANGUAGE CPP #-}+#ifdef DEBUG_TRACETREE+{-# LANGUAGE FlexibleInstances #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}+#endif+module Distribution.Solver.Modular.Solver+    ( SolverConfig(..)+    , solve+    ) where++import Data.Map as M+import Data.List as L+import Data.Set as S+import Distribution.Verbosity+import Distribution.Version++import Distribution.Compiler (CompilerInfo)++import Distribution.Solver.Types.PackagePath+import Distribution.Solver.Types.PackagePreferences+import Distribution.Solver.Types.PkgConfigDb (PkgConfigDb)+import Distribution.Solver.Types.LabeledPackageConstraint+import Distribution.Solver.Types.Settings+import Distribution.Solver.Types.Variable++import Distribution.Solver.Modular.Assignment+import Distribution.Solver.Modular.Builder+import Distribution.Solver.Modular.Cycles+import Distribution.Solver.Modular.Dependency+import Distribution.Solver.Modular.Explore+import Distribution.Solver.Modular.Index+import Distribution.Solver.Modular.Log+import Distribution.Solver.Modular.Message+import Distribution.Solver.Modular.Package+import qualified Distribution.Solver.Modular.Preference as P+import Distribution.Solver.Modular.Validate+import Distribution.Solver.Modular.Linking+import Distribution.Solver.Modular.PSQ (PSQ)+import Distribution.Solver.Modular.Tree+import qualified Distribution.Solver.Modular.PSQ as PSQ++import Distribution.Simple.Setup (BooleanFlag(..))++#ifdef DEBUG_TRACETREE+import Distribution.Solver.Modular.Flag+import qualified Distribution.Solver.Modular.ConflictSet as CS+import qualified Distribution.Solver.Modular.WeightedPSQ as W+import qualified Distribution.Text as T++import Debug.Trace.Tree (gtraceJson)+import Debug.Trace.Tree.Simple+import Debug.Trace.Tree.Generic+import Debug.Trace.Tree.Assoc (Assoc(..))+#endif++-- | Various options for the modular solver.+data SolverConfig = SolverConfig {+  reorderGoals          :: ReorderGoals,+  countConflicts        :: CountConflicts,+  independentGoals      :: IndependentGoals,+  avoidReinstalls       :: AvoidReinstalls,+  shadowPkgs            :: ShadowPkgs,+  strongFlags           :: StrongFlags,+  allowBootLibInstalls  :: AllowBootLibInstalls,+  maxBackjumps          :: Maybe Int,+  enableBackjumping     :: EnableBackjumping,+  solveExecutables      :: SolveExecutables,+  goalOrder             :: Maybe (Variable QPN -> Variable QPN -> Ordering),+  solverVerbosity       :: Verbosity+}++-- | Run all solver phases.+--+-- In principle, we have a valid tree after 'validationPhase', which+-- means that every 'Done' node should correspond to valid solution.+--+-- There is one exception, though, and that is cycle detection, which+-- has been added relatively recently. Cycles are only removed directly+-- before exploration.+--+solve :: SolverConfig                         -- ^ solver parameters+      -> CompilerInfo+      -> Index                                -- ^ all available packages as an index+      -> PkgConfigDb                          -- ^ available pkg-config pkgs+      -> (PN -> PackagePreferences)           -- ^ preferences+      -> Map PN [LabeledPackageConstraint]    -- ^ global constraints+      -> Set PN                               -- ^ global goals+      -> Log Message (Assignment, RevDepMap)+solve sc cinfo idx pkgConfigDB userPrefs userConstraints userGoals =+  explorePhase     $+  detectCycles     $+  heuristicsPhase  $+  preferencesPhase $+  validationPhase  $+  prunePhase       $+  buildPhase+  where+    explorePhase     = backjumpAndExplore (enableBackjumping sc) (countConflicts sc)+    detectCycles     = traceTree "cycles.json" id . detectCyclesPhase+    heuristicsPhase  =+      let heuristicsTree = traceTree "heuristics.json" id+      in case goalOrder sc of+           Nothing -> goalChoiceHeuristics .+                      heuristicsTree .+                      P.deferSetupChoices .+                      P.deferWeakFlagChoices .+                      P.preferBaseGoalChoice+           Just order -> P.firstGoal .+                         heuristicsTree .+                         P.sortGoals order+    preferencesPhase = P.preferLinked .+                       P.preferPackagePreferences userPrefs+    validationPhase  = traceTree "validated.json" id .+                       P.enforcePackageConstraints userConstraints .+                       P.enforceManualFlags userConstraints .+                       P.enforceSingleInstanceRestriction .+                       validateLinking idx .+                       validateTree cinfo idx pkgConfigDB+    prunePhase       = (if asBool (avoidReinstalls sc) then P.avoidReinstalls (const True) else id) .+                       (if asBool (allowBootLibInstalls sc)+                        then id+                        else P.requireInstalled (`elem` nonInstallable))+    buildPhase       = traceTree "build.json" id+                     $ buildTree idx (independentGoals sc) (S.toList userGoals)++    -- packages that can never be installed or upgraded+    -- If you change this enumeration, make sure to update the list in+    -- "Distribution.Client.Dependency" as well+    nonInstallable :: [PackageName]+    nonInstallable =+        L.map mkPackageName+             [ "base"+             , "ghc-prim"+             , "integer-gmp"+             , "integer-simple"+             , "template-haskell"+             ]++    -- When --reorder-goals is set, we use preferReallyEasyGoalChoices, which+    -- prefers (keeps) goals only if the have 0 or 1 enabled choice.+    --+    -- In the past, we furthermore used P.firstGoal to trim down the goal choice nodes+    -- to just a single option. This was a way to work around a space leak that was+    -- unnecessary and is now fixed, so we no longer do it.+    --+    -- If --count-conflicts is active, it will then choose among the remaining goals+    -- the one that has been responsible for the most conflicts so far.+    --+    -- Otherwise, we simply choose the first remaining goal.+    --+    goalChoiceHeuristics+      | asBool (reorderGoals sc) = P.preferReallyEasyGoalChoices+      | otherwise                = id {- P.firstGoal -}++-- | Dump solver tree to a file (in debugging mode)+--+-- This only does something if the @debug-tracetree@ configure argument was+-- given; otherwise this is just the identity function.+traceTree ::+#ifdef DEBUG_TRACETREE+  GSimpleTree a =>+#endif+     FilePath  -- ^ Output file+  -> (a -> a)  -- ^ Function to summarize the tree before dumping+  -> a -> a+#ifdef DEBUG_TRACETREE+traceTree = gtraceJson+#else+traceTree _ _ = id+#endif++#ifdef DEBUG_TRACETREE+instance GSimpleTree (Tree d c) where+  fromGeneric = go+    where+      go :: Tree d c -> SimpleTree+      go (PChoice qpn _ _       psq) = Node "P" $ Assoc $ L.map (uncurry (goP qpn)) $ psqToList  psq+      go (FChoice _   _ _ _ _ _ psq) = Node "F" $ Assoc $ L.map (uncurry goFS)      $ psqToList  psq+      go (SChoice _   _ _ _     psq) = Node "S" $ Assoc $ L.map (uncurry goFS)      $ psqToList  psq+      go (GoalChoice  _         psq) = Node "G" $ Assoc $ L.map (uncurry goG)       $ PSQ.toList psq+      go (Done _rdm _s)              = Node "D" $ Assoc []+      go (Fail cs _reason)           = Node "X" $ Assoc [("CS", Leaf $ goCS cs)]++      psqToList :: W.WeightedPSQ w k v -> [(k, v)]+      psqToList = L.map (\(_, k, v) -> (k, v)) . W.toList++      -- Show package choice+      goP :: QPN -> POption -> Tree d c -> (String, SimpleTree)+      goP _        (POption (I ver _loc) Nothing)  subtree = (T.display ver, go subtree)+      goP (Q _ pn) (POption _           (Just pp)) subtree = (showQPN (Q pp pn), go subtree)++      -- Show flag or stanza choice+      goFS :: Bool -> Tree d c -> (String, SimpleTree)+      goFS val subtree = (show val, go subtree)++      -- Show goal choice+      goG :: Goal QPN -> Tree d c -> (String, SimpleTree)+      goG (Goal var gr) subtree = (showVar var ++ " (" ++ shortGR gr ++ ")", go subtree)++      -- Variation on 'showGR' that produces shorter strings+      -- (Actually, QGoalReason records more info than necessary: we only need+      -- to know the variable that introduced the goal, not the value assigned+      -- to that variable)+      shortGR :: QGoalReason -> String+      shortGR UserGoal               = "user"+      shortGR (PDependency (PI nm _)) = showQPN nm+      shortGR (FDependency nm _)      = showQFN nm+      shortGR (SDependency nm)        = showQSN nm++      -- Show conflict set+      goCS :: ConflictSet -> String+      goCS cs = "{" ++ (intercalate "," . L.map showVar . CS.toList $ cs) ++ "}"+#endif++-- | Replace all goal reasons with a dummy goal reason in the tree+--+-- This is useful for debugging (when experimenting with the impact of GRs)+_removeGR :: Tree d c -> Tree d QGoalReason+_removeGR = trav go+  where+   go :: TreeF d c (Tree d QGoalReason) -> TreeF d QGoalReason (Tree d QGoalReason)+   go (PChoiceF qpn rdm _       psq) = PChoiceF qpn rdm dummy       psq+   go (FChoiceF qfn rdm _ a b d psq) = FChoiceF qfn rdm dummy a b d psq+   go (SChoiceF qsn rdm _ a     psq) = SChoiceF qsn rdm dummy a     psq+   go (GoalChoiceF  rdm         psq) = GoalChoiceF  rdm             (goG psq)+   go (DoneF rdm s)                  = DoneF rdm s+   go (FailF cs reason)              = FailF cs reason++   goG :: PSQ (Goal QPN) (Tree d QGoalReason) -> PSQ (Goal QPN) (Tree d QGoalReason)+   goG = PSQ.fromList+       . L.map (\(Goal var _, subtree) -> (Goal var dummy, subtree))+       . PSQ.toList++   dummy :: QGoalReason+   dummy = PDependency+         $ PI (Q (PackagePath DefaultNamespace QualToplevel) (mkPackageName "$"))+              (I (mkVersion [1]) InRepo)
+ Distribution/Solver/Modular/Tree.hs view
@@ -0,0 +1,179 @@+{-# LANGUAGE DeriveFunctor, DeriveFoldable, DeriveTraversable #-}+module Distribution.Solver.Modular.Tree+    ( FailReason(..)+    , POption(..)+    , Tree(..)+    , TreeF(..)+    , Weight+    , ana+    , cata+    , inn+    , innM+    , para+    , trav+    , zeroOrOneChoices+    ) where++import Control.Monad hiding (mapM, sequence)+import Data.Foldable+import Data.Traversable+import Prelude hiding (foldr, mapM, sequence)++import Distribution.Solver.Modular.Dependency+import Distribution.Solver.Modular.Flag+import Distribution.Solver.Modular.Package+import Distribution.Solver.Modular.PSQ (PSQ)+import Distribution.Solver.Modular.Version+import Distribution.Solver.Modular.WeightedPSQ (WeightedPSQ)+import qualified Distribution.Solver.Modular.WeightedPSQ as W+import Distribution.Solver.Types.ConstraintSource+import Distribution.Solver.Types.Flag+import Distribution.Solver.Types.PackagePath++type Weight = Double++-- | Type of the search tree. Inlining the choice nodes for now. Weights on+-- package, flag, and stanza choices control the traversal order.+--+-- The tree can hold additional data on 'Done' nodes (type 'd') and choice nodes+-- (type 'c'). For example, during the final traversal, choice nodes contain the+-- variables that introduced the choices, and 'Done' nodes contain the+-- assignments for all variables.+--+-- TODO: The weight type should be changed from [Double] to Double to avoid+-- giving too much weight to preferences that are applied later.+data Tree d c =+    -- | Choose a version for a package (or choose to link)+    PChoice QPN RevDepMap c (WeightedPSQ [Weight] POption (Tree d c))++    -- | Choose a value for a flag+    --+    -- The Bool is the default value.+  | FChoice QFN RevDepMap c WeakOrTrivial FlagType Bool (WeightedPSQ [Weight] Bool (Tree d c))++    -- | Choose whether or not to enable a stanza+  | SChoice QSN RevDepMap c WeakOrTrivial (WeightedPSQ [Weight] Bool (Tree d c))++    -- | Choose which choice to make next+    --+    -- Invariants:+    --+    -- * PSQ should never be empty+    -- * For each choice we additionally record the 'QGoalReason' why we are+    --   introducing that goal into tree. Note that most of the time we are+    --   working with @Tree QGoalReason@; in that case, we must have the+    --   invariant that the 'QGoalReason' cached in the 'PChoice', 'FChoice'+    --   or 'SChoice' directly below a 'GoalChoice' node must equal the reason+    --   recorded on that 'GoalChoice' node.+  | GoalChoice RevDepMap (PSQ (Goal QPN) (Tree d c))++    -- | We're done -- we found a solution!+  | Done RevDepMap d++    -- | We failed to find a solution in this path through the tree+  | Fail ConflictSet FailReason+  deriving (Eq, Show)++-- | A package option is a package instance with an optional linking annotation+--+-- The modular solver has a number of package goals to solve for, and can only+-- pick a single package version for a single goal. In order to allow to+-- install multiple versions of the same package as part of a single solution+-- the solver uses qualified goals. For example, @0.P@ and @1.P@ might both+-- be qualified goals for @P@, allowing to pick a difference version of package+-- @P@ for @0.P@ and @1.P@.+--+-- Linking is an essential part of this story. In addition to picking a specific+-- version for @1.P@, the solver can also decide to link @1.P@ to @0.P@ (or+-- vice versa). It means that @1.P@ and @0.P@ really must be the very same package+-- (and hence must have the same build time configuration, and their+-- dependencies must also be the exact same).+--+-- See <http://www.well-typed.com/blog/2015/03/qualified-goals/> for details.+data POption = POption I (Maybe PackagePath)+  deriving (Eq, Show)++data FailReason = InconsistentInitialConstraints+                | Conflicting [Dep QPN]+                | CannotInstall+                | CannotReinstall+                | Shadowed+                | Broken+                | GlobalConstraintVersion VR ConstraintSource+                | GlobalConstraintInstalled ConstraintSource+                | GlobalConstraintSource ConstraintSource+                | GlobalConstraintFlag ConstraintSource+                | ManualFlag+                | MalformedFlagChoice QFN+                | MalformedStanzaChoice QSN+                | EmptyGoalChoice+                | Backjump+                | MultipleInstances+                | DependenciesNotLinked String+                | CyclicDependencies+  deriving (Eq, Show)++-- | Functor for the tree type. 'a' is the type of nodes' children. 'd' and 'c'+-- have the same meaning as in 'Tree'.+data TreeF d c a =+    PChoiceF    QPN RevDepMap c                             (WeightedPSQ [Weight] POption a)+  | FChoiceF    QFN RevDepMap c WeakOrTrivial FlagType Bool (WeightedPSQ [Weight] Bool    a)+  | SChoiceF    QSN RevDepMap c WeakOrTrivial               (WeightedPSQ [Weight] Bool    a)+  | GoalChoiceF     RevDepMap                               (PSQ (Goal QPN) a)+  | DoneF           RevDepMap d+  | FailF       ConflictSet FailReason+  deriving (Functor, Foldable, Traversable)++out :: Tree d c -> TreeF d c (Tree d c)+out (PChoice    p s i       ts) = PChoiceF    p s i       ts+out (FChoice    p s i b m d ts) = FChoiceF    p s i b m d ts+out (SChoice    p s i b     ts) = SChoiceF    p s i b     ts+out (GoalChoice   s         ts) = GoalChoiceF   s         ts+out (Done       x s           ) = DoneF       x s+out (Fail       c x           ) = FailF       c x++inn :: TreeF d c (Tree d c) -> Tree d c+inn (PChoiceF    p s i       ts) = PChoice    p s i       ts+inn (FChoiceF    p s i b m d ts) = FChoice    p s i b m d ts+inn (SChoiceF    p s i b     ts) = SChoice    p s i b     ts+inn (GoalChoiceF   s         ts) = GoalChoice   s         ts+inn (DoneF       x s           ) = Done       x s+inn (FailF       c x           ) = Fail       c x++innM :: Monad m => TreeF d c (m (Tree d c)) -> m (Tree d c)+innM (PChoiceF    p s i       ts) = liftM (PChoice    p s i      ) (sequence ts)+innM (FChoiceF    p s i b m d ts) = liftM (FChoice    p s i b m d) (sequence ts)+innM (SChoiceF    p s i b     ts) = liftM (SChoice    p s i b    ) (sequence ts)+innM (GoalChoiceF   s         ts) = liftM (GoalChoice   s        ) (sequence ts)+innM (DoneF       x s           ) = return $ Done     x s+innM (FailF       c x           ) = return $ Fail     c x++-- | Determines whether a tree is active, i.e., isn't a failure node.+active :: Tree d c -> Bool+active (Fail _ _) = False+active _          = True++-- | Approximates the number of active choices that are available in a node.+-- Note that we count goal choices as having one choice, always.+zeroOrOneChoices :: Tree d c -> Bool+zeroOrOneChoices (PChoice    _ _ _       ts) = W.isZeroOrOne (W.filter active ts)+zeroOrOneChoices (FChoice    _ _ _ _ _ _ ts) = W.isZeroOrOne (W.filter active ts)+zeroOrOneChoices (SChoice    _ _ _ _     ts) = W.isZeroOrOne (W.filter active ts)+zeroOrOneChoices (GoalChoice _           _ ) = True+zeroOrOneChoices (Done       _ _           ) = True+zeroOrOneChoices (Fail       _ _           ) = True++-- | Catamorphism on trees.+cata :: (TreeF d c a -> a) -> Tree d c -> a+cata phi x = (phi . fmap (cata phi) . out) x++trav :: (TreeF d c (Tree d a) -> TreeF d a (Tree d a)) -> Tree d c -> Tree d a+trav psi x = cata (inn . psi) x++-- | Paramorphism on trees.+para :: (TreeF d c (a, Tree d c) -> a) -> Tree d c -> a+para phi = phi . fmap (\ x -> (para phi x, x)) . out++-- | Anamorphism on trees.+ana :: (a -> TreeF d c a) -> a -> Tree d c+ana psi = inn . fmap (ana psi) . psi
+ Distribution/Solver/Modular/Validate.hs view
@@ -0,0 +1,278 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE MultiParamTypeClasses #-}+module Distribution.Solver.Modular.Validate (validateTree) where++-- Validation of the tree.+--+-- The task here is to make sure all constraints hold. After validation, any+-- assignment returned by exploration of the tree should be a complete valid+-- assignment, i.e., actually constitute a solution.++import Control.Applicative+import Control.Monad.Reader hiding (sequence)+import Data.List as L+import Data.Map as M+import Data.Set as S+import Data.Traversable+import Prelude hiding (sequence)++import Language.Haskell.Extension (Extension, Language)++import Distribution.Compiler (CompilerInfo(..))++import Distribution.Solver.Modular.Assignment+import Distribution.Solver.Modular.Dependency+import Distribution.Solver.Modular.Flag+import Distribution.Solver.Modular.Index+import Distribution.Solver.Modular.Package+import Distribution.Solver.Modular.Tree+import Distribution.Solver.Modular.Version (VR)+import qualified Distribution.Solver.Modular.WeightedPSQ as W++import Distribution.Solver.Types.ComponentDeps (Component)++import Distribution.Solver.Types.PackagePath+import Distribution.Solver.Types.PkgConfigDb (PkgConfigDb, pkgConfigPkgIsPresent)++-- In practice, most constraints are implication constraints (IF we have made+-- a number of choices, THEN we also have to ensure that). We call constraints+-- that for which the preconditions are fulfilled ACTIVE. We maintain a set+-- of currently active constraints that we pass down the node.+--+-- We aim at detecting inconsistent states as early as possible.+--+-- Whenever we make a choice, there are two things that need to happen:+--+--   (1) We must check that the choice is consistent with the currently+--       active constraints.+--+--   (2) The choice increases the set of active constraints. For the new+--       active constraints, we must check that they are consistent with+--       the current state.+--+-- We can actually merge (1) and (2) by saying the the current choice is+-- a new active constraint, fixing the choice.+--+-- If a test fails, we have detected an inconsistent state. We can+-- disable the current subtree and do not have to traverse it any further.+--+-- We need a good way to represent the current state, i.e., the current+-- set of active constraints. Since the main situation where we have to+-- search in it is (1), it seems best to store the state by package: for+-- every package, we store which versions are still allowed. If for any+-- package, we have inconsistent active constraints, we can also stop.+-- This is a particular way to read task (2):+--+--   (2, weak) We only check if the new constraints are consistent with+--       the choices we've already made, and add them to the active set.+--+--   (2, strong) We check if the new constraints are consistent with the+--       choices we've already made, and the constraints we already have.+--+-- It currently seems as if we're implementing the weak variant. However,+-- when used together with 'preferEasyGoalChoices', we will find an+-- inconsistent state in the very next step.+--+-- What do we do about flags?+--+-- Like for packages, we store the flag choices we have already made.+-- Now, regarding (1), we only have to test whether we've decided the+-- current flag before. Regarding (2), the interesting bit is in discovering+-- the new active constraints. To this end, we look up the constraints for+-- the package the flag belongs to, and traverse its flagged dependencies.+-- Wherever we find the flag in question, we start recording dependencies+-- underneath as new active dependencies. If we encounter other flags, we+-- check if we've chosen them already and either proceed or stop.++-- | The state needed during validation.+data ValidateState = VS {+  supportedExt  :: Extension -> Bool,+  supportedLang :: Language  -> Bool,+  presentPkgs   :: PkgconfigName -> VR  -> Bool,+  index :: Index,+  saved :: Map QPN (FlaggedDeps Component QPN), -- saved, scoped, dependencies+  pa    :: PreAssignment,+  qualifyOptions :: QualifyOptions+}++newtype Validate a = Validate (Reader ValidateState a)+  deriving (Functor, Applicative, Monad, MonadReader ValidateState)++runValidate :: Validate a -> ValidateState -> a+runValidate (Validate r) = runReader r++validate :: Tree d c -> Validate (Tree d c)+validate = cata go+  where+    go :: TreeF d c (Validate (Tree d c)) -> Validate (Tree d c)++    go (PChoiceF qpn rdm gr       ts) = PChoice qpn rdm gr <$> sequence (W.mapWithKey (goP qpn) ts)+    go (FChoiceF qfn rdm gr b m d ts) =+      do+        -- Flag choices may occur repeatedly (because they can introduce new constraints+        -- in various places). However, subsequent choices must be consistent. We thereby+        -- collapse repeated flag choice nodes.+        PA _ pfa _ <- asks pa -- obtain current flag-preassignment+        case M.lookup qfn pfa of+          Just rb -> -- flag has already been assigned; collapse choice to the correct branch+                     case W.lookup rb ts of+                       Just t  -> goF qfn rb t+                       Nothing -> return $ Fail (varToConflictSet (F qfn)) (MalformedFlagChoice qfn)+          Nothing -> -- flag choice is new, follow both branches+                     FChoice qfn rdm gr b m d <$> sequence (W.mapWithKey (goF qfn) ts)+    go (SChoiceF qsn rdm gr b   ts) =+      do+        -- Optional stanza choices are very similar to flag choices.+        PA _ _ psa <- asks pa -- obtain current stanza-preassignment+        case M.lookup qsn psa of+          Just rb -> -- stanza choice has already been made; collapse choice to the correct branch+                     case W.lookup rb ts of+                       Just t  -> goS qsn rb t+                       Nothing -> return $ Fail (varToConflictSet (S qsn)) (MalformedStanzaChoice qsn)+          Nothing -> -- stanza choice is new, follow both branches+                     SChoice qsn rdm gr b <$> sequence (W.mapWithKey (goS qsn) ts)++    -- We don't need to do anything for goal choices or failure nodes.+    go (GoalChoiceF rdm           ts) = GoalChoice rdm <$> sequence ts+    go (DoneF       rdm s           ) = pure (Done rdm s)+    go (FailF    c fr               ) = pure (Fail c fr)++    -- What to do for package nodes ...+    goP :: QPN -> POption -> Validate (Tree d c) -> Validate (Tree d c)+    goP qpn@(Q _pp pn) (POption i _) r = do+      PA ppa pfa psa <- asks pa    -- obtain current preassignment+      extSupported   <- asks supportedExt  -- obtain the supported extensions+      langSupported  <- asks supportedLang -- obtain the supported languages+      pkgPresent     <- asks presentPkgs -- obtain the present pkg-config pkgs+      idx            <- asks index -- obtain the index+      svd            <- asks saved -- obtain saved dependencies+      qo             <- asks qualifyOptions+      -- obtain dependencies and index-dictated exclusions introduced by the choice+      let (PInfo deps _ mfr) = idx ! pn ! i+      -- qualify the deps in the current scope+      let qdeps = qualifyDeps qo qpn deps+      -- the new active constraints are given by the instance we have chosen,+      -- plus the dependency information we have for that instance+      -- TODO: is the False here right?+      let newactives = Dep False {- not exe -} qpn (Fixed i (P qpn)) : L.map (resetVar (P qpn)) (extractDeps pfa psa qdeps)+      -- We now try to extend the partial assignment with the new active constraints.+      let mnppa = extend extSupported langSupported pkgPresent (P qpn) ppa newactives+      -- In case we continue, we save the scoped dependencies+      let nsvd = M.insert qpn qdeps svd+      case mfr of+        Just fr -> -- The index marks this as an invalid choice. We can stop.+                   return (Fail (varToConflictSet (P qpn)) fr)+        _       -> case mnppa of+                     Left (c, d) -> -- We have an inconsistency. We can stop.+                                    return (Fail c (Conflicting d))+                     Right nppa  -> -- We have an updated partial assignment for the recursive validation.+                                    local (\ s -> s { pa = PA nppa pfa psa, saved = nsvd }) r++    -- What to do for flag nodes ...+    goF :: QFN -> Bool -> Validate (Tree d c) -> Validate (Tree d c)+    goF qfn@(FN (PI qpn _i) _f) b r = do+      PA ppa pfa psa <- asks pa -- obtain current preassignment+      extSupported   <- asks supportedExt  -- obtain the supported extensions+      langSupported  <- asks supportedLang -- obtain the supported languages+      pkgPresent     <- asks presentPkgs   -- obtain the present pkg-config pkgs+      svd <- asks saved         -- obtain saved dependencies+      -- Note that there should be saved dependencies for the package in question,+      -- because while building, we do not choose flags before we see the packages+      -- that define them.+      let qdeps = svd ! qpn+      -- We take the *saved* dependencies, because these have been qualified in the+      -- correct scope.+      --+      -- Extend the flag assignment+      let npfa = M.insert qfn b pfa+      -- We now try to get the new active dependencies we might learn about because+      -- we have chosen a new flag.+      let newactives = extractNewDeps (F qfn) b npfa psa qdeps+      -- As in the package case, we try to extend the partial assignment.+      case extend extSupported langSupported pkgPresent (F qfn) ppa newactives of+        Left (c, d) -> return (Fail c (Conflicting d)) -- inconsistency found+        Right nppa  -> local (\ s -> s { pa = PA nppa npfa psa }) r++    -- What to do for stanza nodes (similar to flag nodes) ...+    goS :: QSN -> Bool -> Validate (Tree d c) -> Validate (Tree d c)+    goS qsn@(SN (PI qpn _i) _f) b r = do+      PA ppa pfa psa <- asks pa -- obtain current preassignment+      extSupported   <- asks supportedExt  -- obtain the supported extensions+      langSupported  <- asks supportedLang -- obtain the supported languages+      pkgPresent     <- asks presentPkgs -- obtain the present pkg-config pkgs+      svd <- asks saved         -- obtain saved dependencies+      -- Note that there should be saved dependencies for the package in question,+      -- because while building, we do not choose flags before we see the packages+      -- that define them.+      let qdeps = svd ! qpn+      -- We take the *saved* dependencies, because these have been qualified in the+      -- correct scope.+      --+      -- Extend the flag assignment+      let npsa = M.insert qsn b psa+      -- We now try to get the new active dependencies we might learn about because+      -- we have chosen a new flag.+      let newactives = extractNewDeps (S qsn) b pfa npsa qdeps+      -- As in the package case, we try to extend the partial assignment.+      case extend extSupported langSupported pkgPresent (S qsn) ppa newactives of+        Left (c, d) -> return (Fail c (Conflicting d)) -- inconsistency found+        Right nppa  -> local (\ s -> s { pa = PA nppa pfa npsa }) r++-- | We try to extract as many concrete dependencies from the given flagged+-- dependencies as possible. We make use of all the flag knowledge we have+-- already acquired.+extractDeps :: FAssignment -> SAssignment -> FlaggedDeps comp QPN -> [Dep QPN]+extractDeps fa sa deps = do+  d <- deps+  case d of+    Simple sd _         -> return sd+    Flagged qfn _ td fd -> case M.lookup qfn fa of+                             Nothing    -> mzero+                             Just True  -> extractDeps fa sa td+                             Just False -> extractDeps fa sa fd+    Stanza qsn td       -> case M.lookup qsn sa of+                             Nothing    -> mzero+                             Just True  -> extractDeps fa sa td+                             Just False -> []++-- | We try to find new dependencies that become available due to the given+-- flag or stanza choice. We therefore look for the choice in question, and then call+-- 'extractDeps' for everything underneath.+extractNewDeps :: Var QPN -> Bool -> FAssignment -> SAssignment -> FlaggedDeps comp QPN -> [Dep QPN]+extractNewDeps v b fa sa = go+  where+    go :: FlaggedDeps comp QPN -> [Dep QPN] -- Type annotation necessary (polymorphic recursion)+    go deps = do+      d <- deps+      case d of+        Simple _ _           -> mzero+        Flagged qfn' _ td fd+          | v == F qfn'      -> L.map (resetVar v) $+                                if b then extractDeps fa sa td else extractDeps fa sa fd+          | otherwise        -> case M.lookup qfn' fa of+                                  Nothing    -> mzero+                                  Just True  -> go td+                                  Just False -> go fd+        Stanza qsn' td+          | v == S qsn'      -> L.map (resetVar v) $+                                if b then extractDeps fa sa td else []+          | otherwise        -> case M.lookup qsn' sa of+                                  Nothing    -> mzero+                                  Just True  -> go td+                                  Just False -> []++-- | Interface.+validateTree :: CompilerInfo -> Index -> PkgConfigDb -> Tree d c -> Tree d c+validateTree cinfo idx pkgConfigDb t = runValidate (validate t) VS {+    supportedExt   = maybe (const True) -- if compiler has no list of extensions, we assume everything is supported+                           (\ es -> let s = S.fromList es in \ x -> S.member x s)+                           (compilerInfoExtensions cinfo)+  , supportedLang  = maybe (const True)+                           (flip L.elem) -- use list lookup because language list is small and no Ord instance+                           (compilerInfoLanguages  cinfo)+  , presentPkgs    = pkgConfigPkgIsPresent pkgConfigDb+  , index          = idx+  , saved          = M.empty+  , pa             = PA M.empty M.empty M.empty+  , qualifyOptions = defaultQualifyOptions idx+  }
+ Distribution/Solver/Modular/Var.hs view
@@ -0,0 +1,46 @@+{-# LANGUAGE DeriveFunctor #-}+module Distribution.Solver.Modular.Var (+    Var(..)+  , simplifyVar+  , showVar+  , varPI+  ) where++import Prelude hiding (pi)++import Distribution.Solver.Modular.Flag+import Distribution.Solver.Modular.Package+import Distribution.Solver.Types.PackagePath++{-------------------------------------------------------------------------------+  Variables+-------------------------------------------------------------------------------}++-- | The type of variables that play a role in the solver.+-- Note that the tree currently does not use this type directly,+-- and rather has separate tree nodes for the different types of+-- variables. This fits better with the fact that in most cases,+-- these have to be treated differently.+data Var qpn = P qpn | F (FN qpn) | S (SN qpn)+  deriving (Eq, Ord, Show, Functor)++-- | For computing conflict sets, we map flag choice vars to a+-- single flag choice. This means that all flag choices are treated+-- as interdependent. So if one flag of a package ends up in a+-- conflict set, then all flags are being treated as being part of+-- the conflict set.+simplifyVar :: Var qpn -> Var qpn+simplifyVar (P qpn)       = P qpn+simplifyVar (F (FN pi _)) = F (FN pi (mkFlag "flag"))+simplifyVar (S qsn)       = S qsn++showVar :: Var QPN -> String+showVar (P qpn) = showQPN qpn+showVar (F qfn) = showQFN qfn+showVar (S qsn) = showQSN qsn++-- | Extract the package instance from a Var+varPI :: Var QPN -> (QPN, Maybe I)+varPI (P qpn)               = (qpn, Nothing)+varPI (F (FN (PI qpn i) _)) = (qpn, Just i)+varPI (S (SN (PI qpn i) _)) = (qpn, Just i)
+ Distribution/Solver/Modular/Version.hs view
@@ -0,0 +1,53 @@+module Distribution.Solver.Modular.Version+    ( Ver+    , VR+    , anyVR+    , checkVR+    , eqVR+    , showVer+    , showVR+    , simplifyVR+    , (.&&.)+    , (.||.)+    ) where++import qualified Distribution.Version as CV -- from Cabal+import Distribution.Text -- from Cabal++-- | Preliminary type for versions.+type Ver = CV.Version++-- | String representation of a version.+showVer :: Ver -> String+showVer = display++-- | Version range. Consists of a lower and upper bound.+type VR = CV.VersionRange++-- | String representation of a version range.+showVR :: VR -> String+showVR = display++-- | Unconstrained version range.+anyVR :: VR+anyVR = CV.anyVersion++-- | Version range fixing a single version.+eqVR :: Ver -> VR+eqVR = CV.thisVersion++-- | Intersect two version ranges.+(.&&.) :: VR -> VR -> VR+(.&&.) = CV.intersectVersionRanges++-- | Union of two version ranges.+(.||.) :: VR -> VR -> VR+(.||.) = CV.unionVersionRanges++-- | Simplify a version range.+simplifyVR :: VR -> VR+simplifyVR = CV.simplifyVersionRange++-- | Checking a version against a version range.+checkVR :: VR -> Ver -> Bool+checkVR = flip CV.withinRange
+ Distribution/Solver/Modular/WeightedPSQ.hs view
@@ -0,0 +1,86 @@+{-# LANGUAGE DeriveFunctor, DeriveFoldable, DeriveTraversable #-}+module Distribution.Solver.Modular.WeightedPSQ (+    WeightedPSQ+  , fromList+  , toList+  , keys+  , weights+  , isZeroOrOne+  , filter+  , lookup+  , mapWithKey+  , mapWeightsWithKey+  , union+  ) where++import qualified Data.Foldable as F+import qualified Data.List as L+import Data.Ord (comparing)+import qualified Data.Traversable as T+import Prelude hiding (filter, lookup)++-- | An association list that is sorted by weight.+--+-- Each element has a key ('k'), value ('v'), and weight ('w'). All operations+-- that add elements or modify weights stably sort the elements by weight.+newtype WeightedPSQ w k v = WeightedPSQ [(w, k, v)]+  deriving (Eq, Show, Functor, F.Foldable, T.Traversable)++-- | /O(N)/.+filter :: (v -> Bool) -> WeightedPSQ k w v -> WeightedPSQ k w v+filter p (WeightedPSQ xs) = WeightedPSQ (L.filter (p . triple_3) xs)++-- | /O(1)/. Return @True@ if the @WeightedPSQ@ contains zero or one elements.+isZeroOrOne :: WeightedPSQ w k v -> Bool+isZeroOrOne (WeightedPSQ [])  = True+isZeroOrOne (WeightedPSQ [_]) = True+isZeroOrOne _                 = False++-- | /O(1)/. Return the elements in order.+toList :: WeightedPSQ w k v -> [(w, k, v)]+toList (WeightedPSQ xs) = xs++-- | /O(N log N)/.+fromList :: Ord w => [(w, k, v)] -> WeightedPSQ w k v+fromList = WeightedPSQ . L.sortBy (comparing triple_1)++-- | /O(N)/. Return the weights in order.+weights :: WeightedPSQ w k v -> [w]+weights (WeightedPSQ xs) = L.map triple_1 xs++-- | /O(N)/. Return the keys in order.+keys :: WeightedPSQ w k v -> [k]+keys (WeightedPSQ xs) = L.map triple_2 xs++-- | /O(N)/. Return the value associated with the first occurrence of the give+-- key, if it exists.+lookup :: Eq k => k -> WeightedPSQ w k v -> Maybe v+lookup k (WeightedPSQ xs) = triple_3 `fmap` L.find ((k ==) . triple_2) xs++-- | /O(N log N)/. Update the weights.+mapWeightsWithKey :: Ord w2+                  => (k -> w1 -> w2)+                  -> WeightedPSQ w1 k v+                  -> WeightedPSQ w2 k v+mapWeightsWithKey f (WeightedPSQ xs) = fromList $+                                       L.map (\ (w, k, v) -> (f k w, k, v)) xs++-- | /O(N)/. Update the values.+mapWithKey :: (k -> v1 -> v2) -> WeightedPSQ w k v1 -> WeightedPSQ w k v2+mapWithKey f (WeightedPSQ xs) = WeightedPSQ $+                                L.map (\ (w, k, v) -> (w, k, f k v)) xs++-- | /O((N + M) log (N + M))/. Combine two @WeightedPSQ@s, preserving all+-- elements. Elements from the first @WeightedPSQ@ come before elements in the+-- second when they have the same weight.+union :: Ord w => WeightedPSQ w k v -> WeightedPSQ w k v -> WeightedPSQ w k v+union (WeightedPSQ xs) (WeightedPSQ ys) = fromList (xs ++ ys)++triple_1 :: (x, y, z) -> x+triple_1 (x, _, _) = x++triple_2 :: (x, y, z) -> y+triple_2 (_, y, _) = y++triple_3 :: (x, y, z) -> z+triple_3 (_, _, z) = z
+ Distribution/Solver/Types/ComponentDeps.hs view
@@ -0,0 +1,194 @@+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE DeriveGeneric #-}++-- | Fine-grained package dependencies+--+-- Like many others, this module is meant to be "double-imported":+--+-- > import Distribution.Solver.Types.ComponentDeps (+-- >     Component+-- >   , ComponentDep+-- >   , ComponentDeps+-- >   )+-- > import qualified Distribution.Solver.Types.ComponentDeps as CD+module Distribution.Solver.Types.ComponentDeps (+    -- * Fine-grained package dependencies+    Component(..)+  , componentNameToComponent+  , ComponentDep+  , ComponentDeps -- opaque+    -- ** Constructing ComponentDeps+  , empty+  , fromList+  , singleton+  , insert+  , zip+  , filterDeps+  , fromLibraryDeps+  , fromSetupDeps+  , fromInstalled+    -- ** Deconstructing ComponentDeps+  , toList+  , flatDeps+  , nonSetupDeps+  , libraryDeps+  , setupDeps+  , select+  ) where++import Prelude ()+import Distribution.Types.UnqualComponentName+import Distribution.Client.Compat.Prelude hiding (empty,zip)++import qualified Data.Map as Map+import Data.Foldable (fold)++import qualified Distribution.Types.ComponentName as CN++{-------------------------------------------------------------------------------+  Types+-------------------------------------------------------------------------------}++-- | Component of a package.+data Component =+    ComponentLib+  | ComponentSubLib UnqualComponentName+  | ComponentFLib   UnqualComponentName+  | ComponentExe    UnqualComponentName+  | ComponentTest   UnqualComponentName+  | ComponentBench  UnqualComponentName+  | ComponentSetup+  deriving (Show, Eq, Ord, Generic)++instance Binary Component++-- | Dependency for a single component.+type ComponentDep a = (Component, a)++-- | Fine-grained dependencies for a package.+--+-- Typically used as @ComponentDeps [Dependency]@, to represent the list of+-- dependencies for each named component within a package.+--+newtype ComponentDeps a = ComponentDeps { unComponentDeps :: Map Component a }+  deriving (Show, Functor, Eq, Ord, Generic)++instance Semigroup a => Monoid (ComponentDeps a) where+  mempty = ComponentDeps Map.empty+  mappend = (<>)++instance Semigroup a => Semigroup (ComponentDeps a) where+  ComponentDeps d <> ComponentDeps d' =+      ComponentDeps (Map.unionWith (<>) d d')++instance Foldable ComponentDeps where+  foldMap f = foldMap f . unComponentDeps++instance Traversable ComponentDeps where+  traverse f = fmap ComponentDeps . traverse f . unComponentDeps++instance Binary a => Binary (ComponentDeps a)++componentNameToComponent :: CN.ComponentName -> Component+componentNameToComponent (CN.CLibName)      = ComponentLib+componentNameToComponent (CN.CSubLibName s) = ComponentSubLib s+componentNameToComponent (CN.CFLibName s)   = ComponentFLib s+componentNameToComponent (CN.CExeName s)    = ComponentExe s+componentNameToComponent (CN.CTestName s)   = ComponentTest s+componentNameToComponent (CN.CBenchName s)  = ComponentBench s++{-------------------------------------------------------------------------------+  Construction+-------------------------------------------------------------------------------}++empty :: ComponentDeps a+empty = ComponentDeps $ Map.empty++fromList :: Monoid a => [ComponentDep a] -> ComponentDeps a+fromList = ComponentDeps . Map.fromListWith mappend++singleton :: Component -> a -> ComponentDeps a+singleton comp = ComponentDeps . Map.singleton comp++insert :: Monoid a => Component -> a -> ComponentDeps a -> ComponentDeps a+insert comp a = ComponentDeps . Map.alter aux comp . unComponentDeps+  where+    aux Nothing   = Just a+    aux (Just a') = Just $ a `mappend` a'++-- | Zip two 'ComponentDeps' together by 'Component', using 'mempty'+-- as the neutral element when a 'Component' is present only in one.+zip :: (Monoid a, Monoid b) => ComponentDeps a -> ComponentDeps b -> ComponentDeps (a, b)+{- TODO/FIXME: Once we can expect containers>=0.5, switch to the more efficient version below:++zip (ComponentDeps d1) (ComponentDeps d2) =+    ComponentDeps $+      Map.mergeWithKey+        (\_ a b -> Just (a,b))+        (fmap (\a -> (a, mempty)))+        (fmap (\b -> (mempty, b)))+        d1 d2++-}+zip (ComponentDeps d1) (ComponentDeps d2) =+    ComponentDeps $+      Map.unionWith+        mappend+        (Map.map (\a -> (a, mempty)) d1)+        (Map.map (\b -> (mempty, b)) d2)+++-- | Keep only selected components (and their associated deps info).+filterDeps :: (Component -> a -> Bool) -> ComponentDeps a -> ComponentDeps a+filterDeps p = ComponentDeps . Map.filterWithKey p . unComponentDeps++-- | ComponentDeps containing library dependencies only+fromLibraryDeps :: a -> ComponentDeps a+fromLibraryDeps = singleton ComponentLib++-- | ComponentDeps containing setup dependencies only.+fromSetupDeps :: a -> ComponentDeps a+fromSetupDeps = singleton ComponentSetup++-- | ComponentDeps for installed packages.+--+-- We assume that installed packages only record their library dependencies.+fromInstalled :: a -> ComponentDeps a+fromInstalled = fromLibraryDeps++{-------------------------------------------------------------------------------+  Deconstruction+-------------------------------------------------------------------------------}++toList :: ComponentDeps a -> [ComponentDep a]+toList = Map.toList . unComponentDeps++-- | All dependencies of a package.+--+-- This is just a synonym for 'fold', but perhaps a use of 'flatDeps' is more+-- obvious than a use of 'fold', and moreover this avoids introducing lots of+-- @#ifdef@s for 7.10 just for the use of 'fold'.+flatDeps :: Monoid a => ComponentDeps a -> a+flatDeps = fold++-- | All dependencies except the setup dependencies.+--+-- Prior to the introduction of setup dependencies in version 1.24 this+-- would have been _all_ dependencies.+nonSetupDeps :: Monoid a => ComponentDeps a -> a+nonSetupDeps = select (/= ComponentSetup)++-- | Library dependencies proper only.  (Includes dependencies+-- of internal libraries.)+libraryDeps :: Monoid a => ComponentDeps a -> a+libraryDeps = select (\c -> case c of ComponentSubLib _ -> True+                                      ComponentLib -> True+                                      _ -> False)++-- | Setup dependencies.+setupDeps :: Monoid a => ComponentDeps a -> a+setupDeps = select (== ComponentSetup)++-- | Select dependencies satisfying a given predicate.+select :: Monoid a => (Component -> Bool) -> ComponentDeps a -> a+select p = foldMap snd . filter (p . fst) . toList
+ Distribution/Solver/Types/ConstraintSource.hs view
@@ -0,0 +1,76 @@+{-# LANGUAGE DeriveGeneric #-}+module Distribution.Solver.Types.ConstraintSource+    ( ConstraintSource(..)+    , showConstraintSource+    ) where++import GHC.Generics (Generic)+import Distribution.Compat.Binary (Binary(..))++-- | Source of a 'PackageConstraint'.+data ConstraintSource =++  -- | Main config file, which is ~/.cabal/config by default.+  ConstraintSourceMainConfig FilePath++  -- | Local cabal.project file+  | ConstraintSourceProjectConfig FilePath++  -- | Sandbox config file, which is ./cabal.sandbox.config by default.+  | ConstraintSourceSandboxConfig FilePath++  -- | User config file, which is ./cabal.config by default.+  | ConstraintSourceUserConfig FilePath++  -- | Flag specified on the command line.+  | ConstraintSourceCommandlineFlag++  -- | Target specified by the user, e.g., @cabal install package-0.1.0.0@+  -- implies @package==0.1.0.0@.+  | ConstraintSourceUserTarget++  -- | Internal requirement to use installed versions of packages like ghc-prim.+  | ConstraintSourceNonUpgradeablePackage++  -- | Internal requirement to use the add-source version of a package when that+  -- version is installed and the source is modified.+  | ConstraintSourceModifiedAddSourceDep++  -- | Internal constraint used by @cabal freeze@.+  | ConstraintSourceFreeze++  -- | Constraint specified by a config file, a command line flag, or a user+  -- target, when a more specific source is not known.+  | ConstraintSourceConfigFlagOrTarget++  -- | The source of the constraint is not specified.+  | ConstraintSourceUnknown++  -- | An internal constraint due to compatibility issues with the Setup.hs+  -- command line interface requires a minimum lower bound on Cabal+  | ConstraintSetupCabalMinVersion+  deriving (Eq, Show, Generic)++instance Binary ConstraintSource++-- | Description of a 'ConstraintSource'.+showConstraintSource :: ConstraintSource -> String+showConstraintSource (ConstraintSourceMainConfig path) =+    "main config " ++ path+showConstraintSource (ConstraintSourceProjectConfig path) =+    "project config " ++ path+showConstraintSource (ConstraintSourceSandboxConfig path) =+    "sandbox config " ++ path+showConstraintSource (ConstraintSourceUserConfig path)= "user config " ++ path+showConstraintSource ConstraintSourceCommandlineFlag = "command line flag"+showConstraintSource ConstraintSourceUserTarget = "user target"+showConstraintSource ConstraintSourceNonUpgradeablePackage =+    "non-upgradeable package"+showConstraintSource ConstraintSourceModifiedAddSourceDep =+    "modified add-source dependency"+showConstraintSource ConstraintSourceFreeze = "cabal freeze"+showConstraintSource ConstraintSourceConfigFlagOrTarget =+    "config file, command line flag, or user target"+showConstraintSource ConstraintSourceUnknown = "unknown source"+showConstraintSource ConstraintSetupCabalMinVersion =+    "minimum version of Cabal used by Setup.hs"
+ Distribution/Solver/Types/DependencyResolver.hs view
@@ -0,0 +1,36 @@+module Distribution.Solver.Types.DependencyResolver+    ( DependencyResolver+    ) where++import Data.Set (Set)++import Distribution.Solver.Types.LabeledPackageConstraint+import Distribution.Solver.Types.PkgConfigDb ( PkgConfigDb )+import Distribution.Solver.Types.PackagePreferences+import Distribution.Solver.Types.PackageIndex ( PackageIndex )+import Distribution.Solver.Types.Progress+import Distribution.Solver.Types.ResolverPackage+import Distribution.Solver.Types.SourcePackage++import Distribution.Simple.PackageIndex ( InstalledPackageIndex )+import Distribution.Package ( PackageName )+import Distribution.Compiler ( CompilerInfo )+import Distribution.System ( Platform )++-- | A dependency resolver is a function that works out an installation plan+-- given the set of installed and available packages and a set of deps to+-- solve for.+--+-- The reason for this interface is because there are dozens of approaches to+-- solving the package dependency problem and we want to make it easy to swap+-- in alternatives.+--+type DependencyResolver loc = Platform+                           -> CompilerInfo+                           -> InstalledPackageIndex+                           -> PackageIndex (SourcePackage loc)+                           -> PkgConfigDb+                           -> (PackageName -> PackagePreferences)+                           -> [LabeledPackageConstraint]+                           -> Set PackageName+                           -> Progress String String [ResolverPackage loc]
+ Distribution/Solver/Types/Flag.hs view
@@ -0,0 +1,6 @@+module Distribution.Solver.Types.Flag+    ( FlagType(..)+    ) where++data FlagType = Manual | Automatic+  deriving (Eq, Show)
+ Distribution/Solver/Types/InstSolverPackage.hs view
@@ -0,0 +1,38 @@+{-# LANGUAGE DeriveGeneric #-}+module Distribution.Solver.Types.InstSolverPackage +    ( InstSolverPackage(..)+    ) where++import Distribution.Compat.Binary (Binary(..))+import Distribution.Package ( Package(..), HasMungedPackageId(..), HasUnitId(..) )+import Distribution.Solver.Types.ComponentDeps ( ComponentDeps )+import Distribution.Solver.Types.SolverId+import Distribution.Types.MungedPackageId+import Distribution.Types.PackageId+import Distribution.Types.PackageName+import Distribution.Types.MungedPackageName+import Distribution.InstalledPackageInfo (InstalledPackageInfo)+import GHC.Generics (Generic)++-- | An 'InstSolverPackage' is a pre-existing installed pacakge+-- specified by the dependency solver.+data InstSolverPackage = InstSolverPackage {+      instSolverPkgIPI :: InstalledPackageInfo,+      instSolverPkgLibDeps :: ComponentDeps [SolverId],+      instSolverPkgExeDeps :: ComponentDeps [SolverId]+    }+  deriving (Eq, Show, Generic)++instance Binary InstSolverPackage++instance Package InstSolverPackage where+    packageId i =+        -- HACK! See Note [Index conversion with internal libraries]+        let MungedPackageId mpn v = mungedId i+        in PackageIdentifier (mkPackageName (unMungedPackageName mpn)) v++instance HasMungedPackageId InstSolverPackage where+    mungedId = mungedId . instSolverPkgIPI++instance HasUnitId InstSolverPackage where+    installedUnitId = installedUnitId . instSolverPkgIPI
+ Distribution/Solver/Types/InstalledPreference.hs view
@@ -0,0 +1,9 @@+module Distribution.Solver.Types.InstalledPreference+    ( InstalledPreference(..),+    ) where++-- | Whether we prefer an installed version of a package or simply the latest+-- version.+--+data InstalledPreference = PreferInstalled | PreferLatest+  deriving Show
+ Distribution/Solver/Types/LabeledPackageConstraint.hs view
@@ -0,0 +1,14 @@+module Distribution.Solver.Types.LabeledPackageConstraint+    ( LabeledPackageConstraint(..)+    , unlabelPackageConstraint+    ) where++import Distribution.Solver.Types.ConstraintSource+import Distribution.Solver.Types.PackageConstraint++-- | 'PackageConstraint' labeled with its source.+data LabeledPackageConstraint+   = LabeledPackageConstraint PackageConstraint ConstraintSource++unlabelPackageConstraint :: LabeledPackageConstraint -> PackageConstraint+unlabelPackageConstraint (LabeledPackageConstraint pc _) = pc
+ Distribution/Solver/Types/OptionalStanza.hs view
@@ -0,0 +1,34 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DeriveDataTypeable #-}+module Distribution.Solver.Types.OptionalStanza+    ( OptionalStanza(..)+    , showStanza+    , enableStanzas+    ) where++import GHC.Generics (Generic)+import Data.Typeable+import Distribution.Compat.Binary (Binary(..))+import Distribution.Types.ComponentRequestedSpec+            (ComponentRequestedSpec(..), defaultComponentRequestedSpec)+import Data.List (foldl')++data OptionalStanza+    = TestStanzas+    | BenchStanzas+  deriving (Eq, Ord, Enum, Bounded, Show, Generic, Typeable)++-- | String representation of an OptionalStanza.+showStanza :: OptionalStanza -> String+showStanza TestStanzas  = "test"+showStanza BenchStanzas = "bench"++-- | Convert a list of 'OptionalStanza' into the corresponding+-- 'ComponentRequestedSpec' which records what components are enabled.+enableStanzas :: [OptionalStanza] -> ComponentRequestedSpec+enableStanzas = foldl' addStanza defaultComponentRequestedSpec+  where+    addStanza enabled TestStanzas  = enabled { testsRequested = True }+    addStanza enabled BenchStanzas = enabled { benchmarksRequested = True }++instance Binary OptionalStanza
+ Distribution/Solver/Types/PackageConstraint.hs view
@@ -0,0 +1,133 @@+{-# LANGUAGE DeriveGeneric #-}++-- | Per-package constraints. Package constraints must be respected by the+-- solver. Multiple constraints for each package can be given, though obviously+-- it is possible to construct conflicting constraints (eg impossible version+-- range or inconsistent flag assignment).+--+module Distribution.Solver.Types.PackageConstraint (+    ConstraintScope(..),+    scopeToplevel,+    scopeToPackageName,+    constraintScopeMatches,+    PackageProperty(..),+    dispPackageProperty,+    PackageConstraint(..),+    dispPackageConstraint,+    showPackageConstraint,+    packageConstraintToDependency+  ) where++import Distribution.Compat.Binary      (Binary(..))+import Distribution.Package            (PackageName)+import Distribution.PackageDescription (FlagAssignment, dispFlagAssignment)+import Distribution.Types.Dependency   (Dependency(..))+import Distribution.Version            (VersionRange, simplifyVersionRange)++import Distribution.Client.Compat.Prelude ((<<>>))+import Distribution.Solver.Types.OptionalStanza+import Distribution.Solver.Types.PackagePath++import Distribution.Text                  (disp, flatStyle)+import GHC.Generics                       (Generic)+import Text.PrettyPrint                   ((<+>))+import qualified Text.PrettyPrint as Disp+++-- | Determines to what packages and in what contexts a+-- constraint applies.+data ConstraintScope+     -- | The package with the specified name and qualifier.+   = ScopeQualified Qualifier PackageName+     -- | The package with the specified name when it has a+     -- setup qualifier.+   | ScopeAnySetupQualifier PackageName+     -- | The package with the specified name regardless of+     -- qualifier.+   | ScopeAnyQualifier PackageName+  deriving (Eq, Show)++-- | Constructor for a common use case: the constraint applies to+-- the package with the specified name when that package is a+-- top-level dependency in the default namespace.+scopeToplevel :: PackageName -> ConstraintScope+scopeToplevel = ScopeQualified QualToplevel++-- | Returns the package name associated with a constraint scope.+scopeToPackageName :: ConstraintScope -> PackageName+scopeToPackageName (ScopeQualified _ pn) = pn+scopeToPackageName (ScopeAnySetupQualifier pn) = pn+scopeToPackageName (ScopeAnyQualifier pn) = pn++constraintScopeMatches :: ConstraintScope -> QPN -> Bool+constraintScopeMatches (ScopeQualified q pn) (Q (PackagePath _ q') pn') =+    q == q' && pn == pn'+constraintScopeMatches (ScopeAnySetupQualifier pn) (Q pp pn') =+  let setup (PackagePath _ (QualSetup _)) = True+      setup _                             = False+  in setup pp && pn == pn'+constraintScopeMatches (ScopeAnyQualifier pn) (Q _ pn') = pn == pn'++-- | Pretty-prints a constraint scope.+dispConstraintScope :: ConstraintScope -> Disp.Doc+dispConstraintScope (ScopeQualified q pn) = dispQualifier q <<>> disp pn+dispConstraintScope (ScopeAnySetupQualifier pn) = Disp.text "setup." <<>> disp pn+dispConstraintScope (ScopeAnyQualifier pn) = Disp.text "any." <<>> disp pn++-- | A package property is a logical predicate on packages.+data PackageProperty+   = PackagePropertyVersion   VersionRange+   | PackagePropertyInstalled+   | PackagePropertySource+   | PackagePropertyFlags     FlagAssignment+   | PackagePropertyStanzas   [OptionalStanza]+  deriving (Eq, Show, Generic)++instance Binary PackageProperty++-- | Pretty-prints a package property.+dispPackageProperty :: PackageProperty -> Disp.Doc+dispPackageProperty (PackagePropertyVersion verrange) = disp verrange+dispPackageProperty PackagePropertyInstalled          = Disp.text "installed"+dispPackageProperty PackagePropertySource             = Disp.text "source"+dispPackageProperty (PackagePropertyFlags flags)      = dispFlagAssignment flags+dispPackageProperty (PackagePropertyStanzas stanzas)  =+  Disp.hsep $ map (Disp.text . showStanza) stanzas++-- | A package constraint consists of a scope plus a property+-- that must hold for all packages within that scope.+data PackageConstraint = PackageConstraint ConstraintScope PackageProperty+  deriving (Eq, Show)++-- | Pretty-prints a package constraint.+dispPackageConstraint :: PackageConstraint -> Disp.Doc+dispPackageConstraint (PackageConstraint scope prop) =+  dispConstraintScope scope <+> dispPackageProperty prop++-- | Alternative textual representation of a package constraint+-- for debugging purposes (slightly more verbose than that+-- produced by 'dispPackageConstraint').+--+showPackageConstraint :: PackageConstraint -> String+showPackageConstraint pc@(PackageConstraint scope prop) =+  Disp.renderStyle flatStyle . postprocess $ dispPackageConstraint pc2+  where+    pc2 = case prop of+      PackagePropertyVersion vr ->+        PackageConstraint scope $ PackagePropertyVersion (simplifyVersionRange vr)+      _ -> pc+    postprocess = case prop of+      PackagePropertyFlags _ -> (Disp.text "flags" <+>)+      PackagePropertyStanzas _ -> (Disp.text "stanzas" <+>)+      _ -> id++-- | Lossily convert a 'PackageConstraint' to a 'Dependency'.+packageConstraintToDependency :: PackageConstraint -> Maybe Dependency+packageConstraintToDependency (PackageConstraint scope prop) = toDep prop+  where+    toDep (PackagePropertyVersion vr) = +        Just $ Dependency (scopeToPackageName scope) vr+    toDep (PackagePropertyInstalled)  = Nothing+    toDep (PackagePropertySource)     = Nothing+    toDep (PackagePropertyFlags _)    = Nothing+    toDep (PackagePropertyStanzas _)  = Nothing
+ Distribution/Solver/Types/PackageFixedDeps.hs view
@@ -0,0 +1,23 @@+module Distribution.Solver.Types.PackageFixedDeps+    ( PackageFixedDeps(..)+    ) where++import           Distribution.InstalledPackageInfo ( InstalledPackageInfo )+import           Distribution.Package+                   ( Package(..), UnitId, installedDepends)+import           Distribution.Solver.Types.ComponentDeps ( ComponentDeps )+import qualified Distribution.Solver.Types.ComponentDeps as CD++-- | Subclass of packages that have specific versioned dependencies.+--+-- So for example a not-yet-configured package has dependencies on version+-- ranges, not specific versions. A configured or an already installed package+-- depends on exact versions. Some operations or data structures (like+--  dependency graphs) only make sense on this subclass of package types.+--+class Package pkg => PackageFixedDeps pkg where+  depends :: pkg -> ComponentDeps [UnitId]++instance PackageFixedDeps InstalledPackageInfo where+  depends pkg = CD.fromInstalled (installedDepends pkg)+
+ Distribution/Solver/Types/PackageIndex.hs view
@@ -0,0 +1,316 @@+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE DeriveGeneric #-}+-----------------------------------------------------------------------------+-- |+-- Module      :  Distribution.Solver.Types.PackageIndex+-- Copyright   :  (c) David Himmelstrup 2005,+--                    Bjorn Bringert 2007,+--                    Duncan Coutts 2008+--+-- Maintainer  :  cabal-devel@haskell.org+-- Portability :  portable+--+-- An index of packages.+--+module Distribution.Solver.Types.PackageIndex (+  -- * Package index data type+  PackageIndex,++  -- * Creating an index+  fromList,++  -- * Updates+  merge,+  insert,+  deletePackageName,+  deletePackageId,+  deleteDependency,++  -- * Queries++  -- ** Precise lookups+  elemByPackageId,+  elemByPackageName,+  lookupPackageName,+  lookupPackageId,+  lookupDependency,++  -- ** Case-insensitive searches+  searchByName,+  SearchResult(..),+  searchByNameSubstring,++  -- ** Bulk queries+  allPackages,+  allPackagesByName,+  ) where++import Prelude ()+import Distribution.Client.Compat.Prelude hiding (lookup)++import Control.Exception (assert)+import qualified Data.Map as Map+import Data.List (groupBy, isInfixOf)++import Distribution.Package+         ( PackageName, unPackageName, PackageIdentifier(..)+         , Package(..), packageName, packageVersion )+import Distribution.Types.Dependency+import Distribution.Version+         ( withinRange )+import Distribution.Simple.Utils+         ( lowercase, comparing )+++-- | The collection of information about packages from one or more 'PackageDB's.+--+-- It can be searched efficiently by package name and version.+--+newtype PackageIndex pkg = PackageIndex+  -- This index package names to all the package records matching that package+  -- name case-sensitively. It includes all versions.+  --+  -- This allows us to find all versions satisfying a dependency.+  -- Most queries are a map lookup followed by a linear scan of the bucket.+  --+  (Map PackageName [pkg])++  deriving (Eq, Show, Read, Functor, Generic)+--FIXME: the Functor instance here relies on no package id changes++instance Package pkg => Semigroup (PackageIndex pkg) where+  (<>) = merge++instance Package pkg => Monoid (PackageIndex pkg) where+  mempty  = PackageIndex Map.empty+  mappend = (<>)+  --save one mappend with empty in the common case:+  mconcat [] = mempty+  mconcat xs = foldr1 mappend xs++instance Binary pkg => Binary (PackageIndex pkg)++invariant :: Package pkg => PackageIndex pkg -> Bool+invariant (PackageIndex m) = all (uncurry goodBucket) (Map.toList m)+  where+    goodBucket _    [] = False+    goodBucket name (pkg0:pkgs0) = check (packageId pkg0) pkgs0+      where+        check pkgid []          = packageName pkgid == name+        check pkgid (pkg':pkgs) = packageName pkgid == name+                               && pkgid < pkgid'+                               && check pkgid' pkgs+          where pkgid' = packageId pkg'++--+-- * Internal helpers+--++mkPackageIndex :: Package pkg => Map PackageName [pkg] -> PackageIndex pkg+mkPackageIndex index = assert (invariant (PackageIndex index))+                                         (PackageIndex index)++internalError :: String -> a+internalError name = error ("PackageIndex." ++ name ++ ": internal error")++-- | Lookup a name in the index to get all packages that match that name+-- case-sensitively.+--+lookup :: PackageIndex pkg -> PackageName -> [pkg]+lookup (PackageIndex m) name = fromMaybe [] $ Map.lookup name m++--+-- * Construction+--++-- | Build an index out of a bunch of packages.+--+-- If there are duplicates, later ones mask earlier ones.+--+fromList :: Package pkg => [pkg] -> PackageIndex pkg+fromList pkgs = mkPackageIndex+              . Map.map fixBucket+              . Map.fromListWith (++)+              $ [ (packageName pkg, [pkg])+                | pkg <- pkgs ]+  where+    fixBucket = -- out of groups of duplicates, later ones mask earlier ones+                -- but Map.fromListWith (++) constructs groups in reverse order+                map head+                -- Eq instance for PackageIdentifier is wrong, so use Ord:+              . groupBy (\a b -> EQ == comparing packageId a b)+                -- relies on sortBy being a stable sort so we+                -- can pick consistently among duplicates+              . sortBy (comparing packageId)++--+-- * Updates+--++-- | Merge two indexes.+--+-- Packages from the second mask packages of the same exact name+-- (case-sensitively) from the first.+--+merge :: Package pkg => PackageIndex pkg -> PackageIndex pkg -> PackageIndex pkg+merge i1@(PackageIndex m1) i2@(PackageIndex m2) =+  assert (invariant i1 && invariant i2) $+    mkPackageIndex (Map.unionWith mergeBuckets m1 m2)++-- | Elements in the second list mask those in the first.+mergeBuckets :: Package pkg => [pkg] -> [pkg] -> [pkg]+mergeBuckets []     ys     = ys+mergeBuckets xs     []     = xs+mergeBuckets xs@(x:xs') ys@(y:ys') =+      case packageId x `compare` packageId y of+        GT -> y : mergeBuckets xs  ys'+        EQ -> y : mergeBuckets xs' ys'+        LT -> x : mergeBuckets xs' ys++-- | Inserts a single package into the index.+--+-- This is equivalent to (but slightly quicker than) using 'mappend' or+-- 'merge' with a singleton index.+--+insert :: Package pkg => pkg -> PackageIndex pkg -> PackageIndex pkg+insert pkg (PackageIndex index) = mkPackageIndex $+  Map.insertWith (\_ -> insertNoDup) (packageName pkg) [pkg] index+  where+    pkgid = packageId pkg+    insertNoDup []                = [pkg]+    insertNoDup pkgs@(pkg':pkgs') = case compare pkgid (packageId pkg') of+      LT -> pkg  : pkgs+      EQ -> pkg  : pkgs'+      GT -> pkg' : insertNoDup pkgs'++-- | Internal delete helper.+--+delete :: Package pkg => PackageName -> (pkg -> Bool) -> PackageIndex pkg+       -> PackageIndex pkg+delete name p (PackageIndex index) = mkPackageIndex $+  Map.update filterBucket name index+  where+    filterBucket = deleteEmptyBucket+                 . filter (not . p)+    deleteEmptyBucket []        = Nothing+    deleteEmptyBucket remaining = Just remaining++-- | Removes a single package from the index.+--+deletePackageId :: Package pkg => PackageIdentifier -> PackageIndex pkg+                -> PackageIndex pkg+deletePackageId pkgid =+  delete (packageName pkgid) (\pkg -> packageId pkg == pkgid)++-- | Removes all packages with this (case-sensitive) name from the index.+--+deletePackageName :: Package pkg => PackageName -> PackageIndex pkg+                  -> PackageIndex pkg+deletePackageName name =+  delete name (\pkg -> packageName pkg == name)++-- | Removes all packages satisfying this dependency from the index.+--+deleteDependency :: Package pkg => Dependency -> PackageIndex pkg+                 -> PackageIndex pkg+deleteDependency (Dependency name verstionRange) =+  delete name (\pkg -> packageVersion pkg `withinRange` verstionRange)++--+-- * Bulk queries+--++-- | Get all the packages from the index.+--+allPackages :: PackageIndex pkg -> [pkg]+allPackages (PackageIndex m) = concat (Map.elems m)++-- | Get all the packages from the index.+--+-- They are grouped by package name, case-sensitively.+--+allPackagesByName :: PackageIndex pkg -> [[pkg]]+allPackagesByName (PackageIndex m) = Map.elems m++--+-- * Lookups+--++elemByPackageId :: Package pkg => PackageIndex pkg -> PackageIdentifier -> Bool+elemByPackageId index = isJust . lookupPackageId index++elemByPackageName :: Package pkg => PackageIndex pkg -> PackageName -> Bool+elemByPackageName index = not . null . lookupPackageName index+++-- | Does a lookup by package id (name & version).+--+-- Since multiple package DBs mask each other case-sensitively by package name,+-- then we get back at most one package.+--+lookupPackageId :: Package pkg => PackageIndex pkg -> PackageIdentifier+                -> Maybe pkg+lookupPackageId index pkgid =+  case [ pkg | pkg <- lookup index (packageName pkgid)+             , packageId pkg == pkgid ] of+    []    -> Nothing+    [pkg] -> Just pkg+    _     -> internalError "lookupPackageIdentifier"++-- | Does a case-sensitive search by package name.+--+lookupPackageName :: Package pkg => PackageIndex pkg -> PackageName -> [pkg]+lookupPackageName index name =+  [ pkg | pkg <- lookup index name+        , packageName pkg == name ]++-- | Does a case-sensitive search by package name and a range of versions.+--+-- We get back any number of versions of the specified package name, all+-- satisfying the version range constraint.+--+lookupDependency :: Package pkg => PackageIndex pkg -> Dependency -> [pkg]+lookupDependency index (Dependency name versionRange) =+  [ pkg | pkg <- lookup index name+        , packageName pkg == name+        , packageVersion pkg `withinRange` versionRange ]++--+-- * Case insensitive name lookups+--++-- | Does a case-insensitive search by package name.+--+-- If there is only one package that compares case-insensitively to this name+-- then the search is unambiguous and we get back all versions of that package.+-- If several match case-insensitively but one matches exactly then it is also+-- unambiguous.+--+-- If however several match case-insensitively and none match exactly then we+-- have an ambiguous result, and we get back all the versions of all the+-- packages. The list of ambiguous results is split by exact package name. So+-- it is a non-empty list of non-empty lists.+--+searchByName :: PackageIndex pkg+             -> String -> [(PackageName, [pkg])]+searchByName (PackageIndex m) name =+    [ pkgs+    | pkgs@(pname,_) <- Map.toList m+    , lowercase (unPackageName pname) == lname ]+  where+    lname = lowercase name++data SearchResult a = None | Unambiguous a | Ambiguous [a]++-- | Does a case-insensitive substring search by package name.+--+-- That is, all packages that contain the given string in their name.+--+searchByNameSubstring :: PackageIndex pkg+                      -> String -> [(PackageName, [pkg])]+searchByNameSubstring (PackageIndex m) searchterm =+    [ pkgs+    | pkgs@(pname, _) <- Map.toList m+    , lsearchterm `isInfixOf` lowercase (unPackageName pname) ]+  where+    lsearchterm = lowercase searchterm
+ Distribution/Solver/Types/PackagePath.hs view
@@ -0,0 +1,103 @@+module Distribution.Solver.Types.PackagePath+    ( PackagePath(..)+    , Namespace(..)+    , Qualifier(..)+    , dispQualifier+    , Qualified(..)+    , QPN+    , dispQPN+    , showQPN+    ) where++import Distribution.Package+import Distribution.Text+import qualified Text.PrettyPrint as Disp+import Distribution.Client.Compat.Prelude ((<<>>))++-- | A package path consists of a namespace and a package path inside that+-- namespace.+data PackagePath = PackagePath Namespace Qualifier+  deriving (Eq, Ord, Show)++-- | Top-level namespace+--+-- Package choices in different namespaces are considered completely independent+-- by the solver.+data Namespace =+    -- | The default namespace+    DefaultNamespace++    -- | Independent namespace+    --+    -- For now we just number these (rather than giving them more structure).+  | Independent Int+  deriving (Eq, Ord, Show)++-- | Pretty-prints a namespace. The result is either empty or+-- ends in a period, so it can be prepended onto a qualifier.+dispNamespace :: Namespace -> Disp.Doc+dispNamespace DefaultNamespace = Disp.empty+dispNamespace (Independent i) = Disp.int i <<>> Disp.text "."++-- | Qualifier of a package within a namespace (see 'PackagePath')+data Qualifier =+    -- | Top-level dependency in this namespace+    QualToplevel++    -- | Any dependency on base is considered independent+    --+    -- This makes it possible to have base shims.+  | QualBase PackageName++    -- | Setup dependency+    --+    -- By rights setup dependencies ought to be nestable; after all, the setup+    -- dependencies of a package might themselves have setup dependencies, which+    -- are independent from everything else. However, this very quickly leads to+    -- infinite search trees in the solver. Therefore we limit ourselves to+    -- a single qualifier (within a given namespace).+  | QualSetup PackageName++    -- | If we depend on an executable from a package (via+    -- @build-tools@), we should solve for the dependencies of that+    -- package separately (since we're not going to actually try to+    -- link it.)  We qualify for EACH package separately; e.g.,+    -- @'Exe' pn1 pn2@ qualifies the @build-tools@ dependency on+    -- @pn2@ from package @pn1@.  (If we tracked only @pn1@, that+    -- would require a consistent dependency resolution for all+    -- of the depended upon executables from a package; if we+    -- tracked only @pn2@, that would require us to pick only one+    -- version of an executable over the entire install plan.)+  | QualExe PackageName PackageName+  deriving (Eq, Ord, Show)++-- | Pretty-prints a qualifier. The result is either empty or+-- ends in a period, so it can be prepended onto a package name.+--+-- NOTE: the base qualifier is for a dependency _on_ base; the qualifier is+-- there to make sure different dependencies on base are all independent.+-- So we want to print something like @"A.base"@, where the @"A."@ part+-- is the qualifier and @"base"@ is the actual dependency (which, for the+-- 'Base' qualifier, will always be @base@).+dispQualifier :: Qualifier -> Disp.Doc+dispQualifier QualToplevel = Disp.empty+dispQualifier (QualSetup pn)  = disp pn <<>> Disp.text ":setup."+dispQualifier (QualExe pn pn2) = disp pn <<>> Disp.text ":" <<>>+                                 disp pn2 <<>> Disp.text ":exe."+dispQualifier (QualBase pn)  = disp pn <<>> Disp.text "."++-- | A qualified entity. Pairs a package path with the entity.+data Qualified a = Q PackagePath a+  deriving (Eq, Ord, Show)++-- | Qualified package name.+type QPN = Qualified PackageName++-- | Pretty-prints a qualified package name.+dispQPN :: QPN -> Disp.Doc+dispQPN (Q (PackagePath ns qual) pn) =+  dispNamespace ns <<>> dispQualifier qual <<>> disp pn++-- | String representation of a qualified package name.+showQPN :: QPN -> String+showQPN = Disp.renderStyle flatStyle . dispQPN
+ Distribution/Solver/Types/PackagePreferences.hs view
@@ -0,0 +1,22 @@+module Distribution.Solver.Types.PackagePreferences+    ( PackagePreferences(..)+    ) where++import Distribution.Solver.Types.InstalledPreference+import Distribution.Solver.Types.OptionalStanza+import Distribution.Version (VersionRange)++-- | Per-package preferences on the version. It is a soft constraint that the+-- 'DependencyResolver' should try to respect where possible. It consists of+-- an 'InstalledPreference' which says if we prefer versions of packages+-- that are already installed. It also has (possibly multiple)+-- 'PackageVersionPreference's which are suggested constraints on the version+-- number. The resolver should try to use package versions that satisfy+-- the maximum number of the suggested version constraints.+--+-- It is not specified if preferences on some packages are more important than+-- others.+--+data PackagePreferences = PackagePreferences [VersionRange]+                                             InstalledPreference+                                             [OptionalStanza]
+ Distribution/Solver/Types/PkgConfigDb.hs view
@@ -0,0 +1,159 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DeriveDataTypeable #-}+-----------------------------------------------------------------------------+-- |+-- Module      :  Distribution.Solver.Types.PkgConfigDb+-- Copyright   :  (c) Iñaki García Etxebarria 2016+-- License     :  BSD-like+--+-- Maintainer  :  cabal-devel@haskell.org+-- Portability :  portable+--+-- Read the list of packages available to pkg-config.+-----------------------------------------------------------------------------+module Distribution.Solver.Types.PkgConfigDb+    ( PkgConfigDb+    , readPkgConfigDb+    , pkgConfigDbFromList+    , pkgConfigPkgIsPresent+    , pkgConfigDbPkgVersion+    , getPkgConfigDbDirs+    ) where++import Prelude ()+import Distribution.Client.Compat.Prelude++import Control.Exception (IOException, handle)+import qualified Data.Map as M+import Data.Version (parseVersion)+import Text.ParserCombinators.ReadP (readP_to_S)+import System.FilePath (splitSearchPath)++import Distribution.Package+    ( PkgconfigName, mkPkgconfigName )+import Distribution.Verbosity+    ( Verbosity )+import Distribution.Version+    ( Version, mkVersion', VersionRange, withinRange )++import Distribution.Compat.Environment+    ( lookupEnv )+import Distribution.Simple.Program+    ( ProgramDb, pkgConfigProgram, getProgramOutput, requireProgram )+import Distribution.Simple.Utils+    ( info )++-- | The list of packages installed in the system visible to+-- @pkg-config@. This is an opaque datatype, to be constructed with+-- `readPkgConfigDb` and queried with `pkgConfigPkgPresent`.+data PkgConfigDb =  PkgConfigDb (M.Map PkgconfigName (Maybe Version))+                 -- ^ If an entry is `Nothing`, this means that the+                 -- package seems to be present, but we don't know the+                 -- exact version (because parsing of the version+                 -- number failed).+                 | NoPkgConfigDb+                 -- ^ For when we could not run pkg-config successfully.+     deriving (Show, Generic, Typeable)++instance Binary PkgConfigDb++-- | Query pkg-config for the list of installed packages, together+-- with their versions. Return a `PkgConfigDb` encapsulating this+-- information.+readPkgConfigDb :: Verbosity -> ProgramDb -> IO PkgConfigDb+readPkgConfigDb verbosity progdb = handle ioErrorHandler $ do+  (pkgConfig, _) <- requireProgram verbosity pkgConfigProgram progdb+  pkgList <- lines <$> getProgramOutput verbosity pkgConfig ["--list-all"]+  -- The output of @pkg-config --list-all@ also includes a description+  -- for each package, which we do not need.+  let pkgNames = map (takeWhile (not . isSpace)) pkgList+  pkgVersions <- lines <$> getProgramOutput verbosity pkgConfig+                             ("--modversion" : pkgNames)+  (return . pkgConfigDbFromList . zip pkgNames) pkgVersions+      where+        -- For when pkg-config invocation fails (possibly because of a+        -- too long command line).+        ioErrorHandler :: IOException -> IO PkgConfigDb+        ioErrorHandler e = do+          info verbosity ("Failed to query pkg-config, Cabal will continue"+                          ++ " without solving for pkg-config constraints: "+                          ++ show e)+          return NoPkgConfigDb++-- | Create a `PkgConfigDb` from a list of @(packageName, version)@ pairs.+pkgConfigDbFromList :: [(String, String)] -> PkgConfigDb+pkgConfigDbFromList pairs = (PkgConfigDb . M.fromList . map convert) pairs+    where+      convert :: (String, String) -> (PkgconfigName, Maybe Version)+      convert (n,vs) = (mkPkgconfigName n,+                        case (reverse . readP_to_S parseVersion) vs of+                          (v, "") : _ -> Just (mkVersion' v)+                          _           -> Nothing -- Version not (fully)+                                                 -- understood.+                       )++-- | Check whether a given package range is satisfiable in the given+-- @pkg-config@ database.+pkgConfigPkgIsPresent :: PkgConfigDb -> PkgconfigName -> VersionRange -> Bool+pkgConfigPkgIsPresent (PkgConfigDb db) pn vr =+    case M.lookup pn db of+      Nothing       -> False    -- Package not present in the DB.+      Just Nothing  -> True     -- Package present, but version unknown.+      Just (Just v) -> withinRange v vr+-- If we could not read the pkg-config database successfully we allow+-- the check to succeed. The plan found by the solver may fail to be+-- executed later on, but we have no grounds for rejecting the plan at+-- this stage.+pkgConfigPkgIsPresent NoPkgConfigDb _ _ = True+++-- | Query the version of a package in the @pkg-config@ database.+-- @Nothing@ indicates the package is not in the database, while+-- @Just Nothing@ indicates that the package is in the database,+-- but its version is not known.+pkgConfigDbPkgVersion :: PkgConfigDb -> PkgconfigName -> Maybe (Maybe Version)+pkgConfigDbPkgVersion (PkgConfigDb db) pn = M.lookup pn db+-- NB: Since the solver allows solving to succeed if there is+-- NoPkgConfigDb, we should report that we *guess* that there+-- is a matching pkg-config configuration, but that we just+-- don't know about it.+pkgConfigDbPkgVersion NoPkgConfigDb _ = Just Nothing+++-- | Query pkg-config for the locations of pkg-config's package files. Use this+-- to monitor for changes in the pkg-config DB.+--+getPkgConfigDbDirs :: Verbosity -> ProgramDb -> IO [FilePath]+getPkgConfigDbDirs verbosity progdb =+    (++) <$> getEnvPath <*> getDefPath+ where+    -- According to @man pkg-config@:+    --+    -- PKG_CONFIG_PATH+    -- A  colon-separated  (on Windows, semicolon-separated) list of directories+    -- to search for .pc files.  The default directory will always be searched+    -- after searching the path+    --+    getEnvPath = maybe [] parseSearchPath+             <$> lookupEnv "PKG_CONFIG_PATH"++    -- Again according to @man pkg-config@:+    --+    -- pkg-config can be used to query itself for the default search path,+    -- version number and other information, for instance using:+    --+    -- > pkg-config --variable pc_path pkg-config+    --+    getDefPath = handle ioErrorHandler $ do+      (pkgConfig, _) <- requireProgram verbosity pkgConfigProgram progdb+      parseSearchPath <$>+        getProgramOutput verbosity pkgConfig+                         ["--variable", "pc_path", "pkg-config"]++    parseSearchPath str =+      case lines str of+        [p] | not (null p) -> splitSearchPath p+        _                  -> []++    ioErrorHandler :: IOException -> IO [FilePath]+    ioErrorHandler _e = return []
+ Distribution/Solver/Types/Progress.hs view
@@ -0,0 +1,49 @@+module Distribution.Solver.Types.Progress+    ( Progress(..)+    , foldProgress+    ) where++import Prelude ()+import Distribution.Client.Compat.Prelude hiding (fail)++-- | A type to represent the unfolding of an expensive long running+-- calculation that may fail. We may get intermediate steps before the final+-- result which may be used to indicate progress and\/or logging messages.+--+data Progress step fail done = Step step (Progress step fail done)+                             | Fail fail+                             | Done done++-- This Functor instance works around a bug in GHC 7.6.3.+-- See https://ghc.haskell.org/trac/ghc/ticket/7436#comment:6.+-- The derived functor instance caused a space leak in the solver.+instance Functor (Progress step fail) where+  fmap f (Step s p) = Step s (fmap f p)+  fmap _ (Fail x)   = Fail x+  fmap f (Done r)   = Done (f r)++-- | Consume a 'Progress' calculation. Much like 'foldr' for lists but with two+-- base cases, one for a final result and one for failure.+--+-- Eg to convert into a simple 'Either' result use:+--+-- > foldProgress (flip const) Left Right+--+foldProgress :: (step -> a -> a) -> (fail -> a) -> (done -> a)+             -> Progress step fail done -> a+foldProgress step fail done = fold+  where fold (Step s p) = step s (fold p)+        fold (Fail f)   = fail f+        fold (Done r)   = done r++instance Monad (Progress step fail) where+  return   = pure+  p >>= f  = foldProgress Step Fail f p++instance Applicative (Progress step fail) where+  pure a  = Done a+  p <*> x = foldProgress Step Fail (flip fmap x) p++instance Monoid fail => Alternative (Progress step fail) where+  empty   = Fail mempty+  p <|> q = foldProgress Step (const q) Done p
+ Distribution/Solver/Types/ResolverPackage.hs view
@@ -0,0 +1,50 @@+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE DeriveGeneric #-}+module Distribution.Solver.Types.ResolverPackage+    ( ResolverPackage(..)+    , resolverPackageLibDeps+    , resolverPackageExeDeps+    ) where++import Distribution.Solver.Types.InstSolverPackage+import Distribution.Solver.Types.SolverId+import Distribution.Solver.Types.SolverPackage+import qualified Distribution.Solver.Types.ComponentDeps as CD++import Distribution.Compat.Binary (Binary(..))+import Distribution.Compat.Graph (IsNode(..))+import Distribution.Package (Package(..), HasUnitId(..))+import Distribution.Simple.Utils (ordNub)+import GHC.Generics (Generic)++-- | The dependency resolver picks either pre-existing installed packages+-- or it picks source packages along with package configuration.+--+-- This is like the 'InstallPlan.PlanPackage' but with fewer cases.+--+data ResolverPackage loc = PreExisting InstSolverPackage+                         | Configured  (SolverPackage loc)+  deriving (Eq, Show, Generic)++instance Binary loc => Binary (ResolverPackage loc)++instance Package (ResolverPackage loc) where+  packageId (PreExisting ipkg)     = packageId ipkg+  packageId (Configured  spkg)     = packageId spkg++resolverPackageLibDeps :: ResolverPackage loc -> CD.ComponentDeps [SolverId]+resolverPackageLibDeps (PreExisting ipkg) = instSolverPkgLibDeps ipkg+resolverPackageLibDeps (Configured spkg) = solverPkgLibDeps spkg++resolverPackageExeDeps :: ResolverPackage loc -> CD.ComponentDeps [SolverId]+resolverPackageExeDeps (PreExisting ipkg) = instSolverPkgExeDeps ipkg+resolverPackageExeDeps (Configured spkg) = solverPkgExeDeps spkg++instance IsNode (ResolverPackage loc) where+  type Key (ResolverPackage loc) = SolverId+  nodeKey (PreExisting ipkg) = PreExistingId (packageId ipkg) (installedUnitId ipkg)+  nodeKey (Configured spkg) = PlannedId (packageId spkg)+  -- Use dependencies for ALL components+  nodeNeighbors pkg =+    ordNub $ CD.flatDeps (resolverPackageLibDeps pkg) +++             CD.flatDeps (resolverPackageExeDeps pkg)
+ Distribution/Solver/Types/Settings.hs view
@@ -0,0 +1,53 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+module Distribution.Solver.Types.Settings+    ( ReorderGoals(..)+    , IndependentGoals(..)+    , AvoidReinstalls(..)+    , ShadowPkgs(..)+    , StrongFlags(..)+    , AllowBootLibInstalls(..)+    , EnableBackjumping(..)+    , CountConflicts(..)+    , SolveExecutables(..)+    ) where++import Distribution.Simple.Setup ( BooleanFlag(..) )+import Distribution.Compat.Binary (Binary(..))+import GHC.Generics (Generic)++newtype ReorderGoals = ReorderGoals Bool+  deriving (BooleanFlag, Eq, Generic, Show)++newtype CountConflicts = CountConflicts Bool+  deriving (BooleanFlag, Eq, Generic, Show)++newtype IndependentGoals = IndependentGoals Bool+  deriving (BooleanFlag, Eq, Generic, Show)++newtype AvoidReinstalls = AvoidReinstalls Bool+  deriving (BooleanFlag, Eq, Generic, Show)++newtype ShadowPkgs = ShadowPkgs Bool+  deriving (BooleanFlag, Eq, Generic, Show)++newtype StrongFlags = StrongFlags Bool+  deriving (BooleanFlag, Eq, Generic, Show)++newtype AllowBootLibInstalls = AllowBootLibInstalls Bool+  deriving (BooleanFlag, Eq, Generic, Show)++newtype EnableBackjumping = EnableBackjumping Bool+  deriving (BooleanFlag, Eq, Generic, Show)++newtype SolveExecutables = SolveExecutables Bool+  deriving (BooleanFlag, Eq, Generic, Show)++instance Binary ReorderGoals+instance Binary CountConflicts+instance Binary IndependentGoals+instance Binary AvoidReinstalls+instance Binary ShadowPkgs+instance Binary StrongFlags+instance Binary AllowBootLibInstalls+instance Binary SolveExecutables
+ Distribution/Solver/Types/SolverId.hs view
@@ -0,0 +1,27 @@+{-# LANGUAGE DeriveGeneric #-}+module Distribution.Solver.Types.SolverId+    ( SolverId(..)+    )++where++import Distribution.Compat.Binary (Binary(..))+import Distribution.Package (PackageId, Package(..), UnitId)+import GHC.Generics (Generic)++-- | The solver can produce references to existing packages or+-- packages we plan to install.  Unlike 'ConfiguredId' we don't+-- yet know the 'UnitId' for planned packages, because it's+-- not the solver's job to compute them.+--+data SolverId = PreExistingId { solverSrcId :: PackageId, solverInstId :: UnitId }+              | PlannedId     { solverSrcId :: PackageId }+  deriving (Eq, Ord, Generic)++instance Binary SolverId++instance Show SolverId where+    show = show . solverSrcId++instance Package SolverId where+  packageId = solverSrcId
+ Distribution/Solver/Types/SolverPackage.hs view
@@ -0,0 +1,34 @@+{-# LANGUAGE DeriveGeneric #-}+module Distribution.Solver.Types.SolverPackage+    ( SolverPackage(..)+    ) where++import Distribution.Compat.Binary (Binary(..))+import Distribution.Package ( Package(..) )+import Distribution.PackageDescription ( FlagAssignment )+import Distribution.Solver.Types.ComponentDeps ( ComponentDeps )+import Distribution.Solver.Types.OptionalStanza+import Distribution.Solver.Types.SolverId+import Distribution.Solver.Types.SourcePackage+import GHC.Generics (Generic)++-- | A 'SolverPackage' is a package specified by the dependency solver.+-- It will get elaborated into a 'ConfiguredPackage' or even an+-- 'ElaboratedConfiguredPackage'.+--+-- NB: 'SolverPackage's are essentially always with 'UnresolvedPkgLoc',+-- but for symmetry we have the parameter.  (Maybe it can be removed.)+--+data SolverPackage loc = SolverPackage {+        solverPkgSource :: SourcePackage loc,+        solverPkgFlags :: FlagAssignment,+        solverPkgStanzas :: [OptionalStanza],+        solverPkgLibDeps :: ComponentDeps [SolverId],+        solverPkgExeDeps :: ComponentDeps [SolverId]+    }+  deriving (Eq, Show, Generic)++instance Binary loc => Binary (SolverPackage loc)++instance Package (SolverPackage loc) where+  packageId = packageId . solverPkgSource
+ Distribution/Solver/Types/SourcePackage.hs view
@@ -0,0 +1,34 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DeriveDataTypeable #-}+module Distribution.Solver.Types.SourcePackage+    ( PackageDescriptionOverride+    , SourcePackage(..)+    ) where++import Distribution.Package+         ( PackageId, Package(..) )+import Distribution.PackageDescription+         ( GenericPackageDescription(..) )++import Data.ByteString.Lazy (ByteString)+import GHC.Generics (Generic)+import Distribution.Compat.Binary (Binary(..))+import Data.Typeable++-- | A package description along with the location of the package sources.+--+data SourcePackage loc = SourcePackage {+    packageInfoId        :: PackageId,+    packageDescription   :: GenericPackageDescription,+    packageSource        :: loc,+    packageDescrOverride :: PackageDescriptionOverride+  }+  deriving (Eq, Show, Generic, Typeable)++instance (Binary loc) => Binary (SourcePackage loc)++instance Package (SourcePackage a) where packageId = packageInfoId++-- | We sometimes need to override the .cabal file in the tarball with+-- the newer one from the package index.+type PackageDescriptionOverride = Maybe ByteString
+ Distribution/Solver/Types/Variable.hs view
@@ -0,0 +1,14 @@+module Distribution.Solver.Types.Variable where++import Distribution.Solver.Types.OptionalStanza++import Distribution.PackageDescription (FlagName)++-- | Variables used by the dependency solver. This type is similar to the+-- internal 'Var' type, except that flags and stanzas are associated with+-- package names instead of package instances.+data Variable qpn =+    PackageVar qpn+  | FlagVar qpn FlagName+  | StanzaVar qpn OptionalStanza+  deriving Eq
LICENSE view
@@ -1,8 +1,5 @@-Copyright (c) 2003-2008, Isaac Jones, Simon Marlow, Martin Sjögren,-                         Bjorn Bringert, Krasimir Angelov,-                         Malcolm Wallace, Ross Patterson,-                         Lemmih, Paolo Martini, Don Stewart,-                         Duncan Coutts+Copyright (c) 2003-2017, Cabal Development Team.+See the AUTHORS file for the full list of copyright holders. All rights reserved.  Redistribution and use in source and binary forms, with or without
Main.hs view
@@ -19,1305 +19,1206 @@          ( GlobalFlags(..), globalCommand, withRepoContext          , ConfigFlags(..)          , ConfigExFlags(..), defaultConfigExFlags, configureExCommand-         , BuildFlags(..), BuildExFlags(..), SkipAddSourceDepsCheck(..)-         , buildCommand, replCommand, testCommand, benchmarkCommand-         , InstallFlags(..), defaultInstallFlags-         , installCommand, upgradeCommand, uninstallCommand-         , FetchFlags(..), fetchCommand-         , FreezeFlags(..), freezeCommand-         , genBoundsCommand-         , GetFlags(..), getCommand, unpackCommand-         , checkCommand-         , formatCommand-         , updateCommand-         , ListFlags(..), listCommand-         , InfoFlags(..), infoCommand-         , UploadFlags(..), uploadCommand-         , ReportFlags(..), reportCommand-         , runCommand-         , InitFlags(initVerbosity), initCommand-         , SDistFlags(..), SDistExFlags(..), sdistCommand-         , Win32SelfUpgradeFlags(..), win32SelfUpgradeCommand-         , ActAsSetupFlags(..), actAsSetupCommand-         , SandboxFlags(..), sandboxCommand-         , ExecFlags(..), execCommand-         , UserConfigFlags(..), userConfigCommand-         , reportCommand-         , manpageCommand-         )-import Distribution.Simple.Setup-         ( HaddockFlags(..), haddockCommand, defaultHaddockFlags-         , HscolourFlags(..), hscolourCommand-         , ReplFlags(..)-         , CopyFlags(..), copyCommand-         , RegisterFlags(..), registerCommand-         , CleanFlags(..), cleanCommand-         , TestFlags(..), BenchmarkFlags(..)-         , Flag(..), fromFlag, fromFlagOrDefault, flagToMaybe, toFlag-         , configAbsolutePaths-         )--import Distribution.Client.SetupWrapper-         ( setupWrapper, SetupScriptOptions(..), defaultSetupScriptOptions )-import Distribution.Client.Config-         ( SavedConfig(..), loadConfig, defaultConfigFile, userConfigDiff-         , userConfigUpdate, createDefaultConfigFile, getConfigFilePath )-import Distribution.Client.Targets-         ( readUserTargets )-import qualified Distribution.Client.List as List-         ( list, info )--import qualified Distribution.Client.CmdConfigure as CmdConfigure-import qualified Distribution.Client.CmdBuild     as CmdBuild-import qualified Distribution.Client.CmdRepl      as CmdRepl--import Distribution.Client.Install            (install)-import Distribution.Client.Configure          (configure)-import Distribution.Client.Update             (update)-import Distribution.Client.Exec               (exec)-import Distribution.Client.Fetch              (fetch)-import Distribution.Client.Freeze             (freeze)-import Distribution.Client.GenBounds          (genBounds)-import Distribution.Client.Check as Check     (check)---import Distribution.Client.Clean            (clean)-import qualified Distribution.Client.Upload as Upload-import Distribution.Client.Run                (run, splitRunArgs)-import Distribution.Client.SrcDist            (sdist)-import Distribution.Client.Get                (get)-import Distribution.Client.Sandbox            (sandboxInit-                                              ,sandboxAddSource-                                              ,sandboxDelete-                                              ,sandboxDeleteSource-                                              ,sandboxListSources-                                              ,sandboxHcPkg-                                              ,dumpPackageEnvironment--                                              ,getSandboxConfigFilePath-                                              ,loadConfigOrSandboxConfig-                                              ,findSavedDistPref-                                              ,initPackageDBIfNeeded-                                              ,maybeWithSandboxDirOnSearchPath-                                              ,maybeWithSandboxPackageInfo-                                              ,WereDepsReinstalled(..)-                                              ,maybeReinstallAddSourceDeps-                                              ,tryGetIndexFilePath-                                              ,sandboxBuildDir-                                              ,updateSandboxConfigFileFlag-                                              ,updateInstallDirs--                                              ,configCompilerAux'-                                              ,getPersistOrConfigCompiler-                                              ,configPackageDB')-import Distribution.Client.Sandbox.PackageEnvironment-                                              (setPackageDB-                                              ,userPackageEnvironmentFile)-import Distribution.Client.Sandbox.Timestamp  (maybeAddCompilerTimestampRecord)-import Distribution.Client.Sandbox.Types      (UseSandbox(..), whenUsingSandbox)-import Distribution.Client.Tar                (createTarGzFile)-import Distribution.Client.Types              (Password (..))-import Distribution.Client.Init               (initCabal)-import Distribution.Client.Manpage            (manpage)-import qualified Distribution.Client.Win32SelfUpgrade as Win32SelfUpgrade-import Distribution.Client.Utils              (determineNumJobs-#if defined(mingw32_HOST_OS)-                                              ,relaxEncodingErrors-#endif-                                              ,existsAndIsMoreRecentThan)--import Distribution.Package (packageId)-import Distribution.PackageDescription-         ( BuildType(..), Executable(..), buildable )-import Distribution.PackageDescription.Parse-         ( readPackageDescription )-import Distribution.PackageDescription.PrettyPrint-         ( writeGenericPackageDescription )-import qualified Distribution.Simple as Simple-import qualified Distribution.Make as Make-import Distribution.Simple.Build-         ( startInterpreter )-import Distribution.Simple.Command-         ( CommandParse(..), CommandUI(..), Command, CommandSpec(..)-         , CommandType(..), commandsRun, commandAddAction, hiddenCommand-         , commandFromSpec)-import Distribution.Simple.Compiler-         ( Compiler(..) )-import Distribution.Simple.Configure-         ( checkPersistBuildConfigOutdated, configCompilerAuxEx-         , ConfigStateFileError(..), localBuildInfoFile-         , getPersistBuildConfig, tryGetPersistBuildConfig )-import qualified Distribution.Simple.LocalBuildInfo as LBI-import Distribution.Simple.Program (defaultProgramConfiguration-                                   ,configureAllKnownPrograms-                                   ,simpleProgramInvocation-                                   ,getProgramInvocationOutput)-import Distribution.Simple.Program.Db (reconfigurePrograms)-import qualified Distribution.Simple.Setup as Cabal-import Distribution.Simple.Utils-         ( cabalVersion, die, notice, info, topHandler-         , findPackageDesc, tryFindPackageDesc )-import Distribution.Text-         ( display )-import Distribution.Verbosity as Verbosity-         ( Verbosity, normal )-import Distribution.Version-         ( Version(..), orLaterVersion )-import qualified Paths_cabal_install (version)--import System.Environment       (getArgs, getProgName)-import System.Exit              (exitFailure, exitSuccess)-import System.FilePath          ( dropExtension, splitExtension-                                , takeExtension, (</>), (<.>))-import System.IO                ( BufferMode(LineBuffering), hSetBuffering-#ifdef mingw32_HOST_OS-                                , stderr-#endif-                                , stdout )-import System.Directory         (doesFileExist, getCurrentDirectory)-import Data.List                (intercalate)-import Data.Maybe               (listToMaybe)-#if !MIN_VERSION_base(4,8,0)-import Data.Monoid              (Monoid(..))-import Control.Applicative      (pure, (<$>))-#endif-import Control.Exception        (SomeException(..), try)-import Control.Monad            (when, unless, void)---- | Entry point----main :: IO ()-main = do-  -- Enable line buffering so that we can get fast feedback even when piped.-  -- This is especially important for CI and build systems.-  hSetBuffering stdout LineBuffering-  -- The default locale encoding for Windows CLI is not UTF-8 and printing-  -- Unicode characters to it will fail unless we relax the handling of encoding-  -- errors when writing to stderr and stdout.-#ifdef mingw32_HOST_OS-  relaxEncodingErrors stdout-  relaxEncodingErrors stderr-#endif-  getArgs >>= mainWorker--mainWorker :: [String] -> IO ()-mainWorker args = topHandler $-  case commandsRun (globalCommand commands) commands args of-    CommandHelp   help                 -> printGlobalHelp help-    CommandList   opts                 -> printOptionsList opts-    CommandErrors errs                 -> printErrors errs-    CommandReadyToGo (globalFlags, commandParse)  ->-      case commandParse of-        _ | fromFlagOrDefault False (globalVersion globalFlags)-            -> printVersion-          | fromFlagOrDefault False (globalNumericVersion globalFlags)-            -> printNumericVersion-        CommandHelp     help           -> printCommandHelp help-        CommandList     opts           -> printOptionsList opts-        CommandErrors   errs           -> printErrors errs-        CommandReadyToGo action        -> do-          globalFlags' <- updateSandboxConfigFileFlag globalFlags-          action globalFlags'--  where-    printCommandHelp help = do-      pname <- getProgName-      putStr (help pname)-    printGlobalHelp help = do-      pname <- getProgName-      configFile <- defaultConfigFile-      putStr (help pname)-      putStr $ "\nYou can edit the cabal configuration file to set defaults:\n"-            ++ "  " ++ configFile ++ "\n"-      exists <- doesFileExist configFile-      when (not exists) $-          putStrLn $ "This file will be generated with sensible "-                  ++ "defaults if you run 'cabal update'."-    printOptionsList = putStr . unlines-    printErrors errs = die $ intercalate "\n" errs-    printNumericVersion = putStrLn $ display Paths_cabal_install.version-    printVersion        = putStrLn $ "cabal-install version "-                                  ++ display Paths_cabal_install.version-                                  ++ "\ncompiled using version "-                                  ++ display cabalVersion-                                  ++ " of the Cabal library "--    commands = map commandFromSpec commandSpecs-    commandSpecs =-      [ regularCmd installCommand installAction-      , regularCmd updateCommand updateAction-      , regularCmd listCommand listAction-      , regularCmd infoCommand infoAction-      , regularCmd fetchCommand fetchAction-      , regularCmd freezeCommand freezeAction-      , regularCmd getCommand getAction-      , hiddenCmd  unpackCommand unpackAction-      , regularCmd checkCommand checkAction-      , regularCmd sdistCommand sdistAction-      , regularCmd uploadCommand uploadAction-      , regularCmd reportCommand reportAction-      , regularCmd runCommand runAction-      , regularCmd initCommand initAction-      , regularCmd configureExCommand configureAction-      , regularCmd buildCommand buildAction-      , regularCmd replCommand replAction-      , regularCmd sandboxCommand sandboxAction-      , regularCmd haddockCommand haddockAction-      , regularCmd execCommand execAction-      , regularCmd userConfigCommand userConfigAction-      , regularCmd cleanCommand cleanAction-      , regularCmd genBoundsCommand genBoundsAction-      , wrapperCmd copyCommand copyVerbosity copyDistPref-      , wrapperCmd hscolourCommand hscolourVerbosity hscolourDistPref-      , wrapperCmd registerCommand regVerbosity regDistPref-      , regularCmd testCommand testAction-      , regularCmd benchmarkCommand benchmarkAction-      , hiddenCmd  uninstallCommand uninstallAction-      , hiddenCmd  formatCommand formatAction-      , hiddenCmd  upgradeCommand upgradeAction-      , hiddenCmd  win32SelfUpgradeCommand win32SelfUpgradeAction-      , hiddenCmd  actAsSetupCommand actAsSetupAction-      , hiddenCmd  manpageCommand (manpageAction commandSpecs)--      , hiddenCmd  installCommand { commandName = "new-configure" }-                                  CmdConfigure.configureAction-      , hiddenCmd  installCommand { commandName = "new-build" }-                                  CmdBuild.buildAction-      , hiddenCmd  installCommand { commandName = "new-repl" }-                                  CmdRepl.replAction-      ]--type Action = GlobalFlags -> IO ()--regularCmd :: CommandUI flags -> (flags -> [String] -> action)-           -> CommandSpec action-regularCmd ui action =-  CommandSpec ui ((flip commandAddAction) action) NormalCommand--hiddenCmd :: CommandUI flags -> (flags -> [String] -> action)-          -> CommandSpec action-hiddenCmd ui action =-  CommandSpec ui (\ui' -> hiddenCommand (commandAddAction ui' action))-  HiddenCommand--wrapperCmd :: Monoid flags => CommandUI flags -> (flags -> Flag Verbosity)-           -> (flags -> Flag String) -> CommandSpec Action-wrapperCmd ui verbosity distPref =-  CommandSpec ui (\ui' -> wrapperAction ui' verbosity distPref) NormalCommand--wrapperAction :: Monoid flags-              => CommandUI flags-              -> (flags -> Flag Verbosity)-              -> (flags -> Flag String)-              -> Command Action-wrapperAction command verbosityFlag distPrefFlag =-  commandAddAction command-    { commandDefaultFlags = mempty } $ \flags extraArgs globalFlags -> do-    let verbosity = fromFlagOrDefault normal (verbosityFlag flags)-    load <- try (loadConfigOrSandboxConfig verbosity globalFlags)-    let config = either (\(SomeException _) -> mempty) snd load-    distPref <- findSavedDistPref config (distPrefFlag flags)-    let setupScriptOptions = defaultSetupScriptOptions { useDistPref = distPref }-    setupWrapper verbosity setupScriptOptions Nothing-                 command (const flags) extraArgs--configureAction :: (ConfigFlags, ConfigExFlags)-                -> [String] -> Action-configureAction (configFlags, configExFlags) extraArgs globalFlags = do-  let verbosity = fromFlagOrDefault normal (configVerbosity configFlags)--  (useSandbox, config) <- fmap-                          (updateInstallDirs (configUserInstall configFlags))-                          (loadConfigOrSandboxConfig verbosity globalFlags)-  let configFlags'   = savedConfigureFlags   config `mappend` configFlags-      configExFlags' = savedConfigureExFlags config `mappend` configExFlags-      globalFlags'   = savedGlobalFlags      config `mappend` globalFlags-  (comp, platform, conf) <- configCompilerAuxEx configFlags'--  -- If we're working inside a sandbox and the user has set the -w option, we-  -- may need to create a sandbox-local package DB for this compiler and add a-  -- timestamp record for this compiler to the timestamp file.-  let configFlags''  = case useSandbox of-        NoSandbox               -> configFlags'-        (UseSandbox sandboxDir) -> setPackageDB sandboxDir-                                   comp platform configFlags'--  whenUsingSandbox useSandbox $ \sandboxDir -> do-    initPackageDBIfNeeded verbosity configFlags'' comp conf-    -- NOTE: We do not write the new sandbox package DB location to-    -- 'cabal.sandbox.config' here because 'configure -w' must not affect-    -- subsequent 'install' (for UI compatibility with non-sandboxed mode).--    indexFile     <- tryGetIndexFilePath config-    maybeAddCompilerTimestampRecord verbosity sandboxDir indexFile-      (compilerId comp) platform--  maybeWithSandboxDirOnSearchPath useSandbox $-   withRepoContext verbosity globalFlags' $ \repoContext ->-    configure verbosity-              (configPackageDB' configFlags'')-              repoContext-              comp platform conf configFlags'' configExFlags' extraArgs--buildAction :: (BuildFlags, BuildExFlags) -> [String] -> Action-buildAction (buildFlags, buildExFlags) extraArgs globalFlags = do-  let verbosity   = fromFlagOrDefault normal (buildVerbosity buildFlags)-      noAddSource = fromFlagOrDefault DontSkipAddSourceDepsCheck-                    (buildOnly buildExFlags)--  -- Calls 'configureAction' to do the real work, so nothing special has to be-  -- done to support sandboxes.-  (useSandbox, config, distPref) <- reconfigure verbosity-                                    (buildDistPref buildFlags)-                                    mempty [] globalFlags noAddSource-                                    (buildNumJobs buildFlags) (const Nothing)--  maybeWithSandboxDirOnSearchPath useSandbox $-    build verbosity config distPref buildFlags extraArgs----- | Actually do the work of building the package. This is separate from--- 'buildAction' so that 'testAction' and 'benchmarkAction' do not invoke--- 'reconfigure' twice.-build :: Verbosity -> SavedConfig -> FilePath -> BuildFlags -> [String] -> IO ()-build verbosity config distPref buildFlags extraArgs =-  setupWrapper verbosity setupOptions Nothing-               (Cabal.buildCommand progConf) mkBuildFlags extraArgs-  where-    progConf     = defaultProgramConfiguration-    setupOptions = defaultSetupScriptOptions { useDistPref = distPref }--    mkBuildFlags version = filterBuildFlags version config buildFlags'-    buildFlags' = buildFlags-      { buildVerbosity = toFlag verbosity-      , buildDistPref  = toFlag distPref-      }---- | Make sure that we don't pass new flags to setup scripts compiled against--- old versions of Cabal.-filterBuildFlags :: Version -> SavedConfig -> BuildFlags -> BuildFlags-filterBuildFlags version config buildFlags-  | version >= Version [1,19,1] [] = buildFlags_latest-  -- Cabal < 1.19.1 doesn't support 'build -j'.-  | otherwise                      = buildFlags_pre_1_19_1-  where-    buildFlags_pre_1_19_1 = buildFlags {-      buildNumJobs = NoFlag-      }-    buildFlags_latest     = buildFlags {-      -- Take the 'jobs' setting '~/.cabal/config' into account.-      buildNumJobs = Flag . Just . determineNumJobs $-                     (numJobsConfigFlag `mappend` numJobsCmdLineFlag)-      }-    numJobsConfigFlag  = installNumJobs . savedInstallFlags $ config-    numJobsCmdLineFlag = buildNumJobs buildFlags---replAction :: (ReplFlags, BuildExFlags) -> [String] -> Action-replAction (replFlags, buildExFlags) extraArgs globalFlags = do-  cwd     <- getCurrentDirectory-  pkgDesc <- findPackageDesc cwd-  either (const onNoPkgDesc) (const onPkgDesc) pkgDesc-  where-    verbosity = fromFlagOrDefault normal (replVerbosity replFlags)--    -- There is a .cabal file in the current directory: start a REPL and load-    -- the project's modules.-    onPkgDesc = do-      let noAddSource = case replReload replFlags of-            Flag True -> SkipAddSourceDepsCheck-            _         -> fromFlagOrDefault DontSkipAddSourceDepsCheck-                         (buildOnly buildExFlags)-      -- Calls 'configureAction' to do the real work, so nothing special has to-      -- be done to support sandboxes.-      (useSandbox, _config, distPref) <--        reconfigure verbosity (replDistPref replFlags)-                    mempty [] globalFlags noAddSource NoFlag-                    (const Nothing)-      let progConf     = defaultProgramConfiguration-          setupOptions = defaultSetupScriptOptions-            { useCabalVersion = orLaterVersion $ Version [1,18,0] []-            , useDistPref     = distPref-            }-          replFlags'   = replFlags-            { replVerbosity = toFlag verbosity-            , replDistPref  = toFlag distPref-            }--      maybeWithSandboxDirOnSearchPath useSandbox $-        setupWrapper verbosity setupOptions Nothing-        (Cabal.replCommand progConf) (const replFlags') extraArgs--    -- No .cabal file in the current directory: just start the REPL (possibly-    -- using the sandbox package DB).-    onNoPkgDesc = do-      (_useSandbox, config) <- loadConfigOrSandboxConfig verbosity globalFlags-      let configFlags = savedConfigureFlags config-      (comp, platform, programDb) <- configCompilerAux' configFlags-      programDb' <- reconfigurePrograms verbosity-                                        (replProgramPaths replFlags)-                                        (replProgramArgs replFlags)-                                        programDb-      startInterpreter verbosity programDb' comp platform-                       (configPackageDB' configFlags)---- | Re-configure the package in the current directory if needed. Deciding--- when to reconfigure and with which options is convoluted:------ If we are reconfiguring, we must always run @configure@ with the--- verbosity option we are given; however, that a previous configuration--- uses a different verbosity setting is not reason enough to reconfigure.------ The package should be configured to use the same \"dist\" prefix as--- given to the @build@ command, otherwise the build will probably--- fail. Not only does this determine the \"dist\" prefix setting if we--- need to reconfigure anyway, but an existing configuration should be--- invalidated if its \"dist\" prefix differs.------ If the package has never been configured (i.e., there is no--- LocalBuildInfo), we must configure first, using the default options.------ If the package has been configured, there will be a 'LocalBuildInfo'.--- If there no package description file, we assume that the--- 'PackageDescription' is up to date, though the configuration may need--- to be updated for other reasons (see above). If there is a package--- description file, and it has been modified since the 'LocalBuildInfo'--- was generated, then we need to reconfigure.------ The caller of this function may also have specific requirements--- regarding the flags the last configuration used. For example,--- 'testAction' requires that the package be configured with test suites--- enabled. The caller may pass the required settings to this function--- along with a function to check the validity of the saved 'ConfigFlags';--- these required settings will be checked first upon determining that--- a previous configuration exists.-reconfigure :: Verbosity    -- ^ Verbosity setting-            -> Flag FilePath  -- ^ \"dist\" prefix-            -> ConfigFlags  -- ^ Additional config flags to set. These flags-                            -- will be 'mappend'ed to the last used or-                            -- default 'ConfigFlags' as appropriate, so-                            -- this value should be 'mempty' with only the-                            -- required flags set. The required verbosity-                            -- and \"dist\" prefix flags will be set-                            -- automatically because they are always-                            -- required; therefore, it is not necessary to-                            -- set them here.-            -> [String]     -- ^ Extra arguments-            -> GlobalFlags  -- ^ Global flags-            -> SkipAddSourceDepsCheck-                            -- ^ Should we skip the timestamp check for modified-                            -- add-source dependencies?-            -> Flag (Maybe Int)-                            -- ^ -j flag for reinstalling add-source deps.-            -> (ConfigFlags -> Maybe String)-                            -- ^ Check that the required flags are set in-                            -- the last used 'ConfigFlags'. If the required-                            -- flags are not set, provide a message to the-                            -- user explaining the reason for-                            -- reconfiguration. Because the correct \"dist\"-                            -- prefix setting is always required, it is checked-                            -- automatically; this function need not check-                            -- for it.-            -> IO (UseSandbox, SavedConfig, FilePath)-reconfigure verbosity flagDistPref addConfigFlags extraArgs globalFlags-            skipAddSourceDepsCheck numJobsFlag    checkFlags = do-  (useSandbox, config) <- loadConfigOrSandboxConfig verbosity globalFlags-  distPref <- findSavedDistPref config flagDistPref-  eLbi <- tryGetPersistBuildConfig distPref-  config' <- case eLbi of-    Left err  -> onNoBuildConfig (useSandbox, config) distPref err-    Right lbi -> onBuildConfig (useSandbox, config) distPref lbi-  return (useSandbox, config', distPref)--  where--    -- We couldn't load the saved package config file.-    ---    -- If we're in a sandbox: add-source deps don't have to be reinstalled-    -- (since we don't know the compiler & platform).-    onNoBuildConfig :: (UseSandbox, SavedConfig) -> FilePath-                    -> ConfigStateFileError -> IO SavedConfig-    onNoBuildConfig (_, config) distPref err = do-      let msg = case err of-            ConfigStateFileMissing -> "Package has never been configured."-            ConfigStateFileNoParse -> "Saved package config file seems "-                                      ++ "to be corrupt."-            _ -> show err-      case err of-        -- Note: the build config could have been generated by a custom setup-        -- script built against a different Cabal version, so it's crucial that-        -- we ignore the bad version error here.-        ConfigStateFileBadVersion _ _ _ -> info verbosity msg-        _                               -> do-          let distVerbFlags = mempty-                { configVerbosity = toFlag verbosity-                , configDistPref  = toFlag distPref-                }-              defaultFlags = mappend addConfigFlags distVerbFlags-          notice verbosity-            $ msg ++ " Configuring with default flags." ++ configureManually-          configureAction (defaultFlags, defaultConfigExFlags)-            extraArgs globalFlags-      return config--    -- Package has been configured, but the configuration may be out of-    -- date or required flags may not be set.-    ---    -- If we're in a sandbox: reinstall the modified add-source deps and-    -- force reconfigure if we did.-    onBuildConfig :: (UseSandbox, SavedConfig) -> FilePath-                  -> LBI.LocalBuildInfo -> IO SavedConfig-    onBuildConfig (useSandbox, config) distPref lbi = do-      let configFlags = LBI.configFlags lbi-          distVerbFlags = mempty-            { configVerbosity = toFlag verbosity-            , configDistPref  = toFlag distPref-            }-          flags       = mconcat [configFlags, addConfigFlags, distVerbFlags]--      -- Was the sandbox created after the package was already configured? We-      -- may need to skip reinstallation of add-source deps and force-      -- reconfigure.-      let buildConfig       = localBuildInfoFile distPref-      sandboxConfig        <- getSandboxConfigFilePath globalFlags-      isSandboxConfigNewer <--        sandboxConfig `existsAndIsMoreRecentThan` buildConfig--      let skipAddSourceDepsCheck'-            | isSandboxConfigNewer = SkipAddSourceDepsCheck-            | otherwise            = skipAddSourceDepsCheck--      when (skipAddSourceDepsCheck' == SkipAddSourceDepsCheck) $-        info verbosity "Skipping add-source deps check..."--      let (_, config') = updateInstallDirs-                         (configUserInstall flags)-                         (useSandbox, config)--      depsReinstalled <--        case skipAddSourceDepsCheck' of-          DontSkipAddSourceDepsCheck ->-            maybeReinstallAddSourceDeps-              verbosity numJobsFlag flags globalFlags-              (useSandbox, config')-          SkipAddSourceDepsCheck -> do-            return NoDepsReinstalled--      -- Is the @cabal.config@ file newer than @dist/setup.config@? Then we need-      -- to force reconfigure. Note that it's possible to use @cabal.config@-      -- even without sandboxes.-      isUserPackageEnvironmentFileNewer <--        userPackageEnvironmentFile `existsAndIsMoreRecentThan` buildConfig--      -- Determine whether we need to reconfigure and which message to show to-      -- the user if that is the case.-      mMsg <- determineMessageToShow distPref lbi configFlags-                                     depsReinstalled isSandboxConfigNewer-                                     isUserPackageEnvironmentFileNewer-      case mMsg of--        -- No message for the user indicates that reconfiguration-        -- is not required.-        Nothing -> return config'--        -- Show the message and reconfigure.-        Just msg -> do-          notice verbosity msg-          configureAction (flags, defaultConfigExFlags)-            extraArgs globalFlags-          return config'--    -- Determine what message, if any, to display to the user if reconfiguration-    -- is required.-    determineMessageToShow :: FilePath -> LBI.LocalBuildInfo -> ConfigFlags-                            -> WereDepsReinstalled -> Bool -> Bool-                            -> IO (Maybe String)-    determineMessageToShow _ _   _           _               True  _     =-      -- The sandbox was created after the package was already configured.-      return $! Just $! sandboxConfigNewerMessage--    determineMessageToShow _ _   _           _               False True  =-      -- The user package environment file was modified.-      return $! Just $! userPackageEnvironmentFileModifiedMessage--    determineMessageToShow distPref lbi configFlags depsReinstalled-                           False False = do-      let savedDistPref = fromFlagOrDefault-                          (useDistPref defaultSetupScriptOptions)-                          (configDistPref configFlags)-      case depsReinstalled of-        ReinstalledSomeDeps ->-          -- Some add-source deps were reinstalled.-          return $! Just $! reinstalledDepsMessage-        NoDepsReinstalled ->-          case checkFlags configFlags of-            -- Flag required by the caller is not set.-            Just msg -> return $! Just $! msg ++ configureManually--            Nothing-              -- Required "dist" prefix is not set.-              | savedDistPref /= distPref ->-                return $! Just distPrefMessage--              -- All required flags are set, but the configuration-              -- may be outdated.-              | otherwise -> case LBI.pkgDescrFile lbi of-                Nothing -> return Nothing-                Just pdFile -> do-                  outdated <- checkPersistBuildConfigOutdated-                              distPref pdFile-                  return $! if outdated-                            then Just $! outdatedMessage pdFile-                            else Nothing--    reconfiguringMostRecent = " Re-configuring with most recently used options."-    configureManually       = " If this fails, please run configure manually."-    sandboxConfigNewerMessage =-        "The sandbox was created after the package was already configured."-        ++ reconfiguringMostRecent-        ++ configureManually-    userPackageEnvironmentFileModifiedMessage =-        "The user package environment file ('"-        ++ userPackageEnvironmentFile ++ "') was modified."-        ++ reconfiguringMostRecent-        ++ configureManually-    distPrefMessage =-        "Package previously configured with different \"dist\" prefix."-        ++ reconfiguringMostRecent-        ++ configureManually-    outdatedMessage pdFile =-        pdFile ++ " has been changed."-        ++ reconfiguringMostRecent-        ++ configureManually-    reinstalledDepsMessage =-        "Some add-source dependencies have been reinstalled."-        ++ reconfiguringMostRecent-        ++ configureManually--installAction :: (ConfigFlags, ConfigExFlags, InstallFlags, HaddockFlags)-              -> [String] -> Action-installAction (configFlags, _, installFlags, _) _ globalFlags-  | fromFlagOrDefault False (installOnly installFlags) = do-      let verbosity = fromFlagOrDefault normal (configVerbosity configFlags)-      load <- try (loadConfigOrSandboxConfig verbosity globalFlags)-      let config = either (\(SomeException _) -> mempty) snd load-      distPref <- findSavedDistPref config (configDistPref configFlags)-      let setupOpts = defaultSetupScriptOptions { useDistPref = distPref }-      setupWrapper verbosity setupOpts Nothing installCommand (const mempty) []--installAction (configFlags, configExFlags, installFlags, haddockFlags)-              extraArgs globalFlags = do-  let verbosity = fromFlagOrDefault normal (configVerbosity configFlags)-  (useSandbox, config) <- fmap-                          (updateInstallDirs (configUserInstall configFlags))-                          (loadConfigOrSandboxConfig verbosity globalFlags)-  targets <- readUserTargets verbosity extraArgs--  -- TODO: It'd be nice if 'cabal install' picked up the '-w' flag passed to-  -- 'configure' when run inside a sandbox.  Right now, running-  ---  -- $ cabal sandbox init && cabal configure -w /path/to/ghc-  --   && cabal build && cabal install-  ---  -- performs the compilation twice unless you also pass -w to 'install'.-  -- However, this is the same behaviour that 'cabal install' has in the normal-  -- mode of operation, so we stick to it for consistency.--  let sandboxDistPref = case useSandbox of-        NoSandbox             -> NoFlag-        UseSandbox sandboxDir -> Flag $ sandboxBuildDir sandboxDir-  distPref <- findSavedDistPref config-              (configDistPref configFlags `mappend` sandboxDistPref)--  let configFlags'    = maybeForceTests installFlags' $-                        savedConfigureFlags   config `mappend`-                        configFlags { configDistPref = toFlag distPref }-      configExFlags'  = defaultConfigExFlags         `mappend`-                        savedConfigureExFlags config `mappend` configExFlags-      installFlags'   = defaultInstallFlags          `mappend`-                        savedInstallFlags     config `mappend` installFlags-      haddockFlags'   = defaultHaddockFlags          `mappend`-                        savedHaddockFlags     config `mappend`-                        haddockFlags { haddockDistPref = toFlag distPref }-      globalFlags'    = savedGlobalFlags      config `mappend` globalFlags-  (comp, platform, conf) <- configCompilerAux' configFlags'-  -- TODO: Redesign ProgramDB API to prevent such problems as #2241 in the-  -- future.-  conf' <- configureAllKnownPrograms verbosity conf--  -- If we're working inside a sandbox and the user has set the -w option, we-  -- may need to create a sandbox-local package DB for this compiler and add a-  -- timestamp record for this compiler to the timestamp file.-  configFlags'' <- case useSandbox of-    NoSandbox               -> configAbsolutePaths $ configFlags'-    (UseSandbox sandboxDir) -> return $ setPackageDB sandboxDir comp platform-                                                     configFlags'--  whenUsingSandbox useSandbox $ \sandboxDir -> do-    initPackageDBIfNeeded verbosity configFlags'' comp conf'--    indexFile     <- tryGetIndexFilePath config-    maybeAddCompilerTimestampRecord verbosity sandboxDir indexFile-      (compilerId comp) platform--  -- TODO: Passing 'SandboxPackageInfo' to install unconditionally here means-  -- that 'cabal install some-package' inside a sandbox will sometimes reinstall-  -- modified add-source deps, even if they are not among the dependencies of-  -- 'some-package'. This can also prevent packages that depend on older-  -- versions of add-source'd packages from building (see #1362).-  maybeWithSandboxPackageInfo verbosity configFlags'' globalFlags'-                              comp platform conf useSandbox $ \mSandboxPkgInfo ->-                              maybeWithSandboxDirOnSearchPath useSandbox $-    withRepoContext verbosity globalFlags' $ \repoContext ->-      install verbosity-              (configPackageDB' configFlags'')-              repoContext-              comp platform conf'-              useSandbox mSandboxPkgInfo-              globalFlags' configFlags'' configExFlags'-              installFlags' haddockFlags'-              targets--    where-      -- '--run-tests' implies '--enable-tests'.-      maybeForceTests installFlags' configFlags' =-        if fromFlagOrDefault False (installRunTests installFlags')-        then configFlags' { configTests = toFlag True }-        else configFlags'--testAction :: (TestFlags, BuildFlags, BuildExFlags) -> [String] -> GlobalFlags-           -> IO ()-testAction (testFlags, buildFlags, buildExFlags) extraArgs globalFlags = do-  let verbosity      = fromFlagOrDefault normal (testVerbosity testFlags)-      addConfigFlags = mempty { configTests = toFlag True }-      noAddSource    = fromFlagOrDefault DontSkipAddSourceDepsCheck-                       (buildOnly buildExFlags)-      buildFlags'    = buildFlags-                       { buildVerbosity = testVerbosity testFlags }-      checkFlags flags-        | fromFlagOrDefault False (configTests flags) = Nothing-        | otherwise  = Just "Re-configuring with test suites enabled."--  -- reconfigure also checks if we're in a sandbox and reinstalls add-source-  -- deps if needed.-  (useSandbox, config, distPref) <--    reconfigure verbosity (testDistPref testFlags)-                addConfigFlags [] globalFlags noAddSource-                (buildNumJobs buildFlags') checkFlags-  let setupOptions   = defaultSetupScriptOptions { useDistPref = distPref }-      testFlags'     = testFlags { testDistPref = toFlag distPref }--  -- The package was just configured, so the LBI must be available.-  names <- componentNamesFromLBI verbosity distPref "test suites"-             (\c -> case c of { LBI.CTest{} -> True; _ -> False })-  let extraArgs'-        | null extraArgs = case names of-          ComponentNamesUnknown -> []-          ComponentNames names' -> [ name | LBI.CTestName name <- names' ]-        | otherwise      = extraArgs--  maybeWithSandboxDirOnSearchPath useSandbox $-    build verbosity config distPref buildFlags' extraArgs'--  maybeWithSandboxDirOnSearchPath useSandbox $-    setupWrapper verbosity setupOptions Nothing-    Cabal.testCommand (const testFlags') extraArgs'--data ComponentNames = ComponentNamesUnknown-                    | ComponentNames [LBI.ComponentName]---- | Return the names of all buildable components matching a given predicate.-componentNamesFromLBI :: Verbosity -> FilePath -> String-                         -> (LBI.Component -> Bool)-                         -> IO ComponentNames-componentNamesFromLBI verbosity distPref targetsDescr compPred = do-  eLBI <- tryGetPersistBuildConfig distPref-  case eLBI of-    Left err -> case err of-      -- Note: the build config could have been generated by a custom setup-      -- script built against a different Cabal version, so it's crucial that-      -- we ignore the bad version error here.-      ConfigStateFileBadVersion _ _ _ -> return ComponentNamesUnknown-      _                               -> die (show err)-    Right lbi -> do-      let pkgDescr = LBI.localPkgDescr lbi-          names    = map LBI.componentName-                     . filter (buildable . LBI.componentBuildInfo)-                     . filter compPred $-                     LBI.pkgComponents pkgDescr-      if null names-        then do notice verbosity $ "Package has no buildable "-                  ++ targetsDescr ++ "."-                exitSuccess -- See #3215.--        else return $! (ComponentNames names)--benchmarkAction :: (BenchmarkFlags, BuildFlags, BuildExFlags)-                   -> [String] -> GlobalFlags-                   -> IO ()-benchmarkAction (benchmarkFlags, buildFlags, buildExFlags)-                extraArgs globalFlags = do-  let verbosity      = fromFlagOrDefault normal-                       (benchmarkVerbosity benchmarkFlags)-      addConfigFlags = mempty { configBenchmarks = toFlag True }-      buildFlags'    = buildFlags-                       { buildVerbosity = benchmarkVerbosity benchmarkFlags }-      checkFlags flags-        | fromFlagOrDefault False (configBenchmarks flags) = Nothing-        | otherwise = Just "Re-configuring with benchmarks enabled."-      noAddSource   = fromFlagOrDefault DontSkipAddSourceDepsCheck-                      (buildOnly buildExFlags)--  -- reconfigure also checks if we're in a sandbox and reinstalls add-source-  -- deps if needed.-  (useSandbox, config, distPref) <--    reconfigure verbosity (benchmarkDistPref benchmarkFlags)-                addConfigFlags [] globalFlags noAddSource-                (buildNumJobs buildFlags') checkFlags-  let setupOptions   = defaultSetupScriptOptions { useDistPref = distPref }-      benchmarkFlags'= benchmarkFlags { benchmarkDistPref = toFlag distPref }--  -- The package was just configured, so the LBI must be available.-  names <- componentNamesFromLBI verbosity distPref "benchmarks"-           (\c -> case c of { LBI.CBench{} -> True; _ -> False; })-  let extraArgs'-        | null extraArgs = case names of-          ComponentNamesUnknown -> []-          ComponentNames names' -> [name | LBI.CBenchName name <- names']-        | otherwise      = extraArgs--  maybeWithSandboxDirOnSearchPath useSandbox $-    build verbosity config distPref buildFlags' extraArgs'--  maybeWithSandboxDirOnSearchPath useSandbox $-    setupWrapper verbosity setupOptions Nothing-    Cabal.benchmarkCommand (const benchmarkFlags') extraArgs'--haddockAction :: HaddockFlags -> [String] -> Action-haddockAction haddockFlags extraArgs globalFlags = do-  let verbosity = fromFlag (haddockVerbosity haddockFlags)-  (_useSandbox, config, distPref) <--    reconfigure verbosity (haddockDistPref haddockFlags)-                mempty [] globalFlags DontSkipAddSourceDepsCheck-                NoFlag (const Nothing)-  let haddockFlags' = defaultHaddockFlags      `mappend`-                      savedHaddockFlags config `mappend`-                      haddockFlags { haddockDistPref = toFlag distPref }-      setupScriptOptions = defaultSetupScriptOptions { useDistPref = distPref }-  setupWrapper verbosity setupScriptOptions Nothing-    haddockCommand (const haddockFlags') extraArgs-  when (haddockForHackage haddockFlags == Flag True) $ do-    pkg <- fmap LBI.localPkgDescr (getPersistBuildConfig distPref)-    let dest = distPref </> name <.> "tar.gz"-        name = display (packageId pkg) ++ "-docs"-        docDir = distPref </> "doc" </> "html"-    createTarGzFile dest docDir name-    notice verbosity $ "Documentation tarball created: " ++ dest--cleanAction :: CleanFlags -> [String] -> Action-cleanAction cleanFlags extraArgs globalFlags = do-  load <- try (loadConfigOrSandboxConfig verbosity globalFlags)-  let config = either (\(SomeException _) -> mempty) snd load-  distPref <- findSavedDistPref config (cleanDistPref cleanFlags)-  let setupScriptOptions = defaultSetupScriptOptions-                           { useDistPref = distPref-                           , useWin32CleanHack = True-                           }-      cleanFlags' = cleanFlags { cleanDistPref = toFlag distPref }-  setupWrapper verbosity setupScriptOptions Nothing-               cleanCommand (const cleanFlags') extraArgs-  where-    verbosity = fromFlagOrDefault normal (cleanVerbosity cleanFlags)--listAction :: ListFlags -> [String] -> Action-listAction listFlags extraArgs globalFlags = do-  let verbosity = fromFlag (listVerbosity listFlags)-  (_useSandbox, config) <- loadConfigOrSandboxConfig verbosity-                           (globalFlags { globalRequireSandbox = Flag False })-  let configFlags' = savedConfigureFlags config-      configFlags  = configFlags' {-        configPackageDBs = configPackageDBs configFlags'-                           `mappend` listPackageDBs listFlags-        }-      globalFlags' = savedGlobalFlags    config `mappend` globalFlags-  (comp, _, conf) <- configCompilerAux' configFlags-  withRepoContext verbosity globalFlags' $ \repoContext ->-    List.list verbosity-       (configPackageDB' configFlags)-       repoContext-       comp-       conf-       listFlags-       extraArgs--infoAction :: InfoFlags -> [String] -> Action-infoAction infoFlags extraArgs globalFlags = do-  let verbosity = fromFlag (infoVerbosity infoFlags)-  targets <- readUserTargets verbosity extraArgs-  (_useSandbox, config) <- loadConfigOrSandboxConfig verbosity-                           (globalFlags { globalRequireSandbox = Flag False })-  let configFlags' = savedConfigureFlags config-      configFlags  = configFlags' {-        configPackageDBs = configPackageDBs configFlags'-                           `mappend` infoPackageDBs infoFlags-        }-      globalFlags' = savedGlobalFlags    config `mappend` globalFlags-  (comp, _, conf) <- configCompilerAuxEx configFlags-  withRepoContext verbosity globalFlags' $ \repoContext ->-    List.info verbosity-       (configPackageDB' configFlags)-       repoContext-       comp-       conf-       globalFlags'-       infoFlags-       targets--updateAction :: Flag Verbosity -> [String] -> Action-updateAction verbosityFlag extraArgs globalFlags = do-  unless (null extraArgs) $-    die $ "'update' doesn't take any extra arguments: " ++ unwords extraArgs-  let verbosity = fromFlag verbosityFlag-  (_useSandbox, config) <- loadConfigOrSandboxConfig verbosity-                           (globalFlags { globalRequireSandbox = Flag False })-  let globalFlags' = savedGlobalFlags config `mappend` globalFlags-  withRepoContext verbosity globalFlags' $ \repoContext ->-    update verbosity repoContext--upgradeAction :: (ConfigFlags, ConfigExFlags, InstallFlags, HaddockFlags)-              -> [String] -> Action-upgradeAction _ _ _ = die $-    "Use the 'cabal install' command instead of 'cabal upgrade'.\n"- ++ "You can install the latest version of a package using 'cabal install'. "- ++ "The 'cabal upgrade' command has been removed because people found it "- ++ "confusing and it often led to broken packages.\n"- ++ "If you want the old upgrade behaviour then use the install command "- ++ "with the --upgrade-dependencies flag (but check first with --dry-run "- ++ "to see what would happen). This will try to pick the latest versions "- ++ "of all dependencies, rather than the usual behaviour of trying to pick "- ++ "installed versions of all dependencies. If you do use "- ++ "--upgrade-dependencies, it is recommended that you do not upgrade core "- ++ "packages (e.g. by using appropriate --constraint= flags)."--fetchAction :: FetchFlags -> [String] -> Action-fetchAction fetchFlags extraArgs globalFlags = do-  let verbosity = fromFlag (fetchVerbosity fetchFlags)-  targets <- readUserTargets verbosity extraArgs-  config <- loadConfig verbosity (globalConfigFile globalFlags)-  let configFlags  = savedConfigureFlags config-      globalFlags' = savedGlobalFlags config `mappend` globalFlags-  (comp, platform, conf) <- configCompilerAux' configFlags-  withRepoContext verbosity globalFlags' $ \repoContext ->-    fetch verbosity-        (configPackageDB' configFlags)-        repoContext-        comp platform conf globalFlags' fetchFlags-        targets--freezeAction :: FreezeFlags -> [String] -> Action-freezeAction freezeFlags _extraArgs globalFlags = do-  let verbosity = fromFlag (freezeVerbosity freezeFlags)-  (useSandbox, config) <- loadConfigOrSandboxConfig verbosity globalFlags-  let configFlags  = savedConfigureFlags config-      globalFlags' = savedGlobalFlags config `mappend` globalFlags-  (comp, platform, conf) <- configCompilerAux' configFlags--  maybeWithSandboxPackageInfo verbosity configFlags globalFlags'-                              comp platform conf useSandbox $ \mSandboxPkgInfo ->-                              maybeWithSandboxDirOnSearchPath useSandbox $-    withRepoContext verbosity globalFlags' $ \repoContext ->-      freeze verbosity-            (configPackageDB' configFlags)-            repoContext-            comp platform conf-            mSandboxPkgInfo-            globalFlags' freezeFlags--genBoundsAction :: FreezeFlags -> [String] -> GlobalFlags -> IO ()-genBoundsAction freezeFlags _extraArgs globalFlags = do-  let verbosity = fromFlag (freezeVerbosity freezeFlags)-  (useSandbox, config) <- loadConfigOrSandboxConfig verbosity globalFlags-  let configFlags  = savedConfigureFlags config-      globalFlags' = savedGlobalFlags config `mappend` globalFlags-  (comp, platform, conf) <- configCompilerAux' configFlags--  maybeWithSandboxPackageInfo verbosity configFlags globalFlags'-                              comp platform conf useSandbox $ \mSandboxPkgInfo ->-                              maybeWithSandboxDirOnSearchPath useSandbox $-    withRepoContext verbosity globalFlags' $ \repoContext ->-      genBounds verbosity-            (configPackageDB' configFlags)-            repoContext-            comp platform conf-            mSandboxPkgInfo-            globalFlags' freezeFlags--uploadAction :: UploadFlags -> [String] -> Action-uploadAction uploadFlags extraArgs globalFlags = do-  config <- loadConfig verbosity (globalConfigFile globalFlags)-  let uploadFlags' = savedUploadFlags config `mappend` uploadFlags-      globalFlags' = savedGlobalFlags config `mappend` globalFlags-      tarfiles     = extraArgs-  when (null tarfiles && not (fromFlag (uploadDoc uploadFlags'))) $-    die "the 'upload' command expects at least one .tar.gz archive."-  when (fromFlag (uploadCheck uploadFlags')-        && fromFlag (uploadDoc uploadFlags')) $-    die "--check and --doc cannot be used together."-  checkTarFiles extraArgs-  maybe_password <--    case uploadPasswordCmd uploadFlags'-    of Flag (xs:xss) -> Just . Password <$>-                        getProgramInvocationOutput verbosity-                        (simpleProgramInvocation xs xss)-       _             -> pure $ flagToMaybe $ uploadPassword uploadFlags'-  withRepoContext verbosity globalFlags' $ \repoContext -> do-    if fromFlag (uploadCheck uploadFlags')-    then do-      Upload.check verbosity repoContext tarfiles-    else if fromFlag (uploadDoc uploadFlags')-    then do-      when (length tarfiles > 1) $-       die $ "the 'upload' command can only upload documentation "-             ++ "for one package at a time."-      tarfile <- maybe (generateDocTarball config) return $ listToMaybe tarfiles-      Upload.uploadDoc verbosity-                       repoContext-                       (flagToMaybe $ uploadUsername uploadFlags')-                       maybe_password-                       tarfile-    else do-      Upload.upload verbosity-                    repoContext-                    (flagToMaybe $ uploadUsername uploadFlags')-                    maybe_password-                    tarfiles-    where-    verbosity = fromFlag (uploadVerbosity uploadFlags)-    checkTarFiles tarfiles-      | not (null otherFiles)-      = die $ "the 'upload' command expects only .tar.gz archives: "-           ++ intercalate ", " otherFiles-      | otherwise = sequence_-                      [ do exists <- doesFileExist tarfile-                           unless exists $ die $ "file not found: " ++ tarfile-                      | tarfile <- tarfiles ]--      where otherFiles = filter (not . isTarGzFile) tarfiles-            isTarGzFile file = case splitExtension file of-              (file', ".gz") -> takeExtension file' == ".tar"-              _              -> False-    generateDocTarball config = do-      notice verbosity $-        "No documentation tarball specified. "-        ++ "Building a documentation tarball with default settings...\n"-        ++ "If you need to customise Haddock options, "-        ++ "run 'haddock --for-hackage' first "-        ++ "to generate a documentation tarball."-      haddockAction (defaultHaddockFlags { haddockForHackage = Flag True })-                    [] globalFlags-      distPref <- findSavedDistPref config NoFlag-      pkg <- fmap LBI.localPkgDescr (getPersistBuildConfig distPref)-      return $ distPref </> display (packageId pkg) ++ "-docs" <.> "tar.gz"--checkAction :: Flag Verbosity -> [String] -> Action-checkAction verbosityFlag extraArgs _globalFlags = do-  unless (null extraArgs) $-    die $ "'check' doesn't take any extra arguments: " ++ unwords extraArgs-  allOk <- Check.check (fromFlag verbosityFlag)-  unless allOk exitFailure--formatAction :: Flag Verbosity -> [String] -> Action-formatAction verbosityFlag extraArgs _globalFlags = do-  let verbosity = fromFlag verbosityFlag-  path <- case extraArgs of-    [] -> do cwd <- getCurrentDirectory-             tryFindPackageDesc cwd-    (p:_) -> return p-  pkgDesc <- readPackageDescription verbosity path-  -- Uses 'writeFileAtomic' under the hood.-  writeGenericPackageDescription path pkgDesc--uninstallAction :: Flag Verbosity -> [String] -> Action-uninstallAction _verbosityFlag extraArgs _globalFlags = do-  let package = case extraArgs of-        p:_ -> p-        _   -> "PACKAGE_NAME"-  die $ "This version of 'cabal-install' does not support the 'uninstall' "-    ++ "operation. "-    ++ "It will likely be implemented at some point in the future; "-    ++ "in the meantime you're advised to use either 'ghc-pkg unregister "-    ++ package ++ "' or 'cabal sandbox hc-pkg -- unregister " ++ package ++ "'."---sdistAction :: (SDistFlags, SDistExFlags) -> [String] -> Action-sdistAction (sdistFlags, sdistExFlags) extraArgs globalFlags = do-  unless (null extraArgs) $-    die $ "'sdist' doesn't take any extra arguments: " ++ unwords extraArgs-  let verbosity = fromFlag (sDistVerbosity sdistFlags)-  load <- try (loadConfigOrSandboxConfig verbosity globalFlags)-  let config = either (\(SomeException _) -> mempty) snd load-  distPref <- findSavedDistPref config (sDistDistPref sdistFlags)-  let sdistFlags' = sdistFlags { sDistDistPref = toFlag distPref }-  sdist sdistFlags' sdistExFlags--reportAction :: ReportFlags -> [String] -> Action-reportAction reportFlags extraArgs globalFlags = do-  unless (null extraArgs) $-    die $ "'report' doesn't take any extra arguments: " ++ unwords extraArgs--  let verbosity = fromFlag (reportVerbosity reportFlags)-  config <- loadConfig verbosity (globalConfigFile globalFlags)-  let globalFlags' = savedGlobalFlags config `mappend` globalFlags-      reportFlags' = savedReportFlags config `mappend` reportFlags--  withRepoContext verbosity globalFlags' $ \repoContext ->-   Upload.report verbosity repoContext-    (flagToMaybe $ reportUsername reportFlags')-    (flagToMaybe $ reportPassword reportFlags')--runAction :: (BuildFlags, BuildExFlags) -> [String] -> Action-runAction (buildFlags, buildExFlags) extraArgs globalFlags = do-  let verbosity   = fromFlagOrDefault normal (buildVerbosity buildFlags)-  let noAddSource = fromFlagOrDefault DontSkipAddSourceDepsCheck-                    (buildOnly buildExFlags)--  -- reconfigure also checks if we're in a sandbox and reinstalls add-source-  -- deps if needed.-  (useSandbox, config, distPref) <--    reconfigure verbosity (buildDistPref buildFlags) mempty []-                globalFlags noAddSource (buildNumJobs buildFlags)-                (const Nothing)--  lbi <- getPersistBuildConfig distPref-  (exe, exeArgs) <- splitRunArgs verbosity lbi extraArgs--  maybeWithSandboxDirOnSearchPath useSandbox $-    build verbosity config distPref buildFlags ["exe:" ++ exeName exe]--  maybeWithSandboxDirOnSearchPath useSandbox $-    run verbosity lbi exe exeArgs--getAction :: GetFlags -> [String] -> Action-getAction getFlags extraArgs globalFlags = do-  let verbosity = fromFlag (getVerbosity getFlags)-  targets <- readUserTargets verbosity extraArgs-  (_useSandbox, config) <- loadConfigOrSandboxConfig verbosity-                           (globalFlags { globalRequireSandbox = Flag False })-  let globalFlags' = savedGlobalFlags config `mappend` globalFlags-  withRepoContext verbosity (savedGlobalFlags config) $ \repoContext ->-   get verbosity-    repoContext-    globalFlags'-    getFlags-    targets--unpackAction :: GetFlags -> [String] -> Action-unpackAction getFlags extraArgs globalFlags = do-  getAction getFlags extraArgs globalFlags--initAction :: InitFlags -> [String] -> Action-initAction initFlags extraArgs globalFlags = do-  when (extraArgs /= []) $-    die $ "'init' doesn't take any extra arguments: " ++ unwords extraArgs-  let verbosity = fromFlag (initVerbosity initFlags)-  (_useSandbox, config) <- loadConfigOrSandboxConfig verbosity-                           (globalFlags { globalRequireSandbox = Flag False })-  let configFlags  = savedConfigureFlags config-  let globalFlags' = savedGlobalFlags    config `mappend` globalFlags-  (comp, _, conf) <- configCompilerAux' configFlags-  withRepoContext verbosity globalFlags' $ \repoContext ->-    initCabal verbosity-            (configPackageDB' configFlags)-            repoContext-            comp-            conf-            initFlags--sandboxAction :: SandboxFlags -> [String] -> Action-sandboxAction sandboxFlags extraArgs globalFlags = do-  let verbosity = fromFlag (sandboxVerbosity sandboxFlags)-  case extraArgs of-    -- Basic sandbox commands.-    ["init"] -> sandboxInit verbosity sandboxFlags globalFlags-    ["delete"] -> sandboxDelete verbosity sandboxFlags globalFlags-    ("add-source":extra) -> do-        when (noExtraArgs extra) $-          die "The 'sandbox add-source' command expects at least one argument"-        sandboxAddSource verbosity extra sandboxFlags globalFlags-    ("delete-source":extra) -> do-        when (noExtraArgs extra) $-          die ("The 'sandbox delete-source' command expects " ++-              "at least one argument")-        sandboxDeleteSource verbosity extra sandboxFlags globalFlags-    ["list-sources"] -> sandboxListSources verbosity sandboxFlags globalFlags--    -- More advanced commands.-    ("hc-pkg":extra) -> do-        when (noExtraArgs extra) $-            die $ "The 'sandbox hc-pkg' command expects at least one argument"-        sandboxHcPkg verbosity sandboxFlags globalFlags extra-    ["buildopts"] -> die "Not implemented!"--    -- Hidden commands.-    ["dump-pkgenv"]  -> dumpPackageEnvironment verbosity sandboxFlags globalFlags--    -- Error handling.-    [] -> die $ "Please specify a subcommand (see 'help sandbox')"-    _  -> die $ "Unknown 'sandbox' subcommand: " ++ unwords extraArgs--  where-    noExtraArgs = (<1) . length--execAction :: ExecFlags -> [String] -> Action-execAction execFlags extraArgs globalFlags = do-  let verbosity = fromFlag (execVerbosity execFlags)-  (useSandbox, config) <- loadConfigOrSandboxConfig verbosity globalFlags-  let configFlags = savedConfigureFlags config-  (comp, platform, conf) <- getPersistOrConfigCompiler configFlags-  exec verbosity useSandbox comp platform conf extraArgs--userConfigAction :: UserConfigFlags -> [String] -> Action-userConfigAction ucflags extraArgs globalFlags = do-  let verbosity = fromFlag (userConfigVerbosity ucflags)-      force     = fromFlag (userConfigForce ucflags)-  case extraArgs of-    ("init":_) -> do-      path       <- configFile-      fileExists <- doesFileExist path-      if (not fileExists || (fileExists && force))-      then void $ createDefaultConfigFile verbosity path-      else die $ path ++ " already exists."-    ("diff":_) -> mapM_ putStrLn =<< userConfigDiff globalFlags-    ("update":_) -> userConfigUpdate verbosity globalFlags-    -- Error handling.-    [] -> die $ "Please specify a subcommand (see 'help user-config')"-    _  -> die $ "Unknown 'user-config' subcommand: " ++ unwords extraArgs-  where configFile = getConfigFilePath (globalConfigFile globalFlags)---- | See 'Distribution.Client.Install.withWin32SelfUpgrade' for details.----win32SelfUpgradeAction :: Win32SelfUpgradeFlags -> [String] -> Action-win32SelfUpgradeAction selfUpgradeFlags (pid:path:_extraArgs) _globalFlags = do-  let verbosity = fromFlag (win32SelfUpgradeVerbosity selfUpgradeFlags)-  Win32SelfUpgrade.deleteOldExeFile verbosity (read pid) path-win32SelfUpgradeAction _ _ _ = return ()---- | Used as an entry point when cabal-install needs to invoke itself--- as a setup script. This can happen e.g. when doing parallel builds.----actAsSetupAction :: ActAsSetupFlags -> [String] -> Action-actAsSetupAction actAsSetupFlags args _globalFlags =-  let bt = fromFlag (actAsSetupBuildType actAsSetupFlags)-  in case bt of-    Simple    -> Simple.defaultMainArgs args-    Configure -> Simple.defaultMainWithHooksArgs-                  Simple.autoconfUserHooks args-    Make      -> Make.defaultMainArgs args-    Custom               -> error "actAsSetupAction Custom"-    (UnknownBuildType _) -> error "actAsSetupAction UnknownBuildType"--manpageAction :: [CommandSpec action] -> Flag Verbosity -> [String] -> Action-manpageAction commands _ extraArgs _ = do-  unless (null extraArgs) $-    die $ "'manpage' doesn't take any extra arguments: " ++ unwords extraArgs+         , reconfigureCommand+         , configCompilerAux', configPackageDB'+         , BuildFlags(..), BuildExFlags(..), SkipAddSourceDepsCheck(..)+         , buildCommand, replCommand, testCommand, benchmarkCommand+         , InstallFlags(..), defaultInstallFlags+         , installCommand, upgradeCommand, uninstallCommand+         , FetchFlags(..), fetchCommand+         , FreezeFlags(..), freezeCommand+         , genBoundsCommand+         , OutdatedFlags(..), outdatedCommand+         , GetFlags(..), getCommand, unpackCommand+         , checkCommand+         , formatCommand+         , updateCommand+         , ListFlags(..), listCommand+         , InfoFlags(..), infoCommand+         , UploadFlags(..), uploadCommand+         , ReportFlags(..), reportCommand+         , runCommand+         , InitFlags(initVerbosity), initCommand+         , SDistFlags(..), SDistExFlags(..), sdistCommand+         , Win32SelfUpgradeFlags(..), win32SelfUpgradeCommand+         , ActAsSetupFlags(..), actAsSetupCommand+         , SandboxFlags(..), sandboxCommand+         , ExecFlags(..), execCommand+         , UserConfigFlags(..), userConfigCommand+         , reportCommand+         , manpageCommand+         )+import Distribution.Simple.Setup+         ( HaddockTarget(..)+         , DoctestFlags(..), doctestCommand+         , HaddockFlags(..), haddockCommand, defaultHaddockFlags+         , HscolourFlags(..), hscolourCommand+         , ReplFlags(..)+         , CopyFlags(..), copyCommand+         , RegisterFlags(..), registerCommand+         , CleanFlags(..), cleanCommand+         , TestFlags(..), BenchmarkFlags(..)+         , Flag(..), fromFlag, fromFlagOrDefault, flagToMaybe, toFlag+         , configAbsolutePaths+         )++import Prelude ()+import Distribution.Client.Compat.Prelude hiding (get)++import Distribution.Client.SetupWrapper+         ( setupWrapper, SetupScriptOptions(..), defaultSetupScriptOptions )+import Distribution.Client.Config+         ( SavedConfig(..), loadConfig, defaultConfigFile, userConfigDiff+         , userConfigUpdate, createDefaultConfigFile, getConfigFilePath )+import Distribution.Client.Targets+         ( readUserTargets )+import qualified Distribution.Client.List as List+         ( list, info )++import qualified Distribution.Client.CmdConfigure as CmdConfigure+import qualified Distribution.Client.CmdBuild     as CmdBuild+import qualified Distribution.Client.CmdRepl      as CmdRepl+import qualified Distribution.Client.CmdFreeze    as CmdFreeze+import qualified Distribution.Client.CmdHaddock   as CmdHaddock+import qualified Distribution.Client.CmdRun       as CmdRun+import qualified Distribution.Client.CmdTest      as CmdTest+import qualified Distribution.Client.CmdBench     as CmdBench++import Distribution.Client.Install            (install)+import Distribution.Client.Configure          (configure, writeConfigFlags)+import Distribution.Client.Update             (update)+import Distribution.Client.Exec               (exec)+import Distribution.Client.Fetch              (fetch)+import Distribution.Client.Freeze             (freeze)+import Distribution.Client.GenBounds          (genBounds)+import Distribution.Client.Outdated           (outdated)+import Distribution.Client.Check as Check     (check)+--import Distribution.Client.Clean            (clean)+import qualified Distribution.Client.Upload as Upload+import Distribution.Client.Run                (run, splitRunArgs)+import Distribution.Client.SrcDist            (sdist)+import Distribution.Client.Get                (get)+import Distribution.Client.Reconfigure        (Check(..), reconfigure)+import Distribution.Client.Nix                (nixInstantiate+                                              ,nixShell+                                              ,nixShellIfSandboxed)+import Distribution.Client.Sandbox            (sandboxInit+                                              ,sandboxAddSource+                                              ,sandboxDelete+                                              ,sandboxDeleteSource+                                              ,sandboxListSources+                                              ,sandboxHcPkg+                                              ,dumpPackageEnvironment++                                              ,loadConfigOrSandboxConfig+                                              ,findSavedDistPref+                                              ,initPackageDBIfNeeded+                                              ,maybeWithSandboxDirOnSearchPath+                                              ,maybeWithSandboxPackageInfo+                                              ,tryGetIndexFilePath+                                              ,sandboxBuildDir+                                              ,updateSandboxConfigFileFlag+                                              ,updateInstallDirs++                                              ,getPersistOrConfigCompiler)+import Distribution.Client.Sandbox.PackageEnvironment (setPackageDB)+import Distribution.Client.Sandbox.Timestamp  (maybeAddCompilerTimestampRecord)+import Distribution.Client.Sandbox.Types      (UseSandbox(..), whenUsingSandbox)+import Distribution.Client.Tar                (createTarGzFile)+import Distribution.Client.Types              (Password (..))+import Distribution.Client.Init               (initCabal)+import Distribution.Client.Manpage            (manpage)+import qualified Distribution.Client.Win32SelfUpgrade as Win32SelfUpgrade+import Distribution.Client.Utils              (determineNumJobs+#if defined(mingw32_HOST_OS)+                                              ,relaxEncodingErrors+#endif+                                              )++import Distribution.Package (packageId)+import Distribution.PackageDescription+         ( BuildType(..), Executable(..), buildable )+#ifdef CABAL_PARSEC+import Distribution.PackageDescription.Parsec ( readGenericPackageDescription )+#else+import Distribution.PackageDescription.Parse ( readGenericPackageDescription )+#endif++import Distribution.PackageDescription.PrettyPrint+         ( writeGenericPackageDescription )+import qualified Distribution.Simple as Simple+import qualified Distribution.Make as Make+import qualified Distribution.Types.UnqualComponentName as Make+import Distribution.Simple.Build+         ( startInterpreter )+import Distribution.Simple.Command+         ( CommandParse(..), CommandUI(..), Command, CommandSpec(..)+         , CommandType(..), commandsRun, commandAddAction, hiddenCommand+         , commandFromSpec, commandShowOptions )+import Distribution.Simple.Compiler (Compiler(..), PackageDBStack)+import Distribution.Simple.Configure+         ( configCompilerAuxEx, ConfigStateFileError(..)+         , getPersistBuildConfig, interpretPackageDbFlags+         , tryGetPersistBuildConfig )+import qualified Distribution.Simple.LocalBuildInfo as LBI+import Distribution.Simple.Program (defaultProgramDb+                                   ,configureAllKnownPrograms+                                   ,simpleProgramInvocation+                                   ,getProgramInvocationOutput)+import Distribution.Simple.Program.Db (reconfigurePrograms)+import qualified Distribution.Simple.Setup as Cabal+import Distribution.Simple.Utils+         ( cabalVersion, die', dieNoVerbosity, info, notice, topHandler+         , findPackageDesc, tryFindPackageDesc )+import Distribution.Text+         ( display )+import Distribution.Verbosity as Verbosity+         ( Verbosity, normal )+import Distribution.Version+         ( Version, mkVersion, orLaterVersion )+import qualified Paths_cabal_install (version)++import System.Environment       (getArgs, getProgName)+import System.Exit              (exitFailure, exitSuccess)+import System.FilePath          ( dropExtension, splitExtension+                                , takeExtension, (</>), (<.>))+import System.IO                ( BufferMode(LineBuffering), hSetBuffering+#ifdef mingw32_HOST_OS+                                , stderr+#endif+                                , stdout )+import System.Directory         (doesFileExist, getCurrentDirectory)+import Data.Monoid              (Any(..))+import Control.Exception        (SomeException(..), try)+import Control.Monad            (mapM_)++#ifdef MONOLITHIC+import qualified UnitTests+import qualified MemoryUsageTests+import qualified SolverQuickCheck+import qualified IntegrationTests2+import qualified System.Environment as Monolithic+#endif++-- | Entry point+--+main :: IO ()+#ifdef MONOLITHIC+main = do+    mb_exec <- Monolithic.lookupEnv "CABAL_INSTALL_MONOLITHIC_MODE"+    case mb_exec of+        Just "UnitTests"         -> UnitTests.main+        Just "MemoryUsageTests"  -> MemoryUsageTests.main+        Just "SolverQuickCheck"  -> SolverQuickCheck.main+        Just "IntegrationTests2" -> IntegrationTests2.main+        Just s -> error $ "Unrecognized mode '" ++ show s ++ "' in CABAL_INSTALL_MONOLITHIC_MODE"+        Nothing -> main'+#else+main = main'+#endif++main' :: IO ()+main' = do+  -- Enable line buffering so that we can get fast feedback even when piped.+  -- This is especially important for CI and build systems.+  hSetBuffering stdout LineBuffering+  -- The default locale encoding for Windows CLI is not UTF-8 and printing+  -- Unicode characters to it will fail unless we relax the handling of encoding+  -- errors when writing to stderr and stdout.+#ifdef mingw32_HOST_OS+  relaxEncodingErrors stdout+  relaxEncodingErrors stderr+#endif+  getArgs >>= mainWorker++mainWorker :: [String] -> IO ()+mainWorker args = topHandler $+  case commandsRun (globalCommand commands) commands args of+    CommandHelp   help                 -> printGlobalHelp help+    CommandList   opts                 -> printOptionsList opts+    CommandErrors errs                 -> printErrors errs+    CommandReadyToGo (globalFlags, commandParse)  ->+      case commandParse of+        _ | fromFlagOrDefault False (globalVersion globalFlags)+            -> printVersion+          | fromFlagOrDefault False (globalNumericVersion globalFlags)+            -> printNumericVersion+        CommandHelp     help           -> printCommandHelp help+        CommandList     opts           -> printOptionsList opts+        CommandErrors   errs           -> printErrors errs+        CommandReadyToGo action        -> do+          globalFlags' <- updateSandboxConfigFileFlag globalFlags+          action globalFlags'++  where+    printCommandHelp help = do+      pname <- getProgName+      putStr (help pname)+    printGlobalHelp help = do+      pname <- getProgName+      configFile <- defaultConfigFile+      putStr (help pname)+      putStr $ "\nYou can edit the cabal configuration file to set defaults:\n"+            ++ "  " ++ configFile ++ "\n"+      exists <- doesFileExist configFile+      when (not exists) $+          putStrLn $ "This file will be generated with sensible "+                  ++ "defaults if you run 'cabal update'."+    printOptionsList = putStr . unlines+    printErrors errs = dieNoVerbosity $ intercalate "\n" errs+    printNumericVersion = putStrLn $ display Paths_cabal_install.version+    printVersion        = putStrLn $ "cabal-install version "+                                  ++ display Paths_cabal_install.version+                                  ++ "\ncompiled using version "+                                  ++ display cabalVersion+                                  ++ " of the Cabal library "++    commands = map commandFromSpec commandSpecs+    commandSpecs =+      [ regularCmd installCommand installAction+      , regularCmd updateCommand updateAction+      , regularCmd listCommand listAction+      , regularCmd infoCommand infoAction+      , regularCmd fetchCommand fetchAction+      , regularCmd freezeCommand freezeAction+      , regularCmd getCommand getAction+      , hiddenCmd  unpackCommand unpackAction+      , regularCmd checkCommand checkAction+      , regularCmd sdistCommand sdistAction+      , regularCmd uploadCommand uploadAction+      , regularCmd reportCommand reportAction+      , regularCmd runCommand runAction+      , regularCmd initCommand initAction+      , regularCmd configureExCommand configureAction+      , regularCmd reconfigureCommand reconfigureAction+      , regularCmd buildCommand buildAction+      , regularCmd replCommand replAction+      , regularCmd sandboxCommand sandboxAction+      , regularCmd doctestCommand doctestAction+      , regularCmd haddockCommand haddockAction+      , regularCmd execCommand execAction+      , regularCmd userConfigCommand userConfigAction+      , regularCmd cleanCommand cleanAction+      , regularCmd genBoundsCommand genBoundsAction+      , regularCmd outdatedCommand outdatedAction+      , wrapperCmd copyCommand copyVerbosity copyDistPref+      , wrapperCmd hscolourCommand hscolourVerbosity hscolourDistPref+      , wrapperCmd registerCommand regVerbosity regDistPref+      , regularCmd testCommand testAction+      , regularCmd benchmarkCommand benchmarkAction+      , hiddenCmd  uninstallCommand uninstallAction+      , hiddenCmd  formatCommand formatAction+      , hiddenCmd  upgradeCommand upgradeAction+      , hiddenCmd  win32SelfUpgradeCommand win32SelfUpgradeAction+      , hiddenCmd  actAsSetupCommand actAsSetupAction+      , hiddenCmd  manpageCommand (manpageAction commandSpecs)++      , regularCmd  CmdConfigure.configureCommand CmdConfigure.configureAction+      , regularCmd  CmdBuild.buildCommand         CmdBuild.buildAction+      , regularCmd  CmdRepl.replCommand           CmdRepl.replAction+      , regularCmd  CmdFreeze.freezeCommand       CmdFreeze.freezeAction+      , regularCmd  CmdHaddock.haddockCommand     CmdHaddock.haddockAction+      , regularCmd  CmdRun.runCommand             CmdRun.runAction+      , regularCmd  CmdTest.testCommand           CmdTest.testAction+      , regularCmd  CmdBench.benchCommand         CmdBench.benchAction+      ]++type Action = GlobalFlags -> IO ()++regularCmd :: CommandUI flags -> (flags -> [String] -> action)+           -> CommandSpec action+regularCmd ui action =+  CommandSpec ui ((flip commandAddAction) action) NormalCommand++hiddenCmd :: CommandUI flags -> (flags -> [String] -> action)+          -> CommandSpec action+hiddenCmd ui action =+  CommandSpec ui (\ui' -> hiddenCommand (commandAddAction ui' action))+  HiddenCommand++wrapperCmd :: Monoid flags => CommandUI flags -> (flags -> Flag Verbosity)+           -> (flags -> Flag String) -> CommandSpec Action+wrapperCmd ui verbosity distPref =+  CommandSpec ui (\ui' -> wrapperAction ui' verbosity distPref) NormalCommand++wrapperAction :: Monoid flags+              => CommandUI flags+              -> (flags -> Flag Verbosity)+              -> (flags -> Flag String)+              -> Command Action+wrapperAction command verbosityFlag distPrefFlag =+  commandAddAction command+    { commandDefaultFlags = mempty } $ \flags extraArgs globalFlags -> do+    let verbosity = fromFlagOrDefault normal (verbosityFlag flags)+    load <- try (loadConfigOrSandboxConfig verbosity globalFlags)+    let config = either (\(SomeException _) -> mempty) snd load+    distPref <- findSavedDistPref config (distPrefFlag flags)+    let setupScriptOptions = defaultSetupScriptOptions { useDistPref = distPref }+    setupWrapper verbosity setupScriptOptions Nothing+                 command (const flags) extraArgs++configureAction :: (ConfigFlags, ConfigExFlags)+                -> [String] -> Action+configureAction (configFlags, configExFlags) extraArgs globalFlags = do+  let verbosity = fromFlagOrDefault normal (configVerbosity configFlags)+  (useSandbox, config) <- updateInstallDirs (configUserInstall configFlags)+                          <$> loadConfigOrSandboxConfig verbosity globalFlags+  distPref <- findSavedDistPref config (configDistPref configFlags)+  nixInstantiate verbosity distPref True globalFlags config+  nixShell verbosity distPref globalFlags config $ do+    let configFlags'   = savedConfigureFlags   config `mappend` configFlags+        configExFlags' = savedConfigureExFlags config `mappend` configExFlags+        globalFlags'   = savedGlobalFlags      config `mappend` globalFlags+    (comp, platform, progdb) <- configCompilerAuxEx configFlags'++    -- If we're working inside a sandbox and the user has set the -w option, we+    -- may need to create a sandbox-local package DB for this compiler and add a+    -- timestamp record for this compiler to the timestamp file.+    let configFlags''  = case useSandbox of+          NoSandbox               -> configFlags'+          (UseSandbox sandboxDir) -> setPackageDB sandboxDir+                                    comp platform configFlags'++    writeConfigFlags verbosity distPref (configFlags'', configExFlags')++    -- What package database(s) to use+    let packageDBs :: PackageDBStack+        packageDBs+          = interpretPackageDbFlags+            (fromFlag (configUserInstall configFlags''))+            (configPackageDBs configFlags'')++    whenUsingSandbox useSandbox $ \sandboxDir -> do+      initPackageDBIfNeeded verbosity configFlags'' comp progdb+      -- NOTE: We do not write the new sandbox package DB location to+      -- 'cabal.sandbox.config' here because 'configure -w' must not affect+      -- subsequent 'install' (for UI compatibility with non-sandboxed mode).++      indexFile     <- tryGetIndexFilePath verbosity config+      maybeAddCompilerTimestampRecord verbosity sandboxDir indexFile+        (compilerId comp) platform++    maybeWithSandboxDirOnSearchPath useSandbox $+      withRepoContext verbosity globalFlags' $ \repoContext ->+        configure verbosity packageDBs repoContext+                  comp platform progdb configFlags'' configExFlags' extraArgs++reconfigureAction :: (ConfigFlags, ConfigExFlags)+                  -> [String] -> Action+reconfigureAction flags@(configFlags, _) _ globalFlags = do+  let verbosity = fromFlagOrDefault normal (configVerbosity configFlags)+  (useSandbox, config) <- updateInstallDirs (configUserInstall configFlags)+                          <$> loadConfigOrSandboxConfig verbosity globalFlags+  distPref <- findSavedDistPref config (configDistPref configFlags)+  let checkFlags = Check $ \_ saved -> do+        let flags' = saved <> flags+        unless (saved == flags') $ info verbosity message+        pure (Any True, flags')+        where+          -- This message is correct, but not very specific: it will list all+          -- of the new flags, even if some have not actually changed. The+          -- *minimal* set of changes is more difficult to determine.+          message =+            "flags changed: "+            ++ unwords (commandShowOptions configureExCommand flags)+  nixInstantiate verbosity distPref True globalFlags config+  _ <-+    reconfigure configureAction+    verbosity distPref useSandbox DontSkipAddSourceDepsCheck NoFlag+    checkFlags [] globalFlags config+  pure ()++buildAction :: (BuildFlags, BuildExFlags) -> [String] -> Action+buildAction (buildFlags, buildExFlags) extraArgs globalFlags = do+  let verbosity = fromFlagOrDefault normal (buildVerbosity buildFlags)+      noAddSource = fromFlagOrDefault DontSkipAddSourceDepsCheck+                    (buildOnly buildExFlags)+  (useSandbox, config) <- loadConfigOrSandboxConfig verbosity globalFlags+  distPref <- findSavedDistPref config (buildDistPref buildFlags)+  -- Calls 'configureAction' to do the real work, so nothing special has to be+  -- done to support sandboxes.+  config' <-+    reconfigure configureAction+    verbosity distPref useSandbox noAddSource (buildNumJobs buildFlags)+    mempty [] globalFlags config+  nixShell verbosity distPref globalFlags config $ do+    maybeWithSandboxDirOnSearchPath useSandbox $+      build verbosity config' distPref buildFlags extraArgs+++-- | Actually do the work of building the package. This is separate from+-- 'buildAction' so that 'testAction' and 'benchmarkAction' do not invoke+-- 'reconfigure' twice.+build :: Verbosity -> SavedConfig -> FilePath -> BuildFlags -> [String] -> IO ()+build verbosity config distPref buildFlags extraArgs =+  setupWrapper verbosity setupOptions Nothing+               (Cabal.buildCommand progDb) mkBuildFlags extraArgs+  where+    progDb       = defaultProgramDb+    setupOptions = defaultSetupScriptOptions { useDistPref = distPref }++    mkBuildFlags version = filterBuildFlags version config buildFlags'+    buildFlags' = buildFlags+      { buildVerbosity = toFlag verbosity+      , buildDistPref  = toFlag distPref+      }++-- | Make sure that we don't pass new flags to setup scripts compiled against+-- old versions of Cabal.+filterBuildFlags :: Version -> SavedConfig -> BuildFlags -> BuildFlags+filterBuildFlags version config buildFlags+  | version >= mkVersion [1,19,1] = buildFlags_latest+  -- Cabal < 1.19.1 doesn't support 'build -j'.+  | otherwise                      = buildFlags_pre_1_19_1+  where+    buildFlags_pre_1_19_1 = buildFlags {+      buildNumJobs = NoFlag+      }+    buildFlags_latest     = buildFlags {+      -- Take the 'jobs' setting '~/.cabal/config' into account.+      buildNumJobs = Flag . Just . determineNumJobs $+                     (numJobsConfigFlag `mappend` numJobsCmdLineFlag)+      }+    numJobsConfigFlag  = installNumJobs . savedInstallFlags $ config+    numJobsCmdLineFlag = buildNumJobs buildFlags+++replAction :: (ReplFlags, BuildExFlags) -> [String] -> Action+replAction (replFlags, buildExFlags) extraArgs globalFlags = do+  let verbosity = fromFlagOrDefault normal (replVerbosity replFlags)+  (useSandbox, config) <- loadConfigOrSandboxConfig verbosity globalFlags+  distPref <- findSavedDistPref config (replDistPref replFlags)+  cwd     <- getCurrentDirectory+  pkgDesc <- findPackageDesc cwd+  let+    -- There is a .cabal file in the current directory: start a REPL and load+    -- the project's modules.+    onPkgDesc = do+      let noAddSource = case replReload replFlags of+            Flag True -> SkipAddSourceDepsCheck+            _         -> fromFlagOrDefault DontSkipAddSourceDepsCheck+                        (buildOnly buildExFlags)++      -- Calls 'configureAction' to do the real work, so nothing special has to+      -- be done to support sandboxes.+      _ <-+        reconfigure configureAction+        verbosity distPref useSandbox noAddSource NoFlag+        mempty [] globalFlags config+      let progDb = defaultProgramDb+          setupOptions = defaultSetupScriptOptions+            { useCabalVersion = orLaterVersion $ mkVersion [1,18,0]+            , useDistPref     = distPref+            }+          replFlags'   = replFlags+            { replVerbosity = toFlag verbosity+            , replDistPref  = toFlag distPref+            }++      nixShell verbosity distPref globalFlags config $ do+        maybeWithSandboxDirOnSearchPath useSandbox $+          setupWrapper verbosity setupOptions Nothing+          (Cabal.replCommand progDb) (const replFlags') extraArgs++    -- No .cabal file in the current directory: just start the REPL (possibly+    -- using the sandbox package DB).+    onNoPkgDesc = do+      let configFlags = savedConfigureFlags config+      (comp, platform, programDb) <- configCompilerAux' configFlags+      programDb' <- reconfigurePrograms verbosity+                                        (replProgramPaths replFlags)+                                        (replProgramArgs replFlags)+                                        programDb+      nixShell verbosity distPref globalFlags config $ do+        startInterpreter verbosity programDb' comp platform+                        (configPackageDB' configFlags)++  either (const onNoPkgDesc) (const onPkgDesc) pkgDesc++installAction :: (ConfigFlags, ConfigExFlags, InstallFlags, HaddockFlags)+              -> [String] -> Action+installAction (configFlags, _, installFlags, _) _ globalFlags+  | fromFlagOrDefault False (installOnly installFlags) = do+      let verb = fromFlagOrDefault normal (configVerbosity configFlags)+      (useSandbox, config) <- loadConfigOrSandboxConfig verb globalFlags+      dist <- findSavedDistPref config (configDistPref configFlags)+      let setupOpts = defaultSetupScriptOptions { useDistPref = dist }+      nixShellIfSandboxed verb dist globalFlags config useSandbox $+        setupWrapper+        verb setupOpts Nothing+        installCommand (const mempty) []++installAction+  (configFlags, configExFlags, installFlags, haddockFlags)+  extraArgs globalFlags = do+  let verb = fromFlagOrDefault normal (configVerbosity configFlags)+  (useSandbox, config) <- updateInstallDirs (configUserInstall configFlags)+                          <$> loadConfigOrSandboxConfig verb globalFlags++  let sandboxDist =+        case useSandbox of+          NoSandbox             -> NoFlag+          UseSandbox sandboxDir -> Flag $ sandboxBuildDir sandboxDir+  dist <- findSavedDistPref config+          (configDistPref configFlags `mappend` sandboxDist)++  nixShellIfSandboxed verb dist globalFlags config useSandbox $ do+    targets <- readUserTargets verb extraArgs++    -- TODO: It'd be nice if 'cabal install' picked up the '-w' flag passed to+    -- 'configure' when run inside a sandbox.  Right now, running+    --+    -- $ cabal sandbox init && cabal configure -w /path/to/ghc+    --   && cabal build && cabal install+    --+    -- performs the compilation twice unless you also pass -w to 'install'.+    -- However, this is the same behaviour that 'cabal install' has in the normal+    -- mode of operation, so we stick to it for consistency.++    let configFlags'    = maybeForceTests installFlags' $+                          savedConfigureFlags   config `mappend`+                          configFlags { configDistPref = toFlag dist }+        configExFlags'  = defaultConfigExFlags         `mappend`+                          savedConfigureExFlags config `mappend` configExFlags+        installFlags'   = defaultInstallFlags          `mappend`+                          savedInstallFlags     config `mappend` installFlags+        haddockFlags'   = defaultHaddockFlags          `mappend`+                          savedHaddockFlags     config `mappend`+                          haddockFlags { haddockDistPref = toFlag dist }+        globalFlags'    = savedGlobalFlags      config `mappend` globalFlags+    (comp, platform, progdb) <- configCompilerAux' configFlags'+    -- TODO: Redesign ProgramDB API to prevent such problems as #2241 in the+    -- future.+    progdb' <- configureAllKnownPrograms verb progdb++    -- If we're working inside a sandbox and the user has set the -w option, we+    -- may need to create a sandbox-local package DB for this compiler and add a+    -- timestamp record for this compiler to the timestamp file.+    configFlags'' <- case useSandbox of+      NoSandbox               -> configAbsolutePaths $ configFlags'+      (UseSandbox sandboxDir) -> return $ setPackageDB sandboxDir comp platform+                                                      configFlags'++    whenUsingSandbox useSandbox $ \sandboxDir -> do+      initPackageDBIfNeeded verb configFlags'' comp progdb'++      indexFile     <- tryGetIndexFilePath verb config+      maybeAddCompilerTimestampRecord verb sandboxDir indexFile+        (compilerId comp) platform++    -- TODO: Passing 'SandboxPackageInfo' to install unconditionally here means+    -- that 'cabal install some-package' inside a sandbox will sometimes reinstall+    -- modified add-source deps, even if they are not among the dependencies of+    -- 'some-package'. This can also prevent packages that depend on older+    -- versions of add-source'd packages from building (see #1362).+    maybeWithSandboxPackageInfo verb configFlags'' globalFlags'+                                comp platform progdb useSandbox $ \mSandboxPkgInfo ->+                                maybeWithSandboxDirOnSearchPath useSandbox $+      withRepoContext verb globalFlags' $ \repoContext ->+        install verb+                (configPackageDB' configFlags'')+                repoContext+                comp platform progdb'+                useSandbox mSandboxPkgInfo+                globalFlags' configFlags'' configExFlags'+                installFlags' haddockFlags'+                targets++      where+        -- '--run-tests' implies '--enable-tests'.+        maybeForceTests installFlags' configFlags' =+          if fromFlagOrDefault False (installRunTests installFlags')+          then configFlags' { configTests = toFlag True }+          else configFlags'++testAction :: (TestFlags, BuildFlags, BuildExFlags) -> [String] -> GlobalFlags+           -> IO ()+testAction (testFlags, buildFlags, buildExFlags) extraArgs globalFlags = do+  let verbosity      = fromFlagOrDefault normal (testVerbosity testFlags)+  (useSandbox, config) <- loadConfigOrSandboxConfig verbosity globalFlags+  distPref <- findSavedDistPref config (testDistPref testFlags)+  let noAddSource    = fromFlagOrDefault DontSkipAddSourceDepsCheck+                      (buildOnly buildExFlags)+      buildFlags'    = buildFlags+                      { buildVerbosity = testVerbosity testFlags }+      checkFlags = Check $ \_ flags@(configFlags, configExFlags) ->+        if fromFlagOrDefault False (configTests configFlags)+          then pure (mempty, flags)+          else do+            info verbosity "reconfiguring to enable tests"+            let flags' = ( configFlags { configTests = toFlag True }+                        , configExFlags+                        )+            pure (Any True, flags')++  -- reconfigure also checks if we're in a sandbox and reinstalls add-source+  -- deps if needed.+  _ <-+    reconfigure configureAction+    verbosity distPref useSandbox noAddSource (buildNumJobs buildFlags')+    checkFlags [] globalFlags config+  nixShell verbosity distPref globalFlags config $ do+    let setupOptions   = defaultSetupScriptOptions { useDistPref = distPref }+        testFlags'     = testFlags { testDistPref = toFlag distPref }++    -- The package was just configured, so the LBI must be available.+    names <- componentNamesFromLBI verbosity distPref "test suites"+              (\c -> case c of { LBI.CTest{} -> True; _ -> False })+    let extraArgs'+          | null extraArgs = case names of+            ComponentNamesUnknown -> []+            ComponentNames names' -> [ Make.unUnqualComponentName name+                                    | LBI.CTestName name <- names' ]+          | otherwise      = extraArgs++    maybeWithSandboxDirOnSearchPath useSandbox $+      build verbosity config distPref buildFlags' extraArgs'++    maybeWithSandboxDirOnSearchPath useSandbox $+      setupWrapper verbosity setupOptions Nothing+      Cabal.testCommand (const testFlags') extraArgs'++data ComponentNames = ComponentNamesUnknown+                    | ComponentNames [LBI.ComponentName]++-- | Return the names of all buildable components matching a given predicate.+componentNamesFromLBI :: Verbosity -> FilePath -> String+                         -> (LBI.Component -> Bool)+                         -> IO ComponentNames+componentNamesFromLBI verbosity distPref targetsDescr compPred = do+  eLBI <- tryGetPersistBuildConfig distPref+  case eLBI of+    Left err -> case err of+      -- Note: the build config could have been generated by a custom setup+      -- script built against a different Cabal version, so it's crucial that+      -- we ignore the bad version error here.+      ConfigStateFileBadVersion _ _ _ -> return ComponentNamesUnknown+      _                               -> die' verbosity (show err)+    Right lbi -> do+      let pkgDescr = LBI.localPkgDescr lbi+          names    = map LBI.componentName+                     . filter (buildable . LBI.componentBuildInfo)+                     . filter compPred $+                     LBI.pkgComponents pkgDescr+      if null names+        then do notice verbosity $ "Package has no buildable "+                  ++ targetsDescr ++ "."+                exitSuccess -- See #3215.++        else return $! (ComponentNames names)++benchmarkAction :: (BenchmarkFlags, BuildFlags, BuildExFlags)+                   -> [String] -> GlobalFlags+                   -> IO ()+benchmarkAction+  (benchmarkFlags, buildFlags, buildExFlags)+  extraArgs globalFlags = do+  let verbosity      = fromFlagOrDefault normal+                       (benchmarkVerbosity benchmarkFlags)++  (useSandbox, config) <- loadConfigOrSandboxConfig verbosity globalFlags+  distPref <- findSavedDistPref config (benchmarkDistPref benchmarkFlags)+  let buildFlags'    = buildFlags+                      { buildVerbosity = benchmarkVerbosity benchmarkFlags }+      noAddSource   = fromFlagOrDefault DontSkipAddSourceDepsCheck+                      (buildOnly buildExFlags)++  let checkFlags = Check $ \_ flags@(configFlags, configExFlags) ->+        if fromFlagOrDefault False (configBenchmarks configFlags)+          then pure (mempty, flags)+          else do+            info verbosity "reconfiguring to enable benchmarks"+            let flags' = ( configFlags { configBenchmarks = toFlag True }+                        , configExFlags+                        )+            pure (Any True, flags')+++  -- reconfigure also checks if we're in a sandbox and reinstalls add-source+  -- deps if needed.+  config' <-+    reconfigure configureAction+    verbosity distPref useSandbox noAddSource (buildNumJobs buildFlags')+    checkFlags [] globalFlags config+  nixShell verbosity distPref globalFlags config $ do+    let setupOptions   = defaultSetupScriptOptions { useDistPref = distPref }+        benchmarkFlags'= benchmarkFlags { benchmarkDistPref = toFlag distPref }++    -- The package was just configured, so the LBI must be available.+    names <- componentNamesFromLBI verbosity distPref "benchmarks"+            (\c -> case c of { LBI.CBench{} -> True; _ -> False; })+    let extraArgs'+          | null extraArgs = case names of+            ComponentNamesUnknown -> []+            ComponentNames names' -> [ Make.unUnqualComponentName name+                                    | LBI.CBenchName name <- names']+          | otherwise      = extraArgs++    maybeWithSandboxDirOnSearchPath useSandbox $+      build verbosity config' distPref buildFlags' extraArgs'++    maybeWithSandboxDirOnSearchPath useSandbox $+      setupWrapper verbosity setupOptions Nothing+      Cabal.benchmarkCommand (const benchmarkFlags') extraArgs'++haddockAction :: HaddockFlags -> [String] -> Action+haddockAction haddockFlags extraArgs globalFlags = do+  let verbosity = fromFlag (haddockVerbosity haddockFlags)+  (useSandbox, config) <- loadConfigOrSandboxConfig verbosity globalFlags+  distPref <- findSavedDistPref config (haddockDistPref haddockFlags)+  config' <-+    reconfigure configureAction+    verbosity distPref useSandbox DontSkipAddSourceDepsCheck NoFlag+    mempty [] globalFlags config+  nixShell verbosity distPref globalFlags config $ do+    let haddockFlags' = defaultHaddockFlags      `mappend`+                        savedHaddockFlags config' `mappend`+                        haddockFlags { haddockDistPref = toFlag distPref }+        setupScriptOptions = defaultSetupScriptOptions+                             { useDistPref = distPref }+    setupWrapper verbosity setupScriptOptions Nothing+      haddockCommand (const haddockFlags') extraArgs+    when (haddockForHackage haddockFlags == Flag ForHackage) $ do+      pkg <- fmap LBI.localPkgDescr (getPersistBuildConfig distPref)+      let dest = distPref </> name <.> "tar.gz"+          name = display (packageId pkg) ++ "-docs"+          docDir = distPref </> "doc" </> "html"+      createTarGzFile dest docDir name+      notice verbosity $ "Documentation tarball created: " ++ dest++doctestAction :: DoctestFlags -> [String] -> Action+doctestAction doctestFlags extraArgs _globalFlags = do+  let verbosity = fromFlag (doctestVerbosity doctestFlags)++  setupWrapper verbosity defaultSetupScriptOptions Nothing+    doctestCommand (const doctestFlags) extraArgs++cleanAction :: CleanFlags -> [String] -> Action+cleanAction cleanFlags extraArgs globalFlags = do+  load <- try (loadConfigOrSandboxConfig verbosity globalFlags)+  let config = either (\(SomeException _) -> mempty) snd load+  distPref <- findSavedDistPref config (cleanDistPref cleanFlags)+  let setupScriptOptions = defaultSetupScriptOptions+                           { useDistPref = distPref+                           , useWin32CleanHack = True+                           }+      cleanFlags' = cleanFlags { cleanDistPref = toFlag distPref }+  setupWrapper verbosity setupScriptOptions Nothing+               cleanCommand (const cleanFlags') extraArgs+  where+    verbosity = fromFlagOrDefault normal (cleanVerbosity cleanFlags)++listAction :: ListFlags -> [String] -> Action+listAction listFlags extraArgs globalFlags = do+  let verbosity = fromFlag (listVerbosity listFlags)+  (_useSandbox, config) <- loadConfigOrSandboxConfig verbosity+                           (globalFlags { globalRequireSandbox = Flag False })+  let configFlags' = savedConfigureFlags config+      configFlags  = configFlags' {+        configPackageDBs = configPackageDBs configFlags'+                           `mappend` listPackageDBs listFlags+        }+      globalFlags' = savedGlobalFlags    config `mappend` globalFlags+  (comp, _, progdb) <- configCompilerAux' configFlags+  withRepoContext verbosity globalFlags' $ \repoContext ->+    List.list verbosity+       (configPackageDB' configFlags)+       repoContext+       comp+       progdb+       listFlags+       extraArgs++infoAction :: InfoFlags -> [String] -> Action+infoAction infoFlags extraArgs globalFlags = do+  let verbosity = fromFlag (infoVerbosity infoFlags)+  targets <- readUserTargets verbosity extraArgs+  (_useSandbox, config) <- loadConfigOrSandboxConfig verbosity+                           (globalFlags { globalRequireSandbox = Flag False })+  let configFlags' = savedConfigureFlags config+      configFlags  = configFlags' {+        configPackageDBs = configPackageDBs configFlags'+                           `mappend` infoPackageDBs infoFlags+        }+      globalFlags' = savedGlobalFlags    config `mappend` globalFlags+  (comp, _, progdb) <- configCompilerAuxEx configFlags+  withRepoContext verbosity globalFlags' $ \repoContext ->+    List.info verbosity+       (configPackageDB' configFlags)+       repoContext+       comp+       progdb+       globalFlags'+       infoFlags+       targets++updateAction :: Flag Verbosity -> [String] -> Action+updateAction verbosityFlag extraArgs globalFlags = do+  let verbosity = fromFlag verbosityFlag+  unless (null extraArgs) $+    die' verbosity $ "'update' doesn't take any extra arguments: " ++ unwords extraArgs+  (_useSandbox, config) <- loadConfigOrSandboxConfig verbosity+                           (globalFlags { globalRequireSandbox = Flag False })+  let globalFlags' = savedGlobalFlags config `mappend` globalFlags+  withRepoContext verbosity globalFlags' $ \repoContext ->+    update verbosity repoContext++upgradeAction :: (ConfigFlags, ConfigExFlags, InstallFlags, HaddockFlags)+              -> [String] -> Action+upgradeAction (configFlags, _, _, _) _ _ = die' verbosity $+    "Use the 'cabal install' command instead of 'cabal upgrade'.\n"+ ++ "You can install the latest version of a package using 'cabal install'. "+ ++ "The 'cabal upgrade' command has been removed because people found it "+ ++ "confusing and it often led to broken packages.\n"+ ++ "If you want the old upgrade behaviour then use the install command "+ ++ "with the --upgrade-dependencies flag (but check first with --dry-run "+ ++ "to see what would happen). This will try to pick the latest versions "+ ++ "of all dependencies, rather than the usual behaviour of trying to pick "+ ++ "installed versions of all dependencies. If you do use "+ ++ "--upgrade-dependencies, it is recommended that you do not upgrade core "+ ++ "packages (e.g. by using appropriate --constraint= flags)."+ where+  verbosity = fromFlag (configVerbosity configFlags)++fetchAction :: FetchFlags -> [String] -> Action+fetchAction fetchFlags extraArgs globalFlags = do+  let verbosity = fromFlag (fetchVerbosity fetchFlags)+  targets <- readUserTargets verbosity extraArgs+  config <- loadConfig verbosity (globalConfigFile globalFlags)+  let configFlags  = savedConfigureFlags config+      globalFlags' = savedGlobalFlags config `mappend` globalFlags+  (comp, platform, progdb) <- configCompilerAux' configFlags+  withRepoContext verbosity globalFlags' $ \repoContext ->+    fetch verbosity+        (configPackageDB' configFlags)+        repoContext+        comp platform progdb globalFlags' fetchFlags+        targets++freezeAction :: FreezeFlags -> [String] -> Action+freezeAction freezeFlags _extraArgs globalFlags = do+  let verbosity = fromFlag (freezeVerbosity freezeFlags)+  (useSandbox, config) <- loadConfigOrSandboxConfig verbosity globalFlags+  distPref <- findSavedDistPref config NoFlag+  nixShell verbosity distPref globalFlags config $ do+    let configFlags  = savedConfigureFlags config+        globalFlags' = savedGlobalFlags config `mappend` globalFlags+    (comp, platform, progdb) <- configCompilerAux' configFlags++    maybeWithSandboxPackageInfo+      verbosity configFlags globalFlags'+      comp platform progdb useSandbox $ \mSandboxPkgInfo ->+        maybeWithSandboxDirOnSearchPath useSandbox $+        withRepoContext verbosity globalFlags' $ \repoContext ->+          freeze verbosity+            (configPackageDB' configFlags)+            repoContext+            comp platform progdb+            mSandboxPkgInfo+            globalFlags' freezeFlags++genBoundsAction :: FreezeFlags -> [String] -> GlobalFlags -> IO ()+genBoundsAction freezeFlags _extraArgs globalFlags = do+  let verbosity = fromFlag (freezeVerbosity freezeFlags)+  (useSandbox, config) <- loadConfigOrSandboxConfig verbosity globalFlags+  distPref <- findSavedDistPref config NoFlag+  nixShell verbosity distPref globalFlags config $ do+    let configFlags  = savedConfigureFlags config+        globalFlags' = savedGlobalFlags config `mappend` globalFlags+    (comp, platform, progdb) <- configCompilerAux' configFlags++    maybeWithSandboxPackageInfo+      verbosity configFlags globalFlags'+      comp platform progdb useSandbox $ \mSandboxPkgInfo ->+        maybeWithSandboxDirOnSearchPath useSandbox $+        withRepoContext verbosity globalFlags' $ \repoContext ->+          genBounds verbosity+                (configPackageDB' configFlags)+                repoContext+                comp platform progdb+                mSandboxPkgInfo+                globalFlags' freezeFlags++outdatedAction :: OutdatedFlags -> [String] -> GlobalFlags -> IO ()+outdatedAction outdatedFlags _extraArgs globalFlags = do+  let verbosity = fromFlag (outdatedVerbosity outdatedFlags)+  (_useSandbox, config) <- loadConfigOrSandboxConfig verbosity globalFlags+  let configFlags  = savedConfigureFlags config+      globalFlags' = savedGlobalFlags config `mappend` globalFlags+  (comp, platform, _progdb) <- configCompilerAux' configFlags+  withRepoContext verbosity globalFlags' $ \repoContext ->+    outdated verbosity outdatedFlags repoContext+             comp platform++uploadAction :: UploadFlags -> [String] -> Action+uploadAction uploadFlags extraArgs globalFlags = do+  config <- loadConfig verbosity (globalConfigFile globalFlags)+  let uploadFlags' = savedUploadFlags config `mappend` uploadFlags+      globalFlags' = savedGlobalFlags config `mappend` globalFlags+      tarfiles     = extraArgs+  when (null tarfiles && not (fromFlag (uploadDoc uploadFlags'))) $+    die' verbosity "the 'upload' command expects at least one .tar.gz archive."+  checkTarFiles extraArgs+  maybe_password <-+    case uploadPasswordCmd uploadFlags'+    of Flag (xs:xss) -> Just . Password <$>+                        getProgramInvocationOutput verbosity+                        (simpleProgramInvocation xs xss)+       _             -> pure $ flagToMaybe $ uploadPassword uploadFlags'+  withRepoContext verbosity globalFlags' $ \repoContext -> do+    if fromFlag (uploadDoc uploadFlags')+    then do+      when (length tarfiles > 1) $+       die' verbosity $ "the 'upload' command can only upload documentation "+             ++ "for one package at a time."+      tarfile <- maybe (generateDocTarball config) return $ listToMaybe tarfiles+      Upload.uploadDoc verbosity+                       repoContext+                       (flagToMaybe $ uploadUsername uploadFlags')+                       maybe_password+                       (fromFlag (uploadCandidate uploadFlags'))+                       tarfile+    else do+      Upload.upload verbosity+                    repoContext+                    (flagToMaybe $ uploadUsername uploadFlags')+                    maybe_password+                    (fromFlag (uploadCandidate uploadFlags'))+                    tarfiles+    where+    verbosity = fromFlag (uploadVerbosity uploadFlags)+    checkTarFiles tarfiles+      | not (null otherFiles)+      = die' verbosity $ "the 'upload' command expects only .tar.gz archives: "+           ++ intercalate ", " otherFiles+      | otherwise = sequence_+                      [ do exists <- doesFileExist tarfile+                           unless exists $ die' verbosity $ "file not found: " ++ tarfile+                      | tarfile <- tarfiles ]++      where otherFiles = filter (not . isTarGzFile) tarfiles+            isTarGzFile file = case splitExtension file of+              (file', ".gz") -> takeExtension file' == ".tar"+              _              -> False+    generateDocTarball config = do+      notice verbosity $+        "No documentation tarball specified. "+        ++ "Building a documentation tarball with default settings...\n"+        ++ "If you need to customise Haddock options, "+        ++ "run 'haddock --for-hackage' first "+        ++ "to generate a documentation tarball."+      haddockAction (defaultHaddockFlags { haddockForHackage = Flag ForHackage })+                    [] globalFlags+      distPref <- findSavedDistPref config NoFlag+      pkg <- fmap LBI.localPkgDescr (getPersistBuildConfig distPref)+      return $ distPref </> display (packageId pkg) ++ "-docs" <.> "tar.gz"++checkAction :: Flag Verbosity -> [String] -> Action+checkAction verbosityFlag extraArgs _globalFlags = do+  let verbosity = fromFlag verbosityFlag+  unless (null extraArgs) $+    die' verbosity $ "'check' doesn't take any extra arguments: " ++ unwords extraArgs+  allOk <- Check.check (fromFlag verbosityFlag)+  unless allOk exitFailure++formatAction :: Flag Verbosity -> [String] -> Action+formatAction verbosityFlag extraArgs _globalFlags = do+  let verbosity = fromFlag verbosityFlag+  path <- case extraArgs of+    [] -> do cwd <- getCurrentDirectory+             tryFindPackageDesc cwd+    (p:_) -> return p+  pkgDesc <- readGenericPackageDescription verbosity path+  -- Uses 'writeFileAtomic' under the hood.+  writeGenericPackageDescription path pkgDesc++uninstallAction :: Flag Verbosity -> [String] -> Action+uninstallAction verbosityFlag extraArgs _globalFlags = do+  let verbosity = fromFlag verbosityFlag+      package = case extraArgs of+        p:_ -> p+        _   -> "PACKAGE_NAME"+  die' verbosity $ "This version of 'cabal-install' does not support the 'uninstall' "+    ++ "operation. "+    ++ "It will likely be implemented at some point in the future; "+    ++ "in the meantime you're advised to use either 'ghc-pkg unregister "+    ++ package ++ "' or 'cabal sandbox hc-pkg -- unregister " ++ package ++ "'."+++sdistAction :: (SDistFlags, SDistExFlags) -> [String] -> Action+sdistAction (sdistFlags, sdistExFlags) extraArgs globalFlags = do+  let verbosity = fromFlag (sDistVerbosity sdistFlags)+  unless (null extraArgs) $+    die' verbosity $ "'sdist' doesn't take any extra arguments: " ++ unwords extraArgs+  load <- try (loadConfigOrSandboxConfig verbosity globalFlags)+  let config = either (\(SomeException _) -> mempty) snd load+  distPref <- findSavedDistPref config (sDistDistPref sdistFlags)+  let sdistFlags' = sdistFlags { sDistDistPref = toFlag distPref }+  sdist sdistFlags' sdistExFlags++reportAction :: ReportFlags -> [String] -> Action+reportAction reportFlags extraArgs globalFlags = do+  let verbosity = fromFlag (reportVerbosity reportFlags)+  unless (null extraArgs) $+    die' verbosity $ "'report' doesn't take any extra arguments: " ++ unwords extraArgs+  config <- loadConfig verbosity (globalConfigFile globalFlags)+  let globalFlags' = savedGlobalFlags config `mappend` globalFlags+      reportFlags' = savedReportFlags config `mappend` reportFlags++  withRepoContext verbosity globalFlags' $ \repoContext ->+   Upload.report verbosity repoContext+    (flagToMaybe $ reportUsername reportFlags')+    (flagToMaybe $ reportPassword reportFlags')++runAction :: (BuildFlags, BuildExFlags) -> [String] -> Action+runAction (buildFlags, buildExFlags) extraArgs globalFlags = do+  let verbosity   = fromFlagOrDefault normal (buildVerbosity buildFlags)+  (useSandbox, config) <- loadConfigOrSandboxConfig verbosity globalFlags+  distPref <- findSavedDistPref config (buildDistPref buildFlags)+  let noAddSource = fromFlagOrDefault DontSkipAddSourceDepsCheck+                    (buildOnly buildExFlags)+  -- reconfigure also checks if we're in a sandbox and reinstalls add-source+  -- deps if needed.+  config' <-+    reconfigure configureAction+    verbosity distPref useSandbox noAddSource (buildNumJobs buildFlags)+    mempty [] globalFlags config+  nixShell verbosity distPref globalFlags config $ do+    lbi <- getPersistBuildConfig distPref+    (exe, exeArgs) <- splitRunArgs verbosity lbi extraArgs++    maybeWithSandboxDirOnSearchPath useSandbox $+      build verbosity config' distPref buildFlags ["exe:" ++ display (exeName exe)]++    maybeWithSandboxDirOnSearchPath useSandbox $+      run verbosity lbi exe exeArgs++getAction :: GetFlags -> [String] -> Action+getAction getFlags extraArgs globalFlags = do+  let verbosity = fromFlag (getVerbosity getFlags)+  targets <- readUserTargets verbosity extraArgs+  (_useSandbox, config) <- loadConfigOrSandboxConfig verbosity+                           (globalFlags { globalRequireSandbox = Flag False })+  let globalFlags' = savedGlobalFlags config `mappend` globalFlags+  withRepoContext verbosity (savedGlobalFlags config) $ \repoContext ->+   get verbosity+    repoContext+    globalFlags'+    getFlags+    targets++unpackAction :: GetFlags -> [String] -> Action+unpackAction getFlags extraArgs globalFlags = do+  getAction getFlags extraArgs globalFlags++initAction :: InitFlags -> [String] -> Action+initAction initFlags extraArgs globalFlags = do+  let verbosity = fromFlag (initVerbosity initFlags)+  when (extraArgs /= []) $+    die' verbosity $ "'init' doesn't take any extra arguments: " ++ unwords extraArgs+  (_useSandbox, config) <- loadConfigOrSandboxConfig verbosity+                           (globalFlags { globalRequireSandbox = Flag False })+  let configFlags  = savedConfigureFlags config+  let globalFlags' = savedGlobalFlags    config `mappend` globalFlags+  (comp, _, progdb) <- configCompilerAux' configFlags+  withRepoContext verbosity globalFlags' $ \repoContext ->+    initCabal verbosity+            (configPackageDB' configFlags)+            repoContext+            comp+            progdb+            initFlags++sandboxAction :: SandboxFlags -> [String] -> Action+sandboxAction sandboxFlags extraArgs globalFlags = do+  let verbosity = fromFlag (sandboxVerbosity sandboxFlags)+  case extraArgs of+    -- Basic sandbox commands.+    ["init"] -> sandboxInit verbosity sandboxFlags globalFlags+    ["delete"] -> sandboxDelete verbosity sandboxFlags globalFlags+    ("add-source":extra) -> do+        when (noExtraArgs extra) $+          die' verbosity "The 'sandbox add-source' command expects at least one argument"+        sandboxAddSource verbosity extra sandboxFlags globalFlags+    ("delete-source":extra) -> do+        when (noExtraArgs extra) $+          die' verbosity ("The 'sandbox delete-source' command expects " +++              "at least one argument")+        sandboxDeleteSource verbosity extra sandboxFlags globalFlags+    ["list-sources"] -> sandboxListSources verbosity sandboxFlags globalFlags++    -- More advanced commands.+    ("hc-pkg":extra) -> do+        when (noExtraArgs extra) $+            die' verbosity $ "The 'sandbox hc-pkg' command expects at least one argument"+        sandboxHcPkg verbosity sandboxFlags globalFlags extra+    ["buildopts"] -> die' verbosity "Not implemented!"++    -- Hidden commands.+    ["dump-pkgenv"]  -> dumpPackageEnvironment verbosity sandboxFlags globalFlags++    -- Error handling.+    [] -> die' verbosity $ "Please specify a subcommand (see 'help sandbox')"+    _  -> die' verbosity $ "Unknown 'sandbox' subcommand: " ++ unwords extraArgs++  where+    noExtraArgs = (<1) . length++execAction :: ExecFlags -> [String] -> Action+execAction execFlags extraArgs globalFlags = do+  let verbosity = fromFlag (execVerbosity execFlags)+  (useSandbox, config) <- loadConfigOrSandboxConfig verbosity globalFlags+  distPref <- findSavedDistPref config (execDistPref execFlags)+  let configFlags = savedConfigureFlags config+      configFlags' = configFlags { configDistPref = Flag distPref }+  (comp, platform, progdb) <- getPersistOrConfigCompiler configFlags'+  exec verbosity useSandbox comp platform progdb extraArgs++userConfigAction :: UserConfigFlags -> [String] -> Action+userConfigAction ucflags extraArgs globalFlags = do+  let verbosity = fromFlag (userConfigVerbosity ucflags)+      force     = fromFlag (userConfigForce ucflags)+  case extraArgs of+    ("init":_) -> do+      path       <- configFile+      fileExists <- doesFileExist path+      if (not fileExists || (fileExists && force))+      then void $ createDefaultConfigFile verbosity path+      else die' verbosity $ path ++ " already exists."+    ("diff":_) -> mapM_ putStrLn =<< userConfigDiff globalFlags+    ("update":_) -> userConfigUpdate verbosity globalFlags+    -- Error handling.+    [] -> die' verbosity $ "Please specify a subcommand (see 'help user-config')"+    _  -> die' verbosity $ "Unknown 'user-config' subcommand: " ++ unwords extraArgs+  where configFile = getConfigFilePath (globalConfigFile globalFlags)++-- | See 'Distribution.Client.Install.withWin32SelfUpgrade' for details.+--+win32SelfUpgradeAction :: Win32SelfUpgradeFlags -> [String] -> Action+win32SelfUpgradeAction selfUpgradeFlags (pid:path:_extraArgs) _globalFlags = do+  let verbosity = fromFlag (win32SelfUpgradeVerbosity selfUpgradeFlags)+  Win32SelfUpgrade.deleteOldExeFile verbosity (read pid) path -- TODO: eradicateNoParse+win32SelfUpgradeAction _ _ _ = return ()++-- | Used as an entry point when cabal-install needs to invoke itself+-- as a setup script. This can happen e.g. when doing parallel builds.+--+actAsSetupAction :: ActAsSetupFlags -> [String] -> Action+actAsSetupAction actAsSetupFlags args _globalFlags =+  let bt = fromFlag (actAsSetupBuildType actAsSetupFlags)+  in case bt of+    Simple    -> Simple.defaultMainArgs args+    Configure -> Simple.defaultMainWithHooksArgs+                  Simple.autoconfUserHooks args+    Make      -> Make.defaultMainArgs args+    Custom               -> error "actAsSetupAction Custom"+    (UnknownBuildType _) -> error "actAsSetupAction UnknownBuildType"++manpageAction :: [CommandSpec action] -> Flag Verbosity -> [String] -> Action+manpageAction commands flagVerbosity extraArgs _ = do+  let verbosity = fromFlag flagVerbosity+  unless (null extraArgs) $+    die' verbosity $ "'manpage' doesn't take any extra arguments: " ++ unwords extraArgs   pname <- getProgName   let cabalCmd = if takeExtension pname == ".exe"                  then dropExtension pname
Setup.hs view
@@ -11,7 +11,7 @@ import Distribution.Simple.LocalBuildInfo ( LocalBuildInfo(..)                                           , absoluteInstallDirs                                           )-import Distribution.Simple.Utils ( copyFiles+import Distribution.Simple.Utils ( installOrdinaryFiles                                  , notice ) import Distribution.Simple.Setup ( buildVerbosity                                  , copyDest@@ -27,6 +27,16 @@ import System.Process ( runProcess ) import System.FilePath ( (</>) ) +-- WARNING to editors of this file:+-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+-- At this moment (Cabal 1.23), whatever you write here must be+-- compatible with ALL Cabal libraries which we support bootstrapping+-- with.  This is because pre-setup-depends versions of cabal-install will+-- build Setup.hs against the version of Cabal which MATCHES the library+-- that cabal-install was built against.  There is no way of overriding+-- this behavior without bumping the required 'cabal-version' in our+-- Cabal file.  Travis will let you know if we fail to install from+-- tarball!  main :: IO () main = defaultMainWithHooks $ simpleUserHooks@@ -50,4 +60,4 @@ installManpage :: PackageDescription -> LocalBuildInfo -> Verbosity -> CopyDest -> IO () installManpage pkg lbi verbosity copy = do   let destDir = mandir (absoluteInstallDirs pkg lbi copy) </> "man1"-  copyFiles verbosity destDir [(buildDir lbi </> "cabal", "cabal.1")]+  installOrdinaryFiles verbosity destDir [(buildDir lbi </> "cabal", "cabal.1")]
bash-completion/cabal view
@@ -10,9 +10,10 @@ #   - executable|test-suite|benchmark for the three _cabal_list() {-	cat *.cabal |-	grep -Ei "^[[:space:]]*($1)[[:space:]]" |-	sed -e "s/.* \([^ ]*\).*/\1/"+    for f in ./*.cabal; do+        grep -Ei "^[[:space:]]*($1)[[:space:]]" "$f" |+        sed -e "s/.* \([^ ]*\).*/\1/"+    done }  # List possible targets depending on the command supplied as parameter.  The@@ -20,17 +21,17 @@ # This is a temporary workaround. _cabal_targets() {-	# If command ($*) contains build, repl, test or bench completes with-	# targets of according type.-	[ -f *.cabal ] || return 0-	local comp-	for comp in $*; do-		[ $comp == build ] && _cabal_list "executable|test-suite|benchmark" && break-		[ $comp == repl  ] && _cabal_list "executable|test-suite|benchmark" && break-		[ $comp == run   ] && _cabal_list "executable"                      && break-		[ $comp == test  ] && _cabal_list            "test-suite"           && break-		[ $comp == bench ] && _cabal_list                       "benchmark" && break-	done+    # If command ($*) contains build, repl, test or bench completes with+    # targets of according type.+    local comp+    for comp in "$@"; do+        [ "$comp" == new-build ] && _cabal_list "executable|test-suite|benchmark" && break+        [ "$comp" == build     ] && _cabal_list "executable|test-suite|benchmark" && break+        [ "$comp" == repl      ] && _cabal_list "executable|test-suite|benchmark" && break+        [ "$comp" == run       ] && _cabal_list "executable"                      && break+        [ "$comp" == test      ] && _cabal_list            "test-suite"           && break+        [ "$comp" == bench     ] && _cabal_list                       "benchmark" && break+    done }  # List possible subcommands of a cabal subcommand.@@ -87,7 +88,7 @@     cmd[${COMP_CWORD}]="--list-options"      # the resulting completions should be put into this array-    COMPREPLY=( $( compgen -W "$( ${cmd[@]} ) $( _cabal_targets ${cmd[@]} ) $( _cabal_subcommands ${COMP_WORDS[@]} )" -- $cur ) )+    COMPREPLY=( $( compgen -W "$( eval "${cmd[@]}" 2>/dev/null ) $( _cabal_targets "${cmd[@]}" ) $( _cabal_subcommands "${COMP_WORDS[@]}" )" -- "$cur" ) ) }  complete -F _cabal -o default cabal
bootstrap.sh view
@@ -16,7 +16,10 @@ #EXTRA_BUILD_OPTS #EXTRA_INSTALL_OPTS -die () { printf "\nError during cabal-install bootstrap:\n$1\n" >&2 && exit 2 ;}+die() {+    printf "\nError during cabal-install bootstrap:\n%s\n" "$1" >&2+    exit 2+}  # programs, you can override these by setting environment vars GHC="${GHC:-ghc}"@@ -51,13 +54,12 @@ SCOPE_OF_INSTALLATION="${SCOPE_OF_INSTALLATION:---user}" DEFAULT_PREFIX="${HOME}/.cabal" -# Try to respect $TMPDIR.-[ -"$TMPDIR"- = -""- ] &&-  export TMPDIR=/tmp/cabal-$(echo $(od -XN4 -An /dev/random)) && mkdir $TMPDIR+TMPDIR=$(mktemp -d -p /tmp -t cabal-XXXXXXX || mktemp -d -t cabal-XXXXXXX)+export TMPDIR  # Check for a C compiler, using user-set $CC, if any, first. for c in $CC gcc clang cc icc; do-  $c --version 2>&1 >/dev/null && CC=$c &&+  $c --version 1>/dev/null 2>&1 && CC=$c &&   echo "Using $c for C compiler. If this is not what you want, set CC." >&2 &&   break done@@ -67,9 +69,15 @@   If a C compiler is installed make sure it is on your PATH, or set $CC.'  # Find the correct linker/linker-wrapper.+#+# See https://github.com/haskell/cabal/pull/4187#issuecomment-269074153. LINK="$(for link in collect2 ld; do-          [ $($CC -print-prog-name=$link) = $link ] && continue ||-          $CC -print-prog-name=$link+          if [ $($CC -print-prog-name=$link) = $link ]+          then+              continue+          else+              $CC -print-prog-name=$link && break+          fi         done)"  # Fall back to "ld"... might work.@@ -158,9 +166,9 @@   esac done -# Do not try to use -j with GHC older than 7.8+# Do not try to use -j with GHC 7.8 or older case $GHC_VER in-    7.4*|7.6*)+    7.4*|7.6*|7.8*)         JOBS=""         ;;     *)@@ -200,9 +208,9 @@  # Versions of the packages to install. # The version regex says what existing installed versions are ok.-PARSEC_VER="3.1.9";    PARSEC_VER_REGEXP="[3]\.[01]\."+PARSEC_VER="3.1.11";    PARSEC_VER_REGEXP="[3]\.[01]\."                        # >= 3.0 && < 3.2-DEEPSEQ_VER="1.4.2.0"; DEEPSEQ_VER_REGEXP="1\.[1-9]\."+DEEPSEQ_VER="1.4.3.0"; DEEPSEQ_VER_REGEXP="1\.[1-9]\."                        # >= 1.1 && < 2  case "$GHC_VER" in@@ -213,35 +221,35 @@         ;;     *)         # GHC >= 7.8-        BINARY_VER="0.8.3.0"+        BINARY_VER="0.8.5.1"         BINARY_VER_REGEXP="[0]\.[78]\." # >= 0.7 && < 0.9         ;; esac  -TEXT_VER="1.2.2.1";    TEXT_VER_REGEXP="((1\.[012]\.)|(0\.([2-9]|(1[0-1]))\.))"+TEXT_VER="1.2.2.2";    TEXT_VER_REGEXP="((1\.[012]\.)|(0\.([2-9]|(1[0-1]))\.))"                        # >= 0.2 && < 1.3-NETWORK_VER="2.6.3.1"; NETWORK_VER_REGEXP="2\.[0-6]\."+NETWORK_VER="2.6.3.2"; NETWORK_VER_REGEXP="2\.[0-6]\."                        # >= 2.0 && < 2.7 NETWORK_URI_VER="2.6.1.0"; NETWORK_URI_VER_REGEXP="2\.6\."                        # >= 2.6 && < 2.7-CABAL_VER="1.24.2.0";  CABAL_VER_REGEXP="1\.24\.[2-9]"-                       # >= 1.24.2 && < 1.25-TRANS_VER="0.5.2.0";   TRANS_VER_REGEXP="0\.[45]\."+CABAL_VER="2.0.0.2";  CABAL_VER_REGEXP="2\.0\.[0-9]"+                       # >= 2.0 && < 2.1+TRANS_VER="0.5.4.0";   TRANS_VER_REGEXP="0\.[45]\."                        # >= 0.2.* && < 0.6 MTL_VER="2.2.1";       MTL_VER_REGEXP="[2]\."                        #  >= 2.0 && < 3-HTTP_VER="4000.3.3";   HTTP_VER_REGEXP="4000\.(2\.([5-9]|1[0-9]|2[0-9])|3\.?)"+HTTP_VER="4000.3.7";   HTTP_VER_REGEXP="4000\.(2\.([5-9]|1[0-9]|2[0-9])|3\.?)"                        # >= 4000.2.5 < 4000.4 ZLIB_VER="0.6.1.2";    ZLIB_VER_REGEXP="(0\.5\.([3-9]|1[0-9])|0\.6)"                        # >= 0.5.3 && <= 0.7-TIME_VER="1.7"         TIME_VER_REGEXP="1\.[1-7]\.?"-                       # >= 1.1 && < 1.8+TIME_VER="1.8.0.2"     TIME_VER_REGEXP="1\.[1-8]\.?"+                       # >= 1.1 && < 1.9 RANDOM_VER="1.1"       RANDOM_VER_REGEXP="1\.[01]\.?"                        # >= 1 && < 1.2 STM_VER="2.4.4.1";     STM_VER_REGEXP="2\."                        # == 2.*-ASYNC_VER="2.1.0";     ASYNC_VER_REGEXP="2\."+ASYNC_VER="2.1.1.1";   ASYNC_VER_REGEXP="2\."                        # 2.* OLD_TIME_VER="1.1.0.3"; OLD_TIME_VER_REGEXP="1\.[01]\.?"                        # >=1.0.0.0 && <1.2@@ -249,10 +257,16 @@                        # >=1.0.0.0 && <1.1 BASE16_BYTESTRING_VER="0.1.1.6"; BASE16_BYTESTRING_VER_REGEXP="0\.1"                        # 0.1.*-BASE64_BYTESTRING_VER="1.0.0.1"; BASE64_BYTESTRING_REGEXP="1\."+BASE64_BYTESTRING_VER="1.0.0.1"; BASE64_BYTESTRING_VER_REGEXP="1\."                        # >=1.0 CRYPTOHASH_SHA256_VER="0.11.100.1"; CRYPTOHASH_SHA256_VER_REGEXP="0\.11\.?"                        # 0.11.*+MINTTY_VER="0.1";      MINTTY_VER_REGEXP="0\.1\.?"+                       # 0.1.*+ECHO_VER="0.1.3";      ECHO_VER_REGEXP="0\.1\.[3-9]"+                       # >= 0.1.3 && < 0.2+EDIT_DISTANCE_VER="0.2.2.1"; EDIT_DISTANCE_VER_REGEXP="0\.2\.2\.?"+                       # 0.2.2.* ED25519_VER="0.0.5.0"; ED25519_VER_REGEXP="0\.0\.?"                        # 0.0.* HACKAGE_SECURITY_VER="0.5.2.2"; HACKAGE_SECURITY_VER_REGEXP="0\.5\.(2\.[2-9]|[3-9])"@@ -260,7 +274,7 @@ BYTESTRING_BUILDER_VER="0.10.8.1.0"; BYTESTRING_BUILDER_VER_REGEXP="0\.10\.?" TAR_VER="0.5.0.3";     TAR_VER_REGEXP="0\.5\.([1-9]|1[0-9]|0\.[3-9]|0\.1[0-9])\.?"                        # >= 0.5.0.3  && < 0.6-HASHABLE_VER="1.2.4.0"; HASHABLE_VER_REGEXP="1\."+HASHABLE_VER="1.2.6.1"; HASHABLE_VER_REGEXP="1\."                        # 1.*  HACKAGE_URL="https://hackage.haskell.org/package"@@ -354,7 +368,7 @@   [ -x Setup ] && ./Setup clean   [ -f Setup ] && rm Setup -  ${GHC} --make ${JOBS} Setup -o Setup ||+  ${GHC} --make ${JOBS} Setup -o Setup -XRank2Types -XFlexibleContexts ||     die "Compiling the Setup script failed."    [ -x Setup ] || die "The Setup script does not exist or cannot be run"@@ -399,10 +413,8 @@         echo "Downloading ${PKG}-${VER}..."         fetch_pkg ${PKG} ${VER}     fi-    unpack_pkg ${PKG} ${VER}-    cd "${PKG}-${VER}"-    install_pkg ${PKG} ${VER}-    cd ..+    unpack_pkg "${PKG}" "${VER}"+    (cd "${PKG}-${VER}" && install_pkg ${PKG} ${VER})   fi } @@ -414,9 +426,7 @@         if need_pkg "Cabal" ${CABAL_VER_REGEXP}         then             echo "Cabal-${CABAL_VER} will be installed from the local Git clone."-            cd ../Cabal-            install_pkg ${CABAL_VER} ${CABAL_VER_REGEXP}-            cd ../cabal-install+            (cd ../Cabal && install_pkg ${CABAL_VER} ${CABAL_VER_REGEXP})         else             echo "Cabal is already installed and the version is ok."         fi@@ -481,6 +491,9 @@     ${BASE64_BYTESTRING_VER_REGEXP} info_pkg "cryptohash-sha256" ${CRYPTOHASH_SHA256_VER} \     ${CRYPTOHASH_SHA256_VER_REGEXP}+info_pkg "mintty"        ${MINTTY_VER}        ${MINTTY_VER_REGEXP}+info_pkg "echo"          ${ECHO_VER}          ${ECHO_VER_REGEXP}+info_pkg "edit-distance" ${EDIT_DISTANCE_VER} ${EDIT_DISTANCE_VER_REGEXP} info_pkg "ed25519"           ${ED25519_VER}          ${ED25519_VER_REGEXP} info_pkg "tar"               ${TAR_VER}              ${TAR_VER_REGEXP} info_pkg "hashable"          ${HASHABLE_VER}         ${HASHABLE_VER_REGEXP}@@ -516,6 +529,9 @@     ${BASE64_BYTESTRING_VER_REGEXP} do_pkg   "cryptohash-sha256" ${CRYPTOHASH_SHA256_VER} \     ${CRYPTOHASH_SHA256_VER_REGEXP}+do_pkg "mintty"        ${MINTTY_VER}        ${MINTTY_VER_REGEXP}+do_pkg "echo"          ${ECHO_VER}          ${ECHO_VER_REGEXP}+do_pkg "edit-distance" ${EDIT_DISTANCE_VER} ${EDIT_DISTANCE_VER_REGEXP} do_pkg   "ed25519"           ${ED25519_VER}          ${ED25519_VER_REGEXP}  # We conditionally install bytestring-builder, depending on the bytestring
cabal-install.cabal view
@@ -1,5 +1,5 @@ Name:               cabal-install-Version:            1.24.0.2+Version:            2.0.0.0 Synopsis:           The command-line interface for Cabal and Hackage. Description:     The \'cabal\' command-line program simplifies the process of managing@@ -9,17 +9,9 @@ bug-reports:        https://github.com/haskell/cabal/issues License:            BSD3 License-File:       LICENSE-Author:             Lemmih <lemmih@gmail.com>-                    Paolo Martini <paolo@nemail.it>-                    Bjorn Bringert <bjorn@bringert.net>-                    Isaac Potoczny-Jones <ijones@syntaxpolice.org>-                    Duncan Coutts <duncan@community.haskell.org>-Maintainer:         cabal-devel@haskell.org-Copyright:          2005 Lemmih <lemmih@gmail.com>-                    2006 Paolo Martini <paolo@nemail.it>-                    2007 Bjorn Bringert <bjorn@bringert.net>-                    2007 Isaac Potoczny-Jones <ijones@syntaxpolice.org>-                    2007-2012 Duncan Coutts <duncan@community.haskell.org>+Author:             Cabal Development Team (see AUTHORS file)+Maintainer:         Cabal Development Team <cabal-devel@haskell.org>+Copyright:          2003-2017, Cabal Development Team Category:           Distribution Build-type:         Custom Cabal-Version:      >= 1.10@@ -30,98 +22,67 @@   -- Generated with '../Cabal/misc/gen-extra-source-files.sh'   -- Do NOT edit this section manually; instead, run the script.   -- BEGIN gen-extra-source-files-  tests/IntegrationTests/custom-setup/common.sh-  tests/IntegrationTests/custom-setup/should_run/Cabal-99998/Cabal.cabal-  tests/IntegrationTests/custom-setup/should_run/Cabal-99998/CabalMessage.hs-  tests/IntegrationTests/custom-setup/should_run/Cabal-99999/Cabal.cabal-  tests/IntegrationTests/custom-setup/should_run/Cabal-99999/CabalMessage.hs-  tests/IntegrationTests/custom-setup/should_run/custom-setup-without-cabal-defaultMain/Setup.hs-  tests/IntegrationTests/custom-setup/should_run/custom-setup-without-cabal-defaultMain/custom-setup-without-cabal-defaultMain.cabal-  tests/IntegrationTests/custom-setup/should_run/custom-setup-without-cabal/Setup.hs-  tests/IntegrationTests/custom-setup/should_run/custom-setup-without-cabal/custom-setup-without-cabal.cabal-  tests/IntegrationTests/custom-setup/should_run/custom-setup/Setup.hs-  tests/IntegrationTests/custom-setup/should_run/custom-setup/custom-setup.cabal-  tests/IntegrationTests/custom-setup/should_run/custom_setup_without_Cabal_doesnt_allow_Cabal_import.sh-  tests/IntegrationTests/custom-setup/should_run/custom_setup_without_Cabal_doesnt_require_Cabal.sh-  tests/IntegrationTests/custom-setup/should_run/installs_Cabal_as_setup_dep.sh-  tests/IntegrationTests/custom/common.sh-  tests/IntegrationTests/custom/should_run/plain.err-  tests/IntegrationTests/custom/should_run/plain.sh-  tests/IntegrationTests/custom/should_run/plain/A.hs-  tests/IntegrationTests/custom/should_run/plain/Setup.hs-  tests/IntegrationTests/custom/should_run/plain/plain.cabal-  tests/IntegrationTests/exec/common.sh-  tests/IntegrationTests/exec/should_fail/exit_with_failure_without_args.err-  tests/IntegrationTests/exec/should_fail/exit_with_failure_without_args.sh-  tests/IntegrationTests/exec/should_run/Foo.hs-  tests/IntegrationTests/exec/should_run/My.hs-  tests/IntegrationTests/exec/should_run/adds_sandbox_bin_directory_to_path.out-  tests/IntegrationTests/exec/should_run/adds_sandbox_bin_directory_to_path.sh-  tests/IntegrationTests/exec/should_run/auto_configures_on_exec.out-  tests/IntegrationTests/exec/should_run/auto_configures_on_exec.sh-  tests/IntegrationTests/exec/should_run/can_run_executables_installed_in_sandbox.out-  tests/IntegrationTests/exec/should_run/can_run_executables_installed_in_sandbox.sh-  tests/IntegrationTests/exec/should_run/configures_cabal_to_use_sandbox.sh-  tests/IntegrationTests/exec/should_run/configures_ghc_to_use_sandbox.sh-  tests/IntegrationTests/exec/should_run/my.cabal-  tests/IntegrationTests/exec/should_run/runs_given_command.out-  tests/IntegrationTests/exec/should_run/runs_given_command.sh-  tests/IntegrationTests/freeze/common.sh-  tests/IntegrationTests/freeze/should_run/disable_benchmarks_freezes_bench_deps.sh-  tests/IntegrationTests/freeze/should_run/disable_tests_freezes_test_deps.sh-  tests/IntegrationTests/freeze/should_run/does_not_freeze_nondeps.sh-  tests/IntegrationTests/freeze/should_run/does_not_freeze_self.sh-  tests/IntegrationTests/freeze/should_run/dry_run_does_not_create_config.sh-  tests/IntegrationTests/freeze/should_run/enable_benchmarks_freezes_bench_deps.sh-  tests/IntegrationTests/freeze/should_run/enable_tests_freezes_test_deps.sh-  tests/IntegrationTests/freeze/should_run/freezes_direct_dependencies.sh-  tests/IntegrationTests/freeze/should_run/freezes_transitive_dependencies.sh-  tests/IntegrationTests/freeze/should_run/my.cabal-  tests/IntegrationTests/freeze/should_run/runs_without_error.sh-  tests/IntegrationTests/manpage/common.sh-  tests/IntegrationTests/manpage/should_run/outputs_manpage.sh-  tests/IntegrationTests/multiple-source/common.sh-  tests/IntegrationTests/multiple-source/should_run/finds_second_source_of_multiple_source.sh-  tests/IntegrationTests/multiple-source/should_run/p/Setup.hs-  tests/IntegrationTests/multiple-source/should_run/p/p.cabal-  tests/IntegrationTests/multiple-source/should_run/q/Setup.hs-  tests/IntegrationTests/multiple-source/should_run/q/q.cabal-  tests/IntegrationTests/new-build/monitor_cabal_files.sh-  tests/IntegrationTests/new-build/monitor_cabal_files/p/P.hs-  tests/IntegrationTests/new-build/monitor_cabal_files/p/Setup.hs-  tests/IntegrationTests/new-build/monitor_cabal_files/p/p.cabal-  tests/IntegrationTests/new-build/monitor_cabal_files/q/Main.hs-  tests/IntegrationTests/new-build/monitor_cabal_files/q/Setup.hs-  tests/IntegrationTests/new-build/monitor_cabal_files/q/q-broken.cabal.in-  tests/IntegrationTests/new-build/monitor_cabal_files/q/q-fixed.cabal.in-  tests/IntegrationTests/regression/common.sh-  tests/IntegrationTests/regression/t3199.sh-  tests/IntegrationTests/regression/t3199/Main.hs-  tests/IntegrationTests/regression/t3199/Setup.hs-  tests/IntegrationTests/regression/t3199/test-3199.cabal-  tests/IntegrationTests/sandbox-sources/common.sh-  tests/IntegrationTests/sandbox-sources/should_fail/fail_removing_source_thats_not_registered.err-  tests/IntegrationTests/sandbox-sources/should_fail/fail_removing_source_thats_not_registered.sh-  tests/IntegrationTests/sandbox-sources/should_fail/p/Setup.hs-  tests/IntegrationTests/sandbox-sources/should_fail/p/p.cabal-  tests/IntegrationTests/sandbox-sources/should_fail/q/Setup.hs-  tests/IntegrationTests/sandbox-sources/should_fail/q/q.cabal-  tests/IntegrationTests/sandbox-sources/should_run/p/Setup.hs-  tests/IntegrationTests/sandbox-sources/should_run/p/p.cabal-  tests/IntegrationTests/sandbox-sources/should_run/q/Setup.hs-  tests/IntegrationTests/sandbox-sources/should_run/q/q.cabal-  tests/IntegrationTests/sandbox-sources/should_run/remove_nonexistent_source.sh-  tests/IntegrationTests/sandbox-sources/should_run/report_success_removing_source.out-  tests/IntegrationTests/sandbox-sources/should_run/report_success_removing_source.sh-  tests/IntegrationTests/user-config/common.sh-  tests/IntegrationTests/user-config/should_fail/doesnt_overwrite_without_f.err-  tests/IntegrationTests/user-config/should_fail/doesnt_overwrite_without_f.sh-  tests/IntegrationTests/user-config/should_run/overwrites_with_f.out-  tests/IntegrationTests/user-config/should_run/overwrites_with_f.sh-  tests/IntegrationTests/user-config/should_run/runs_without_error.out-  tests/IntegrationTests/user-config/should_run/runs_without_error.sh-  tests/IntegrationTests/user-config/should_run/uses_CABAL_CONFIG.out-  tests/IntegrationTests/user-config/should_run/uses_CABAL_CONFIG.sh+  tests/IntegrationTests2/build/keep-going/cabal.project+  tests/IntegrationTests2/build/keep-going/p/P.hs+  tests/IntegrationTests2/build/keep-going/p/p.cabal+  tests/IntegrationTests2/build/keep-going/q/Q.hs+  tests/IntegrationTests2/build/keep-going/q/q.cabal+  tests/IntegrationTests2/build/setup-custom1/A.hs+  tests/IntegrationTests2/build/setup-custom1/Setup.hs+  tests/IntegrationTests2/build/setup-custom1/a.cabal+  tests/IntegrationTests2/build/setup-custom2/A.hs+  tests/IntegrationTests2/build/setup-custom2/Setup.hs+  tests/IntegrationTests2/build/setup-custom2/a.cabal+  tests/IntegrationTests2/build/setup-simple/A.hs+  tests/IntegrationTests2/build/setup-simple/Setup.hs+  tests/IntegrationTests2/build/setup-simple/a.cabal+  tests/IntegrationTests2/exception/bad-config/cabal.project+  tests/IntegrationTests2/exception/build/Main.hs+  tests/IntegrationTests2/exception/build/a.cabal+  tests/IntegrationTests2/exception/configure/a.cabal+  tests/IntegrationTests2/exception/no-pkg/empty.in+  tests/IntegrationTests2/exception/no-pkg2/cabal.project+  tests/IntegrationTests2/regression/3324/cabal.project+  tests/IntegrationTests2/regression/3324/p/P.hs+  tests/IntegrationTests2/regression/3324/p/p.cabal+  tests/IntegrationTests2/regression/3324/q/Q.hs+  tests/IntegrationTests2/regression/3324/q/q.cabal+  tests/IntegrationTests2/targets/all-disabled/cabal.project+  tests/IntegrationTests2/targets/all-disabled/p.cabal+  tests/IntegrationTests2/targets/benchmarks-disabled/cabal.project+  tests/IntegrationTests2/targets/benchmarks-disabled/p.cabal+  tests/IntegrationTests2/targets/benchmarks-disabled/q/q.cabal+  tests/IntegrationTests2/targets/complex/cabal.project+  tests/IntegrationTests2/targets/complex/q/Q.hs+  tests/IntegrationTests2/targets/complex/q/q.cabal+  tests/IntegrationTests2/targets/empty-pkg/cabal.project+  tests/IntegrationTests2/targets/empty-pkg/p.cabal+  tests/IntegrationTests2/targets/empty/cabal.project+  tests/IntegrationTests2/targets/empty/foo.hs+  tests/IntegrationTests2/targets/exes-disabled/cabal.project+  tests/IntegrationTests2/targets/exes-disabled/p/p.cabal+  tests/IntegrationTests2/targets/exes-disabled/q/q.cabal+  tests/IntegrationTests2/targets/libs-disabled/cabal.project+  tests/IntegrationTests2/targets/libs-disabled/p/p.cabal+  tests/IntegrationTests2/targets/libs-disabled/q/q.cabal+  tests/IntegrationTests2/targets/multiple-exes/cabal.project+  tests/IntegrationTests2/targets/multiple-exes/p.cabal+  tests/IntegrationTests2/targets/multiple-libs/cabal.project+  tests/IntegrationTests2/targets/multiple-libs/p/p.cabal+  tests/IntegrationTests2/targets/multiple-libs/q/q.cabal+  tests/IntegrationTests2/targets/multiple-tests/cabal.project+  tests/IntegrationTests2/targets/multiple-tests/p.cabal+  tests/IntegrationTests2/targets/simple/P.hs+  tests/IntegrationTests2/targets/simple/cabal.project+  tests/IntegrationTests2/targets/simple/p.cabal+  tests/IntegrationTests2/targets/simple/q/QQ.hs+  tests/IntegrationTests2/targets/simple/q/q.cabal+  tests/IntegrationTests2/targets/test-only/p.cabal+  tests/IntegrationTests2/targets/tests-disabled/cabal.project+  tests/IntegrationTests2/targets/tests-disabled/p.cabal+  tests/IntegrationTests2/targets/tests-disabled/q/q.cabal+  tests/IntegrationTests2/targets/variety/cabal.project+  tests/IntegrationTests2/targets/variety/p.cabal   -- END gen-extra-source-files  source-repository head@@ -141,55 +102,65 @@   description:  Get Network.URI from the network-uri package   default:      True +Flag debug-expensive-assertions+  description:  Enable expensive assertions for testing or debugging+  default:      False+  manual:       True++Flag debug-conflict-sets+  description:  Add additional information to ConflictSets+  default:      False+  manual:       True++Flag debug-tracetree+  description:  Compile in support for tracetree (used to debug the solver)+  default:      False+  manual:       True++flag parsec+  description:  Use parsec parser. This requires 'Cabal' library built with its parsec flag enabled.+  default:      False+  manual:       True++-- When we do CI, we build our binaries on one machine, and then+-- ship them to another machine for testing.  Because we use+-- static linking (since it makes this sort of redeploy MUCH+-- easier), if we build five executables, that means we+-- need to ship ALL the Haskell libraries five times.  That's+-- a waste of space!  A better strategy is to statically link+-- everything into a single binary.  That's what this flag does.+flag monolithic+  description:  Build cabal-install also with all of its test and support code.  Used by our continuous integration.+  default:      False+  manual:       True+ executable cabal     main-is:        Main.hs-    ghc-options:    -Wall -fwarn-tabs+    ghc-options:    -Wall -fwarn-tabs -rtsopts     if impl(ghc >= 8.0)         ghc-options: -Wcompat                      -Wnoncanonical-monad-instances                      -Wnoncanonical-monadfail-instances      other-modules:-        Distribution.Client.BuildTarget         Distribution.Client.BuildReports.Anonymous         Distribution.Client.BuildReports.Storage         Distribution.Client.BuildReports.Types         Distribution.Client.BuildReports.Upload         Distribution.Client.Check+        Distribution.Client.CmdBench         Distribution.Client.CmdBuild         Distribution.Client.CmdConfigure+        Distribution.Client.CmdFreeze+        Distribution.Client.CmdHaddock         Distribution.Client.CmdRepl-        Distribution.Client.ComponentDeps+        Distribution.Client.CmdRun+        Distribution.Client.CmdTest+        Distribution.Client.CmdErrorMessages         Distribution.Client.Config         Distribution.Client.Configure         Distribution.Client.Dependency-        Distribution.Client.Dependency.TopDown-        Distribution.Client.Dependency.TopDown.Constraints-        Distribution.Client.Dependency.TopDown.Types         Distribution.Client.Dependency.Types-        Distribution.Client.Dependency.Modular-        Distribution.Client.Dependency.Modular.Assignment-        Distribution.Client.Dependency.Modular.Builder-        Distribution.Client.Dependency.Modular.Configured-        Distribution.Client.Dependency.Modular.ConfiguredConversion-        Distribution.Client.Dependency.Modular.ConflictSet-        Distribution.Client.Dependency.Modular.Cycles-        Distribution.Client.Dependency.Modular.Dependency-        Distribution.Client.Dependency.Modular.Explore-        Distribution.Client.Dependency.Modular.Flag-        Distribution.Client.Dependency.Modular.Index-        Distribution.Client.Dependency.Modular.IndexConversion-        Distribution.Client.Dependency.Modular.Linking-        Distribution.Client.Dependency.Modular.Log-        Distribution.Client.Dependency.Modular.Message-        Distribution.Client.Dependency.Modular.Package-        Distribution.Client.Dependency.Modular.Preference-        Distribution.Client.Dependency.Modular.PSQ-        Distribution.Client.Dependency.Modular.Solver-        Distribution.Client.Dependency.Modular.Tree-        Distribution.Client.Dependency.Modular.Validate-        Distribution.Client.Dependency.Modular.Var-        Distribution.Client.Dependency.Modular.Version         Distribution.Client.DistDirLayout         Distribution.Client.Exec         Distribution.Client.Fetch@@ -204,6 +175,7 @@         Distribution.Client.Haddock         Distribution.Client.HttpUtils         Distribution.Client.IndexUtils+        Distribution.Client.IndexUtils.Timestamp         Distribution.Client.Init         Distribution.Client.Init.Heuristics         Distribution.Client.Init.Licenses@@ -214,13 +186,13 @@         Distribution.Client.JobControl         Distribution.Client.List         Distribution.Client.Manpage+        Distribution.Client.Nix+        Distribution.Client.Outdated         Distribution.Client.PackageHash-        Distribution.Client.PackageIndex         Distribution.Client.PackageUtils         Distribution.Client.ParseUtils-        Distribution.Client.PkgConfigDb-        Distribution.Client.PlanIndex         Distribution.Client.ProjectBuilding+        Distribution.Client.ProjectBuilding.Types         Distribution.Client.ProjectConfig         Distribution.Client.ProjectConfig.Types         Distribution.Client.ProjectConfig.Legacy@@ -228,32 +200,87 @@         Distribution.Client.ProjectPlanning         Distribution.Client.ProjectPlanning.Types         Distribution.Client.ProjectPlanOutput-        Distribution.Client.Run         Distribution.Client.RebuildMonad+        Distribution.Client.Reconfigure+        Distribution.Client.Run         Distribution.Client.Sandbox         Distribution.Client.Sandbox.Index         Distribution.Client.Sandbox.PackageEnvironment         Distribution.Client.Sandbox.Timestamp         Distribution.Client.Sandbox.Types+        Distribution.Client.SavedFlags+        Distribution.Client.Security.DNS         Distribution.Client.Security.HTTP         Distribution.Client.Setup         Distribution.Client.SetupWrapper         Distribution.Client.SrcDist+        Distribution.Client.SolverInstallPlan+        Distribution.Client.SourceFiles+        Distribution.Client.Store         Distribution.Client.Tar         Distribution.Client.Targets+        Distribution.Client.TargetSelector         Distribution.Client.Types         Distribution.Client.Update         Distribution.Client.Upload         Distribution.Client.Utils-        Distribution.Client.Utils.LabeledGraph+        Distribution.Client.Utils.Assertion         Distribution.Client.Utils.Json         Distribution.Client.World         Distribution.Client.Win32SelfUpgrade         Distribution.Client.Compat.ExecutablePath+        Distribution.Client.Compat.FileLock         Distribution.Client.Compat.FilePerms+        Distribution.Client.Compat.Prelude         Distribution.Client.Compat.Process         Distribution.Client.Compat.Semaphore-        Distribution.Client.Compat.Time+        Distribution.Solver.Types.ComponentDeps+        Distribution.Solver.Types.ConstraintSource+        Distribution.Solver.Types.DependencyResolver+        Distribution.Solver.Types.Flag+        Distribution.Solver.Types.InstalledPreference+        Distribution.Solver.Types.InstSolverPackage+        Distribution.Solver.Types.LabeledPackageConstraint+        Distribution.Solver.Types.OptionalStanza+        Distribution.Solver.Types.PackageConstraint+        Distribution.Solver.Types.PackageFixedDeps+        Distribution.Solver.Types.PackageIndex+        Distribution.Solver.Types.PackagePath+        Distribution.Solver.Types.PackagePreferences+        Distribution.Solver.Types.PkgConfigDb+        Distribution.Solver.Types.Progress+        Distribution.Solver.Types.ResolverPackage+        Distribution.Solver.Types.Settings+        Distribution.Solver.Types.SolverId+        Distribution.Solver.Types.SolverPackage+        Distribution.Solver.Types.SourcePackage+        Distribution.Solver.Types.Variable+        Distribution.Solver.Modular+        Distribution.Solver.Modular.Assignment+        Distribution.Solver.Modular.Builder+        Distribution.Solver.Modular.Configured+        Distribution.Solver.Modular.ConfiguredConversion+        Distribution.Solver.Modular.ConflictSet+        Distribution.Solver.Modular.Cycles+        Distribution.Solver.Modular.Dependency+        Distribution.Solver.Modular.Explore+        Distribution.Solver.Modular.Flag+        Distribution.Solver.Modular.Index+        Distribution.Solver.Modular.IndexConversion+        Distribution.Solver.Modular.Linking+        Distribution.Solver.Modular.LabeledGraph+        Distribution.Solver.Modular.Log+        Distribution.Solver.Modular.Message+        Distribution.Solver.Modular.Package+        Distribution.Solver.Modular.Preference+        Distribution.Solver.Modular.PSQ+        Distribution.Solver.Modular.RetryLog+        Distribution.Solver.Modular.Solver+        Distribution.Solver.Modular.Tree+        Distribution.Solver.Modular.Validate+        Distribution.Solver.Modular.Var+        Distribution.Solver.Modular.Version+        Distribution.Solver.Modular.WeightedPSQ         Paths_cabal_install      -- NOTE: when updating build-depends, don't forget to update version regexps@@ -265,9 +292,12 @@         base16-bytestring >= 0.1.1 && < 0.2,         binary     >= 0.5      && < 0.9,         bytestring >= 0.9      && < 1,-        Cabal      >= 1.24.2 && < 1.25,+        Cabal      >= 2.0      && < 2.1,         containers >= 0.4      && < 0.6,         cryptohash-sha256 >= 0.11 && < 0.12,+        deepseq    >= 1.3      && < 1.5,+        echo       >= 0.1.3    && < 0.2,+        edit-distance >= 0.2.2 && < 0.3,         filepath   >= 1.3      && < 1.5,         hashable   >= 1.0      && < 2,         HTTP       >= 4000.1.5 && < 4000.4,@@ -276,7 +306,7 @@         random     >= 1        && < 1.2,         stm        >= 2.0      && < 3,         tar        >= 0.5.0.3  && < 0.6,-        time       >= 1.4      && < 1.8,+        time       >= 1.4      && < 1.9,         zlib       >= 0.5.3    && < 0.7,         hackage-security >= 0.5.2.2 && < 0.6 @@ -290,7 +320,7 @@                      process   >= 1.0.1.1  && < 1.1.0.2     else       build-depends: directory >= 1.2 && < 1.4,-                     process   >= 1.1.0.2  && < 1.5+                     process   >= 1.1.0.2  && < 1.7      -- NOTE: you MUST include the network dependency even when network-uri     -- is pulled in, otherwise the constraint solver doesn't have enough@@ -309,13 +339,83 @@     else       build-depends: unix >= 2.5 && < 2.8 -    if arch(arm) && impl(ghc < 7.6)-       -- older ghc on arm does not support -threaded-       cc-options:  -DCABAL_NO_THREADED-    else-       ghc-options: -threaded+    if !(arch(arm) && impl(ghc < 7.6))+      ghc-options: -threaded -    c-sources: cbits/getnumcores.c+    -- On AIX, some legacy BSD operations such as flock(2) are provided by libbsd.a+    if os(aix)+      extra-libraries: bsd++    if flag(debug-expensive-assertions)+      cpp-options: -DDEBUG_EXPENSIVE_ASSERTIONS++    if flag(debug-conflict-sets)+      cpp-options: -DDEBUG_CONFLICT_SETS+      build-depends: base >= 4.8++    if flag(debug-tracetree)+      cpp-options: -DDEBUG_TRACETREE+      build-depends: tracetree >= 0.1 && < 0.2++    if flag(parsec)+      cpp-options: -DCABAL_PARSEC++    hs-source-dirs: .+    if flag(monolithic)+      hs-source-dirs: tests+      other-modules:+        UnitTests+        UnitTests.Distribution.Client.ArbitraryInstances+        UnitTests.Distribution.Client.FileMonitor+        UnitTests.Distribution.Client.GZipUtils+        UnitTests.Distribution.Client.Glob+        UnitTests.Distribution.Client.IndexUtils.Timestamp+        UnitTests.Distribution.Client.InstallPlan+        UnitTests.Distribution.Client.JobControl+        UnitTests.Distribution.Client.ProjectConfig+        UnitTests.Distribution.Client.Sandbox+        UnitTests.Distribution.Client.Sandbox.Timestamp+        UnitTests.Distribution.Client.Store+        UnitTests.Distribution.Client.Tar+        UnitTests.Distribution.Client.Targets+        UnitTests.Distribution.Client.UserConfig+        UnitTests.Distribution.Solver.Modular.DSL+        UnitTests.Distribution.Solver.Modular.DSL.TestCaseUtils+        UnitTests.Distribution.Solver.Modular.MemoryUsage+        UnitTests.Distribution.Solver.Modular.PSQ+        UnitTests.Distribution.Solver.Modular.QuickCheck+        UnitTests.Distribution.Solver.Modular.RetryLog+        UnitTests.Distribution.Solver.Modular.Solver+        UnitTests.Distribution.Solver.Modular.WeightedPSQ+        UnitTests.Options+        MemoryUsageTests+        SolverQuickCheck+        IntegrationTests2+      cpp-options: -DMONOLITHIC+      build-depends:+        Cabal      >= 2.0 && < 2.1,+        QuickCheck >= 2.8.2,+        array,+        async,+        bytestring,+        containers,+        deepseq,+        directory,+        edit-distance,+        filepath,+        mtl,+        network,+        network-uri,+        pretty-show,+        random,+        tagged,+        tar,+        tasty,+        tasty-hunit,+        tasty-quickcheck,+        time,+        zlib+     default-language: Haskell2010  -- Small, fast running tests.@@ -323,29 +423,39 @@   type: exitcode-stdio-1.0   main-is: UnitTests.hs   hs-source-dirs: tests, .-  ghc-options: -Wall -fwarn-tabs+  ghc-options: -Wall -fwarn-tabs -main-is UnitTests   other-modules:+    Distribution.Client.Compat.FileLock     UnitTests.Distribution.Client.ArbitraryInstances-    UnitTests.Distribution.Client.Targets-    UnitTests.Distribution.Client.Compat.Time-    UnitTests.Distribution.Client.Dependency.Modular.PSQ-    UnitTests.Distribution.Client.Dependency.Modular.Solver-    UnitTests.Distribution.Client.Dependency.Modular.DSL     UnitTests.Distribution.Client.FileMonitor-    UnitTests.Distribution.Client.Glob     UnitTests.Distribution.Client.GZipUtils+    UnitTests.Distribution.Client.Glob+    UnitTests.Distribution.Client.IndexUtils.Timestamp+    UnitTests.Distribution.Client.InstallPlan+    UnitTests.Distribution.Client.JobControl+    UnitTests.Distribution.Client.ProjectConfig     UnitTests.Distribution.Client.Sandbox     UnitTests.Distribution.Client.Sandbox.Timestamp+    UnitTests.Distribution.Client.Store     UnitTests.Distribution.Client.Tar+    UnitTests.Distribution.Client.Targets     UnitTests.Distribution.Client.UserConfig-    UnitTests.Distribution.Client.ProjectConfig+    UnitTests.Distribution.Solver.Modular.DSL+    UnitTests.Distribution.Solver.Modular.DSL.TestCaseUtils+    UnitTests.Distribution.Solver.Modular.PSQ+    UnitTests.Distribution.Solver.Modular.RetryLog+    UnitTests.Distribution.Solver.Modular.Solver+    UnitTests.Distribution.Solver.Modular.WeightedPSQ     UnitTests.Options+   build-depends:         base,+        async,         array,         bytestring,         Cabal,         containers,+        deepseq,         mtl,         pretty,         process,@@ -382,37 +492,224 @@   else     build-depends: unix -  if arch(arm)-    cc-options:  -DCABAL_NO_THREADED+  ghc-options: -fno-ignore-asserts++  if !(arch(arm) && impl(ghc < 7.6))+    ghc-options: -threaded++  if flag(debug-expensive-assertions)+    cpp-options: -DDEBUG_EXPENSIVE_ASSERTIONS++  if flag(debug-conflict-sets)+    cpp-options: -DDEBUG_CONFLICT_SETS+    build-depends: base >= 4.8++  if flag(debug-tracetree)+      cpp-options: -DDEBUG_TRACETREE+      build-depends: tracetree >= 0.1 && < 0.2++  default-language: Haskell2010++-- Tests to run with a limited stack and heap size+Test-Suite memory-usage-tests+  type: exitcode-stdio-1.0+  main-is: MemoryUsageTests.hs+  hs-source-dirs: tests, .+  ghc-options: -Wall -fwarn-tabs "-with-rtsopts=-M4M -K1K" -main-is MemoryUsageTests+  other-modules:+    UnitTests.Distribution.Solver.Modular.DSL+    UnitTests.Distribution.Solver.Modular.DSL.TestCaseUtils+    UnitTests.Distribution.Solver.Modular.MemoryUsage+    UnitTests.Options+  build-depends:+        base,+        async,+        array,+        bytestring,+        Cabal,+        containers,+        deepseq,+        mtl,+        pretty,+        process,+        directory,+        filepath,+        hashable,+        stm,+        tar,+        time,+        HTTP,+        zlib,+        binary,+        random,+        hackage-security,+        tagged,+        tasty,+        tasty-hunit++  if flag(old-directory)+    build-depends: old-time++  if flag(network-uri)+    build-depends: network-uri >= 2.6, network >= 2.6   else+    build-depends: network-uri < 2.6, network < 2.6++  if impl(ghc < 7.6)+    build-depends: ghc-prim >= 0.2 && < 0.3++  if os(windows)+    build-depends: Win32+  else+    build-depends: unix++  ghc-options: -fno-ignore-asserts++  if !(arch(arm) && impl(ghc < 7.6))     ghc-options: -threaded++  if flag(debug-expensive-assertions)+    cpp-options: -DDEBUG_EXPENSIVE_ASSERTIONS++  if flag(debug-conflict-sets)+    cpp-options: -DDEBUG_CONFLICT_SETS+    build-depends: base >= 4.8++  if flag(debug-tracetree)+      cpp-options: -DDEBUG_TRACETREE+      build-depends: tracetree >= 0.1 && < 0.2+   default-language: Haskell2010 -test-suite integration-tests+-- Slow solver tests+Test-Suite solver-quickcheck   type: exitcode-stdio-1.0-  hs-source-dirs: tests-  main-is: IntegrationTests.hs+  main-is: SolverQuickCheck.hs+  hs-source-dirs: tests, .+  ghc-options: -Wall -fwarn-tabs -fno-ignore-asserts -main-is SolverQuickCheck+  other-modules:+    UnitTests.Distribution.Solver.Modular.DSL+    UnitTests.Distribution.Solver.Modular.QuickCheck   build-depends:-    Cabal,-    async,-    base,-    bytestring,-    directory,-    filepath,-    process,-    regex-posix,-    tasty,-    tasty-hunit+        base,+        async,+        array,+        bytestring,+        Cabal,+        containers,+        deepseq >= 1.2,+        mtl,+        pretty,+        process,+        directory,+        filepath,+        hashable,+        stm,+        tar,+        time,+        HTTP,+        zlib,+        binary,+        random,+        hackage-security,+        tasty,+        tasty-quickcheck,+        QuickCheck >= 2.8.2,+        pretty-show +  if flag(old-directory)+    build-depends: old-time++  if flag(network-uri)+    build-depends: network-uri >= 2.6, network >= 2.6+  else+    build-depends: network-uri < 2.6, network < 2.6++  if impl(ghc < 7.6)+    build-depends: ghc-prim >= 0.2 && < 0.3+   if os(windows)-    build-depends: Win32 >= 2 && < 3+    build-depends: Win32   else-    build-depends: unix >= 2.5 && < 2.8+    build-depends: unix +  if !(arch(arm) && impl(ghc < 7.6))+    ghc-options: -threaded++  if flag(debug-expensive-assertions)+    cpp-options: -DDEBUG_EXPENSIVE_ASSERTIONS++  if flag(debug-conflict-sets)+    cpp-options: -DDEBUG_CONFLICT_SETS+    build-depends: base >= 4.8++  if flag(debug-tracetree)+      cpp-options: -DDEBUG_TRACETREE+      build-depends: tracetree >= 0.1 && < 0.2++  default-language: Haskell2010++-- Integration tests that use the cabal-install code directly+-- but still build whole projects+test-suite integration-tests2+  type: exitcode-stdio-1.0+  main-is: IntegrationTests2.hs+  hs-source-dirs: tests, .+  ghc-options: -Wall -fwarn-tabs -fno-ignore-asserts -main-is IntegrationTests2+  other-modules: Distribution.Client.Compat.FileLock+  build-depends:+        async,+        array,+        base,+        base16-bytestring,+        binary,+        bytestring,+        Cabal,+        containers,+        cryptohash-sha256,+        deepseq,+        directory,+        edit-distance,+        filepath,+        hackage-security,+        hashable,+        HTTP,+        mtl,+        network,+        network-uri,+        pretty,+        process,+        random,+        stm,+        tar,+        time,+        zlib,+        tasty,+        tasty-hunit,+        tagged++  if flag(old-bytestring)+    build-depends: bytestring-builder++  if flag(old-directory)+    build-depends: old-time++  if impl(ghc < 7.6)+    build-depends: ghc-prim >= 0.2 && < 0.3++  if os(windows)+    build-depends: Win32+  else+    build-depends: unix+   if arch(arm)     cc-options:  -DCABAL_NO_THREADED   else     ghc-options: -threaded--  ghc-options: -Wall -fwarn-tabs -fno-ignore-asserts   default-language: Haskell2010++custom-setup+  setup-depends: Cabal >= 2.0,+                 base,+                 process   >= 1.1.0.1  && < 1.7,+                 filepath   >= 1.3      && < 1.5
− cbits/getnumcores.c
@@ -1,46 +0,0 @@-#if defined(__GLASGOW_HASKELL__) && (__GLASGOW_HASKELL__ >= 612) && !defined(CABAL_NO_THREADED)-/* Since version 6.12, GHC's threaded RTS includes a getNumberOfProcessors-   function, so we try to use that if available. cabal-install is always built-   with -threaded nowadays.  */-#define HAS_GET_NUMBER_OF_PROCESSORS-#endif---#ifndef HAS_GET_NUMBER_OF_PROCESSORS--#if defined(_WIN32) && !defined(__CYGWIN__)-#include <windows.h>-#elif MACOS-#include <sys/param.h>-#include <sys/sysctl.h>-#elif __linux__-#include <unistd.h>-#endif--int getNumberOfProcessors() {-#if defined(_WIN32) && !defined(__CYGWIN__)-    SYSTEM_INFO sysinfo;-    GetSystemInfo(&sysinfo);-    return sysinfo.dwNumberOfProcessors;-#elif MACOS-    int nm[2];-    size_t len = 4;-    uint32_t count;--    nm[0] = CTL_HW; nm[1] = HW_AVAILCPU;-    sysctl(nm, 2, &count, &len, NULL, 0);--    if(count < 1) {-        nm[1] = HW_NCPU;-        sysctl(nm, 2, &count, &len, NULL, 0);-        if(count < 1) { count = 1; }-    }-    return count;-#elif __linux__-    return sysconf(_SC_NPROCESSORS_ONLN);-#else-    return 1;-#endif-}--#endif /* HAS_GET_NUMBER_OF_PROCESSORS */
changelog view
@@ -1,28 +1,53 @@ -*-change-log-*--1.24.0.2 Mikhail Glushenkov <mikhail.glushenkov@gmail.com> December 2016-	* Adapted to the revert of a PVP-noncompliant API change in-	Cabal 1.24.2.0 (#4123).-	* Bumped the directory upper bound to < 1.4 (#4158). -1.24.0.1 Ryan Thomas <ryan@ryant.org> October 2016-	* Fixed issue with passing '--enable-profiling' when invoking-	Setup scripts built with older versions of Cabal (#3873).+2.0.0.0 Mikhail Glushenkov <mikhail.glushenkov@gmail.com> August 2017+	* Removed the '--root-cmd' parameter of the 'install' command+	(#3356).+	* Deprecated 'cabal install --global' (#3356).+	* Changed 'cabal upload' to upload a package candidate by default+	(#3419). Same applies to uploading documentation.+	* Added a new 'cabal upload' flag '--publish' for publishing a+	package on Hackage instead of uploading a candidate (#3419).+	* Added optional solver output visualisation support via the+	tracetree package. Mainly intended for debugging (#3410).+	* Removed the '--check' option from 'cabal upload'+	(#1823). It was replaced by package candidates. 	* Fixed various behaviour differences between network transports 	(#3429).-	* Updated to depend on the latest hackage-security that fixes-	various issues on Windows.-	* Fixed 'new-build' to exit with a non-zero exit code on failure-	(#3506).-	* Store secure repo index data as 01-index.* (#3862).-	* Added new hackage-security root keys for distribution with-	cabal-install.-	* Fix an issue where 'cabal install' sometimes had to be run twice-	for packages with build-type: Custom and a custom-setup stanza-	(#3723).-	* 'cabal sdist' no longer ignores '--builddir' when the package's-	build-type is Custom (#3794).+	* The bootstrap script now works correctly when run from a Git+	clone (#3439).+	* Removed the top-down solver (#3598).+	* The '-v/--verbosity' option no longer affects GHC verbosity+	(except in the case of '-v0'). Use '--ghc-options=-v' to enable+	verbose GHC output (#3540, #3671).+	* Changed the default logfile template from+	'.../$pkgid.log' to '.../$compiler/$libname.log' (#3807).+	* Added a new command, 'cabal reconfigure', which re-runs 'configure'+	with the most recently used flags (#2214).+	* Added add the '--index-state' flag for requesting a specific+	version of the package index (#3893, #4115).+	* Support for building Backpack packages.  See+	https://github.com/ezyang/ghc-proposals/blob/backpack/proposals/0000-backpack.rst+	for more details.+	* Support the Nix package manager (#3651).+	* Made the 'template-haskell' package non-upgradable again (#4185).+	* Fixed password echoing on MinTTY (#4128).+	* Added a new solver flag, '--install-base-libraries', that allows+	any package to be installed or upgraded (#4209).+	* New 'cabal-install' command: 'outdated', for listing outdated+	version bounds in a .cabal file or a freeze file (#4207).+	* Added qualified constraints for setup dependencies. For example,+	--constraint="setup.bar == 1.0" constrains all setup dependencies on+	bar, and --constraint="foo:setup.bar == 1.0" constrains foo's setup+	dependency on bar (part of #3502).+	* Non-qualified constraints, such as --constraint="bar == 1.0", now+	only apply to top-level dependencies. They don't constrain setup or+	build-tool dependencies. The new syntax --constraint="any.bar == 1.0"+	constrains all uses of bar.+	* Added a technical preview version of the 'cabal doctest' command+	(#4480). -1.24.0.0 Ryan Thomas <ryan@ryant.org> May 2016+1.24.0.0 Ryan Thomas <ryan@ryant.org> March 2016 	* If there are multiple remote repos, 'cabal update' now updates 	them in parallel (#2503). 	* New 'cabal upload' option '-P'/'--password-command' for reading@@ -187,7 +212,7 @@ 	* HTTP-4000 package required, should fix bugs with http proxies 	* Now works with authenticated proxies. 	* On Windows can now override the proxy setting using an env var-	* Fix compatability with config files generated by older versions+	* Fix compatibility with config files generated by older versions 	* Warn if the hackage package list is very old 	* More helpful --help output, mention config file and examples 	* Better documentation in ~/.cabal/config file@@ -244,7 +269,7 @@  0.4 Duncan Coutts <duncan@haskell.org> Oct 2007 	* Renamed executable from 'cabal-install' to 'cabal'-	* Partial Windows compatability+	* Partial Windows compatibility 	* Do per-user installs by default 	* cabal install now installs the package in the current directory 	* Allow multiple remote servers
− tests/IntegrationTests.hs
@@ -1,310 +0,0 @@-{-# LANGUAGE CPP #-}--- | Groups black-box tests of cabal-install and configures them to test--- the correct binary.------ This file should do nothing but import tests from other modules and run--- them with the path to the correct cabal-install binary.-module Main-       where---- Modules from Cabal.-import Distribution.Compat.CreatePipe (createPipe)-import Distribution.Compat.Environment (setEnv)-import Distribution.Compat.Internal.TempFile (createTempDirectory)-import Distribution.Simple.Configure (findDistPrefOrDefault)-import Distribution.Simple.Program.Builtin (ghcPkgProgram)-import Distribution.Simple.Program.Db-        (defaultProgramDb, requireProgram, setProgramSearchPath)-import Distribution.Simple.Program.Find-        (ProgramSearchPathEntry(ProgramSearchPathDir), defaultProgramSearchPath)-import Distribution.Simple.Program.Types-        ( Program(..), simpleProgram, programPath)-import Distribution.Simple.Setup ( Flag(..) )-import Distribution.Simple.Utils ( findProgramVersion, copyDirectoryRecursive )-import Distribution.Verbosity (normal)---- Third party modules.-import Control.Concurrent.Async (withAsync, wait)-import Control.Exception (bracket)-import Data.Maybe (fromMaybe)-import System.Directory-        ( canonicalizePath-        , findExecutable-        , getDirectoryContents-        , getTemporaryDirectory-        , doesDirectoryExist-        , removeDirectoryRecursive-        , doesFileExist )-import System.FilePath-import Test.Tasty (TestTree, defaultMain, testGroup)-import Test.Tasty.HUnit (testCase, Assertion, assertFailure)-import Control.Monad ( filterM, forM, unless, when )-import Data.List (isPrefixOf, isSuffixOf, sort)-import Data.IORef (newIORef, writeIORef, readIORef)-import System.Exit (ExitCode(..))-import System.IO (withBinaryFile, IOMode(ReadMode))-import System.Process (runProcess, waitForProcess)-import Text.Regex.Posix ((=~))-import qualified Data.ByteString as B-import qualified Data.ByteString.Char8 as C8-import           Data.ByteString (ByteString)--#if MIN_VERSION_base(4,6,0)-import System.Environment ( getExecutablePath )-#endif---- | Test case.-data TestCase = TestCase-    { tcName :: String -- ^ Name of the shell script-    , tcBaseDirectory :: FilePath-    , tcCategory :: String-    , tcShouldX :: String-    , tcStdOutPath :: Maybe FilePath -- ^ File path of "golden standard output"-    , tcStdErrPath :: Maybe FilePath -- ^ File path of "golden standard error"-    }---- | Test result.-data TestResult = TestResult-    { trExitCode :: ExitCode-    , trStdOut :: ByteString-    , trStdErr :: ByteString-    , trWorkingDirectory :: FilePath-    }---- | Cabal executable-cabalProgram :: Program-cabalProgram = (simpleProgram "cabal") {-    programFindVersion = findProgramVersion "--numeric-version" id-  }---- | Convert test result to string.-testResultToString :: TestResult -> String-testResultToString testResult =-    exitStatus ++ "\n" ++ workingDirectory ++ "\n\n" ++ stdOut ++ "\n\n" ++ stdErr-  where-    exitStatus = "Exit status: " ++ show (trExitCode testResult)-    workingDirectory = "Working directory: " ++ (trWorkingDirectory testResult)-    stdOut = "<stdout> was:\n" ++ C8.unpack (trStdOut testResult)-    stdErr = "<stderr> was:\n" ++ C8.unpack (trStdErr testResult)---- | Returns the command that was issued, the return code, and the output text-run :: FilePath -> String -> [String] -> IO TestResult-run cwd path args = do-  -- path is relative to the current directory; canonicalizePath makes it-  -- absolute, so that runProcess will find it even when changing directory.-  path' <- canonicalizePath path--  (pid, hReadStdOut, hReadStdErr) <- do-    -- Create pipes for StdOut and StdErr-    (hReadStdOut, hWriteStdOut) <- createPipe-    (hReadStdErr, hWriteStdErr) <- createPipe-    -- Run the process-    pid <- runProcess path' args (Just cwd) Nothing Nothing (Just hWriteStdOut) (Just hWriteStdErr)-    -- Return the pid and read ends of the pipes-    return (pid, hReadStdOut, hReadStdErr)-  -- Read subprocess output using asynchronous threads; we need to-  -- do this aynchronously to avoid deadlocks due to buffers filling-  -- up.-  withAsync (B.hGetContents hReadStdOut) $ \stdOutAsync -> do-    withAsync (B.hGetContents hReadStdErr) $ \stdErrAsync -> do-      -- Wait for the subprocess to terminate-      exitcode <- waitForProcess pid-      -- We can now be sure that no further output is going to arrive,-      -- so we wait for the results of the asynchronous reads.-      stdOut <- wait stdOutAsync-      stdErr <- wait stdErrAsync-      -- Done-      return $ TestResult exitcode stdOut stdErr cwd---- | Get a list of all names in a directory, excluding all hidden or--- system files/directories such as '.', '..'  or any files/directories--- starting with a '.'.-listDirectory :: FilePath -> IO [String]-listDirectory directory = do-  fmap (filter notHidden) $ getDirectoryContents directory-  where-    notHidden = not . isHidden-    isHidden name = "." `isPrefixOf` name---- | List a directory as per 'listDirectory', but return an empty list--- in case the directory does not exist.-listDirectoryLax :: FilePath -> IO [String]-listDirectoryLax directory = do-  d <- doesDirectoryExist directory-  if d then-    listDirectory directory-  else-    return [ ]--pathIfExists :: FilePath -> IO (Maybe FilePath)-pathIfExists p = do-  e <- doesFileExist p-  if e then-    return $ Just p-    else-      return Nothing--fileMatchesString :: FilePath -> ByteString -> IO Bool-fileMatchesString p s = do-  withBinaryFile p ReadMode $ \h -> do-    expected <- (C8.lines . normalizeLinebreaks) `fmap` B.hGetContents h -- Strict-    let actual = C8.lines $ normalizeLinebreaks s-    return $ length expected == length actual &&-             and (zipWith matches expected actual)-  where-    matches :: ByteString -> ByteString -> Bool-    matches pattern line-        | C8.pack "RE:" `B.isPrefixOf` pattern = line =~ C8.drop 3 pattern-        | otherwise                            = line == pattern--    -- This is a bit of a hack, but since we're comparing-    -- *text* output, we should be OK.-    normalizeLinebreaks = B.filter (not . ((==) 13))--mustMatch :: TestResult -> String -> ByteString -> Maybe FilePath -> Assertion-mustMatch _          _          _      Nothing         =  return ()-mustMatch testResult handleName actual (Just expected) = do-  m <- fileMatchesString expected actual-  unless m $ assertFailure $-      "<" ++ handleName ++ "> did not match file '"-      ++ expected ++ "'.\n" ++ testResultToString testResult--discoverTestCategories :: FilePath -> IO [String]-discoverTestCategories directory = do-  names <- listDirectory directory-  fmap sort $ filterM (\name -> doesDirectoryExist $ directory </> name) names--discoverTestCases :: FilePath -> String -> String -> IO [TestCase]-discoverTestCases baseDirectory category shouldX = do-  -- Find the names of the shell scripts-  names <- fmap (filter isTestCase) $ listDirectoryLax directory-  -- Fill in TestCase for each script-  forM (sort names) $ \name -> do-    stdOutPath <- pathIfExists $ directory </> name `replaceExtension` ".out"-    stdErrPath <- pathIfExists $ directory </> name `replaceExtension` ".err"-    return $ TestCase { tcName = name-                      , tcBaseDirectory = baseDirectory-                      , tcCategory = category-                      , tcShouldX = shouldX-                      , tcStdOutPath = stdOutPath-                      , tcStdErrPath = stdErrPath-                      }-  where-    directory = baseDirectory </> category </> shouldX-    isTestCase name = ".sh" `isSuffixOf` name--createTestCases :: [TestCase] -> (TestCase -> Assertion) -> IO [TestTree]-createTestCases testCases mk =-  return $ (flip map) testCases $ \tc -> testCase (tcName tc ++ suffix tc) $ mk tc-  where-    suffix tc = case (tcStdOutPath tc, tcStdErrPath tc) of-      (Nothing, Nothing) -> " (ignoring stdout+stderr)"-      (Just _ , Nothing) -> " (ignoring stderr)"-      (Nothing, Just _ ) -> " (ignoring stdout)"-      (Just _ , Just _ ) -> ""--runTestCase :: (TestResult -> Assertion) -> TestCase -> IO ()-runTestCase assertResult tc = do-  doRemove <- newIORef False-  bracket createWorkDirectory (removeWorkDirectory doRemove) $ \workDirectory -> do-    -- Run-    let scriptDirectory = workDirectory </> tcShouldX tc-    sh <- fmap (fromMaybe $ error "Cannot find 'sh' executable") $ findExecutable "sh"-    testResult <- run scriptDirectory sh [ "-e", tcName tc]-    -- Assert that we got what we expected-    assertResult testResult-    mustMatch testResult "stdout" (trStdOut testResult) (tcStdOutPath tc)-    mustMatch testResult "stderr" (trStdErr testResult) (tcStdErrPath tc)-    -- Only remove working directory if test succeeded-    writeIORef doRemove True-  where-    createWorkDirectory = do-      -- Create the temporary directory-      tempDirectory <- getTemporaryDirectory-      workDirectory <- createTempDirectory tempDirectory "cabal-install-test"-      -- Copy all the files from the category into the working directory.-      copyDirectoryRecursive normal-        (tcBaseDirectory tc </> tcCategory tc)-        workDirectory-      -- Done-      return workDirectory-    removeWorkDirectory doRemove workDirectory = do-        remove <- readIORef doRemove-        when remove $ removeDirectoryRecursive workDirectory--makeShouldXTests :: FilePath -> String -> String -> (TestResult -> Assertion) -> IO [TestTree]-makeShouldXTests baseDirectory category shouldX assertResult = do-  testCases <- discoverTestCases baseDirectory category shouldX-  createTestCases testCases $ \tc ->-      runTestCase assertResult tc--makeShouldRunTests :: FilePath -> String -> IO [TestTree]-makeShouldRunTests baseDirectory category = do-  makeShouldXTests baseDirectory category "should_run" $ \testResult -> do-    case trExitCode testResult of-      ExitSuccess ->-        return () -- We're good-      ExitFailure _ ->-        assertFailure $ "Unexpected exit status.\n\n" ++ testResultToString testResult--makeShouldFailTests :: FilePath -> String -> IO [TestTree]-makeShouldFailTests baseDirectory category = do-  makeShouldXTests baseDirectory category "should_fail" $ \testResult -> do-    case trExitCode testResult of-      ExitSuccess ->-        assertFailure $ "Unexpected exit status.\n\n" ++ testResultToString testResult-      ExitFailure _ ->-        return () -- We're good--discoverCategoryTests :: FilePath -> String -> IO [TestTree]-discoverCategoryTests baseDirectory category = do-  srTests <- makeShouldRunTests baseDirectory category-  sfTests <- makeShouldFailTests baseDirectory category-  return [ testGroup "should_run" srTests-         , testGroup "should_fail" sfTests-         ]--main :: IO ()-main = do-  -- Find executables and build directories, etc.-  distPref <- guessDistDir-  buildDir <- canonicalizePath (distPref </> "build/cabal")-  let programSearchPath = ProgramSearchPathDir buildDir : defaultProgramSearchPath-  (cabal, _) <- requireProgram normal cabalProgram (setProgramSearchPath programSearchPath defaultProgramDb)-  (ghcPkg, _) <- requireProgram normal ghcPkgProgram defaultProgramDb-  baseDirectory <- canonicalizePath $ "tests" </> "IntegrationTests"-  -- Set up environment variables for test scripts-  setEnv "GHC_PKG" $ programPath ghcPkg-  setEnv "CABAL" $ programPath cabal-  -- Define default arguments-  setEnv "CABAL_ARGS" $ "--config-file=config-file"-  setEnv "CABAL_ARGS_NO_CONFIG_FILE" " "-  -- Discover all the test caregories-  categories <- discoverTestCategories baseDirectory-  -- Discover tests in each category-  tests <- forM categories $ \category -> do-    categoryTests <- discoverCategoryTests baseDirectory category-    return (category, categoryTests)-  -- Map into a test tree-  let testTree = map (\(category, categoryTests) -> testGroup category categoryTests) tests-  -- Run the tests-  defaultMain $ testGroup "Integration Tests" $ testTree---- See this function in Cabal's PackageTests. If you update this,--- update its copy in cabal-install.  (Why a copy here? I wanted--- to try moving this into the Cabal library, but to do this properly--- I'd have to BC'ify getExecutablePath, and then it got hairy, so--- I aborted and did something simple.)-guessDistDir :: IO FilePath-guessDistDir = do-#if MIN_VERSION_base(4,6,0)-    exe_path <- canonicalizePath =<< getExecutablePath-    let dist0 = dropFileName exe_path </> ".." </> ".."-    b <- doesFileExist (dist0 </> "setup-config")-#else-    let dist0 = error "no path"-        b = False-#endif-    -- Method (2)-    if b then canonicalizePath dist0-         else findDistPrefOrDefault NoFlag >>= canonicalizePath
− tests/IntegrationTests/custom-setup/common.sh
@@ -1,9 +0,0 @@-# Helper to run Cabal-cabal() {-    "$CABAL" $CABAL_ARGS "$@"-}--die() {-    echo "die: $@"-    exit 1-}
− tests/IntegrationTests/custom-setup/should_run/Cabal-99998/Cabal.cabal
@@ -1,8 +0,0 @@-name: Cabal-version: 99998-build-type: Simple-cabal-version: >= 1.2--library-  build-depends: base-  exposed-modules: CabalMessage
− tests/IntegrationTests/custom-setup/should_run/Cabal-99998/CabalMessage.hs
@@ -1,3 +0,0 @@-module CabalMessage where--message = "This is Cabal-99998"
− tests/IntegrationTests/custom-setup/should_run/Cabal-99999/Cabal.cabal
@@ -1,8 +0,0 @@-name: Cabal-version: 99999-build-type: Simple-cabal-version: >= 1.2--library-  build-depends: base-  exposed-modules: CabalMessage
− tests/IntegrationTests/custom-setup/should_run/Cabal-99999/CabalMessage.hs
@@ -1,3 +0,0 @@-module CabalMessage where--message = "This is Cabal-99999"
− tests/IntegrationTests/custom-setup/should_run/custom-setup-without-cabal-defaultMain/Setup.hs
@@ -1,3 +0,0 @@-import Distribution.Simple--main = defaultMain
− tests/IntegrationTests/custom-setup/should_run/custom-setup-without-cabal-defaultMain/custom-setup-without-cabal-defaultMain.cabal
@@ -1,9 +0,0 @@-name: custom-setup-without-cabal-defaultMain-version: 1.0-build-type: Custom-cabal-version: >= 1.2--custom-setup-  setup-depends: base--library
− tests/IntegrationTests/custom-setup/should_run/custom-setup-without-cabal/Setup.hs
@@ -1,4 +0,0 @@-import System.Exit-import System.IO--main = hPutStrLn stderr "My custom Setup" >> exitFailure
− tests/IntegrationTests/custom-setup/should_run/custom-setup-without-cabal/custom-setup-without-cabal.cabal
@@ -1,9 +0,0 @@-name: custom-setup-without-cabal-version: 1.0-build-type: Custom-cabal-version: >= 99999--custom-setup-  setup-depends: base--library
− tests/IntegrationTests/custom-setup/should_run/custom-setup/Setup.hs
@@ -1,5 +0,0 @@-import CabalMessage (message)-import System.Exit-import System.IO--main = hPutStrLn stderr message >> exitFailure
− tests/IntegrationTests/custom-setup/should_run/custom-setup/custom-setup.cabal
@@ -1,9 +0,0 @@-name: custom-setup-version: 1.0-build-type: Custom-cabal-version: >= 99999--custom-setup-  setup-depends: base, Cabal >= 99999--library
− tests/IntegrationTests/custom-setup/should_run/custom_setup_without_Cabal_doesnt_allow_Cabal_import.sh
@@ -1,12 +0,0 @@-. ../common.sh-cd custom-setup-without-cabal-defaultMain--# This package has explicit setup dependencies that do not include Cabal.-# Compilation should fail because Setup.hs imports Distribution.Simple.-! cabal new-build custom-setup-without-cabal-defaultMain > output 2>&1-cat output-grep -q "\(Could not find module\|Failed to load interface for\).*Distribution\\.Simple" output \-    || die "Should not have been able to import Cabal"--grep -q "It is a member of the hidden package .*Cabal-" output \-    || die "Cabal should be available"
− tests/IntegrationTests/custom-setup/should_run/custom_setup_without_Cabal_doesnt_require_Cabal.sh
@@ -1,11 +0,0 @@-. ../common.sh-cd custom-setup-without-cabal--# This package has explicit setup dependencies that do not include Cabal.-# new-build should try to build it, even though the cabal-version cannot be-# satisfied by an installed version of Cabal (cabal-version: >= 99999). However,-# configure should fail because Setup.hs just prints an error message and exits.-! cabal new-build custom-setup-without-cabal > output 2>&1-cat output-grep -q "My custom Setup" output \-    || die "Expected output from custom Setup"
− tests/IntegrationTests/custom-setup/should_run/installs_Cabal_as_setup_dep.sh
@@ -1,15 +0,0 @@-# Regression test for issue #3436--. ../common.sh-cabal sandbox init-cabal install ./Cabal-99998-cabal sandbox add-source Cabal-99999--# Install custom-setup, which has a setup dependency on Cabal-99999.-# cabal should build the setup script with Cabal-99999, but then-# configure should fail because Setup just prints an error message-# imported from Cabal and exits.-! cabal install custom-setup/ > output 2>&1--cat output-grep -q "This is Cabal-99999" output || die "Expected output from Cabal-99999"
− tests/IntegrationTests/custom/common.sh
@@ -1,9 +0,0 @@-# Helper to run Cabal-cabal() {-    "$CABAL" $CABAL_ARGS "$@"-}--die() {-    echo "die: $@"-    exit 1-}
− tests/IntegrationTests/custom/should_run/plain.err
@@ -1,2 +0,0 @@-Custom-Custom
− tests/IntegrationTests/custom/should_run/plain.sh
@@ -1,4 +0,0 @@-. ../common.sh-cd plain-cabal configure-cabal build
− tests/IntegrationTests/custom/should_run/plain/A.hs
@@ -1,1 +0,0 @@-module A where
− tests/IntegrationTests/custom/should_run/plain/Setup.hs
@@ -1,3 +0,0 @@-import Distribution.Simple-import System.IO-main = hPutStrLn stderr "Custom" >> defaultMain
− tests/IntegrationTests/custom/should_run/plain/plain.cabal
@@ -1,12 +0,0 @@-name:                plain-version:             0.1.0.0-license:             BSD3-author:              Edward Z. Yang-maintainer:          ezyang@cs.stanford.edu-build-type:          Custom-cabal-version:       >=1.10--library-  exposed-modules:     A-  build-depends:       base-  default-language:    Haskell2010
− tests/IntegrationTests/exec/common.sh
@@ -1,9 +0,0 @@-# Helper to run Cabal-cabal() {-    "$CABAL" $CABAL_ARGS "$@"-}--die() {-    echo "die: $@"-    exit 1-}
− tests/IntegrationTests/exec/should_fail/exit_with_failure_without_args.err
@@ -1,1 +0,0 @@-RE:^cabal(\.exe)?: Please specify an executable to run$
− tests/IntegrationTests/exec/should_fail/exit_with_failure_without_args.sh
@@ -1,3 +0,0 @@-. ../common.sh--cabal exec
− tests/IntegrationTests/exec/should_run/Foo.hs
@@ -1,4 +0,0 @@-module Foo where--foo :: String-foo = "foo"
− tests/IntegrationTests/exec/should_run/My.hs
@@ -1,5 +0,0 @@-module Main where--main :: IO ()-main = do-    putStrLn "This is my-executable"
− tests/IntegrationTests/exec/should_run/adds_sandbox_bin_directory_to_path.out
@@ -1,1 +0,0 @@-This is my-executable
− tests/IntegrationTests/exec/should_run/adds_sandbox_bin_directory_to_path.sh
@@ -1,10 +0,0 @@-. ../common.sh--cabal sandbox delete > /dev/null-cabal exec my-executable && die "Unexpectedly found executable"--cabal sandbox init > /dev/null-cabal install > /dev/null--# Execute indirectly via bash to ensure that we go through $PATH-cabal exec sh -- -c my-executable || die "Did not find executable"
− tests/IntegrationTests/exec/should_run/auto_configures_on_exec.out
@@ -1,4 +0,0 @@-Config file path source is commandline option.-Config file config-file not found.-Writing default configuration to config-file-find_me_in_output
− tests/IntegrationTests/exec/should_run/auto_configures_on_exec.sh
@@ -1,2 +0,0 @@-. ../common.sh-cabal exec echo find_me_in_output
− tests/IntegrationTests/exec/should_run/can_run_executables_installed_in_sandbox.out
@@ -1,1 +0,0 @@-This is my-executable
− tests/IntegrationTests/exec/should_run/can_run_executables_installed_in_sandbox.sh
@@ -1,9 +0,0 @@-. ../common.sh--cabal sandbox delete > /dev/null-cabal exec my-executable && die "Unexpectedly found executable"--cabal sandbox init > /dev/null-cabal install > /dev/null--cabal exec my-executable || die "Did not find executable"
− tests/IntegrationTests/exec/should_run/configures_cabal_to_use_sandbox.sh
@@ -1,14 +0,0 @@-. ../common.sh--cabal sandbox delete > /dev/null-cabal exec my-executable && die "Unexpectedly found executable"--cabal sandbox init > /dev/null-cabal install > /dev/null--# The library should not be available outside the sandbox-"$GHC_PKG" list | grep -v "my-0.1"--# When run inside 'cabal-exec' the 'sandbox hc-pkg list' sub-command-# should find the library.-cabal exec sh -- -c 'cd subdir && "$CABAL" sandbox hc-pkg list' | grep "my-0.1"
− tests/IntegrationTests/exec/should_run/configures_ghc_to_use_sandbox.sh
@@ -1,13 +0,0 @@-. ../common.sh--cabal sandbox delete > /dev/null-cabal exec my-executable && die "Unexpectedly found executable"--cabal sandbox init > /dev/null-cabal install > /dev/null--# The library should not be available outside the sandbox-"$GHC_PKG" list | grep -v "my-0.1"--# Execute ghc-pkg inside the sandbox; it should find my-0.1-cabal exec ghc-pkg list | grep "my-0.1"
− tests/IntegrationTests/exec/should_run/my.cabal
@@ -1,14 +0,0 @@-name:           my-version:        0.1-license:        BSD3-cabal-version:  >= 1.2-build-type:     Simple--library-    exposed-modules:    Foo-    build-depends:      base---executable my-executable-    main-is:            My.hs-    build-depends:      base
− tests/IntegrationTests/exec/should_run/runs_given_command.out
@@ -1,1 +0,0 @@-this string
− tests/IntegrationTests/exec/should_run/runs_given_command.sh
@@ -1,3 +0,0 @@-. ../common.sh-cabal configure > /dev/null-cabal exec echo this string
− tests/IntegrationTests/freeze/common.sh
@@ -1,9 +0,0 @@-# Helper to run Cabal-cabal() {-    "$CABAL" $CABAL_ARGS "$@"-}--die() {-    echo "die: $@"-    exit 1-}
− tests/IntegrationTests/freeze/should_run/disable_benchmarks_freezes_bench_deps.sh
@@ -1,3 +0,0 @@-. ../common.sh-cabal freeze --disable-benchmarks-grep -v " criterion ==" cabal.config || die "should NOT have frozen criterion"
− tests/IntegrationTests/freeze/should_run/disable_tests_freezes_test_deps.sh
@@ -1,3 +0,0 @@-. ../common.sh-cabal freeze --disable-tests-grep -v " test-framework ==" cabal.config || die "should NOT have frozen test-framework"
− tests/IntegrationTests/freeze/should_run/does_not_freeze_nondeps.sh
@@ -1,5 +0,0 @@-. ../common.sh-# TODO: Test this against a package installed in the sandbox but not-# depended upon.-cabal freeze-grep -v "exceptions ==" cabal.config || die "should not have frozen exceptions"
− tests/IntegrationTests/freeze/should_run/does_not_freeze_self.sh
@@ -1,3 +0,0 @@-. ../common.sh-cabal freeze-grep -v " my ==" cabal.config || die "should not have frozen self"
− tests/IntegrationTests/freeze/should_run/dry_run_does_not_create_config.sh
@@ -1,3 +0,0 @@-. ../common.sh-cabal freeze --dry-run-[ ! -e cabal.config ] || die "cabal.config file should not have been created"
− tests/IntegrationTests/freeze/should_run/enable_benchmarks_freezes_bench_deps.sh
@@ -1,4 +0,0 @@-. ../common.sh-# TODO: solver should find solution without extra flags too-cabal freeze --enable-benchmarks --reorder-goals --max-backjumps=-1-grep " criterion ==" cabal.config || die "should have frozen criterion"
− tests/IntegrationTests/freeze/should_run/enable_tests_freezes_test_deps.sh
@@ -1,3 +0,0 @@-. ../common.sh-cabal freeze --enable-tests-grep " test-framework ==" cabal.config || die "should have frozen test-framework"
− tests/IntegrationTests/freeze/should_run/freezes_direct_dependencies.sh
@@ -1,3 +0,0 @@-. ../common.sh-cabal freeze-grep " base ==" cabal.config || die "'base' should have been frozen"
− tests/IntegrationTests/freeze/should_run/freezes_transitive_dependencies.sh
@@ -1,3 +0,0 @@-. ../common.sh-cabal freeze-grep " ghc-prim ==" cabal.config || die "'ghc-prim' should have been frozen"
− tests/IntegrationTests/freeze/should_run/my.cabal
@@ -1,21 +0,0 @@-name:           my-version:        0.1-license:        BSD3-cabal-version:  >= 1.20.0-build-type:     Simple--library-    exposed-modules:    Foo-    build-depends:      base--test-suite test-Foo-    type:   exitcode-stdio-1.0-    hs-source-dirs: tests-    main-is:    test-Foo.hs-    build-depends: base, my, test-framework--benchmark bench-Foo-    type:   exitcode-stdio-1.0-    hs-source-dirs: benchmarks-    main-is:    benchmark-Foo.hs-    build-depends: base, my, criterion
− tests/IntegrationTests/freeze/should_run/runs_without_error.sh
@@ -1,2 +0,0 @@-. ../common.sh-cabal freeze
− tests/IntegrationTests/manpage/common.sh
@@ -1,9 +0,0 @@-# Helper to run Cabal-cabal() {-    "$CABAL" $CABAL_ARGS "$@"-}--die() {-    echo "die: $@"-    exit 1-}
− tests/IntegrationTests/manpage/should_run/outputs_manpage.sh
@@ -1,11 +0,0 @@-. ../common.sh--OUTPUT=`cabal manpage`--# contains visible command descriptions-echo $OUTPUT | grep -q '\.B cabal install' || die "visible command description line not found in:\n----$OUTPUT\n----"--# does not contain hidden command descriptions-echo $OUTPUT | grep -q '\.B cabal manpage' && die "hidden command description line found in:\n----$OUTPUT\n----"--exit 0
− tests/IntegrationTests/multiple-source/common.sh
@@ -1,8 +0,0 @@-cabal() {-    "$CABAL" $CABAL_ARGS "$@"-}--die() {-    echo "die: $@"-    exit 1-}
− tests/IntegrationTests/multiple-source/should_run/finds_second_source_of_multiple_source.sh
@@ -1,11 +0,0 @@-. ../common.sh--# Create the sandbox-cabal sandbox init--# Add the sources-cabal sandbox add-source p-cabal sandbox add-source q--# Install the second package-cabal install q
− tests/IntegrationTests/multiple-source/should_run/p/Setup.hs
@@ -1,2 +0,0 @@-import Distribution.Simple-main = defaultMain
− tests/IntegrationTests/multiple-source/should_run/p/p.cabal
@@ -1,11 +0,0 @@-name:                p-version:             0.1.0.0-license-file:        LICENSE-author:              Edward Z. Yang-maintainer:          ezyang@cs.stanford.edu-build-type:          Simple-cabal-version:       >=1.10--library-  build-depends:       base-  default-language:    Haskell2010
− tests/IntegrationTests/multiple-source/should_run/q/Setup.hs
@@ -1,2 +0,0 @@-import Distribution.Simple-main = defaultMain
− tests/IntegrationTests/multiple-source/should_run/q/q.cabal
@@ -1,11 +0,0 @@-name:                q-version:             0.1.0.0-license-file:        LICENSE-author:              Edward Z. Yang-maintainer:          ezyang@cs.stanford.edu-build-type:          Simple-cabal-version:       >=1.10--library-  build-depends:       base-  default-language:    Haskell2010
− tests/IntegrationTests/new-build/monitor_cabal_files.sh
@@ -1,8 +0,0 @@-. ./common.sh-cd monitor_cabal_files-cp q/q-broken.cabal.in q/q.cabal-echo "Run 1" | awk '{print;print > "/dev/stderr"}'-! cabal new-build q-cp q/q-fixed.cabal.in q/q.cabal-echo "Run 2" | awk '{print;print > "/dev/stderr"}'-cabal new-build q
− tests/IntegrationTests/new-build/monitor_cabal_files/p/P.hs
@@ -1,1 +0,0 @@-module P where
− tests/IntegrationTests/new-build/monitor_cabal_files/p/Setup.hs
@@ -1,2 +0,0 @@-import Distribution.Simple-main = defaultMain
− tests/IntegrationTests/new-build/monitor_cabal_files/p/p.cabal
@@ -1,12 +0,0 @@-name:                p-version:             1.0-license:             BSD3-author:              Edward Z. Yang-maintainer:          ezyang@cs.stanford.edu-build-type:          Simple-cabal-version:       >=1.10--library-  exposed-modules:     P-  build-depends:       base-  default-language:    Haskell2010
− tests/IntegrationTests/new-build/monitor_cabal_files/q/Main.hs
@@ -1,4 +0,0 @@-module Main where-import P-main :: IO ()-main = return ()
− tests/IntegrationTests/new-build/monitor_cabal_files/q/Setup.hs
@@ -1,2 +0,0 @@-import Distribution.Simple-main = defaultMain
− tests/IntegrationTests/new-build/monitor_cabal_files/q/q-broken.cabal.in
@@ -1,12 +0,0 @@-name:                q-version:             0.1.0.0-license:             BSD3-author:              Edward Z. Yang-maintainer:          ezyang@cs.stanford.edu-build-type:          Simple-cabal-version:       >=1.10--executable q-  main-is:             Main.hs-  build-depends:       base-  default-language:    Haskell2010
− tests/IntegrationTests/new-build/monitor_cabal_files/q/q-fixed.cabal.in
@@ -1,12 +0,0 @@-name:                q-version:             0.1.0.0-license:             BSD3-author:              Edward Z. Yang-maintainer:          ezyang@cs.stanford.edu-build-type:          Simple-cabal-version:       >=1.10--executable q-  main-is:             Main.hs-  build-depends:       base, p-  default-language:    Haskell2010
− tests/IntegrationTests/regression/common.sh
@@ -1,9 +0,0 @@-# Helper to run Cabal-cabal() {-    "$CABAL" $CABAL_ARGS "$@"-}--die() {-    echo "die: $@"-    exit 1-}
− tests/IntegrationTests/regression/t3199.sh
@@ -1,12 +0,0 @@-. ./common.sh--if [[ `ghc --numeric-version` =~ "7\\." ]]; then-    cd t3199-    tmpfile=$(mktemp /tmp/cabal-t3199.XXXXXX)-    cabal sandbox init-    cabal sandbox add-source ../../../../../Cabal-    cabal install --package-db=clear --package-db=global --only-dep --dry-run > $tmpfile-    grep -q "the following would be installed" $tmpfile || die "Should've installed Cabal"-    grep -q Cabal $tmpfile || die "Should've installed Cabal"-    rm $tmpfile-fi
− tests/IntegrationTests/regression/t3199/Main.hs
@@ -1,4 +0,0 @@-module Main where--main :: IO ()-main = putStrLn "Hello, Haskell!"
− tests/IntegrationTests/regression/t3199/Setup.hs
@@ -1,2 +0,0 @@-import Distribution.Simple-main = defaultMain
− tests/IntegrationTests/regression/t3199/test-3199.cabal
@@ -1,27 +0,0 @@-name:                test-t3199-version:             0.1.0.0-license:             BSD3-author:              Mikhail Glushenkov-maintainer:          mikhail.glushenkov@gmail.com-category:            Test-build-type:          Custom-cabal-version:       >=1.10--flag exe_2-     description: Build second exe-     default:     False--executable test-3199-1-  main-is:             Main.hs-  build-depends:       base-  default-language:    Haskell2010--executable test-3199-2-  main-is:             Main.hs-  build-depends:       base, ansi-terminal-  default-language:    Haskell2010--  if flag(exe_2)-     buildable: True-  else-     buildable: False
− tests/IntegrationTests/sandbox-sources/common.sh
@@ -1,8 +0,0 @@-cabal() {-    "$CABAL" $CABAL_ARGS "$@"-}--die() {-    echo "die: $@"-    exit 1-}
− tests/IntegrationTests/sandbox-sources/should_fail/fail_removing_source_thats_not_registered.err
@@ -1,3 +0,0 @@-Warning: Sources not registered: "q"--RE:^cabal(\.exe)?: The sources with the above errors were skipped\. \("q"\)$
− tests/IntegrationTests/sandbox-sources/should_fail/fail_removing_source_thats_not_registered.sh
@@ -1,10 +0,0 @@-. ../common.sh--# Create the sandbox-cabal sandbox init > /dev/null--# Add one source-cabal sandbox add-source p > /dev/null--# Remove a source that exists on disk, but is not registered-cabal sandbox delete-source q
− tests/IntegrationTests/sandbox-sources/should_fail/p/Setup.hs
@@ -1,2 +0,0 @@-import Distribution.Simple-main = defaultMain
− tests/IntegrationTests/sandbox-sources/should_fail/p/p.cabal
@@ -1,11 +0,0 @@-name:                p-version:             0.1.0.0-license-file:        LICENSE-author:              Edward Z. Yang-maintainer:          ezyang@cs.stanford.edu-build-type:          Simple-cabal-version:       >=1.10--library-  build-depends:       base-  default-language:    Haskell2010
− tests/IntegrationTests/sandbox-sources/should_fail/q/Setup.hs
@@ -1,2 +0,0 @@-import Distribution.Simple-main = defaultMain
− tests/IntegrationTests/sandbox-sources/should_fail/q/q.cabal
@@ -1,11 +0,0 @@-name:                q-version:             0.1.0.0-license-file:        LICENSE-author:              Edward Z. Yang-maintainer:          ezyang@cs.stanford.edu-build-type:          Simple-cabal-version:       >=1.10--library-  build-depends:       base-  default-language:    Haskell2010
− tests/IntegrationTests/sandbox-sources/should_run/p/Setup.hs
@@ -1,2 +0,0 @@-import Distribution.Simple-main = defaultMain
− tests/IntegrationTests/sandbox-sources/should_run/p/p.cabal
@@ -1,11 +0,0 @@-name:                p-version:             0.1.0.0-license-file:        LICENSE-author:              Edward Z. Yang-maintainer:          ezyang@cs.stanford.edu-build-type:          Simple-cabal-version:       >=1.10--library-  build-depends:       base-  default-language:    Haskell2010
− tests/IntegrationTests/sandbox-sources/should_run/q/Setup.hs
@@ -1,2 +0,0 @@-import Distribution.Simple-main = defaultMain
− tests/IntegrationTests/sandbox-sources/should_run/q/q.cabal
@@ -1,11 +0,0 @@-name:                q-version:             0.1.0.0-license-file:        LICENSE-author:              Edward Z. Yang-maintainer:          ezyang@cs.stanford.edu-build-type:          Simple-cabal-version:       >=1.10--library-  build-depends:       base-  default-language:    Haskell2010
− tests/IntegrationTests/sandbox-sources/should_run/remove_nonexistent_source.sh
@@ -1,22 +0,0 @@-. ../common.sh--# Create the sandbox-cabal sandbox init--# Add the sources-cabal sandbox add-source p-cabal sandbox add-source q--# delete the directory on disk-rm -R p--# Remove the registered source which is no longer on disk. cabal's handling of-# non-existent sources depends on the behavior of the directory package.-if OUTPUT=`cabal sandbox delete-source p 2>&1`; then-    # 'canonicalizePath' should always succeed with directory >= 1.2.3.0-    echo $OUTPUT | grep 'Success deleting sources: "p"' \-	|| die "Incorrect success message: $OUTPUT"-else-    echo $OUTPUT | grep 'Warning: Source directory not found for paths: "p"' \-	|| die "Incorrect failure message: $OUTPUT"-fi
− tests/IntegrationTests/sandbox-sources/should_run/report_success_removing_source.out
@@ -1,6 +0,0 @@-Success deleting sources: "p" "q"--Note: 'sandbox delete-source' only unregisters the source dependency, but does-not remove the package from the sandbox package DB.--Use 'sandbox hc-pkg -- unregister' to do that.
− tests/IntegrationTests/sandbox-sources/should_run/report_success_removing_source.sh
@@ -1,11 +0,0 @@-. ../common.sh--# Create the sandbox-cabal sandbox init > /dev/null--# Add the sources-cabal sandbox add-source p > /dev/null-cabal sandbox add-source q > /dev/null--# Remove one of the sources-cabal sandbox delete-source p q
− tests/IntegrationTests/user-config/common.sh
@@ -1,9 +0,0 @@-# Helper to run Cabal-cabal() {-    "$CABAL" $CABAL_ARGS_NO_CONFIG_FILE "$@"-}--die() {-    echo "die: $@"-    exit 1-}
− tests/IntegrationTests/user-config/should_fail/doesnt_overwrite_without_f.err
@@ -1,1 +0,0 @@-RE:^cabal(\.exe)?: \./cabal-config already exists\.$
− tests/IntegrationTests/user-config/should_fail/doesnt_overwrite_without_f.sh
@@ -1,6 +0,0 @@-. ../common.sh--rm -f ./cabal-config-cabal --config-file=./cabal-config user-config init > /dev/null-cabal --config-file=./cabal-config user-config init-rm -f ./cabal-config
− tests/IntegrationTests/user-config/should_run/overwrites_with_f.out
@@ -1,2 +0,0 @@-Writing default configuration to ./cabal-config-Writing default configuration to ./cabal-config
− tests/IntegrationTests/user-config/should_run/overwrites_with_f.sh
@@ -1,9 +0,0 @@-. ../common.sh--rm -f ./cabal-config-cabal --config-file=./cabal-config user-config init \-    || die "Couldn't create config file"-cabal --config-file=./cabal-config user-config -f init \-    || die "Couldn't create config file"-test -e ./cabal-config || die "Config file doesn't exist"-rm -f ./cabal-config
− tests/IntegrationTests/user-config/should_run/runs_without_error.out
@@ -1,1 +0,0 @@-Writing default configuration to ./cabal-config
− tests/IntegrationTests/user-config/should_run/runs_without_error.sh
@@ -1,7 +0,0 @@-. ../common.sh--rm -f ./cabal-config-cabal --config-file=./cabal-config user-config init \-    || die "Couldn't create config file"-test -e ./cabal-config || die "Config file doesn't exist"-rm -f ./cabal-config
− tests/IntegrationTests/user-config/should_run/uses_CABAL_CONFIG.out
@@ -1,1 +0,0 @@-Writing default configuration to ./my-config
− tests/IntegrationTests/user-config/should_run/uses_CABAL_CONFIG.sh
@@ -1,5 +0,0 @@-. ../common.sh--export CABAL_CONFIG=./my-config-cabal user-config init || die "Couldn't create config file"-test -e ./my-config || die "Config file doesn't exist"
+ tests/IntegrationTests2.hs view
@@ -0,0 +1,1719 @@+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE OverloadedStrings #-}++-- For the handy instance IsString PackageIdentifier+{-# OPTIONS_GHC -fno-warn-orphans #-}++module IntegrationTests2 where++import Distribution.Client.DistDirLayout+import Distribution.Client.ProjectConfig+import Distribution.Client.Config (defaultCabalDir)+import Distribution.Client.TargetSelector hiding (DirActions(..))+import qualified Distribution.Client.TargetSelector as TS (DirActions(..))+import Distribution.Client.ProjectPlanning+import Distribution.Client.ProjectPlanning.Types+import Distribution.Client.ProjectBuilding+import Distribution.Client.ProjectOrchestration+         ( resolveTargets, TargetProblemCommon(..), distinctTargetComponents )+import Distribution.Client.Types+         ( PackageLocation(..), UnresolvedSourcePackage )+import Distribution.Client.Targets+         ( UserConstraint(..), UserConstraintScope(UserAnyQualifier) )+import qualified Distribution.Client.InstallPlan as InstallPlan+import Distribution.Solver.Types.SourcePackage as SP+import Distribution.Solver.Types.ConstraintSource+         ( ConstraintSource(ConstraintSourceUnknown) )+import Distribution.Solver.Types.PackageConstraint+         ( PackageProperty(PackagePropertySource) )++import qualified Distribution.Client.CmdBuild   as CmdBuild+import qualified Distribution.Client.CmdRepl    as CmdRepl+import qualified Distribution.Client.CmdRun     as CmdRun+import qualified Distribution.Client.CmdTest    as CmdTest+import qualified Distribution.Client.CmdBench   as CmdBench+import qualified Distribution.Client.CmdHaddock as CmdHaddock++import Distribution.Package+import Distribution.PackageDescription+import qualified Distribution.Types.GenericPackageDescription as GPG+import Distribution.InstalledPackageInfo (InstalledPackageInfo)+import Distribution.Simple.Setup (toFlag, HaddockFlags(..), defaultHaddockFlags)+import Distribution.Simple.Compiler+import Distribution.System+import Distribution.Version+import Distribution.ModuleName (ModuleName)+import Distribution.Verbosity+import Distribution.Text++import Data.Monoid+import Data.List (sort)+import Data.String (IsString(..))+import qualified Data.Map as Map+import qualified Data.Set as Set+import Control.Monad+import Control.Exception hiding (assert)+import System.FilePath+import System.Directory++import Test.Tasty+import Test.Tasty.HUnit+import Test.Tasty.Options+import Data.Tagged (Tagged(..))+import Data.Proxy  (Proxy(..))+import Data.Typeable (Typeable)+++main :: IO ()+main =+  defaultMainWithIngredients+    (defaultIngredients ++ [includingOptions projectConfigOptionDescriptions])+    (withProjectConfig $ \config ->+      testGroup "Integration tests (internal)"+                (tests config))+++tests :: ProjectConfig -> [TestTree]+tests config =+    --TODO: tests for:+    -- * normal success+    -- * dry-run tests with changes+  [ testGroup "Discovery and planning" $+    [ testCase "find root"      testFindProjectRoot+    , testCase "find root fail" testExceptionFindProjectRoot+    , testCase "no package"    (testExceptionInFindingPackage config)+    , testCase "no package2"   (testExceptionInFindingPackage2 config)+    , testCase "proj conf1"    (testExceptionInProjectConfig config)+    ]+  , testGroup "Target selectors" $+    [ testCaseSteps "valid"             testTargetSelectors+    , testCase      "bad syntax"        testTargetSelectorBadSyntax+    , testCaseSteps "ambiguous syntax"  testTargetSelectorAmbiguous+    , testCase      "no current pkg"    testTargetSelectorNoCurrentPackage+    , testCase      "no targets"        testTargetSelectorNoTargets+    , testCase      "project empty"     testTargetSelectorProjectEmpty+    , testCase      "problems (common)"  (testTargetProblemsCommon config)+    , testCaseSteps "problems (build)"   (testTargetProblemsBuild config)+    , testCaseSteps "problems (repl)"    (testTargetProblemsRepl config)+    , testCaseSteps "problems (run)"     (testTargetProblemsRun config)+    , testCaseSteps "problems (test)"    (testTargetProblemsTest config)+    , testCaseSteps "problems (bench)"   (testTargetProblemsBench config)+    , testCaseSteps "problems (haddock)" (testTargetProblemsHaddock config)+    ]+  , testGroup "Exceptions during building (local inplace)" $+    [ testCase "configure"   (testExceptionInConfigureStep config)+    , testCase "build"       (testExceptionInBuildStep config)+--    , testCase "register"   testExceptionInRegisterStep+    ]+    --TODO: need to repeat for packages for the store+    --TODO: need to check we can build sub-libs, foreign libs and exes+    -- components for non-local packages / packages in the store.++  , testGroup "Successful builds" $+    [ testCaseSteps "Setup script styles" (testSetupScriptStyles config)+    , testCase      "keep-going"          (testBuildKeepGoing config)+    ]++  , testGroup "Regression tests" $+    [ testCase "issue #3324" (testRegressionIssue3324 config)+    ]+  ]+++testFindProjectRoot :: Assertion+testFindProjectRoot = do+    Left (BadProjectRootExplicitFile file) <- findProjectRoot (Just testdir)+                                                              (Just testfile)+    file @?= testfile+  where+    testdir  = basedir </> "exception" </> "no-pkg2"+    testfile = "bklNI8O1OpOUuDu3F4Ij4nv3oAqN"+++testExceptionFindProjectRoot :: Assertion+testExceptionFindProjectRoot = do+    Right (ProjectRootExplicit dir _) <- findProjectRoot (Just testdir) Nothing+    cwd <- getCurrentDirectory+    dir @?= cwd </> testdir+  where+    testdir = basedir </> "exception" </> "no-pkg2"+++testTargetSelectors :: (String -> IO ()) -> Assertion+testTargetSelectors reportSubCase = do+    (_, _, _, localPackages, _) <- configureProject testdir config+    let readTargetSelectors' = readTargetSelectorsWith (dirActions testdir)+                                                       localPackages++    reportSubCase "cwd"+    do Right ts <- readTargetSelectors' []+       ts @?= [TargetPackage TargetImplicitCwd "p-0.1" Nothing]++    reportSubCase "all"+    do Right ts <- readTargetSelectors'+                     ["all", ":all"]+       ts @?= replicate 2 (TargetAllPackages Nothing)++    reportSubCase "filter"+    do Right ts <- readTargetSelectors'+                     [ "libs",  ":cwd:libs"+                     , "flibs", ":cwd:flibs"+                     , "exes",  ":cwd:exes"+                     , "tests", ":cwd:tests"+                     , "benchmarks", ":cwd:benchmarks"]+       zipWithM_ (@?=) ts+         [ TargetPackage TargetImplicitCwd "p-0.1" (Just kind)+         | kind <- concatMap (replicate 2) [LibKind .. ]+         ]++    reportSubCase "all:filter"+    do Right ts <- readTargetSelectors'+                     [ "all:libs",  ":all:libs"+                     , "all:flibs", ":all:flibs"+                     , "all:exes",  ":all:exes"+                     , "all:tests", ":all:tests"+                     , "all:benchmarks", ":all:benchmarks"]+       zipWithM_ (@?=) ts+         [ TargetAllPackages (Just kind)+         | kind <- concatMap (replicate 2) [LibKind .. ]+         ]++    reportSubCase "pkg"+    do Right ts <- readTargetSelectors'+                     [       ":pkg:p", ".",  "./",   "p.cabal"+                     , "q",  ":pkg:q", "q/", "./q/", "q/q.cabal"]+       ts @?= replicate 4 (mkTargetPackage "p-0.1")+           ++ replicate 5 (mkTargetPackage "q-0.1")++    reportSubCase "pkg:filter"+    do Right ts <- readTargetSelectors'+                     [ "p:libs",  ".:libs",  ":pkg:p:libs"+                     , "p:flibs", ".:flibs", ":pkg:p:flibs"+                     , "p:exes",  ".:exes",  ":pkg:p:exes"+                     , "p:tests", ".:tests",  ":pkg:p:tests"+                     , "p:benchmarks", ".:benchmarks", ":pkg:p:benchmarks"+                     , "q:libs",  "q/:libs", ":pkg:q:libs"+                     , "q:flibs", "q/:flibs", ":pkg:q:flibs"+                     , "q:exes",  "q/:exes", ":pkg:q:exes"+                     , "q:tests", "q/:tests", ":pkg:q:tests"+                     , "q:benchmarks", "q/:benchmarks", ":pkg:q:benchmarks"]+       zipWithM_ (@?=) ts $+         [ TargetPackage TargetExplicitNamed "p-0.1" (Just kind)+         | kind <- concatMap (replicate 3) [LibKind .. ]+         ] +++         [ TargetPackage TargetExplicitNamed "q-0.1" (Just kind)+         | kind <- concatMap (replicate 3) [LibKind .. ]+         ]++    reportSubCase "component"+    do Right ts <- readTargetSelectors'+                     [ "p", "lib:p", "p:lib:p", ":pkg:p:lib:p"+                     ,      "lib:q", "q:lib:q", ":pkg:q:lib:q" ]+       ts @?= replicate 4 (TargetComponent "p-0.1" CLibName WholeComponent)+           ++ replicate 3 (TargetComponent "q-0.1" CLibName WholeComponent)++    reportSubCase "module"+    do Right ts <- readTargetSelectors'+                     [ "P", "lib:p:P", "p:p:P", ":pkg:p:lib:p:module:P"+                     , "QQ", "lib:q:QQ", "q:q:QQ", ":pkg:q:lib:q:module:QQ"+                     , "pexe:PMain" -- p:P or q:QQ would be ambiguous here+                     , "qexe:QMain" -- package p vs component p+                     ]+       ts @?= replicate 4 (TargetComponent "p-0.1" CLibName (ModuleTarget "P"))+           ++ replicate 4 (TargetComponent "q-0.1" CLibName (ModuleTarget "QQ"))+           ++ [ TargetComponent "p-0.1" (CExeName "pexe") (ModuleTarget "PMain")+              , TargetComponent "q-0.1" (CExeName "qexe") (ModuleTarget "QMain")+              ]++    reportSubCase "file"+    do Right ts <- readTargetSelectors'+                     [ "./P.hs", "p:P.lhs", "lib:p:P.hsc", "p:p:P.hsc",+                                 ":pkg:p:lib:p:file:P.y"+                     , "q/QQ.hs", "q:QQ.lhs", "lib:q:QQ.hsc", "q:q:QQ.hsc",+                                  ":pkg:q:lib:q:file:QQ.y"+                     ]+       ts @?= replicate 5 (TargetComponent "p-0.1" CLibName (FileTarget "P"))+           ++ replicate 5 (TargetComponent "q-0.1" CLibName (FileTarget "QQ"))+       -- Note there's a bit of an inconsistency here: for the single-part+       -- syntax the target has to point to a file that exists, whereas for+       -- all the other forms we don't require that.++    cleanProject testdir+  where+    testdir = "targets/simple"+    config  = mempty+++testTargetSelectorBadSyntax :: Assertion+testTargetSelectorBadSyntax = do+    (_, _, _, localPackages, _) <- configureProject testdir config+    let targets = [ "foo bar",  " foo"+                  , "foo:", "foo::bar"+                  , "foo: ", "foo: :bar"+                  , "a:b:c:d:e:f", "a:b:c:d:e:f:g:h" ]+    Left errs <- readTargetSelectors localPackages targets+    zipWithM_ (@?=) errs (map TargetSelectorUnrecognised targets)+    cleanProject testdir+  where+    testdir = "targets/empty"+    config  = mempty+++testTargetSelectorAmbiguous :: (String -> IO ()) -> Assertion+testTargetSelectorAmbiguous reportSubCase = do++    -- 'all' is ambiguous with packages and cwd components+    reportSubCase "ambiguous: all vs pkg"+    assertAmbiguous "all"+      [mkTargetPackage "all", mkTargetAllPackages]+      [mkpkg "all" []]++    reportSubCase "ambiguous: all vs cwd component"+    assertAmbiguous "all"+      [mkTargetComponent "other" (CExeName "all"), mkTargetAllPackages]+      [mkpkg "other" [mkexe "all"]]++    -- but 'all' is not ambiguous with non-cwd components, modules or files+    reportSubCase "unambiguous: all vs non-cwd comp, mod, file"+    assertUnambiguous "All"+      mkTargetAllPackages+      [ mkpkgAt "foo" [mkexe "All"] "foo"+      , mkpkg   "bar" [ mkexe "bar" `withModules` ["All"]+                      , mkexe "baz" `withCFiles` ["All"] ]+      ]++    -- filters 'libs', 'exes' etc are ambiguous with packages and+    -- local components+    reportSubCase "ambiguous: cwd-pkg filter vs pkg"+    assertAmbiguous "libs"+      [ mkTargetPackage "libs"+      , TargetPackage TargetImplicitCwd "dummyPackageInfo" (Just LibKind) ]+      [mkpkg "libs" []]++    reportSubCase "ambiguous: filter vs cwd component"+    assertAmbiguous "exes"+      [ mkTargetComponent "other" (CExeName "exes")+      , TargetPackage TargetImplicitCwd "dummyPackageInfo" (Just ExeKind) ]+      [mkpkg "other" [mkexe "exes"]]++    -- but filters are not ambiguous with non-cwd components, modules or files+    reportSubCase "unambiguous: filter vs non-cwd comp, mod, file"+    assertUnambiguous "Libs"+      (TargetPackage TargetImplicitCwd "bar" (Just LibKind))+      [ mkpkgAt "foo" [mkexe "Libs"] "foo"+      , mkpkg   "bar" [ mkexe "bar" `withModules` ["Libs"]+                      , mkexe "baz" `withCFiles` ["Libs"] ]+      ]++    -- local components shadow packages and other components+    reportSubCase "unambiguous: cwd comp vs pkg, non-cwd comp"+    assertUnambiguous "foo"+      (mkTargetComponent "other" (CExeName "foo"))+      [ mkpkg   "other"  [mkexe "foo"]+      , mkpkgAt "other2" [mkexe "foo"] "other2" -- shadows non-local foo+      , mkpkg "foo" [] ]                        -- shadows package foo++    -- local components shadow modules and files+    reportSubCase "unambiguous: cwd comp vs module, file"+    assertUnambiguous "Foo"+      (mkTargetComponent "bar" (CExeName "Foo"))+      [ mkpkg "bar" [mkexe "Foo"]+      , mkpkg "other" [ mkexe "other"  `withModules` ["Foo"]+                      , mkexe "other2" `withCFiles`  ["Foo"] ]+      ]++    -- packages shadow non-local components+    reportSubCase "unambiguous: pkg vs non-cwd comp"+    assertUnambiguous "foo"+      (mkTargetPackage "foo")+      [ mkpkg "foo" []+      , mkpkgAt "other" [mkexe "foo"] "other" -- shadows non-local foo+      ]++    -- packages shadow modules and files+    reportSubCase "unambiguous: pkg vs module, file"+    assertUnambiguous "Foo"+      (mkTargetPackage "Foo")+      [ mkpkgAt "Foo" [] "foo"+      , mkpkg "other" [ mkexe "other"  `withModules` ["Foo"]+                      , mkexe "other2" `withCFiles`  ["Foo"] ]+      ]++    -- non-exact case packages and components are ambiguous+    reportSubCase "ambiguous: non-exact-case pkg names"+    assertAmbiguous "Foo"+      [ mkTargetPackage "foo", mkTargetPackage "FOO" ]+      [ mkpkg "foo" [], mkpkg "FOO" [] ]+    reportSubCase "ambiguous: non-exact-case comp names"+    assertAmbiguous "Foo"+      [ mkTargetComponent "bar" (CExeName "foo")+      , mkTargetComponent "bar" (CExeName "FOO") ]+      [ mkpkg "bar" [mkexe "foo", mkexe "FOO"] ]++    -- exact-case Module or File over non-exact case package or component+    reportSubCase "unambiguous: module vs non-exact-case pkg, comp"+    assertUnambiguous "Baz"+      (mkTargetModule "other" (CExeName "other") "Baz")+      [ mkpkg "baz" [mkexe "BAZ"]+      , mkpkg "other" [ mkexe "other"  `withModules` ["Baz"] ]+      ]+    reportSubCase "unambiguous: file vs non-exact-case pkg, comp"+    assertUnambiguous "Baz"+      (mkTargetFile "other" (CExeName "other") "Baz")+      [ mkpkg "baz" [mkexe "BAZ"]+      , mkpkg "other" [ mkexe "other"  `withCFiles` ["Baz"] ]+      ]+  where+    assertAmbiguous :: String+                    -> [TargetSelector PackageId]+                    -> [SourcePackage (PackageLocation a)]+                    -> Assertion+    assertAmbiguous str tss pkgs = do+      res <- readTargetSelectorsWith fakeDirActions pkgs [str]+      case res of+        Left [TargetSelectorAmbiguous _ tss'] ->+          sort (map snd tss') @?= sort tss+        _ -> assertFailure $ "expected Left [TargetSelectorAmbiguous _ _], "+                          ++ "got " ++ show res++    assertUnambiguous :: String+                      -> TargetSelector PackageId+                      -> [SourcePackage (PackageLocation a)]+                      -> Assertion+    assertUnambiguous str ts pkgs = do+      res <- readTargetSelectorsWith fakeDirActions pkgs [str]+      case res of+        Right [ts'] -> ts' @?= ts+        _ -> assertFailure $ "expected Right [Target...], "+                          ++ "got " ++ show res++    fakeDirActions = TS.DirActions {+      TS.doesFileExist       = \_p -> return True,+      TS.doesDirectoryExist  = \_p -> return True,+      TS.canonicalizePath    = \p -> return ("/" </> p), -- FilePath.Unix.</> ?+      TS.getCurrentDirectory = return "/"+    }++    mkpkg :: String -> [Executable] -> SourcePackage (PackageLocation a)+    mkpkg pkgidstr exes = mkpkgAt pkgidstr exes ""++    mkpkgAt :: String -> [Executable] -> FilePath+            -> SourcePackage (PackageLocation a)+    mkpkgAt pkgidstr exes loc =+      SourcePackage {+        packageInfoId = pkgid,+        packageSource = LocalUnpackedPackage loc,+        packageDescrOverride  = Nothing,+        SP.packageDescription = GenericPackageDescription {+          GPG.packageDescription = emptyPackageDescription { package = pkgid },+          genPackageFlags    = [],+          condLibrary        = Nothing,+          condSubLibraries   = [],+          condForeignLibs    = [],+          condExecutables    = [ ( exeName exe, CondNode exe [] [] )+                               | exe <- exes ],+          condTestSuites     = [],+          condBenchmarks     = []+        }+      }+      where+        Just pkgid = simpleParse pkgidstr++    mkexe :: String -> Executable+    mkexe name = mempty { exeName = fromString name }++    withModules :: Executable -> [String] -> Executable+    withModules exe mods =+      exe { buildInfo = (buildInfo exe) { otherModules = map fromString mods } }++    withCFiles :: Executable -> [FilePath] -> Executable+    withCFiles exe files =+      exe { buildInfo = (buildInfo exe) { cSources = files } }+++mkTargetPackage :: PackageId -> TargetSelector PackageId+mkTargetPackage pkgid =+    TargetPackage TargetExplicitNamed pkgid Nothing++mkTargetComponent :: PackageId -> ComponentName -> TargetSelector PackageId+mkTargetComponent pkgid cname =+    TargetComponent pkgid cname WholeComponent++mkTargetModule :: PackageId -> ComponentName -> ModuleName -> TargetSelector PackageId+mkTargetModule pkgid cname mname =+    TargetComponent pkgid cname (ModuleTarget mname)++mkTargetFile :: PackageId -> ComponentName -> String -> TargetSelector PackageId+mkTargetFile pkgid cname fname =+    TargetComponent pkgid cname (FileTarget fname)++mkTargetAllPackages :: TargetSelector PackageId+mkTargetAllPackages = TargetAllPackages Nothing++instance IsString PackageIdentifier where+    fromString pkgidstr = pkgid+      where Just pkgid = simpleParse pkgidstr+++testTargetSelectorNoCurrentPackage :: Assertion+testTargetSelectorNoCurrentPackage = do+    (_, _, _, localPackages, _) <- configureProject testdir config+    let readTargetSelectors' = readTargetSelectorsWith (dirActions testdir)+                                                       localPackages+        targets = [ "libs",  ":cwd:libs"+                  , "flibs", ":cwd:flibs"+                  , "exes",  ":cwd:exes"+                  , "tests", ":cwd:tests"+                  , "benchmarks", ":cwd:benchmarks"]+    Left errs <- readTargetSelectors' targets+    zipWithM_ (@?=) errs+      [ TargetSelectorNoCurrentPackage ts+      | target <- targets+      , let Just ts = parseTargetString target+      ]+    cleanProject testdir+  where+    testdir = "targets/complex"+    config  = mempty+++testTargetSelectorNoTargets :: Assertion+testTargetSelectorNoTargets = do+    (_, _, _, localPackages, _) <- configureProject testdir config+    Left errs <- readTargetSelectors localPackages []+    errs @?= [TargetSelectorNoTargetsInCwd]+    cleanProject testdir+  where+    testdir = "targets/complex"+    config  = mempty+++testTargetSelectorProjectEmpty :: Assertion+testTargetSelectorProjectEmpty = do+    (_, _, _, localPackages, _) <- configureProject testdir config+    Left errs <- readTargetSelectors localPackages []+    errs @?= [TargetSelectorNoTargetsInProject]+    cleanProject testdir+  where+    testdir = "targets/empty"+    config  = mempty+++testTargetProblemsCommon :: ProjectConfig -> Assertion+testTargetProblemsCommon config0 = do+    (_,elaboratedPlan,_) <- planProject testdir config++    let pkgIdMap :: Map.Map PackageName PackageId+        pkgIdMap = Map.fromList+                     [ (packageName p, packageId p)+                     | p <- InstallPlan.toList elaboratedPlan ]++        cases :: [( TargetSelector PackageId -> CmdBuild.TargetProblem+                  , TargetSelector PackageId+                  )]+        cases =+          [ -- Cannot resolve packages outside of the project+            ( \_ -> CmdBuild.TargetProblemCommon $+                    TargetProblemNoSuchPackage "foobar"+            , mkTargetPackage "foobar" )++            -- We cannot currently build components like testsuites or+            -- benchmarks from packages that are not local to the project+          , ( \_ -> CmdBuild.TargetProblemCommon $+                    TargetComponentNotProjectLocal+                      (pkgIdMap Map.! "filepath") (CTestName "filepath-tests")+                      WholeComponent+            , mkTargetComponent (pkgIdMap Map.! "filepath")+                                (CTestName "filepath-tests") )++            -- Components can be explicitly @buildable: False@+          , ( \_ -> CmdBuild.TargetProblemCommon $+                    TargetComponentNotBuildable "q-0.1" (CExeName "buildable-false") WholeComponent+            , mkTargetComponent "q-0.1" (CExeName "buildable-false") )++            -- Testsuites and benchmarks can be disabled by the solver if it+            -- cannot satisfy deps+          , ( \_ -> CmdBuild.TargetProblemCommon $+                    TargetOptionalStanzaDisabledBySolver "q-0.1" (CTestName "solver-disabled") WholeComponent+            , mkTargetComponent "q-0.1" (CTestName "solver-disabled") )++            -- Testsuites and benchmarks can be disabled explicitly by the+            -- user via config+          , ( \_ -> CmdBuild.TargetProblemCommon $+                    TargetOptionalStanzaDisabledByUser+                      "q-0.1" (CBenchName "user-disabled") WholeComponent+            , mkTargetComponent "q-0.1" (CBenchName "user-disabled") )++            -- An unknown package. The target selector resolution should only+            -- produce known packages, so this should not happen with the+            -- output from 'readTargetSelectors'.+          , ( \_ -> CmdBuild.TargetProblemCommon $+                    TargetProblemNoSuchPackage "foobar"+            , mkTargetPackage "foobar" )++            -- An unknown component of a known package. The target selector+            -- resolution should only produce known packages, so this should+            -- not happen with the output from 'readTargetSelectors'.+          , ( \_ -> CmdBuild.TargetProblemCommon $+                    TargetProblemNoSuchComponent "q-0.1" (CExeName "no-such")+            , mkTargetComponent "q-0.1" (CExeName "no-such") )+          ]+    assertTargetProblems+      elaboratedPlan+      CmdBuild.selectPackageTargets+      CmdBuild.selectComponentTarget+      CmdBuild.TargetProblemCommon+      cases+  where+    testdir = "targets/complex"+    config  = config0 {+      projectConfigLocalPackages = (projectConfigLocalPackages config0) {+        packageConfigBenchmarks = toFlag False+      }+    , projectConfigShared = (projectConfigShared config0) {+        projectConfigConstraints =+          [( UserConstraint (UserAnyQualifier "filepath") PackagePropertySource+           , ConstraintSourceUnknown )]+      }+    }+++testTargetProblemsBuild :: ProjectConfig -> (String -> IO ()) -> Assertion+testTargetProblemsBuild config reportSubCase = do++    reportSubCase "empty-pkg"+    assertProjectTargetProblems+      "targets/empty-pkg" config+      CmdBuild.selectPackageTargets+      CmdBuild.selectComponentTarget+      CmdBuild.TargetProblemCommon+      [ ( CmdBuild.TargetProblemNoTargets, mkTargetPackage "p-0.1" )+      ]++    reportSubCase "all-disabled"+    assertProjectTargetProblems+      "targets/all-disabled"+      config {+        projectConfigLocalPackages = (projectConfigLocalPackages config) {+          packageConfigBenchmarks = toFlag False+        }+      }+      CmdBuild.selectPackageTargets+      CmdBuild.selectComponentTarget+      CmdBuild.TargetProblemCommon+      [ ( flip CmdBuild.TargetProblemNoneEnabled+               [ AvailableTarget "p-0.1" (CBenchName "user-disabled")+                                 TargetDisabledByUser True+               , AvailableTarget "p-0.1" (CTestName "solver-disabled")+                                 TargetDisabledBySolver True+               , AvailableTarget "p-0.1" (CExeName "buildable-false")+                                 TargetNotBuildable True+               , AvailableTarget "p-0.1" CLibName+                                 TargetNotBuildable True+               ]+        , mkTargetPackage "p-0.1" )+      ]++    reportSubCase "enabled component kinds"+    -- When we explicitly enable all the component kinds then selecting the+    -- whole package selects those component kinds too+    do (_,elaboratedPlan,_) <- planProject "targets/variety" config {+           projectConfigLocalPackages = (projectConfigLocalPackages config) {+             packageConfigTests      = toFlag True,+             packageConfigBenchmarks = toFlag True+           }+         }+       assertProjectDistinctTargets+         elaboratedPlan+         CmdBuild.selectPackageTargets+         CmdBuild.selectComponentTarget+         CmdBuild.TargetProblemCommon+         [ mkTargetPackage "p-0.1" ]+         [ ("p-0.1-inplace",             CLibName)+         , ("p-0.1-inplace-a-benchmark", CBenchName "a-benchmark")+         , ("p-0.1-inplace-a-testsuite", CTestName  "a-testsuite")+         , ("p-0.1-inplace-an-exe",      CExeName   "an-exe")+         , ("p-0.1-inplace-libp",        CFLibName  "libp")+         ]++    reportSubCase "disabled component kinds"+    -- When we explicitly disable all the component kinds then selecting the+    -- whole package only selects the library, foreign lib and exes+    do (_,elaboratedPlan,_) <- planProject "targets/variety" config {+           projectConfigLocalPackages = (projectConfigLocalPackages config) {+             packageConfigTests      = toFlag False,+             packageConfigBenchmarks = toFlag False+           }+         }+       assertProjectDistinctTargets+         elaboratedPlan+         CmdBuild.selectPackageTargets+         CmdBuild.selectComponentTarget+         CmdBuild.TargetProblemCommon+         [ mkTargetPackage "p-0.1" ]+         [ ("p-0.1-inplace",        CLibName)+         , ("p-0.1-inplace-an-exe", CExeName  "an-exe")+         , ("p-0.1-inplace-libp",   CFLibName "libp")+         ]++    reportSubCase "requested component kinds"+    -- When we selecting the package with an explicit filter then we get those+    -- components even though we did not explicitly enable tests/benchmarks+    do (_,elaboratedPlan,_) <- planProject "targets/variety" config+       assertProjectDistinctTargets+         elaboratedPlan+         CmdBuild.selectPackageTargets+         CmdBuild.selectComponentTarget+         CmdBuild.TargetProblemCommon+         [ TargetPackage TargetExplicitNamed "p-0.1" (Just TestKind)+         , TargetPackage TargetExplicitNamed "p-0.1" (Just BenchKind)+         ]+         [ ("p-0.1-inplace-a-benchmark", CBenchName "a-benchmark")+         , ("p-0.1-inplace-a-testsuite", CTestName  "a-testsuite")+         ]+++testTargetProblemsRepl :: ProjectConfig -> (String -> IO ()) -> Assertion+testTargetProblemsRepl config reportSubCase = do++    reportSubCase "multiple-libs"+    assertProjectTargetProblems+      "targets/multiple-libs" config+      CmdRepl.selectPackageTargets+      CmdRepl.selectComponentTarget+      CmdRepl.TargetProblemCommon+      [ ( flip CmdRepl.TargetProblemMatchesMultiple+               [ AvailableTarget "p-0.1" CLibName+                   (TargetBuildable () TargetRequestedByDefault) True+               , AvailableTarget "q-0.1" CLibName+                   (TargetBuildable () TargetRequestedByDefault) True+               ]+        , mkTargetAllPackages )+      ]++    reportSubCase "multiple-exes"+    assertProjectTargetProblems+      "targets/multiple-exes" config+      CmdRepl.selectPackageTargets+      CmdRepl.selectComponentTarget+      CmdRepl.TargetProblemCommon+      [ ( flip CmdRepl.TargetProblemMatchesMultiple+               [ AvailableTarget "p-0.1" (CExeName "p2")+                   (TargetBuildable () TargetRequestedByDefault) True+               , AvailableTarget "p-0.1" (CExeName "p1")+                   (TargetBuildable () TargetRequestedByDefault) True+               ]+        , mkTargetPackage "p-0.1" )+      ]++    reportSubCase "multiple-tests"+    assertProjectTargetProblems+      "targets/multiple-tests" config+      CmdRepl.selectPackageTargets+      CmdRepl.selectComponentTarget+      CmdRepl.TargetProblemCommon+      [ ( flip CmdRepl.TargetProblemMatchesMultiple+               [ AvailableTarget "p-0.1" (CTestName "p2")+                   (TargetBuildable () TargetNotRequestedByDefault) True+               , AvailableTarget "p-0.1" (CTestName "p1")+                   (TargetBuildable () TargetNotRequestedByDefault) True+               ]+        , TargetPackage TargetExplicitNamed "p-0.1" (Just TestKind) )+      ]++    reportSubCase "multiple targets"+    do (_,elaboratedPlan,_) <- planProject "targets/multiple-exes" config+       assertProjectDistinctTargets+         elaboratedPlan+         CmdRepl.selectPackageTargets+         CmdRepl.selectComponentTarget+         CmdRepl.TargetProblemCommon+         [ mkTargetComponent "p-0.1" (CExeName "p1")+         , mkTargetComponent "p-0.1" (CExeName "p2")+         ]+         [ ("p-0.1-inplace-p1", CExeName "p1")+         , ("p-0.1-inplace-p2", CExeName "p2")+         ]++    reportSubCase "libs-disabled"+    assertProjectTargetProblems+      "targets/libs-disabled" config+      CmdRepl.selectPackageTargets+      CmdRepl.selectComponentTarget+      CmdRepl.TargetProblemCommon+      [ ( flip CmdRepl.TargetProblemNoneEnabled+               [ AvailableTarget "p-0.1" CLibName TargetNotBuildable True ]+        , mkTargetPackage "p-0.1" )+      ]++    reportSubCase "exes-disabled"+    assertProjectTargetProblems+      "targets/exes-disabled" config+      CmdRepl.selectPackageTargets+      CmdRepl.selectComponentTarget+      CmdRepl.TargetProblemCommon+      [ ( flip CmdRepl.TargetProblemNoneEnabled+               [ AvailableTarget "p-0.1" (CExeName "p") TargetNotBuildable True+               ]+        , mkTargetPackage "p-0.1" )+      ]++    reportSubCase "test-only"+    assertProjectTargetProblems+      "targets/test-only" config+      CmdRepl.selectPackageTargets+      CmdRepl.selectComponentTarget+      CmdRepl.TargetProblemCommon+      [ ( flip CmdRepl.TargetProblemNoneEnabled+               [ AvailableTarget "p-0.1" (CTestName "pexe")+                   (TargetBuildable () TargetNotRequestedByDefault) True+               ]+        , mkTargetPackage "p-0.1" )+      ]++    reportSubCase "empty-pkg"+    assertProjectTargetProblems+      "targets/empty-pkg" config+      CmdRepl.selectPackageTargets+      CmdRepl.selectComponentTarget+      CmdRepl.TargetProblemCommon+      [ ( CmdRepl.TargetProblemNoTargets, mkTargetPackage "p-0.1" )+      ]++    reportSubCase "requested component kinds"+    do (_,elaboratedPlan,_) <- planProject "targets/variety" config+       -- by default we only get the lib+       assertProjectDistinctTargets+         elaboratedPlan+         CmdRepl.selectPackageTargets+         CmdRepl.selectComponentTarget+         CmdRepl.TargetProblemCommon+         [ TargetPackage TargetExplicitNamed "p-0.1" Nothing ]+         [ ("p-0.1-inplace", CLibName) ]+       -- When we select the package with an explicit filter then we get those+       -- components even though we did not explicitly enable tests/benchmarks+       assertProjectDistinctTargets+         elaboratedPlan+         CmdRepl.selectPackageTargets+         CmdRepl.selectComponentTarget+         CmdRepl.TargetProblemCommon+         [ TargetPackage TargetExplicitNamed "p-0.1" (Just TestKind) ]+         [ ("p-0.1-inplace-a-testsuite", CTestName  "a-testsuite") ]+       assertProjectDistinctTargets+         elaboratedPlan+         CmdRepl.selectPackageTargets+         CmdRepl.selectComponentTarget+         CmdRepl.TargetProblemCommon+         [ TargetPackage TargetExplicitNamed "p-0.1" (Just BenchKind) ]+         [ ("p-0.1-inplace-a-benchmark", CBenchName "a-benchmark") ]+++testTargetProblemsRun :: ProjectConfig -> (String -> IO ()) -> Assertion+testTargetProblemsRun config reportSubCase = do++    reportSubCase "multiple-exes"+    assertProjectTargetProblems+      "targets/multiple-exes" config+      CmdRun.selectPackageTargets+      CmdRun.selectComponentTarget+      CmdRun.TargetProblemCommon+      [ ( flip CmdRun.TargetProblemMatchesMultiple+               [ AvailableTarget "p-0.1" (CExeName "p2")+                   (TargetBuildable () TargetRequestedByDefault) True+               , AvailableTarget "p-0.1" (CExeName "p1")+                   (TargetBuildable () TargetRequestedByDefault) True+               ]+        , mkTargetPackage "p-0.1" )+      ]++    reportSubCase "multiple targets"+    do (_,elaboratedPlan,_) <- planProject "targets/multiple-exes" config+       assertProjectDistinctTargets+         elaboratedPlan+         CmdRun.selectPackageTargets+         CmdRun.selectComponentTarget+         CmdRun.TargetProblemCommon+         [ mkTargetComponent "p-0.1" (CExeName "p1")+         , mkTargetComponent "p-0.1" (CExeName "p2")+         ]+         [ ("p-0.1-inplace-p1", CExeName "p1")+         , ("p-0.1-inplace-p2", CExeName "p2")+         ]++    reportSubCase "exes-disabled"+    assertProjectTargetProblems+      "targets/exes-disabled" config+      CmdRun.selectPackageTargets+      CmdRun.selectComponentTarget+      CmdRun.TargetProblemCommon+      [ ( flip CmdRun.TargetProblemNoneEnabled+               [ AvailableTarget "p-0.1" (CExeName "p") TargetNotBuildable True+               ]+        , mkTargetPackage "p-0.1" )+      ]++    reportSubCase "empty-pkg"+    assertProjectTargetProblems+      "targets/empty-pkg" config+      CmdRun.selectPackageTargets+      CmdRun.selectComponentTarget+      CmdRun.TargetProblemCommon+      [ ( CmdRun.TargetProblemNoTargets, mkTargetPackage "p-0.1" )+      ]++    reportSubCase "test-only"+    assertProjectTargetProblems+      "targets/test-only" config+      CmdRun.selectPackageTargets+      CmdRun.selectComponentTarget+      CmdRun.TargetProblemCommon+      [ ( CmdRun.TargetProblemNoExes, mkTargetPackage "p-0.1" )+      ]++    reportSubCase "variety"+    assertProjectTargetProblems+      "targets/variety" config+      CmdRun.selectPackageTargets+      CmdRun.selectComponentTarget+      CmdRun.TargetProblemCommon $+      [ ( const (CmdRun.TargetProblemComponentNotExe "p-0.1" cname)+        , mkTargetComponent "p-0.1" cname )+      | cname <- [ CLibName, CFLibName "libp",+                   CTestName "a-testsuite", CBenchName "a-benchmark" ]+      ] +++      [ ( const (CmdRun.TargetProblemIsSubComponent+                          "p-0.1" cname (ModuleTarget modname))+        , mkTargetModule "p-0.1" cname modname )+      | (cname, modname) <- [ (CTestName  "a-testsuite", "TestModule")+                            , (CBenchName "a-benchmark", "BenchModule")+                            , (CExeName   "an-exe",      "ExeModule")+                            , (CLibName,                 "P")+                            ]+      ] +++      [ ( const (CmdRun.TargetProblemIsSubComponent+                          "p-0.1" cname (FileTarget fname))+        , mkTargetFile "p-0.1" cname fname)+      | (cname, fname) <- [ (CTestName  "a-testsuite", "Test.hs")+                          , (CBenchName "a-benchmark", "Bench.hs")+                          , (CExeName   "an-exe",      "Main.hs")+                          ]+      ]+++testTargetProblemsTest :: ProjectConfig -> (String -> IO ()) -> Assertion+testTargetProblemsTest config reportSubCase = do++    reportSubCase "disabled by config"+    assertProjectTargetProblems+      "targets/tests-disabled"+      config {+        projectConfigLocalPackages = (projectConfigLocalPackages config) {+          packageConfigTests = toFlag False+        }+      }+      CmdTest.selectPackageTargets+      CmdTest.selectComponentTarget+      CmdTest.TargetProblemCommon+      [ ( flip CmdTest.TargetProblemNoneEnabled+               [ AvailableTarget "p-0.1" (CTestName "user-disabled")+                                 TargetDisabledByUser True+               , AvailableTarget "p-0.1" (CTestName "solver-disabled")+                                 TargetDisabledByUser True+               ]+        , mkTargetPackage "p-0.1" )+      ]++    reportSubCase "disabled by solver & buildable false"+    assertProjectTargetProblems+      "targets/tests-disabled"+      config+      CmdTest.selectPackageTargets+      CmdTest.selectComponentTarget+      CmdTest.TargetProblemCommon+      [ ( flip CmdTest.TargetProblemNoneEnabled+               [ AvailableTarget "p-0.1" (CTestName "user-disabled")+                                 TargetDisabledBySolver True+               , AvailableTarget "p-0.1" (CTestName "solver-disabled")+                                 TargetDisabledBySolver True+               ]+        , mkTargetPackage "p-0.1" )++      , ( flip CmdTest.TargetProblemNoneEnabled+               [ AvailableTarget "q-0.1" (CTestName "buildable-false")+                                 TargetNotBuildable True+               ]+        , mkTargetPackage "q-0.1" )+      ]++    reportSubCase "empty-pkg"+    assertProjectTargetProblems+      "targets/empty-pkg" config+      CmdTest.selectPackageTargets+      CmdTest.selectComponentTarget+      CmdTest.TargetProblemCommon+      [ ( CmdTest.TargetProblemNoTargets, mkTargetPackage "p-0.1" )+      ]++    reportSubCase "no tests"+    assertProjectTargetProblems+      "targets/simple"+      config+      CmdTest.selectPackageTargets+      CmdTest.selectComponentTarget+      CmdTest.TargetProblemCommon+      [ ( CmdTest.TargetProblemNoTests, mkTargetPackage "p-0.1" )+      , ( CmdTest.TargetProblemNoTests, mkTargetPackage "q-0.1" )+      ]++    reportSubCase "not a test"+    assertProjectTargetProblems+      "targets/variety"+      config+      CmdTest.selectPackageTargets+      CmdTest.selectComponentTarget+      CmdTest.TargetProblemCommon $+      [ ( const (CmdTest.TargetProblemComponentNotTest+                  "p-0.1" CLibName)+        , mkTargetComponent "p-0.1" CLibName )++      , ( const (CmdTest.TargetProblemComponentNotTest+                  "p-0.1" (CExeName "an-exe"))+        , mkTargetComponent "p-0.1" (CExeName "an-exe") )++      , ( const (CmdTest.TargetProblemComponentNotTest+                  "p-0.1" (CFLibName "libp"))+        , mkTargetComponent "p-0.1" (CFLibName "libp") )++      , ( const (CmdTest.TargetProblemComponentNotTest+                  "p-0.1" (CBenchName "a-benchmark"))+        , mkTargetComponent "p-0.1" (CBenchName "a-benchmark") )+      ] +++      [ ( const (CmdTest.TargetProblemIsSubComponent+                          "p-0.1" cname (ModuleTarget modname))+        , mkTargetModule "p-0.1" cname modname )+      | (cname, modname) <- [ (CTestName  "a-testsuite", "TestModule")+                            , (CBenchName "a-benchmark", "BenchModule")+                            , (CExeName   "an-exe",      "ExeModule")+                            , (CLibName,                 "P")+                            ]+      ] +++      [ ( const (CmdTest.TargetProblemIsSubComponent+                          "p-0.1" cname (FileTarget fname))+        , mkTargetFile "p-0.1" cname fname)+      | (cname, fname) <- [ (CTestName  "a-testsuite", "Test.hs")+                          , (CBenchName "a-benchmark", "Bench.hs")+                          , (CExeName   "an-exe",      "Main.hs")+                          ]+      ]+++testTargetProblemsBench :: ProjectConfig -> (String -> IO ()) -> Assertion+testTargetProblemsBench config reportSubCase = do++    reportSubCase "disabled by config"+    assertProjectTargetProblems+      "targets/benchmarks-disabled"+      config {+        projectConfigLocalPackages = (projectConfigLocalPackages config) {+          packageConfigBenchmarks = toFlag False+        }+      }+      CmdBench.selectPackageTargets+      CmdBench.selectComponentTarget+      CmdBench.TargetProblemCommon+      [ ( flip CmdBench.TargetProblemNoneEnabled+               [ AvailableTarget "p-0.1" (CBenchName "user-disabled")+                                 TargetDisabledByUser True+               , AvailableTarget "p-0.1" (CBenchName "solver-disabled")+                                 TargetDisabledByUser True+               ]+        , mkTargetPackage "p-0.1" )+      ]++    reportSubCase "disabled by solver & buildable false"+    assertProjectTargetProblems+      "targets/benchmarks-disabled"+      config+      CmdBench.selectPackageTargets+      CmdBench.selectComponentTarget+      CmdBench.TargetProblemCommon+      [ ( flip CmdBench.TargetProblemNoneEnabled+               [ AvailableTarget "p-0.1" (CBenchName "user-disabled")+                                 TargetDisabledBySolver True+               , AvailableTarget "p-0.1" (CBenchName "solver-disabled")+                                 TargetDisabledBySolver True+               ]+        , mkTargetPackage "p-0.1" )++      , ( flip CmdBench.TargetProblemNoneEnabled+               [ AvailableTarget "q-0.1" (CBenchName "buildable-false")+                                 TargetNotBuildable True+               ]+        , mkTargetPackage "q-0.1" )+      ]++    reportSubCase "empty-pkg"+    assertProjectTargetProblems+      "targets/empty-pkg" config+      CmdBench.selectPackageTargets+      CmdBench.selectComponentTarget+      CmdBench.TargetProblemCommon+      [ ( CmdBench.TargetProblemNoTargets, mkTargetPackage "p-0.1" )+      ]++    reportSubCase "no benchmarks"+    assertProjectTargetProblems+      "targets/simple"+      config+      CmdBench.selectPackageTargets+      CmdBench.selectComponentTarget+      CmdBench.TargetProblemCommon+      [ ( CmdBench.TargetProblemNoBenchmarks, mkTargetPackage "p-0.1" )+      , ( CmdBench.TargetProblemNoBenchmarks, mkTargetPackage "q-0.1" )+      ]++    reportSubCase "not a benchmark"+    assertProjectTargetProblems+      "targets/variety"+      config+      CmdBench.selectPackageTargets+      CmdBench.selectComponentTarget+      CmdBench.TargetProblemCommon $+      [ ( const (CmdBench.TargetProblemComponentNotBenchmark+                  "p-0.1" CLibName)+        , mkTargetComponent "p-0.1" CLibName )++      , ( const (CmdBench.TargetProblemComponentNotBenchmark+                  "p-0.1" (CExeName "an-exe"))+        , mkTargetComponent "p-0.1" (CExeName "an-exe") )++      , ( const (CmdBench.TargetProblemComponentNotBenchmark+                  "p-0.1" (CFLibName "libp"))+        , mkTargetComponent "p-0.1" (CFLibName "libp") )++      , ( const (CmdBench.TargetProblemComponentNotBenchmark+                  "p-0.1" (CTestName "a-testsuite"))+        , mkTargetComponent "p-0.1" (CTestName "a-testsuite") )+      ] +++      [ ( const (CmdBench.TargetProblemIsSubComponent+                          "p-0.1" cname (ModuleTarget modname))+        , mkTargetModule "p-0.1" cname modname )+      | (cname, modname) <- [ (CTestName  "a-testsuite", "TestModule")+                            , (CBenchName "a-benchmark", "BenchModule")+                            , (CExeName   "an-exe",      "ExeModule")+                            , (CLibName,                 "P")+                            ]+      ] +++      [ ( const (CmdBench.TargetProblemIsSubComponent+                          "p-0.1" cname (FileTarget fname))+        , mkTargetFile "p-0.1" cname fname)+      | (cname, fname) <- [ (CTestName  "a-testsuite", "Test.hs")+                          , (CBenchName "a-benchmark", "Bench.hs")+                          , (CExeName   "an-exe",      "Main.hs")+                          ]+      ]+++testTargetProblemsHaddock :: ProjectConfig -> (String -> IO ()) -> Assertion+testTargetProblemsHaddock config reportSubCase = do++    reportSubCase "all-disabled"+    assertProjectTargetProblems+      "targets/all-disabled"+      config+      (let haddockFlags = mkHaddockFlags False True True False+        in CmdHaddock.selectPackageTargets haddockFlags)+      CmdHaddock.selectComponentTarget+      CmdHaddock.TargetProblemCommon+      [ ( flip CmdHaddock.TargetProblemNoneEnabled+               [ AvailableTarget "p-0.1" (CBenchName "user-disabled")+                                 TargetDisabledByUser True+               , AvailableTarget "p-0.1" (CTestName "solver-disabled")+                                 TargetDisabledBySolver True+               , AvailableTarget "p-0.1" (CExeName "buildable-false")+                                 TargetNotBuildable True+               , AvailableTarget "p-0.1" CLibName+                                 TargetNotBuildable True+               ]+        , mkTargetPackage "p-0.1" )+      ]++    reportSubCase "empty-pkg"+    assertProjectTargetProblems+      "targets/empty-pkg" config+      (let haddockFlags = mkHaddockFlags False False False False+        in CmdHaddock.selectPackageTargets haddockFlags)+      CmdHaddock.selectComponentTarget+      CmdHaddock.TargetProblemCommon+      [ ( CmdHaddock.TargetProblemNoTargets, mkTargetPackage "p-0.1" )+      ]++    reportSubCase "enabled component kinds"+    -- When we explicitly enable all the component kinds then selecting the+    -- whole package selects those component kinds too+    (_,elaboratedPlan,_) <- planProject "targets/variety" config+    let haddockFlags = mkHaddockFlags True True True True+     in assertProjectDistinctTargets+          elaboratedPlan+          (CmdHaddock.selectPackageTargets haddockFlags)+          CmdHaddock.selectComponentTarget+          CmdHaddock.TargetProblemCommon+          [ mkTargetPackage "p-0.1" ]+          [ ("p-0.1-inplace",             CLibName)+          , ("p-0.1-inplace-a-benchmark", CBenchName "a-benchmark")+          , ("p-0.1-inplace-a-testsuite", CTestName  "a-testsuite")+          , ("p-0.1-inplace-an-exe",      CExeName   "an-exe")+          , ("p-0.1-inplace-libp",        CFLibName  "libp")+          ]++    reportSubCase "disabled component kinds"+    -- When we explicitly disable all the component kinds then selecting the+    -- whole package only selects the library+    let haddockFlags = mkHaddockFlags False False False False+     in assertProjectDistinctTargets+          elaboratedPlan+          (CmdHaddock.selectPackageTargets haddockFlags)+          CmdHaddock.selectComponentTarget+          CmdHaddock.TargetProblemCommon+          [ mkTargetPackage "p-0.1" ]+          [ ("p-0.1-inplace", CLibName) ]++    reportSubCase "requested component kinds"+    -- When we selecting the package with an explicit filter then it does not+    -- matter if the config was to disable all the component kinds+    let haddockFlags = mkHaddockFlags False False False False+     in assertProjectDistinctTargets+          elaboratedPlan+          (CmdHaddock.selectPackageTargets haddockFlags)+          CmdHaddock.selectComponentTarget+          CmdHaddock.TargetProblemCommon+          [ TargetPackage TargetExplicitNamed "p-0.1" (Just FLibKind)+          , TargetPackage TargetExplicitNamed "p-0.1" (Just ExeKind)+          , TargetPackage TargetExplicitNamed "p-0.1" (Just TestKind)+          , TargetPackage TargetExplicitNamed "p-0.1" (Just BenchKind)+          ]+          [ ("p-0.1-inplace-a-benchmark", CBenchName "a-benchmark")+          , ("p-0.1-inplace-a-testsuite", CTestName  "a-testsuite")+          , ("p-0.1-inplace-an-exe",      CExeName   "an-exe")+          , ("p-0.1-inplace-libp",        CFLibName  "libp")+          ]+  where+    mkHaddockFlags flib exe test bench =+      defaultHaddockFlags {+        haddockForeignLibs = toFlag flib,+        haddockExecutables = toFlag exe,+        haddockTestSuites  = toFlag test,+        haddockBenchmarks  = toFlag bench+      }++assertProjectDistinctTargets+  :: forall err. (Eq err, Show err) =>+     ElaboratedInstallPlan+  -> (forall k. TargetSelector PackageId -> [AvailableTarget k] -> Either err [k])+  -> (forall k. PackageId -> ComponentName -> SubComponentTarget ->  AvailableTarget k  -> Either err  k )+  -> (TargetProblemCommon -> err)+  -> [TargetSelector PackageId]+  -> [(UnitId, ComponentName)]+  -> Assertion+assertProjectDistinctTargets elaboratedPlan+                             selectPackageTargets+                             selectComponentTarget+                             liftProblem+                             targetSelectors+                             expectedTargets+  | Right targets <- results+  = distinctTargetComponents targets @?= Set.fromList expectedTargets++  | otherwise+  = assertFailure $ "assertProjectDistinctTargets: expected "+                 ++ "(Right targets) but got " ++ show results+  where+    results = resolveTargets+                selectPackageTargets+                selectComponentTarget+                liftProblem+                elaboratedPlan+                targetSelectors+++assertProjectTargetProblems+  :: forall err. (Eq err, Show err) =>+     FilePath -> ProjectConfig+  -> (forall k. TargetSelector PackageId+             -> [AvailableTarget k]+             -> Either err [k])+  -> (forall k. PackageId -> ComponentName -> SubComponentTarget+             -> AvailableTarget k+             -> Either err k )+  -> (TargetProblemCommon -> err)+  -> [(TargetSelector PackageId -> err, TargetSelector PackageId)]+  -> Assertion+assertProjectTargetProblems testdir config+                            selectPackageTargets+                            selectComponentTarget+                            liftProblem+                            cases = do+    (_,elaboratedPlan,_) <- planProject testdir config+    assertTargetProblems+      elaboratedPlan+      selectPackageTargets+      selectComponentTarget+      liftProblem+      cases+++assertTargetProblems+  :: forall err. (Eq err, Show err) =>+     ElaboratedInstallPlan+  -> (forall k. TargetSelector PackageId -> [AvailableTarget k] -> Either err [k])+  -> (forall k. PackageId -> ComponentName -> SubComponentTarget ->  AvailableTarget k  -> Either err  k )+  -> (TargetProblemCommon -> err)+  -> [(TargetSelector PackageId -> err, TargetSelector PackageId)]+  -> Assertion+assertTargetProblems elaboratedPlan+                     selectPackageTargets+                     selectComponentTarget+                     liftProblem =+    mapM_ (uncurry assertTargetProblem)+  where+    assertTargetProblem expected targetSelector =+      let res = resolveTargets selectPackageTargets selectComponentTarget+                               liftProblem elaboratedPlan [targetSelector] in+      case res of+        Left [problem] ->+          problem @?= expected targetSelector++        unexpected ->+          assertFailure $ "expected resolveTargets result: (Left [problem]) "+                       ++ "but got: " ++ show unexpected+++testExceptionInFindingPackage :: ProjectConfig -> Assertion+testExceptionInFindingPackage config = do+    BadPackageLocations _ locs <- expectException "BadPackageLocations" $+      void $ planProject testdir config+    case locs of+      [BadLocGlobEmptyMatch "./*.cabal"] -> return ()+      _ -> assertFailure "expected BadLocGlobEmptyMatch"+    cleanProject testdir+  where+    testdir = "exception/no-pkg"+++testExceptionInFindingPackage2 :: ProjectConfig -> Assertion+testExceptionInFindingPackage2 config = do+    BadPackageLocations _ locs <- expectException "BadPackageLocations" $+      void $ planProject testdir config+    case locs of+      [BadPackageLocationFile (BadLocDirNoCabalFile ".")] -> return ()+      _ -> assertFailure $ "expected BadLocDirNoCabalFile, got " ++ show locs+    cleanProject testdir+  where+    testdir = "exception/no-pkg2"+++testExceptionInProjectConfig :: ProjectConfig -> Assertion+testExceptionInProjectConfig config = do+    BadPerPackageCompilerPaths ps <- expectException "BadPerPackageCompilerPaths" $+      void $ planProject testdir config+    case ps of+      [(pn,"ghc")] | "foo" == pn -> return ()+      _ -> assertFailure $ "expected (PackageName \"foo\",\"ghc\"), got "+                        ++ show ps+    cleanProject testdir+  where+    testdir = "exception/bad-config"+++testExceptionInConfigureStep :: ProjectConfig -> Assertion+testExceptionInConfigureStep config = do+    (plan, res) <- executePlan =<< planProject testdir config+    (_pkga1, failure) <- expectPackageFailed plan res pkgidA1+    case buildFailureReason failure of+      ConfigureFailed _ -> return ()+      _ -> assertFailure $ "expected ConfigureFailed, got " ++ show failure +    cleanProject testdir+  where+    testdir = "exception/configure"+    pkgidA1 = PackageIdentifier "a" (mkVersion [1])+++testExceptionInBuildStep :: ProjectConfig -> Assertion+testExceptionInBuildStep config = do+    (plan, res) <- executePlan =<< planProject testdir config+    (_pkga1, failure) <- expectPackageFailed plan res pkgidA1+    expectBuildFailed failure+  where+    testdir = "exception/build"+    pkgidA1 = PackageIdentifier "a" (mkVersion [1])++testSetupScriptStyles :: ProjectConfig -> (String -> IO ()) -> Assertion+testSetupScriptStyles config reportSubCase = do++  reportSubCase (show SetupCustomExplicitDeps)++  plan0@(_,_,sharedConfig) <- planProject testdir1 config++  let isOSX (Platform _ OSX) = True+      isOSX _ = False+  -- Skip the Custom tests when the shipped Cabal library is buggy+  unless (isOSX (pkgConfigPlatform sharedConfig)+       && compilerVersion (pkgConfigCompiler sharedConfig) < mkVersion [7,10]) $ do++    (plan1, res1) <- executePlan plan0+    (pkg1,  _)    <- expectPackageInstalled plan1 res1 pkgidA+    elabSetupScriptStyle pkg1 @?= SetupCustomExplicitDeps+    hasDefaultSetupDeps pkg1 @?= Just False+    marker1 <- readFile (basedir </> testdir1 </> "marker")+    marker1 @?= "ok"+    removeFile (basedir </> testdir1 </> "marker")++    reportSubCase (show SetupCustomImplicitDeps)+    (plan2, res2) <- executePlan =<< planProject testdir2 config+    (pkg2,  _)    <- expectPackageInstalled plan2 res2 pkgidA+    elabSetupScriptStyle pkg2 @?= SetupCustomImplicitDeps+    hasDefaultSetupDeps pkg2 @?= Just True+    marker2 <- readFile (basedir </> testdir2 </> "marker")+    marker2 @?= "ok"+    removeFile (basedir </> testdir2 </> "marker")++    reportSubCase (show SetupNonCustomInternalLib)+    (plan3, res3) <- executePlan =<< planProject testdir3 config+    (pkg3,  _)    <- expectPackageInstalled plan3 res3 pkgidA+    elabSetupScriptStyle pkg3 @?= SetupNonCustomInternalLib+{-+    --TODO: the SetupNonCustomExternalLib case is hard to test since it+    -- requires a version of Cabal that's later than the one we're testing+    -- e.g. needs a .cabal file that specifies cabal-version: >= 2.0+    -- and a corresponding Cabal package that we can use to try and build a+    -- default Setup.hs.+    reportSubCase (show SetupNonCustomExternalLib)+    (plan4, res4) <- executePlan =<< planProject testdir4 config+    (pkg4,  _)    <- expectPackageInstalled plan4 res4 pkgidA+    pkgSetupScriptStyle pkg4 @?= SetupNonCustomExternalLib+-}+  where+    testdir1 = "build/setup-custom1"+    testdir2 = "build/setup-custom2"+    testdir3 = "build/setup-simple"+    pkgidA   = PackageIdentifier "a" (mkVersion [0,1])+    -- The solver fills in default setup deps explicitly, but marks them as such+    hasDefaultSetupDeps = fmap defaultSetupDepends+                        . setupBuildInfo . elabPkgDescription++-- | Test the behaviour with and without @--keep-going@+--+testBuildKeepGoing :: ProjectConfig -> Assertion+testBuildKeepGoing config = do+    -- P is expected to fail, Q does not depend on P but without+    -- parallel build and without keep-going then we don't build Q yet.+    (plan1, res1) <- executePlan =<< planProject testdir (config  <> keepGoing False)+    (_, failure1) <- expectPackageFailed plan1 res1 "p-0.1"+    expectBuildFailed failure1+    _ <- expectPackageConfigured plan1 res1 "q-0.1"++    -- With keep-going then we should go on to sucessfully build Q+    (plan2, res2) <- executePlan+                 =<< planProject testdir (config <> keepGoing True)+    (_, failure2) <- expectPackageFailed plan2 res2 "p-0.1"+    expectBuildFailed failure2+    _ <- expectPackageInstalled plan2 res2 "q-0.1"+    return ()+  where+    testdir = "build/keep-going"+    keepGoing kg =+      mempty {+        projectConfigBuildOnly = mempty {+          projectConfigKeepGoing = toFlag kg+        }+      }++-- | See <https://github.com/haskell/cabal/issues/3324>+--+testRegressionIssue3324 :: ProjectConfig -> Assertion+testRegressionIssue3324 config = do+    -- expected failure first time due to missing dep+    (plan1, res1) <- executePlan =<< planProject testdir config+    (_pkgq, failure) <- expectPackageFailed plan1 res1 "q-0.1"+    expectBuildFailed failure++    -- add the missing dep, now it should work+    let qcabal  = basedir </> testdir </> "q" </> "q.cabal"+    withFileFinallyRestore qcabal $ do+      appendFile qcabal ("  build-depends: p\n")+      (plan2, res2) <- executePlan =<< planProject testdir config+      _ <- expectPackageInstalled plan2 res2 "p-0.1"+      _ <- expectPackageInstalled plan2 res2 "q-0.1"+      return ()+  where+    testdir = "regression/3324"+++---------------------------------+-- Test utils to plan and build+--++basedir :: FilePath+basedir = "tests" </> "IntegrationTests2"++dirActions :: FilePath -> TS.DirActions IO+dirActions testdir =+    defaultDirActions {+      TS.doesFileExist       = \p ->+        TS.doesFileExist defaultDirActions (virtcwd </> p),++      TS.doesDirectoryExist  = \p ->+        TS.doesDirectoryExist defaultDirActions (virtcwd </> p),++      TS.canonicalizePath    = \p ->+        TS.canonicalizePath defaultDirActions (virtcwd </> p),++      TS.getCurrentDirectory =+        TS.canonicalizePath defaultDirActions virtcwd+    }+  where+    virtcwd = basedir </> testdir++type ProjDetails = (DistDirLayout,+                    CabalDirLayout,+                    ProjectConfig,+                    [UnresolvedSourcePackage],+                    BuildTimeSettings)++configureProject :: FilePath -> ProjectConfig -> IO ProjDetails+configureProject testdir cliConfig = do+    cabalDir <- defaultCabalDir+    let cabalDirLayout = defaultCabalDirLayout cabalDir++    projectRootDir <- canonicalizePath (basedir </> testdir)+    isexplict      <- doesFileExist (projectRootDir </> "cabal.project")+    let projectRoot+          | isexplict = ProjectRootExplicit projectRootDir+                                           (projectRootDir </> "cabal.project")+          | otherwise = ProjectRootImplicit projectRootDir+        distDirLayout = defaultDistDirLayout projectRoot Nothing++    -- Clear state between test runs. The state remains if the previous run+    -- ended in an exception (as we leave the files to help with debugging).+    cleanProject testdir++    (projectConfig, localPackages) <-+      rebuildProjectConfig verbosity+                           distDirLayout+                           cliConfig++    let buildSettings = resolveBuildTimeSettings+                          verbosity cabalDirLayout+                          projectConfig++    return (distDirLayout,+            cabalDirLayout,+            projectConfig,+            localPackages,+            buildSettings)++type PlanDetails = (ProjDetails,+                    ElaboratedInstallPlan,+                    ElaboratedSharedConfig)++planProject :: FilePath -> ProjectConfig -> IO PlanDetails+planProject testdir cliConfig = do++    projDetails@+      (distDirLayout,+       cabalDirLayout,+       projectConfig,+       localPackages,+       _buildSettings) <- configureProject testdir cliConfig++    (elaboratedPlan, _, elaboratedShared) <-+      rebuildInstallPlan verbosity+                         distDirLayout cabalDirLayout+                         projectConfig+                         localPackages++    return (projDetails,+            elaboratedPlan,+            elaboratedShared)++executePlan :: PlanDetails -> IO (ElaboratedInstallPlan, BuildOutcomes)+executePlan ((distDirLayout, cabalDirLayout, _, _, buildSettings),+             elaboratedPlan,+             elaboratedShared) = do++    let targets :: Map.Map UnitId [ComponentTarget]+        targets =+          Map.fromList+            [ (unitid, [ComponentTarget cname WholeComponent])+            | ts <- Map.elems (availableTargets elaboratedPlan)+            , AvailableTarget {+                availableTargetStatus = TargetBuildable (unitid, cname) _+              } <- ts+            ]+        elaboratedPlan' = pruneInstallPlanToTargets+                            TargetActionBuild targets+                            elaboratedPlan++    pkgsBuildStatus <-+      rebuildTargetsDryRun distDirLayout elaboratedShared+                           elaboratedPlan'++    let elaboratedPlan'' = improveInstallPlanWithUpToDatePackages+                             pkgsBuildStatus elaboratedPlan'++    buildOutcomes <-+      rebuildTargets verbosity+                     distDirLayout+                     (cabalStoreDirLayout cabalDirLayout)+                     elaboratedPlan''+                     elaboratedShared+                     pkgsBuildStatus+                     -- Avoid trying to use act-as-setup mode:+                     buildSettings { buildSettingNumJobs = 1 }++    return (elaboratedPlan'', buildOutcomes)++cleanProject :: FilePath -> IO ()+cleanProject testdir = do+    alreadyExists <- doesDirectoryExist distDir+    when alreadyExists $ removeDirectoryRecursive distDir+  where+    projectRoot    = ProjectRootImplicit (basedir </> testdir)+    distDirLayout  = defaultDistDirLayout projectRoot Nothing+    distDir        = distDirectory distDirLayout+++verbosity :: Verbosity+verbosity = minBound --normal --verbose --maxBound --minBound++++-------------------------------------------+-- Tasty integration to adjust the config+--++withProjectConfig :: (ProjectConfig -> TestTree) -> TestTree+withProjectConfig testtree =+    askOption $ \ghcPath ->+      testtree (mkProjectConfig ghcPath)++mkProjectConfig :: GhcPath -> ProjectConfig+mkProjectConfig (GhcPath ghcPath) =+    mempty {+      projectConfigShared = mempty {+        projectConfigHcPath = maybeToFlag ghcPath+      },+     projectConfigBuildOnly = mempty {+       projectConfigNumJobs = toFlag (Just 1)+     }+    }+  where+    maybeToFlag = maybe mempty toFlag+++data GhcPath = GhcPath (Maybe FilePath)+  deriving Typeable++instance IsOption GhcPath where+  defaultValue = GhcPath Nothing+  optionName   = Tagged "with-ghc"+  optionHelp   = Tagged "The ghc compiler to use"+  parseValue   = Just . GhcPath . Just++projectConfigOptionDescriptions :: [OptionDescription]+projectConfigOptionDescriptions = [Option (Proxy :: Proxy GhcPath)]+++---------------------------------------+-- HUint style utils for this context+--++expectException :: Exception e => String -> IO a -> IO e+expectException expected action = do+    res <- try action+    case res of+      Left  e -> return e+      Right _ -> throwIO $ HUnitFailure $ "expected an exception " ++ expected++expectPackagePreExisting :: ElaboratedInstallPlan -> BuildOutcomes -> PackageId+                         -> IO InstalledPackageInfo+expectPackagePreExisting plan buildOutcomes pkgid = do+    planpkg <- expectPlanPackage plan pkgid+    case (planpkg, InstallPlan.lookupBuildOutcome planpkg buildOutcomes) of+      (InstallPlan.PreExisting pkg, Nothing)+                       -> return pkg+      (_, buildResult) -> unexpectedBuildResult "PreExisting" planpkg buildResult++expectPackageConfigured :: ElaboratedInstallPlan -> BuildOutcomes -> PackageId+                        -> IO ElaboratedConfiguredPackage+expectPackageConfigured plan buildOutcomes pkgid = do+    planpkg <- expectPlanPackage plan pkgid+    case (planpkg, InstallPlan.lookupBuildOutcome planpkg buildOutcomes) of+      (InstallPlan.Configured pkg, Nothing)+                       -> return pkg+      (_, buildResult) -> unexpectedBuildResult "Configured" planpkg buildResult++expectPackageInstalled :: ElaboratedInstallPlan -> BuildOutcomes -> PackageId+                       -> IO (ElaboratedConfiguredPackage, BuildResult)+expectPackageInstalled plan buildOutcomes pkgid = do+    planpkg <- expectPlanPackage plan pkgid+    case (planpkg, InstallPlan.lookupBuildOutcome planpkg buildOutcomes) of+      (InstallPlan.Configured pkg, Just (Right result))+                       -> return (pkg, result)+      (_, buildResult) -> unexpectedBuildResult "Installed" planpkg buildResult++expectPackageFailed :: ElaboratedInstallPlan -> BuildOutcomes -> PackageId+                    -> IO (ElaboratedConfiguredPackage, BuildFailure)+expectPackageFailed plan buildOutcomes pkgid = do+    planpkg <- expectPlanPackage plan pkgid+    case (planpkg, InstallPlan.lookupBuildOutcome planpkg buildOutcomes) of+      (InstallPlan.Configured pkg, Just (Left failure))+                       -> return (pkg, failure)+      (_, buildResult) -> unexpectedBuildResult "Failed" planpkg buildResult++unexpectedBuildResult :: String -> ElaboratedPlanPackage+                      -> Maybe (Either BuildFailure BuildResult) -> IO a+unexpectedBuildResult expected planpkg buildResult =+    throwIO $ HUnitFailure $+         "expected to find " ++ display (packageId planpkg) ++ " in the "+      ++ expected ++ " state, but it is actually in the " ++ actual ++ " state."+  where+    actual = case (buildResult, planpkg) of+      (Nothing, InstallPlan.PreExisting{})       -> "PreExisting"+      (Nothing, InstallPlan.Configured{})        -> "Configured"+      (Just (Right _), InstallPlan.Configured{}) -> "Installed"+      (Just (Left  _), InstallPlan.Configured{}) -> "Failed"+      _                                          -> "Impossible!"++expectPlanPackage :: ElaboratedInstallPlan -> PackageId+                  -> IO ElaboratedPlanPackage+expectPlanPackage plan pkgid =+    case [ pkg+         | pkg <- InstallPlan.toList plan+         , packageId pkg == pkgid ] of+      [pkg] -> return pkg+      []    -> throwIO $ HUnitFailure $+                   "expected to find " ++ display pkgid+                ++ " in the install plan but it's not there"+      _     -> throwIO $ HUnitFailure $+                   "expected to find only one instance of " ++ display pkgid+                ++ " in the install plan but there's several"++expectBuildFailed :: BuildFailure -> IO ()+expectBuildFailed (BuildFailure _ (BuildFailed _)) = return ()+expectBuildFailed (BuildFailure _ reason) =+    assertFailure $ "expected BuildFailed, got " ++ show reason++---------------------------------------+-- Other utils+--++-- | Allow altering a file during a test, but then restore it afterwards+--+withFileFinallyRestore :: FilePath -> IO a -> IO a+withFileFinallyRestore file action = do+    copyFile file backup+    action `finally` renameFile backup file+  where+    backup = file <.> "backup"
+ tests/IntegrationTests2/build/keep-going/cabal.project view
@@ -0,0 +1,1 @@+packages: p q
+ tests/IntegrationTests2/build/keep-going/p/P.hs view
@@ -0,0 +1,4 @@+module P where++p :: Int+p = this_is_not_expected_to_compile
+ tests/IntegrationTests2/build/keep-going/p/p.cabal view
@@ -0,0 +1,8 @@+name: p+version: 0.1+build-type: Simple+cabal-version: >= 1.2++library+  exposed-modules: P+  build-depends: base
+ tests/IntegrationTests2/build/keep-going/q/Q.hs view
@@ -0,0 +1,4 @@+module Q where++q :: Int+q = 42
+ tests/IntegrationTests2/build/keep-going/q/q.cabal view
@@ -0,0 +1,9 @@+name: q+version: 0.1+build-type: Simple+cabal-version: >= 1.2++library+  exposed-modules: Q+  build-depends: base+
+ tests/IntegrationTests2/build/setup-custom1/A.hs view
@@ -0,0 +1,4 @@+module A where++a :: Int+a = 42
+ tests/IntegrationTests2/build/setup-custom1/Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain >> writeFile "marker" "ok"
+ tests/IntegrationTests2/build/setup-custom1/a.cabal view
@@ -0,0 +1,13 @@+name: a+version: 0.1+build-type: Custom+cabal-version: >= 1.10++-- explicit setup deps:+custom-setup+  setup-depends: base, Cabal >= 1.18++library+  exposed-modules: A+  build-depends: base+  default-language: Haskell2010
+ tests/IntegrationTests2/build/setup-custom2/A.hs view
@@ -0,0 +1,4 @@+module A where++a :: Int+a = 42
+ tests/IntegrationTests2/build/setup-custom2/Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain >> writeFile "marker" "ok"
+ tests/IntegrationTests2/build/setup-custom2/a.cabal view
@@ -0,0 +1,11 @@+name: a+version: 0.1+build-type: Custom+cabal-version: >= 1.10++-- no explicit setup deps++library+  exposed-modules: A+  build-depends: base+  default-language: Haskell2010
+ tests/IntegrationTests2/build/setup-simple/A.hs view
@@ -0,0 +1,4 @@+module A where++a :: Int+a = 42
+ tests/IntegrationTests2/build/setup-simple/Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ tests/IntegrationTests2/build/setup-simple/a.cabal view
@@ -0,0 +1,9 @@+name: a+version: 0.1+build-type: Simple+cabal-version: >= 1.10++library+  exposed-modules: A+  build-depends: base+  default-language: Haskell2010
+ tests/IntegrationTests2/exception/bad-config/cabal.project view
@@ -0,0 +1,4 @@+packages:++package foo+  ghc-location: bar
+ tests/IntegrationTests2/exception/build/Main.hs view
@@ -0,0 +1,1 @@+main = thisNameDoesNotExist
+ tests/IntegrationTests2/exception/build/a.cabal view
@@ -0,0 +1,8 @@+name: a+version: 1+build-type: Simple+cabal-version: >= 1.2++executable a+  main-is: Main.hs+  build-depends: base
+ tests/IntegrationTests2/exception/configure/a.cabal view
@@ -0,0 +1,9 @@+name: a+version: 1+build-type: Simple+-- This used to be a blank package with no components,+-- but I refactored new-build so that if a package has+-- no buildable components, we skip configuring it.+-- So put in a (failing) component so that we try to+-- configure.+executable a
+ tests/IntegrationTests2/exception/no-pkg/empty.in view
@@ -0,0 +1,1 @@+this is just here to ensure the source control creates the dir
+ tests/IntegrationTests2/exception/no-pkg2/cabal.project view
@@ -0,0 +1,1 @@+packages: ./
+ tests/IntegrationTests2/regression/3324/cabal.project view
@@ -0,0 +1,1 @@+packages: p q
+ tests/IntegrationTests2/regression/3324/p/P.hs view
@@ -0,0 +1,4 @@+module P where++p :: Int+p = 42
+ tests/IntegrationTests2/regression/3324/p/p.cabal view
@@ -0,0 +1,8 @@+name: p+version: 0.1+build-type: Simple+cabal-version: >= 1.2++library+  exposed-modules: P+  build-depends: base
+ tests/IntegrationTests2/regression/3324/q/Q.hs view
@@ -0,0 +1,6 @@+module Q where++import P++q :: Int+q = p
+ tests/IntegrationTests2/regression/3324/q/q.cabal view
@@ -0,0 +1,9 @@+name: q+version: 0.1+build-type: Simple+cabal-version: >= 1.2++library+  exposed-modules: Q+  build-depends: base+  -- missing a dep on p here, so expect failure initially
+ tests/IntegrationTests2/targets/all-disabled/cabal.project view
@@ -0,0 +1,1 @@+packages: ./
+ tests/IntegrationTests2/targets/all-disabled/p.cabal view
@@ -0,0 +1,23 @@+name: p+version: 0.1+build-type: Simple+cabal-version: >= 1.2++library+  exposed-modules: Q+  build-depends: base, filepath+  buildable: False++executable buildable-false+  main-is: Main.hs+  buildable: False++test-suite solver-disabled+  type: exitcode-stdio-1.0+  main-is: Test.hs+  build-depends: a-package-that-does-not-exist++benchmark user-disabled+  type: exitcode-stdio-1.0+  main-is: Test.hs+
+ tests/IntegrationTests2/targets/benchmarks-disabled/cabal.project view
@@ -0,0 +1,1 @@+packages: ./ ./q/
+ tests/IntegrationTests2/targets/benchmarks-disabled/p.cabal view
@@ -0,0 +1,15 @@+name: p+version: 0.1+build-type: Simple+cabal-version: >= 1.10++benchmark solver-disabled+  type: exitcode-stdio-1.0+  main-is: Test.hs+  build-depends: a-package-that-does-not-exist++benchmark user-disabled+  type: exitcode-stdio-1.0+  main-is: Test.hs+  build-depends: base+
+ tests/IntegrationTests2/targets/benchmarks-disabled/q/q.cabal view
@@ -0,0 +1,10 @@+name: q+version: 0.1+build-type: Simple+cabal-version: >= 1.10++benchmark buildable-false+  type: exitcode-stdio-1.0+  main-is: Main.hs+  buildable: False+
+ tests/IntegrationTests2/targets/complex/cabal.project view
@@ -0,0 +1,1 @@+packages: q/
+ tests/IntegrationTests2/targets/complex/q/Q.hs view
+ tests/IntegrationTests2/targets/complex/q/q.cabal view
@@ -0,0 +1,22 @@+name: q+version: 0.1+build-type: Simple+cabal-version: >= 1.2++library+  exposed-modules: Q+  build-depends: base, filepath++executable buildable-false+  main-is: Main.hs+  buildable: False++test-suite solver-disabled+  type: exitcode-stdio-1.0+  main-is: Test.hs+  build-depends: a-package-that-does-not-exist++benchmark user-disabled+  type: exitcode-stdio-1.0+  main-is: Test.hs+
+ tests/IntegrationTests2/targets/empty-pkg/cabal.project view
@@ -0,0 +1,1 @@+packages: ./
+ tests/IntegrationTests2/targets/empty-pkg/p.cabal view
@@ -0,0 +1,5 @@+name: p+version: 0.1+build-type: Simple+cabal-version: >= 1.2+
+ tests/IntegrationTests2/targets/empty/cabal.project view
@@ -0,0 +1,1 @@+packages:
+ tests/IntegrationTests2/targets/empty/foo.hs view
+ tests/IntegrationTests2/targets/exes-disabled/cabal.project view
@@ -0,0 +1,1 @@+packages: p/ q/
+ tests/IntegrationTests2/targets/exes-disabled/p/p.cabal view
@@ -0,0 +1,9 @@+name: p+version: 0.1+build-type: Simple+cabal-version: >= 1.2++executable p+  main-is: P.hs+  build-depends: base+  buildable: False
+ tests/IntegrationTests2/targets/exes-disabled/q/q.cabal view
@@ -0,0 +1,9 @@+name: q+version: 0.1+build-type: Simple+cabal-version: >= 1.2++executable q+  main-is: Q.hs+  build-depends: base+  buildable: False
+ tests/IntegrationTests2/targets/libs-disabled/cabal.project view
@@ -0,0 +1,1 @@+packages: p/ q/
+ tests/IntegrationTests2/targets/libs-disabled/p/p.cabal view
@@ -0,0 +1,9 @@+name: p+version: 0.1+build-type: Simple+cabal-version: >= 1.2++library+  exposed-modules: P+  build-depends: base+  buildable: False
+ tests/IntegrationTests2/targets/libs-disabled/q/q.cabal view
@@ -0,0 +1,9 @@+name: q+version: 0.1+build-type: Simple+cabal-version: >= 1.2++library+  exposed-modules: Q+  build-depends: base+  buildable: False
+ tests/IntegrationTests2/targets/multiple-exes/cabal.project view
@@ -0,0 +1,1 @@+packages: ./
+ tests/IntegrationTests2/targets/multiple-exes/p.cabal view
@@ -0,0 +1,12 @@+name: p+version: 0.1+build-type: Simple+cabal-version: >= 1.10++executable p1+  main-is:  P1.hs+  build-depends: base++executable p2+  main-is:  P2.hs+  build-depends: base
+ tests/IntegrationTests2/targets/multiple-libs/cabal.project view
@@ -0,0 +1,1 @@+packages: p/ q/
+ tests/IntegrationTests2/targets/multiple-libs/p/p.cabal view
@@ -0,0 +1,8 @@+name: p+version: 0.1+build-type: Simple+cabal-version: >= 1.2++library+  exposed-modules: P+  build-depends: base
+ tests/IntegrationTests2/targets/multiple-libs/q/q.cabal view
@@ -0,0 +1,8 @@+name: q+version: 0.1+build-type: Simple+cabal-version: >= 1.2++library+  exposed-modules: Q+  build-depends: base
+ tests/IntegrationTests2/targets/multiple-tests/cabal.project view
@@ -0,0 +1,1 @@+packages: ./
+ tests/IntegrationTests2/targets/multiple-tests/p.cabal view
@@ -0,0 +1,14 @@+name: p+version: 0.1+build-type: Simple+cabal-version: >= 1.10++test-suite p1+  type: exitcode-stdio-1.0+  main-is:  P1.hs+  build-depends: base++test-suite p2+  type: exitcode-stdio-1.0+  main-is:  P2.hs+  build-depends: base
+ tests/IntegrationTests2/targets/simple/P.hs view
+ tests/IntegrationTests2/targets/simple/cabal.project view
@@ -0,0 +1,1 @@+packages: ./ q/
+ tests/IntegrationTests2/targets/simple/p.cabal view
@@ -0,0 +1,12 @@+name: p+version: 0.1+build-type: Simple+cabal-version: >= 1.2++library+  exposed-modules: P+  build-depends: base++executable pexe+  main-is: Main.hs+  other-modules: PMain
+ tests/IntegrationTests2/targets/simple/q/QQ.hs view
+ tests/IntegrationTests2/targets/simple/q/q.cabal view
@@ -0,0 +1,12 @@+name: q+version: 0.1+build-type: Simple+cabal-version: >= 1.2++library+  exposed-modules: QQ+  build-depends: base++executable qexe+  main-is: Main.hs+  other-modules: QMain
+ tests/IntegrationTests2/targets/test-only/p.cabal view
@@ -0,0 +1,9 @@+name: p+version: 0.1+build-type: Simple+cabal-version: >= 1.2++test-suite pexe+  type: exitcode-stdio-1.0+  main-is: Main.hs+  other-modules: PMain
+ tests/IntegrationTests2/targets/tests-disabled/cabal.project view
@@ -0,0 +1,1 @@+packages: ./ ./q/
+ tests/IntegrationTests2/targets/tests-disabled/p.cabal view
@@ -0,0 +1,15 @@+name: p+version: 0.1+build-type: Simple+cabal-version: >= 1.10++test-suite solver-disabled+  type: exitcode-stdio-1.0+  main-is: Test.hs+  build-depends: a-package-that-does-not-exist++test-suite user-disabled+  type: exitcode-stdio-1.0+  main-is: Test.hs+  build-depends: base+
+ tests/IntegrationTests2/targets/tests-disabled/q/q.cabal view
@@ -0,0 +1,10 @@+name: q+version: 0.1+build-type: Simple+cabal-version: >= 1.10++test-suite buildable-false+  type: exitcode-stdio-1.0+  main-is: Main.hs+  buildable: False+
+ tests/IntegrationTests2/targets/variety/cabal.project view
@@ -0,0 +1,1 @@+packages: ./
+ tests/IntegrationTests2/targets/variety/p.cabal view
@@ -0,0 +1,27 @@+name: p+version: 0.1+build-type: Simple+cabal-version: >= 1.10++library+  exposed-modules: P+  build-depends: base++foreign-library libp+  type: native-shared+  other-modules: FLib++executable an-exe+  main-is: Main.hs+  other-modules: AModule++test-suite a-testsuite+  type: exitcode-stdio-1.0+  main-is: Test.hs+  other-modules: AModule  ++benchmark a-benchmark+  type: exitcode-stdio-1.0+  main-is: Test.hs+  other-modules: AModule+
+ tests/MemoryUsageTests.hs view
@@ -0,0 +1,15 @@+module MemoryUsageTests where++import Test.Tasty++import qualified UnitTests.Distribution.Solver.Modular.MemoryUsage++tests :: TestTree+tests =+  testGroup "Memory Usage"+  [ testGroup "UnitTests.Distribution.Solver.Modular.MemoryUsage"+        UnitTests.Distribution.Solver.Modular.MemoryUsage.tests+  ]++main :: IO ()+main = defaultMain tests
+ tests/SolverQuickCheck.hs view
@@ -0,0 +1,16 @@+module SolverQuickCheck where++import Test.Tasty++import qualified UnitTests.Distribution.Solver.Modular.QuickCheck+++tests :: TestTree+tests =+  testGroup "Solver QuickCheck"+  [ testGroup "UnitTests.Distribution.Solver.Modular.QuickCheck"+        UnitTests.Distribution.Solver.Modular.QuickCheck.tests+  ]++main :: IO ()+main = defaultMain tests
tests/UnitTests.hs view
@@ -1,31 +1,31 @@ {-# LANGUAGE ScopedTypeVariables #-} -module Main-       where+module UnitTests where  import Test.Tasty -import Control.Monad-import Data.Time.Clock-import System.FilePath- import Distribution.Simple.Utils import Distribution.Verbosity -import Distribution.Client.Compat.Time+import Distribution.Compat.Time -import qualified UnitTests.Distribution.Client.Compat.Time-import qualified UnitTests.Distribution.Client.Dependency.Modular.PSQ-import qualified UnitTests.Distribution.Client.Dependency.Modular.Solver+import qualified UnitTests.Distribution.Solver.Modular.PSQ+import qualified UnitTests.Distribution.Solver.Modular.WeightedPSQ+import qualified UnitTests.Distribution.Solver.Modular.Solver+import qualified UnitTests.Distribution.Solver.Modular.RetryLog import qualified UnitTests.Distribution.Client.FileMonitor import qualified UnitTests.Distribution.Client.Glob import qualified UnitTests.Distribution.Client.GZipUtils import qualified UnitTests.Distribution.Client.Sandbox import qualified UnitTests.Distribution.Client.Sandbox.Timestamp+import qualified UnitTests.Distribution.Client.Store import qualified UnitTests.Distribution.Client.Tar import qualified UnitTests.Distribution.Client.Targets import qualified UnitTests.Distribution.Client.UserConfig import qualified UnitTests.Distribution.Client.ProjectConfig+import qualified UnitTests.Distribution.Client.JobControl+import qualified UnitTests.Distribution.Client.IndexUtils.Timestamp+import qualified UnitTests.Distribution.Client.InstallPlan  import UnitTests.Options @@ -38,12 +38,14 @@                     else mtimeChangeCalibrated   in   testGroup "Unit Tests"-  [ testGroup "UnitTests.Distribution.Client.Compat.Time" $-        UnitTests.Distribution.Client.Compat.Time.tests mtimeChange-  , testGroup "UnitTests.Distribution.Client.Dependency.Modular.PSQ"-        UnitTests.Distribution.Client.Dependency.Modular.PSQ.tests-  , testGroup "UnitTests.Distribution.Client.Dependency.Modular.Solver"-        UnitTests.Distribution.Client.Dependency.Modular.Solver.tests+  [ testGroup "UnitTests.Distribution.Solver.Modular.PSQ"+        UnitTests.Distribution.Solver.Modular.PSQ.tests+  , testGroup "UnitTests.Distribution.Solver.Modular.WeightedPSQ"+        UnitTests.Distribution.Solver.Modular.WeightedPSQ.tests+  , testGroup "UnitTests.Distribution.Solver.Modular.Solver"+        UnitTests.Distribution.Solver.Modular.Solver.tests+  , testGroup "UnitTests.Distribution.Solver.Modular.RetryLog"+        UnitTests.Distribution.Solver.Modular.RetryLog.tests   , testGroup "UnitTests.Distribution.Client.FileMonitor" $         UnitTests.Distribution.Client.FileMonitor.tests mtimeChange   , testGroup "UnitTests.Distribution.Client.Glob"@@ -54,6 +56,8 @@        UnitTests.Distribution.Client.Sandbox.tests   , testGroup "Distribution.Client.Sandbox.Timestamp"        UnitTests.Distribution.Client.Sandbox.Timestamp.tests+  , testGroup "Distribution.Client.Store"+       UnitTests.Distribution.Client.Store.tests   , testGroup "Distribution.Client.Tar"        UnitTests.Distribution.Client.Tar.tests   , testGroup "Distribution.Client.Targets"@@ -62,45 +66,25 @@        UnitTests.Distribution.Client.UserConfig.tests   , testGroup "UnitTests.Distribution.Client.ProjectConfig"        UnitTests.Distribution.Client.ProjectConfig.tests+  , testGroup "UnitTests.Distribution.Client.JobControl"+       UnitTests.Distribution.Client.JobControl.tests+  , testGroup "UnitTests.Distribution.Client.IndexUtils.Timestamp"+       UnitTests.Distribution.Client.IndexUtils.Timestamp.tests+  , testGroup "UnitTests.Distribution.Client.InstallPlan"+       UnitTests.Distribution.Client.InstallPlan.tests   ]  main :: IO () main = do-  mtimeChangeDelay <- calibrateMtimeChangeDelay+  (mtimeChange, mtimeChange') <- calibrateMtimeChangeDelay+  let toMillis :: Int -> Double+      toMillis x = fromIntegral x / 1000.0+  notice normal $ "File modification time resolution calibration completed, "+    ++ "maximum delay observed: "+    ++ (show . toMillis $ mtimeChange ) ++ " ms. "+    ++ "Will be using delay of " ++ (show . toMillis $ mtimeChange')+    ++ " for test runs."   defaultMainWithIngredients          (includingOptions extraOptions : defaultIngredients)-         (tests mtimeChangeDelay)---- Based on code written by Neill Mitchell for Shake. See--- 'sleepFileTimeCalibrate' in 'Test.Type'. The returned delay is never smaller--- than 10 ms, but never larger than 1 second.-calibrateMtimeChangeDelay :: IO Int-calibrateMtimeChangeDelay = do-  withTempDirectory silent "." "calibration-" $ \dir -> do-    let fileName = dir </> "probe"-    mtimes <- forM [1..25] $ \(i::Int) -> time $ do-      writeFile fileName $ show i-      t0 <- getModTime fileName-      let spin j = do-            writeFile fileName $ show (i,j)-            t1 <- getModTime fileName-            unless (t0 < t1) (spin $ j + 1)-      spin (0::Int)-    let mtimeChange  = maximum mtimes-        mtimeChange' = min 1000000 $ (max 10000 mtimeChange) * 2-    notice normal $ "File modification time resolution calibration completed, "-      ++ "maximum delay observed: "-      ++ (show . toMillis $ mtimeChange ) ++ " ms. "-      ++ "Will be using delay of " ++ (show . toMillis $ mtimeChange')-      ++ " for test runs."-    return mtimeChange'-  where-    toMillis :: Int -> Double-    toMillis x = fromIntegral x / 1000.0+         (tests mtimeChange') -    time :: IO () -> IO Int-    time act = do-      t0 <- getCurrentTime-      act-      t1 <- getCurrentTime-      return . ceiling $! (t1 `diffUTCTime` t0) * 1e6 -- microseconds
tests/UnitTests/Distribution/Client/ArbitraryInstances.hs view
@@ -21,6 +21,7 @@ import Control.Monad  import Distribution.Version+import Distribution.Types.Dependency import Distribution.Package import Distribution.System import Distribution.Verbosity@@ -30,6 +31,8 @@  import Distribution.Utils.NubList +import Distribution.Client.IndexUtils.Timestamp+ import Test.QuickCheck  @@ -67,7 +70,6 @@ arbitraryShortToken :: Gen String arbitraryShortToken = getShortToken <$> arbitrary -#if !MIN_VERSION_QuickCheck(2,9,0) instance Arbitrary Version where   arbitrary = do     branch <- shortListOf1 4 $@@ -75,14 +77,11 @@                           ,(3, return 1)                           ,(2, return 2)                           ,(1, return 3)]-    return (Version branch []) -- deliberate []+    return (mkVersion branch)     where -  shrink (Version branch []) =-    [ Version branch' [] | branch' <- shrink branch, not (null branch') ]-  shrink (Version branch _tags) =-    [ Version branch [] ]-#endif+  shrink ver = [ mkVersion branch' | branch' <- shrink (versionNumbers ver)+                                   , not (null branch') ]  instance Arbitrary VersionRange where   arbitrary = canonicaliseVersionRange <$> sized verRangeExp@@ -113,7 +112,7 @@       canonicaliseVersionRange = fromVersionIntervals . toVersionIntervals  instance Arbitrary PackageName where-    arbitrary = PackageName . intercalate "-" <$> shortListOf1 2 nameComponent+    arbitrary = mkPackageName . intercalate "-" <$> shortListOf1 2 nameComponent       where         nameComponent = shortListOf1 5 (elements packageChars)                         `suchThat` (not . all isDigit)@@ -172,3 +171,10 @@     arbitrary = NoShrink <$> arbitrary     shrink _  = [] +instance Arbitrary Timestamp where+    arbitrary = (maybe (toEnum 0) id . epochTimeToTimestamp) <$> arbitrary++instance Arbitrary IndexState where+    arbitrary = frequency [ (1, pure IndexStateHead)+                          , (50, IndexStateTime <$> arbitrary)+                          ]
− tests/UnitTests/Distribution/Client/Compat/Time.hs
@@ -1,49 +0,0 @@-module UnitTests.Distribution.Client.Compat.Time (tests) where--import Control.Concurrent (threadDelay)-import System.FilePath--import Distribution.Simple.Utils (withTempDirectory)-import Distribution.Verbosity--import Distribution.Client.Compat.Time--import Test.Tasty-import Test.Tasty.HUnit--tests :: Int -> [TestTree]-tests mtimeChange =-  [ testCase "getModTime has sub-second resolution" $ getModTimeTest mtimeChange-  , testCase "getCurTime works as expected"         $ getCurTimeTest mtimeChange-  ]--getModTimeTest :: Int -> Assertion-getModTimeTest mtimeChange =-  withTempDirectory silent "." "getmodtime-" $ \dir -> do-    let fileName = dir </> "foo"-    writeFile fileName "bar"-    t0 <- getModTime fileName-    threadDelay mtimeChange-    writeFile fileName "baz"-    t1 <- getModTime fileName-    assertBool "expected different file mtimes" (t1 > t0)---getCurTimeTest :: Int -> Assertion-getCurTimeTest mtimeChange =-  withTempDirectory silent "." "getmodtime-" $ \dir -> do-    let fileName = dir </> "foo"-    writeFile fileName "bar"-    t0 <- getModTime fileName-    threadDelay mtimeChange-    t1 <- getCurTime-    assertBool("expected file mtime (" ++ show t0-               ++ ") to be earlier than current time (" ++ show t1 ++ ")")-      (t0 < t1)--    threadDelay mtimeChange-    writeFile fileName "baz"-    t2 <- getModTime fileName-    assertBool ("expected current time (" ++ show t1-                ++ ") to be earlier than file mtime (" ++ show t2 ++ ")")-      (t1 < t2)
− tests/UnitTests/Distribution/Client/Dependency/Modular/DSL.hs
@@ -1,418 +0,0 @@-{-# LANGUAGE RecordWildCards #-}--- | DSL for testing the modular solver-module UnitTests.Distribution.Client.Dependency.Modular.DSL (-    ExampleDependency(..)-  , Dependencies(..)-  , ExTest(..)-  , ExPreference(..)-  , ExampleDb-  , ExampleVersionRange-  , ExamplePkgVersion-  , exAv-  , exInst-  , exFlag-  , exResolve-  , extractInstallPlan-  , withSetupDeps-  , withTest-  , withTests-  ) where---- base-import Data.Either (partitionEithers)-import Data.Maybe (catMaybes)-import Data.List (nub)-import Data.Monoid-import Data.Version-import qualified Data.Map as Map---- Cabal-import qualified Distribution.Compiler             as C-import qualified Distribution.InstalledPackageInfo as C-import qualified Distribution.Package              as C-  hiding (HasUnitId(..))-import qualified Distribution.PackageDescription   as C-import qualified Distribution.Simple.PackageIndex  as C.PackageIndex-import qualified Distribution.System               as C-import qualified Distribution.Version              as C-import Language.Haskell.Extension (Extension(..), Language)---- cabal-install-import Distribution.Client.ComponentDeps (ComponentDeps)-import Distribution.Client.Dependency-import Distribution.Client.Dependency.Types-import Distribution.Client.Types-import qualified Distribution.Client.InstallPlan   as CI.InstallPlan-import qualified Distribution.Client.PackageIndex  as CI.PackageIndex-import qualified Distribution.Client.PkgConfigDb   as PC-import qualified Distribution.Client.ComponentDeps as CD--{--------------------------------------------------------------------------------  Example package database DSL--  In order to be able to set simple examples up quickly, we define a very-  simple version of the package database here explicitly designed for use in-  tests.--  The design of `ExampleDb` takes the perspective of the solver, not the-  perspective of the package DB. This makes it easier to set up tests for-  various parts of the solver, but makes the mapping somewhat awkward,  because-  it means we first map from "solver perspective" `ExampleDb` to the package-  database format, and then the modular solver internally in `IndexConversion`-  maps this back to the solver specific data structures.--  IMPLEMENTATION NOTES-  ----------------------  TODO: Perhaps these should be made comments of the corresponding data type-  definitions. For now these are just my own conclusions and may be wrong.--  * The difference between `GenericPackageDescription` and `PackageDescription`-    is that `PackageDescription` describes a particular _configuration_ of a-    package (for instance, see documentation for `checkPackage`). A-    `GenericPackageDescription` can be turned into a `PackageDescription` in-    two ways:--      a. `finalizePackageDescription` does the proper translation, by taking-         into account the platform, available dependencies, etc. and picks a-         flag assignment (or gives an error if no flag assignment can be found)-      b. `flattenPackageDescription` ignores flag assignment and just joins all-         components together.--    The slightly odd thing is that a `GenericPackageDescription` contains a-    `PackageDescription` as a field; both of the above functions do the same-    thing: they take the embedded `PackageDescription` as a basis for the result-    value, but override `library`, `executables`, `testSuites`, `benchmarks`-    and `buildDepends`.-  * The `condTreeComponents` fields of a `CondTree` is a list of triples-    `(condition, then-branch, else-branch)`, where the `else-branch` is-    optional.--------------------------------------------------------------------------------}--type ExamplePkgName    = String-type ExamplePkgVersion = Int-type ExamplePkgHash    = String  -- for example "installed" packages-type ExampleFlagName   = String-type ExampleTestName   = String-type ExampleVersionRange = C.VersionRange-data Dependencies = NotBuildable | Buildable [ExampleDependency]--data ExampleDependency =-    -- | Simple dependency on any version-    ExAny ExamplePkgName--    -- | Simple dependency on a fixed version-  | ExFix ExamplePkgName ExamplePkgVersion--    -- | Dependencies indexed by a flag-  | ExFlag ExampleFlagName Dependencies Dependencies--    -- | Dependency on a language extension-  | ExExt Extension--    -- | Dependency on a language version-  | ExLang Language--    -- | Dependency on a pkg-config package-  | ExPkg (ExamplePkgName, ExamplePkgVersion)--data ExTest = ExTest ExampleTestName [ExampleDependency]--exFlag :: ExampleFlagName -> [ExampleDependency] -> [ExampleDependency]-       -> ExampleDependency-exFlag n t e = ExFlag n (Buildable t) (Buildable e)--data ExPreference = ExPref String ExampleVersionRange--data ExampleAvailable = ExAv {-    exAvName    :: ExamplePkgName-  , exAvVersion :: ExamplePkgVersion-  , exAvDeps    :: ComponentDeps [ExampleDependency]-  }--exAv :: ExamplePkgName -> ExamplePkgVersion -> [ExampleDependency]-     -> ExampleAvailable-exAv n v ds = ExAv { exAvName = n, exAvVersion = v-                   , exAvDeps = CD.fromLibraryDeps ds }--withSetupDeps :: ExampleAvailable -> [ExampleDependency] -> ExampleAvailable-withSetupDeps ex setupDeps = ex {-      exAvDeps = exAvDeps ex <> CD.fromSetupDeps setupDeps-    }--withTest :: ExampleAvailable -> ExTest -> ExampleAvailable-withTest ex test = withTests ex [test]--withTests :: ExampleAvailable -> [ExTest] -> ExampleAvailable-withTests ex tests =-  let testCDs = CD.fromList [(CD.ComponentTest name, deps)-                            | ExTest name deps <- tests]-  in ex { exAvDeps = exAvDeps ex <> testCDs }--data ExampleInstalled = ExInst {-    exInstName         :: ExamplePkgName-  , exInstVersion      :: ExamplePkgVersion-  , exInstHash         :: ExamplePkgHash-  , exInstBuildAgainst :: [ExampleInstalled]-  }--exInst :: ExamplePkgName -> ExamplePkgVersion -> ExamplePkgHash-       -> [ExampleInstalled] -> ExampleInstalled-exInst = ExInst--type ExampleDb = [Either ExampleInstalled ExampleAvailable]--type DependencyTree a = C.CondTree C.ConfVar [C.Dependency] a--exDbPkgs :: ExampleDb -> [ExamplePkgName]-exDbPkgs = map (either exInstName exAvName)--exAvSrcPkg :: ExampleAvailable -> SourcePackage-exAvSrcPkg ex =-    let (libraryDeps, exts, mlang, pcpkgs) = splitTopLevel (CD.libraryDeps (exAvDeps ex))-        testSuites = [(name, deps) | (CD.ComponentTest name, deps) <- CD.toList (exAvDeps ex)]-    in SourcePackage {-           packageInfoId        = exAvPkgId ex-         , packageSource        = LocalTarballPackage "<<path>>"-         , packageDescrOverride = Nothing-         , packageDescription   = C.GenericPackageDescription {-               C.packageDescription = C.emptyPackageDescription {-                   C.package        = exAvPkgId ex-                 , C.library        = error "not yet configured: library"-                 , C.executables    = error "not yet configured: executables"-                 , C.testSuites     = error "not yet configured: testSuites"-                 , C.benchmarks     = error "not yet configured: benchmarks"-                 , C.buildDepends   = error "not yet configured: buildDepends"-                 , C.setupBuildInfo = Just C.SetupBuildInfo {-                       C.setupDepends = mkSetupDeps (CD.setupDeps (exAvDeps ex)),-                       C.defaultSetupDepends = False-                     }-                 }-             , C.genPackageFlags = nub $ concatMap extractFlags $-                                   CD.libraryDeps (exAvDeps ex) ++ concatMap snd testSuites-             , C.condLibrary     = Just $ mkCondTree (extsLib exts <> langLib mlang <> pcpkgLib pcpkgs)-                                                     disableLib-                                                     (Buildable libraryDeps)-             , C.condExecutables = []-             , C.condTestSuites  =-                 let mkTree = mkCondTree mempty disableTest . Buildable-                 in map (\(t, deps) -> (t, mkTree deps)) testSuites-             , C.condBenchmarks  = []-             }-         }-  where-    -- Split the set of dependencies into the set of dependencies of the library,-    -- the dependencies of the test suites and extensions.-    splitTopLevel :: [ExampleDependency]-                  -> ( [ExampleDependency]-                     , [Extension]-                     , Maybe Language-                     , [(ExamplePkgName, ExamplePkgVersion)] -- pkg-config-                     )-    splitTopLevel [] =-        ([], [], Nothing, [])-    splitTopLevel (ExExt ext:deps) =-      let (other, exts, lang, pcpkgs) = splitTopLevel deps-      in (other, ext:exts, lang, pcpkgs)-    splitTopLevel (ExLang lang:deps) =-        case splitTopLevel deps of-            (other, exts, Nothing, pcpkgs) -> (other, exts, Just lang, pcpkgs)-            _ -> error "Only 1 Language dependency is supported"-    splitTopLevel (ExPkg pkg:deps) =-      let (other, exts, lang, pcpkgs) = splitTopLevel deps-      in (other, exts, lang, pkg:pcpkgs)-    splitTopLevel (dep:deps) =-      let (other, exts, lang, pcpkgs) = splitTopLevel deps-      in (dep:other, exts, lang, pcpkgs)--    -- Extract the total set of flags used-    extractFlags :: ExampleDependency -> [C.Flag]-    extractFlags (ExAny _)      = []-    extractFlags (ExFix _ _)    = []-    extractFlags (ExFlag f a b) = C.MkFlag {-                                      C.flagName        = C.FlagName f-                                    , C.flagDescription = ""-                                    , C.flagDefault     = True-                                    , C.flagManual      = False-                                    }-                                : concatMap extractFlags (deps a ++ deps b)-      where-        deps :: Dependencies -> [ExampleDependency]-        deps NotBuildable = []-        deps (Buildable ds) = ds-    extractFlags (ExExt _)      = []-    extractFlags (ExLang _)     = []-    extractFlags (ExPkg _)      = []--    mkCondTree :: Monoid a => a -> (a -> a) -> Dependencies -> DependencyTree a-    mkCondTree x dontBuild NotBuildable =-      C.CondNode {-             C.condTreeData        = dontBuild x-           , C.condTreeConstraints = []-           , C.condTreeComponents  = []-           }-    mkCondTree x dontBuild (Buildable deps) =-      let (directDeps, flaggedDeps) = splitDeps deps-      in C.CondNode {-             C.condTreeData        = x -- Necessary for language extensions-           , C.condTreeConstraints = map mkDirect directDeps-           , C.condTreeComponents  = map (mkFlagged dontBuild) flaggedDeps-           }--    mkDirect :: (ExamplePkgName, Maybe ExamplePkgVersion) -> C.Dependency-    mkDirect (dep, Nothing) = C.Dependency (C.PackageName dep) C.anyVersion-    mkDirect (dep, Just n)  = C.Dependency (C.PackageName dep) (C.thisVersion v)-      where-        v = Version [n, 0, 0] []--    mkFlagged :: Monoid a-              => (a -> a)-              -> (ExampleFlagName, Dependencies, Dependencies)-              -> (C.Condition C.ConfVar-                 , DependencyTree a, Maybe (DependencyTree a))-    mkFlagged dontBuild (f, a, b) = ( C.Var (C.Flag (C.FlagName f))-                                    , mkCondTree mempty dontBuild a-                                    , Just (mkCondTree mempty dontBuild b)-                                    )--    -- Split a set of dependencies into direct dependencies and flagged-    -- dependencies. A direct dependency is a tuple of the name of package and-    -- maybe its version (no version means any version) meant to be converted-    -- to a 'C.Dependency' with 'mkDirect' for example. A flagged dependency is-    -- the set of dependencies guarded by a flag.-    ---    -- TODO: Take care of flagged language extensions and language flavours.-    splitDeps :: [ExampleDependency]-              -> ( [(ExamplePkgName, Maybe Int)]-                 , [(ExampleFlagName, Dependencies, Dependencies)]-                 )-    splitDeps [] =-      ([], [])-    splitDeps (ExAny p:deps) =-      let (directDeps, flaggedDeps) = splitDeps deps-      in ((p, Nothing):directDeps, flaggedDeps)-    splitDeps (ExFix p v:deps) =-      let (directDeps, flaggedDeps) = splitDeps deps-      in ((p, Just v):directDeps, flaggedDeps)-    splitDeps (ExFlag f a b:deps) =-      let (directDeps, flaggedDeps) = splitDeps deps-      in (directDeps, (f, a, b):flaggedDeps)-    splitDeps (_:deps) = splitDeps deps--    -- Currently we only support simple setup dependencies-    mkSetupDeps :: [ExampleDependency] -> [C.Dependency]-    mkSetupDeps deps =-      let (directDeps, []) = splitDeps deps in map mkDirect directDeps--    -- A 'C.Library' with just the given extensions in its 'BuildInfo'-    extsLib :: [Extension] -> C.Library-    extsLib es = mempty { C.libBuildInfo = mempty { C.otherExtensions = es } }--    -- A 'C.Library' with just the given extensions in its 'BuildInfo'-    langLib :: Maybe Language -> C.Library-    langLib (Just lang) = mempty { C.libBuildInfo = mempty { C.defaultLanguage = Just lang } }-    langLib _ = mempty--    disableLib :: C.Library -> C.Library-    disableLib lib =-        lib { C.libBuildInfo = (C.libBuildInfo lib) { C.buildable = False }}--    disableTest :: C.TestSuite -> C.TestSuite-    disableTest test =-        test { C.testBuildInfo = (C.testBuildInfo test) { C.buildable = False }}--    -- A 'C.Library' with just the given pkgconfig-depends in its 'BuildInfo'-    pcpkgLib :: [(ExamplePkgName, ExamplePkgVersion)] -> C.Library-    pcpkgLib ds = mempty { C.libBuildInfo = mempty { C.pkgconfigDepends = [mkDirect (n, (Just v)) | (n,v) <- ds] } }--exAvPkgId :: ExampleAvailable -> C.PackageIdentifier-exAvPkgId ex = C.PackageIdentifier {-      pkgName    = C.PackageName (exAvName ex)-    , pkgVersion = Version [exAvVersion ex, 0, 0] []-    }--exInstInfo :: ExampleInstalled -> C.InstalledPackageInfo-exInstInfo ex = C.emptyInstalledPackageInfo {-      C.installedUnitId    = C.mkUnitId (exInstHash ex)-    , C.sourcePackageId    = exInstPkgId ex-    , C.depends            = map (C.mkUnitId . exInstHash)-                                 (exInstBuildAgainst ex)-    }--exInstPkgId :: ExampleInstalled -> C.PackageIdentifier-exInstPkgId ex = C.PackageIdentifier {-      pkgName    = C.PackageName (exInstName ex)-    , pkgVersion = Version [exInstVersion ex, 0, 0] []-    }--exAvIdx :: [ExampleAvailable] -> CI.PackageIndex.PackageIndex SourcePackage-exAvIdx = CI.PackageIndex.fromList . map exAvSrcPkg--exInstIdx :: [ExampleInstalled] -> C.PackageIndex.InstalledPackageIndex-exInstIdx = C.PackageIndex.fromList . map exInstInfo--exResolve :: ExampleDb-          -- List of extensions supported by the compiler, or Nothing if unknown.-          -> Maybe [Extension]-          -- List of languages supported by the compiler, or Nothing if unknown.-          -> Maybe [Language]-          -> PC.PkgConfigDb-          -> [ExamplePkgName]-          -> Bool-          -> [ExPreference]-          -> ([String], Either String CI.InstallPlan.InstallPlan)-exResolve db exts langs pkgConfigDb targets indepGoals prefs = runProgress $-    resolveDependencies C.buildPlatform-                        compiler pkgConfigDb-                        Modular-                        params-  where-    defaultCompiler = C.unknownCompilerInfo C.buildCompilerId C.NoAbiTag-    compiler = defaultCompiler { C.compilerInfoExtensions = exts-                               , C.compilerInfoLanguages  = langs-                               }-    (inst, avai) = partitionEithers db-    instIdx      = exInstIdx inst-    avaiIdx      = SourcePackageDb {-                       packageIndex       = exAvIdx avai-                     , packagePreferences = Map.empty-                     }-    enableTests  = fmap (\p -> PackageConstraintStanzas-                              (C.PackageName p) [TestStanzas])-                       (exDbPkgs db)-    targets'     = fmap (\p -> NamedPackage (C.PackageName p) []) targets-    params       =   addPreferences (fmap toPref prefs)-                   $ addConstraints (fmap toLpc enableTests)-                   $ (standardInstallPolicy instIdx avaiIdx targets') {-                     depResolverIndependentGoals = indepGoals-                     }-    toLpc     pc = LabeledPackageConstraint pc ConstraintSourceUnknown-    toPref (ExPref n v) = PackageVersionPreference (C.PackageName n) v--extractInstallPlan :: CI.InstallPlan.InstallPlan-                   -> [(ExamplePkgName, ExamplePkgVersion)]-extractInstallPlan = catMaybes . map confPkg . CI.InstallPlan.toList-  where-    confPkg :: CI.InstallPlan.PlanPackage -> Maybe (String, Int)-    confPkg (CI.InstallPlan.Configured pkg) = Just $ srcPkg pkg-    confPkg _                               = Nothing--    srcPkg :: ConfiguredPackage -> (String, Int)-    srcPkg (ConfiguredPackage pkg _flags _stanzas _deps) =-      let C.PackageIdentifier (C.PackageName p) (Version (n:_) _) =-            packageInfoId pkg-      in (p, n)--{--------------------------------------------------------------------------------  Auxiliary--------------------------------------------------------------------------------}---- | Run Progress computation------ Like `runLog`, but for the more general `Progress` type.-runProgress :: Progress step e a -> ([step], Either e a)-runProgress = go-  where-    go (Step s p) = let (ss, result) = go p in (s:ss, result)-    go (Fail e)   = ([], Left e)-    go (Done a)   = ([], Right a)
− tests/UnitTests/Distribution/Client/Dependency/Modular/PSQ.hs
@@ -1,22 +0,0 @@-module UnitTests.Distribution.Client.Dependency.Modular.PSQ (-  tests-  ) where--import Distribution.Client.Dependency.Modular.PSQ--import Test.Tasty-import Test.Tasty.QuickCheck--tests :: [TestTree]-tests = [ testProperty "splitsAltImplementation" splitsTest-        ]---- | Original splits implementation-splits' :: PSQ k a -> PSQ k (a, PSQ k a)-splits' xs =-  casePSQ xs-    (PSQ [])-    (\ k v ys -> cons k (v, ys) (fmap (\ (w, zs) -> (w, cons k v zs)) (splits' ys)))--splitsTest :: [(Int, Int)] -> Bool-splitsTest psq = splits' (PSQ psq) == splits (PSQ psq)
− tests/UnitTests/Distribution/Client/Dependency/Modular/Solver.hs
@@ -1,805 +0,0 @@-{-# LANGUAGE RecordWildCards #-}-module UnitTests.Distribution.Client.Dependency.Modular.Solver (tests)-       where---- base-import Control.Monad-import Data.List (isInfixOf)--import qualified Data.Version         as V-import qualified Distribution.Version as V---- test-framework-import Test.Tasty as TF-import Test.Tasty.HUnit (testCase, assertEqual, assertBool)---- Cabal-import Language.Haskell.Extension ( Extension(..)-                                  , KnownExtension(..), Language(..))---- cabal-install-import Distribution.Client.PkgConfigDb (PkgConfigDb, pkgConfigDbFromList)-import UnitTests.Distribution.Client.Dependency.Modular.DSL-import UnitTests.Options--tests :: [TF.TestTree]-tests = [-      testGroup "Simple dependencies" [-          runTest $         mkTest db1 "alreadyInstalled"   ["A"]      (SolverSuccess [])-        , runTest $         mkTest db1 "installLatest"      ["B"]      (SolverSuccess [("B", 2)])-        , runTest $         mkTest db1 "simpleDep1"         ["C"]      (SolverSuccess [("B", 1), ("C", 1)])-        , runTest $         mkTest db1 "simpleDep2"         ["D"]      (SolverSuccess [("B", 2), ("D", 1)])-        , runTest $         mkTest db1 "failTwoVersions"    ["C", "D"] anySolverFailure-        , runTest $ indep $ mkTest db1 "indepTwoVersions"   ["C", "D"] (SolverSuccess [("B", 1), ("B", 2), ("C", 1), ("D", 1)])-        , runTest $ indep $ mkTest db1 "aliasWhenPossible1" ["C", "E"] (SolverSuccess [("B", 1), ("C", 1), ("E", 1)])-        , runTest $ indep $ mkTest db1 "aliasWhenPossible2" ["D", "E"] (SolverSuccess [("B", 2), ("D", 1), ("E", 1)])-        , runTest $ indep $ mkTest db2 "aliasWhenPossible3" ["C", "D"] (SolverSuccess [("A", 1), ("A", 2), ("B", 1), ("B", 2), ("C", 1), ("D", 1)])-        , runTest $         mkTest db1 "buildDepAgainstOld" ["F"]      (SolverSuccess [("B", 1), ("E", 1), ("F", 1)])-        , runTest $         mkTest db1 "buildDepAgainstNew" ["G"]      (SolverSuccess [("B", 2), ("E", 1), ("G", 1)])-        , runTest $ indep $ mkTest db1 "multipleInstances"  ["F", "G"] anySolverFailure-        , runTest $         mkTest db21 "unknownPackage1"   ["A"]      (SolverSuccess [("A", 1), ("B", 1)])-        , runTest $         mkTest db22 "unknownPackage2"   ["A"]      (SolverFailure (isInfixOf "unknown package: C"))-        ]-    , testGroup "Flagged dependencies" [-          runTest $         mkTest db3 "forceFlagOn"  ["C"]      (SolverSuccess [("A", 1), ("B", 1), ("C", 1)])-        , runTest $         mkTest db3 "forceFlagOff" ["D"]      (SolverSuccess [("A", 2), ("B", 1), ("D", 1)])-        , runTest $ indep $ mkTest db3 "linkFlags1"   ["C", "D"] anySolverFailure-        , runTest $ indep $ mkTest db4 "linkFlags2"   ["C", "D"] anySolverFailure-        , runTest $ indep $ mkTest db18 "linkFlags3"  ["A", "B"] (SolverSuccess [("A", 1), ("B", 1), ("C", 1), ("D", 1), ("D", 2), ("F", 1)])-        ]-    , testGroup "Stanzas" [-          runTest $         mkTest db5 "simpleTest1" ["C"]      (SolverSuccess [("A", 2), ("C", 1)])-        , runTest $         mkTest db5 "simpleTest2" ["D"]      anySolverFailure-        , runTest $         mkTest db5 "simpleTest3" ["E"]      (SolverSuccess [("A", 1), ("E", 1)])-        , runTest $         mkTest db5 "simpleTest4" ["F"]      anySolverFailure -- TODO-        , runTest $         mkTest db5 "simpleTest5" ["G"]      (SolverSuccess [("A", 2), ("G", 1)])-        , runTest $         mkTest db5 "simpleTest6" ["E", "G"] anySolverFailure-        , runTest $ indep $ mkTest db5 "simpleTest7" ["E", "G"] (SolverSuccess [("A", 1), ("A", 2), ("E", 1), ("G", 1)])-        , runTest $         mkTest db6 "depsWithTests1" ["C"]      (SolverSuccess [("A", 1), ("B", 1), ("C", 1)])-        , runTest $ indep $ mkTest db6 "depsWithTests2" ["C", "D"] (SolverSuccess [("A", 1), ("B", 1), ("C", 1), ("D", 1)])-        ]-    , testGroup "Setup dependencies" [-          runTest $         mkTest db7  "setupDeps1" ["B"] (SolverSuccess [("A", 2), ("B", 1)])-        , runTest $         mkTest db7  "setupDeps2" ["C"] (SolverSuccess [("A", 2), ("C", 1)])-        , runTest $         mkTest db7  "setupDeps3" ["D"] (SolverSuccess [("A", 1), ("D", 1)])-        , runTest $         mkTest db7  "setupDeps4" ["E"] (SolverSuccess [("A", 1), ("A", 2), ("E", 1)])-        , runTest $         mkTest db7  "setupDeps5" ["F"] (SolverSuccess [("A", 1), ("A", 2), ("F", 1)])-        , runTest $         mkTest db8  "setupDeps6" ["C", "D"] (SolverSuccess [("A", 1), ("B", 1), ("B", 2), ("C", 1), ("D", 1)])-        , runTest $         mkTest db9  "setupDeps7" ["F", "G"] (SolverSuccess [("A", 1), ("B", 1), ("B",2 ), ("C", 1), ("D", 1), ("E", 1), ("E", 2), ("F", 1), ("G", 1)])-        , runTest $         mkTest db10 "setupDeps8" ["C"] (SolverSuccess [("C", 1)])-        , runTest $ indep $ mkTest dbSetupDeps "setupDeps9" ["A", "B"] (SolverSuccess [("A", 1), ("B", 1), ("C", 1), ("D", 1), ("D", 2)])-        ]-    , testGroup "Base shim" [-          runTest $ mkTest db11 "baseShim1" ["A"] (SolverSuccess [("A", 1)])-        , runTest $ mkTest db12 "baseShim2" ["A"] (SolverSuccess [("A", 1)])-        , runTest $ mkTest db12 "baseShim3" ["B"] (SolverSuccess [("B", 1)])-        , runTest $ mkTest db12 "baseShim4" ["C"] (SolverSuccess [("A", 1), ("B", 1), ("C", 1)])-        , runTest $ mkTest db12 "baseShim5" ["D"] anySolverFailure-        , runTest $ mkTest db12 "baseShim6" ["E"] (SolverSuccess [("E", 1), ("syb", 2)])-        ]-    , testGroup "Cycles" [-          runTest $ mkTest db14 "simpleCycle1"          ["A"]      anySolverFailure-        , runTest $ mkTest db14 "simpleCycle2"          ["A", "B"] anySolverFailure-        , runTest $ mkTest db14 "cycleWithFlagChoice1"  ["C"]      (SolverSuccess [("C", 1), ("E", 1)])-        , runTest $ mkTest db15 "cycleThroughSetupDep1" ["A"]      anySolverFailure-        , runTest $ mkTest db15 "cycleThroughSetupDep2" ["B"]      anySolverFailure-        , runTest $ mkTest db15 "cycleThroughSetupDep3" ["C"]      (SolverSuccess [("C", 2), ("D", 1)])-        , runTest $ mkTest db15 "cycleThroughSetupDep4" ["D"]      (SolverSuccess [("D", 1)])-        , runTest $ mkTest db15 "cycleThroughSetupDep5" ["E"]      (SolverSuccess [("C", 2), ("D", 1), ("E", 1)])-        ]-    , testGroup "Extensions" [-          runTest $ mkTestExts [EnableExtension CPP] dbExts1 "unsupported" ["A"] anySolverFailure-        , runTest $ mkTestExts [EnableExtension CPP] dbExts1 "unsupportedIndirect" ["B"] anySolverFailure-        , runTest $ mkTestExts [EnableExtension RankNTypes] dbExts1 "supported" ["A"] (SolverSuccess [("A",1)])-        , runTest $ mkTestExts (map EnableExtension [CPP,RankNTypes]) dbExts1 "supportedIndirect" ["C"] (SolverSuccess [("A",1),("B",1), ("C",1)])-        , runTest $ mkTestExts [EnableExtension CPP] dbExts1 "disabledExtension" ["D"] anySolverFailure-        , runTest $ mkTestExts (map EnableExtension [CPP,RankNTypes]) dbExts1 "disabledExtension" ["D"] anySolverFailure-        , runTest $ mkTestExts (UnknownExtension "custom" : map EnableExtension [CPP,RankNTypes]) dbExts1 "supportedUnknown" ["E"] (SolverSuccess [("A",1),("B",1),("C",1),("E",1)])-        ]-    , testGroup "Languages" [-          runTest $ mkTestLangs [Haskell98] dbLangs1 "unsupported" ["A"] anySolverFailure-        , runTest $ mkTestLangs [Haskell98,Haskell2010] dbLangs1 "supported" ["A"] (SolverSuccess [("A",1)])-        , runTest $ mkTestLangs [Haskell98] dbLangs1 "unsupportedIndirect" ["B"] anySolverFailure-        , runTest $ mkTestLangs [Haskell98, Haskell2010, UnknownLanguage "Haskell3000"] dbLangs1 "supportedUnknown" ["C"] (SolverSuccess [("A",1),("B",1),("C",1)])-        ]--     , testGroup "Soft Constraints" [-          runTest $ soft [ ExPref "A" $ mkvrThis 1]      $ mkTest db13 "selectPreferredVersionSimple" ["A"] (SolverSuccess [("A", 1)])-        , runTest $ soft [ ExPref "A" $ mkvrOrEarlier 2] $ mkTest db13 "selectPreferredVersionSimple2" ["A"] (SolverSuccess [("A", 2)])-        , runTest $ soft [ ExPref "A" $ mkvrOrEarlier 2-                         , ExPref "A" $ mkvrOrEarlier 1] $ mkTest db13 "selectPreferredVersionMultiple" ["A"] (SolverSuccess [("A", 1)])-        , runTest $ soft [ ExPref "A" $ mkvrOrEarlier 1-                         , ExPref "A" $ mkvrOrEarlier 2] $ mkTest db13 "selectPreferredVersionMultiple2" ["A"] (SolverSuccess [("A", 1)])-        , runTest $ soft [ ExPref "A" $ mkvrThis 1-                         , ExPref "A" $ mkvrThis 2] $ mkTest db13 "selectPreferredVersionMultiple3" ["A"] (SolverSuccess [("A", 2)])-        , runTest $ soft [ ExPref "A" $ mkvrThis 1-                         , ExPref "A" $ mkvrOrEarlier 2] $ mkTest db13 "selectPreferredVersionMultiple4" ["A"] (SolverSuccess [("A", 1)])-        ]-     , testGroup "Buildable Field" [-          testBuildable "avoid building component with unknown dependency" (ExAny "unknown")-        , testBuildable "avoid building component with unknown extension" (ExExt (UnknownExtension "unknown"))-        , testBuildable "avoid building component with unknown language" (ExLang (UnknownLanguage "unknown"))-        , runTest $ mkTest dbBuildable1 "choose flags that set buildable to false" ["pkg"] (SolverSuccess [("flag1-false", 1), ("flag2-true", 1), ("pkg", 1)])-        , runTest $ mkTest dbBuildable2 "choose version that sets buildable to false" ["A"] (SolverSuccess [("A", 1), ("B", 2)])-         ]-    , testGroup "Pkg-config dependencies" [-          runTest $ mkTestPCDepends [] dbPC1 "noPkgs" ["A"] anySolverFailure-        , runTest $ mkTestPCDepends [("pkgA", "0")] dbPC1 "tooOld" ["A"] anySolverFailure-        , runTest $ mkTestPCDepends [("pkgA", "1.0.0"), ("pkgB", "1.0.0")] dbPC1 "pruneNotFound" ["C"] (SolverSuccess [("A", 1), ("B", 1), ("C", 1)])-        , runTest $ mkTestPCDepends [("pkgA", "1.0.0"), ("pkgB", "2.0.0")] dbPC1 "chooseNewest" ["C"] (SolverSuccess [("A", 1), ("B", 2), ("C", 1)])-        ]-    , testGroup "Independent goals" [-          runTest $ indep $ mkTest db16 "indepGoals1" ["A", "B"] (SolverSuccess [("A", 1), ("B", 1), ("C", 1), ("D", 1), ("D", 2), ("E", 1)])-        , runTest $ indep $ mkTest db17 "indepGoals2" ["A", "B"] (SolverSuccess [("A", 1), ("B", 1), ("C", 1), ("D", 1)])-        , runTest $ indep $ mkTest db19 "indepGoals3" ["D", "E", "F"] anySolverFailure -- The target order is important.-        , runTest $ indep $ mkTest db20 "indepGoals4" ["C", "A", "B"] (SolverSuccess [("A", 1), ("B", 1), ("C", 1), ("D", 1), ("D", 2)])-        , runTest $ indep $ mkTest db23 "indepGoals5" ["X", "Y"] (SolverSuccess [("A", 1), ("A", 2), ("B", 1), ("C", 1), ("C", 2), ("X", 1), ("Y", 1)])-        , runTest $ indep $ mkTest db24 "indepGoals6" ["X", "Y"] (SolverSuccess [("A", 1), ("A", 2), ("B", 1), ("B", 2), ("X", 1), ("Y", 1)])-        ]-    ]-  where-    indep test      = test { testIndepGoals = True }-    soft prefs test = test { testSoftConstraints = prefs }-    mkvrThis        = V.thisVersion . makeV-    mkvrOrEarlier   = V.orEarlierVersion . makeV-    makeV v         = V.Version [v,0,0] []--{--------------------------------------------------------------------------------  Solver tests--------------------------------------------------------------------------------}--data SolverTest = SolverTest {-    testLabel          :: String-  , testTargets        :: [String]-  , testResult         :: SolverResult-  , testIndepGoals     :: Bool-  , testSoftConstraints :: [ExPreference]-  , testDb             :: ExampleDb-  , testSupportedExts  :: Maybe [Extension]-  , testSupportedLangs :: Maybe [Language]-  , testPkgConfigDb    :: PkgConfigDb-  }---- | Result of a solver test.-data SolverResult =-    SolverSuccess [(String, Int)]  -- ^ succeeds with given plan-  | SolverFailure (String -> Bool) -- ^ fails, and the error message satisfies the predicate---- | Can be used for test cases where we just want to verify that--- they fail, but do not care about the error message.-anySolverFailure :: SolverResult-anySolverFailure = SolverFailure (const True)--mkTest :: ExampleDb-       -> String-       -> [String]-       -> SolverResult-       -> SolverTest-mkTest = mkTestExtLangPC Nothing Nothing []--mkTestExts :: [Extension]-           -> ExampleDb-           -> String-           -> [String]-           -> SolverResult-           -> SolverTest-mkTestExts exts = mkTestExtLangPC (Just exts) Nothing []--mkTestLangs :: [Language]-            -> ExampleDb-            -> String-            -> [String]-            -> SolverResult-            -> SolverTest-mkTestLangs langs = mkTestExtLangPC Nothing (Just langs) []--mkTestPCDepends :: [(String, String)]-                -> ExampleDb-                -> String-                -> [String]-                -> SolverResult-                -> SolverTest-mkTestPCDepends pkgConfigDb = mkTestExtLangPC Nothing Nothing pkgConfigDb--mkTestExtLangPC :: Maybe [Extension]-                -> Maybe [Language]-                -> [(String, String)]-                -> ExampleDb-                -> String-                -> [String]-                -> SolverResult-                -> SolverTest-mkTestExtLangPC exts langs pkgConfigDb db label targets result = SolverTest {-    testLabel          = label-  , testTargets        = targets-  , testResult         = result-  , testIndepGoals     = False-  , testSoftConstraints = []-  , testDb             = db-  , testSupportedExts  = exts-  , testSupportedLangs = langs-  , testPkgConfigDb    = pkgConfigDbFromList pkgConfigDb-  }--runTest :: SolverTest -> TF.TestTree-runTest SolverTest{..} = askOption $ \(OptionShowSolverLog showSolverLog) ->-    testCase testLabel $ do-      let (_msgs, result) = exResolve testDb testSupportedExts testSupportedLangs-                            testPkgConfigDb testTargets testIndepGoals testSoftConstraints-      when showSolverLog $ mapM_ putStrLn _msgs-      case result of-        Left  err  -> assertBool ("Unexpected error:\n" ++ err) (check testResult err)-        Right plan -> assertEqual "" (toMaybe testResult) (Just (extractInstallPlan plan))-  where-    toMaybe :: SolverResult -> Maybe ([(String, Int)])-    toMaybe (SolverSuccess plan) = Just plan-    toMaybe (SolverFailure _   ) = Nothing--    check :: SolverResult -> (String -> Bool)-    check (SolverFailure f) = f-    check _                 = const False--{--------------------------------------------------------------------------------  Specific example database for the tests--------------------------------------------------------------------------------}--db1 :: ExampleDb-db1 =-    let a = exInst "A" 1 "A-1" []-    in [ Left a-       , Right $ exAv "B" 1 [ExAny "A"]-       , Right $ exAv "B" 2 [ExAny "A"]-       , Right $ exAv "C" 1 [ExFix "B" 1]-       , Right $ exAv "D" 1 [ExFix "B" 2]-       , Right $ exAv "E" 1 [ExAny "B"]-       , Right $ exAv "F" 1 [ExFix "B" 1, ExAny "E"]-       , Right $ exAv "G" 1 [ExFix "B" 2, ExAny "E"]-       , Right $ exAv "Z" 1 []-       ]---- In this example, we _can_ install C and D as independent goals, but we have--- to pick two diferent versions for B (arbitrarily)-db2 :: ExampleDb-db2 = [-    Right $ exAv "A" 1 []-  , Right $ exAv "A" 2 []-  , Right $ exAv "B" 1 [ExAny "A"]-  , Right $ exAv "B" 2 [ExAny "A"]-  , Right $ exAv "C" 1 [ExAny "B", ExFix "A" 1]-  , Right $ exAv "D" 1 [ExAny "B", ExFix "A" 2]-  ]--db3 :: ExampleDb-db3 = [-     Right $ exAv "A" 1 []-   , Right $ exAv "A" 2 []-   , Right $ exAv "B" 1 [exFlag "flagB" [ExFix "A" 1] [ExFix "A" 2]]-   , Right $ exAv "C" 1 [ExFix "A" 1, ExAny "B"]-   , Right $ exAv "D" 1 [ExFix "A" 2, ExAny "B"]-   ]---- | Like db3, but the flag picks a different package rather than a--- different package version------ In db3 we cannot install C and D as independent goals because:------ * The multiple instance restriction says C and D _must_ share B--- * Since C relies on A-1, C needs B to be compiled with flagB on--- * Since D relies on A-2, D needs B to be compiled with flagB off--- * Hence C and D have incompatible requirements on B's flags.------ However, _even_ if we don't check explicitly that we pick the same flag--- assignment for 0.B and 1.B, we will still detect the problem because--- 0.B depends on 0.A-1, 1.B depends on 1.A-2, hence we cannot link 0.A to--- 1.A and therefore we cannot link 0.B to 1.B.------ In db4 the situation however is trickier. We again cannot install--- packages C and D as independent goals because:------ * As above, the multiple instance restriction says that C and D _must_ share B--- * Since C relies on Ax-2, it requires B to be compiled with flagB off--- * Since D relies on Ay-2, it requires B to be compiled with flagB on--- * Hence C and D have incompatible requirements on B's flags.------ But now this requirement is more indirect. If we only check dependencies--- we don't see the problem:------ * We link 0.B to 1.B--- * 0.B relies on Ay-1--- * 1.B relies on Ax-1------ We will insist that 0.Ay will be linked to 1.Ay, and 0.Ax to 1.Ax, but since--- we only ever assign to one of these, these constraints are never broken.-db4 :: ExampleDb-db4 = [-     Right $ exAv "Ax" 1 []-   , Right $ exAv "Ax" 2 []-   , Right $ exAv "Ay" 1 []-   , Right $ exAv "Ay" 2 []-   , Right $ exAv "B"  1 [exFlag "flagB" [ExFix "Ax" 1] [ExFix "Ay" 1]]-   , Right $ exAv "C"  1 [ExFix "Ax" 2, ExAny "B"]-   , Right $ exAv "D"  1 [ExFix "Ay" 2, ExAny "B"]-   ]---- | Some tests involving testsuites------ Note that in this test framework test suites are always enabled; if you--- want to test without test suites just set up a test database without--- test suites.------ * C depends on A (through its test suite)--- * D depends on B-2 (through its test suite), but B-2 is unavailable--- * E depends on A-1 directly and on A through its test suite. We prefer---     to use A-1 for the test suite in this case.--- * F depends on A-1 directly and on A-2 through its test suite. In this---     case we currently fail to install F, although strictly speaking---     test suites should be considered independent goals.--- * G is like E, but for version A-2. This means that if we cannot install---     E and G together, unless we regard them as independent goals.-db5 :: ExampleDb-db5 = [-    Right $ exAv "A" 1 []-  , Right $ exAv "A" 2 []-  , Right $ exAv "B" 1 []-  , Right $ exAv "C" 1 [] `withTest` ExTest "testC" [ExAny "A"]-  , Right $ exAv "D" 1 [] `withTest` ExTest "testD" [ExFix "B" 2]-  , Right $ exAv "E" 1 [ExFix "A" 1] `withTest` ExTest "testE" [ExAny "A"]-  , Right $ exAv "F" 1 [ExFix "A" 1] `withTest` ExTest "testF" [ExFix "A" 2]-  , Right $ exAv "G" 1 [ExFix "A" 2] `withTest` ExTest "testG" [ExAny "A"]-  ]---- Now the _dependencies_ have test suites------ * Installing C is a simple example. C wants version 1 of A, but depends on---   B, and B's testsuite depends on an any version of A. In this case we prefer---   to link (if we don't regard test suites as independent goals then of course---   linking here doesn't even come into it).--- * Installing [C, D] means that we prefer to link B -- depending on how we---   set things up, this means that we should also link their test suites.-db6 :: ExampleDb-db6 = [-    Right $ exAv "A" 1 []-  , Right $ exAv "A" 2 []-  , Right $ exAv "B" 1 [] `withTest` ExTest "testA" [ExAny "A"]-  , Right $ exAv "C" 1 [ExFix "A" 1, ExAny "B"]-  , Right $ exAv "D" 1 [ExAny "B"]-  ]---- Packages with setup dependencies------ Install..--- * B: Simple example, just make sure setup deps are taken into account at all--- * C: Both the package and the setup script depend on any version of A.---      In this case we prefer to link--- * D: Variation on C.1 where the package requires a specific (not latest)---      version but the setup dependency is not fixed. Again, we prefer to---      link (picking the older version)--- * E: Variation on C.2 with the setup dependency the more inflexible.---      Currently, in this case we do not see the opportunity to link because---      we consider setup dependencies after normal dependencies; we will---      pick A.2 for E, then realize we cannot link E.setup.A to A.2, and pick---      A.1 instead. This isn't so easy to fix (if we want to fix it at all);---      in particular, considering setup dependencies _before_ other deps is---      not an improvement, because in general we would prefer to link setup---      setups to package deps, rather than the other way around. (For example,---      if we change this ordering then the test for D would start to install---      two versions of A).--- * F: The package and the setup script depend on different versions of A.---      This will only work if setup dependencies are considered independent.-db7 :: ExampleDb-db7 = [-    Right $ exAv "A" 1 []-  , Right $ exAv "A" 2 []-  , Right $ exAv "B" 1 []            `withSetupDeps` [ExAny "A"]-  , Right $ exAv "C" 1 [ExAny "A"  ] `withSetupDeps` [ExAny "A"  ]-  , Right $ exAv "D" 1 [ExFix "A" 1] `withSetupDeps` [ExAny "A"  ]-  , Right $ exAv "E" 1 [ExAny "A"  ] `withSetupDeps` [ExFix "A" 1]-  , Right $ exAv "F" 1 [ExFix "A" 2] `withSetupDeps` [ExFix "A" 1]-  ]---- If we install C and D together (not as independent goals), we need to build--- both B.1 and B.2, both of which depend on A.-db8 :: ExampleDb-db8 = [-    Right $ exAv "A" 1 []-  , Right $ exAv "B" 1 [ExAny "A"]-  , Right $ exAv "B" 2 [ExAny "A"]-  , Right $ exAv "C" 1 [] `withSetupDeps` [ExFix "B" 1]-  , Right $ exAv "D" 1 [] `withSetupDeps` [ExFix "B" 2]-  ]---- Extended version of `db8` so that we have nested setup dependencies-db9 :: ExampleDb-db9 = db8 ++ [-    Right $ exAv "E" 1 [ExAny "C"]-  , Right $ exAv "E" 2 [ExAny "D"]-  , Right $ exAv "F" 1 [] `withSetupDeps` [ExFix "E" 1]-  , Right $ exAv "G" 1 [] `withSetupDeps` [ExFix "E" 2]-  ]---- Multiple already-installed packages with inter-dependencies, and one package--- (C) that depends on package A-1 for its setup script and package A-2 as a--- library dependency.-db10 :: ExampleDb-db10 =-  let rts         = exInst "rts"         1 "rts-inst"         []-      ghc_prim    = exInst "ghc-prim"    1 "ghc-prim-inst"    [rts]-      base        = exInst "base"        1 "base-inst"        [rts, ghc_prim]-      a1          = exInst "A"           1 "A1-inst"          [base]-      a2          = exInst "A"           2 "A2-inst"          [base]-  in [-      Left rts-    , Left ghc_prim-    , Left base-    , Left a1-    , Left a2-    , Right $ exAv "C" 1 [ExFix "A" 2] `withSetupDeps` [ExFix "A" 1]-    ]---- | This database tests that a package's setup dependencies are correctly--- linked when the package is linked. See pull request #3268.------ When A and B are installed as independent goals, their dependencies on C must--- be linked, due to the single instance restriction. Since C depends on D, 0.D--- and 1.D must be linked. C also has a setup dependency on D, so 0.C-setup.D--- and 1.C-setup.D must be linked. However, D's two link groups must remain--- independent. The solver should be able to choose D-1 for C's library and D-2--- for C's setup script.-dbSetupDeps :: ExampleDb-dbSetupDeps = [-    Right $ exAv "A" 1 [ExAny "C"]-  , Right $ exAv "B" 1 [ExAny "C"]-  , Right $ exAv "C" 1 [ExFix "D" 1] `withSetupDeps` [ExFix "D" 2]-  , Right $ exAv "D" 1 []-  , Right $ exAv "D" 2 []-  ]---- | Tests for dealing with base shims-db11 :: ExampleDb-db11 =-  let base3 = exInst "base" 3 "base-3-inst" [base4]-      base4 = exInst "base" 4 "base-4-inst" []-  in [-      Left base3-    , Left base4-    , Right $ exAv "A" 1 [ExFix "base" 3]-    ]---- | Slightly more realistic version of db11 where base-3 depends on syb--- This means that if a package depends on base-3 and on syb, then they MUST--- share the version of syb------ * Package A relies on base-3 (which relies on base-4)--- * Package B relies on base-4--- * Package C relies on both A and B--- * Package D relies on base-3 and on syb-2, which is not possible because---     base-3 has a dependency on syb-1 (non-inheritance of the Base qualifier)--- * Package E relies on base-4 and on syb-2, which is fine.-db12 :: ExampleDb-db12 =-  let base3 = exInst "base" 3 "base-3-inst" [base4, syb1]-      base4 = exInst "base" 4 "base-4-inst" []-      syb1  = exInst "syb" 1 "syb-1-inst" [base4]-  in [-      Left base3-    , Left base4-    , Left syb1-    , Right $ exAv "syb" 2 [ExFix "base" 4]-    , Right $ exAv "A" 1 [ExFix "base" 3, ExAny "syb"]-    , Right $ exAv "B" 1 [ExFix "base" 4, ExAny "syb"]-    , Right $ exAv "C" 1 [ExAny "A", ExAny "B"]-    , Right $ exAv "D" 1 [ExFix "base" 3, ExFix "syb" 2]-    , Right $ exAv "E" 1 [ExFix "base" 4, ExFix "syb" 2]-    ]--db13 :: ExampleDb-db13 = [-    Right $ exAv "A" 1 []-  , Right $ exAv "A" 2 []-  , Right $ exAv "A" 3 []-  ]---- | Database with some cycles------ * Simplest non-trivial cycle: A -> B and B -> A--- * There is a cycle C -> D -> C, but it can be broken by picking the---   right flag assignment.-db14 :: ExampleDb-db14 = [-    Right $ exAv "A" 1 [ExAny "B"]-  , Right $ exAv "B" 1 [ExAny "A"]-  , Right $ exAv "C" 1 [exFlag "flagC" [ExAny "D"] [ExAny "E"]]-  , Right $ exAv "D" 1 [ExAny "C"]-  , Right $ exAv "E" 1 []-  ]---- | Cycles through setup dependencies------ The first cycle is unsolvable: package A has a setup dependency on B,--- B has a regular dependency on A, and we only have a single version available--- for both.------ The second cycle can be broken by picking different versions: package C-2.0--- has a setup dependency on D, and D has a regular dependency on C-*. However,--- version C-1.0 is already available (perhaps it didn't have this setup dep).--- Thus, we should be able to break this cycle even if we are installing package--- E, which explictly depends on C-2.0.-db15 :: ExampleDb-db15 = [-    -- First example (real cycle, no solution)-    Right $ exAv   "A" 1            []            `withSetupDeps` [ExAny "B"]-  , Right $ exAv   "B" 1            [ExAny "A"]-    -- Second example (cycle can be broken by picking versions carefully)-  , Left  $ exInst "C" 1 "C-1-inst" []-  , Right $ exAv   "C" 2            []            `withSetupDeps` [ExAny "D"]-  , Right $ exAv   "D" 1            [ExAny "C"  ]-  , Right $ exAv   "E" 1            [ExFix "C" 2]-  ]---- | Check that the solver can backtrack after encountering the SIR (issue #2843)------ When A and B are installed as independent goals, the single instance--- restriction prevents B from depending on C.  This database tests that the--- solver can backtrack after encountering the single instance restriction and--- choose the only valid flag assignment (-flagA +flagB):------ > flagA flagB  B depends on--- >  On    _     C-*--- >  Off   On    E-*               <-- only valid flag assignment--- >  Off   Off   D-2.0, C-*------ Since A depends on C-* and D-1.0, and C-1.0 depends on any version of D,--- we must build C-1.0 against D-1.0. Since B depends on D-2.0, we cannot have--- C in the transitive closure of B's dependencies, because that would mean we--- would need two instances of C: one built against D-1.0 and one built against--- D-2.0.-db16 :: ExampleDb-db16 = [-    Right $ exAv "A" 1 [ExAny "C", ExFix "D" 1]-  , Right $ exAv "B" 1 [ ExFix "D" 2-                       , exFlag "flagA"-                             [ExAny "C"]-                             [exFlag "flagB"-                                 [ExAny "E"]-                                 [ExAny "C"]]]-  , Right $ exAv "C" 1 [ExAny "D"]-  , Right $ exAv "D" 1 []-  , Right $ exAv "D" 2 []-  , Right $ exAv "E" 1 []-  ]---- | This database checks that when the solver discovers a constraint on a--- package's version after choosing to link that package, it can backtrack to--- try alternative versions for the linked-to package. See pull request #3327.------ When A and B are installed as independent goals, their dependencies on C--- must be linked. Since C depends on D, A and B's dependencies on D must also--- be linked. This test relies on the fact that the solver chooses D-2 for both--- 0.D and 1.D before it encounters the test suites' constraints. The solver--- must backtrack to try D-1 for both 0.D and 1.D.-db17 :: ExampleDb-db17 = [-    Right $ exAv "A" 1 [ExAny "C"] `withTest` ExTest "test" [ExFix "D" 1]-  , Right $ exAv "B" 1 [ExAny "C"] `withTest` ExTest "test" [ExFix "D" 1]-  , Right $ exAv "C" 1 [ExAny "D"]-  , Right $ exAv "D" 1 []-  , Right $ exAv "D" 2 []-  ]---- | Issue #2834--- When both A and B are installed as independent goals, their dependencies on--- C must be linked. The only combination of C's flags that is consistent with--- A and B's dependencies on D is -flagA +flagB. This database tests that the--- solver can backtrack to find the right combination of flags (requiring F, but--- not E or G) and apply it to both 0.C and 1.C.------ > flagA flagB  C depends on--- >  On    _     D-1, E-*--- >  Off   On    F-*        <-- Only valid choice--- >  Off   Off   D-2, G-*------ The single instance restriction means we cannot have one instance of C--- built against D-1 and one instance built against D-2; since A depends on--- D-1, and B depends on C-2, it is therefore important that C cannot depend--- on any version of D.-db18 :: ExampleDb-db18 = [-    Right $ exAv "A" 1 [ExAny "C", ExFix "D" 1]-  , Right $ exAv "B" 1 [ExAny "C", ExFix "D" 2]-  , Right $ exAv "C" 1 [exFlag "flagA"-                           [ExFix "D" 1, ExAny "E"]-                           [exFlag "flagB"-                               [ExAny "F"]-                               [ExFix "D" 2, ExAny "G"]]]-  , Right $ exAv "D" 1 []-  , Right $ exAv "D" 2 []-  , Right $ exAv "E" 1 []-  , Right $ exAv "F" 1 []-  , Right $ exAv "G" 1 []-  ]---- | Tricky test case with independent goals (issue #2842)------ Suppose we are installing D, E, and F as independent goals:------ * D depends on A-* and C-1, requiring A-1 to be built against C-1--- * E depends on B-* and C-2, requiring B-1 to be built against C-2--- * F depends on A-* and B-*; this means we need A-1 and B-1 both to be built---     against the same version of C, violating the single instance restriction.------ We can visualize this DB as:------ >    C-1   C-2--- >    /|\   /|\--- >   / | \ / | \--- >  /  |  X  |  \--- > |   | / \ |   |--- > |   |/   \|   |--- > |   +     +   |--- > |   |     |   |--- > |   A     B   |--- >  \  |\   /|  /--- >   \ | \ / | /--- >    \|  V  |/--- >     D  F  E-db19 :: ExampleDb-db19 = [-    Right $ exAv "A" 1 [ExAny "C"]-  , Right $ exAv "B" 1 [ExAny "C"]-  , Right $ exAv "C" 1 []-  , Right $ exAv "C" 2 []-  , Right $ exAv "D" 1 [ExAny "A", ExFix "C" 1]-  , Right $ exAv "E" 1 [ExAny "B", ExFix "C" 2]-  , Right $ exAv "F" 1 [ExAny "A", ExAny "B"]-  ]---- | This database tests that the solver correctly backjumps when dependencies--- of linked packages are not linked. It is an example where the conflict set--- from enforcing the single instance restriction is not sufficient. See pull--- request #3327.------ When C, A, and B are installed as independent goals, the solver first--- chooses 0.C-1 and 0.D-2. When choosing dependencies for A and B, it links--- 1.D and 2.D to 0.D. Finally, the solver discovers the test's constraint on--- D. It must backjump to try 1.D-1 and then link 2.D to 1.D.-db20 :: ExampleDb-db20 = [-    Right $ exAv "A" 1 [ExAny "B"]-  , Right $ exAv "B" 1 [ExAny "D"] `withTest` ExTest "test" [ExFix "D" 1]-  , Right $ exAv "C" 1 [ExFix "D" 2]-  , Right $ exAv "D" 1 []-  , Right $ exAv "D" 2 []-  ]---- | Test the trace messages that we get when a package refers to an unknown pkg------ TODO: Currently we don't actually test the trace messages, and this particular--- test still suceeds. The trace can only be verified by hand.-db21 :: ExampleDb-db21 = [-    Right $ exAv "A" 1 [ExAny "B"]-  , Right $ exAv "A" 2 [ExAny "C"] -- A-2.0 will be tried first, but C unknown-  , Right $ exAv "B" 1 []-  ]---- | A variant of 'db21', which actually fails.-db22 :: ExampleDb-db22 = [-    Right $ exAv "A" 1 [ExAny "B"]-  , Right $ exAv "A" 2 [ExAny "C"]-  ]---- | Database for (unsuccessfully) trying to expose a bug in the handling--- of implied linking constraints. The question is whether an implied linking--- constraint should only have the introducing package in its conflict set,--- or also its link target.------ It turns out that as long as the Single Instance Restriction is in place,--- it does not matter, because there will aways be an option that is failing--- due to the SIR, which contains the link target in its conflict set.------ Even if the SIR is not in place, if there is a solution, one will always--- be found, because without the SIR, linking is always optional, but never--- necessary.----db23 :: ExampleDb-db23 = [-    Right $ exAv "X" 1 [ExFix "C" 2, ExAny "A"]-  , Right $ exAv "Y" 1 [ExFix "C" 1, ExFix "A" 2]-  , Right $ exAv "A" 1 []-  , Right $ exAv "A" 2 [ExAny "B"]-  , Right $ exAv "B" 1 [ExAny "C"]-  , Right $ exAv "C" 1 []-  , Right $ exAv "C" 2 []-  ]---- | A simplified version of 'db23'.-db24 :: ExampleDb-db24 = [-    Right $ exAv "X" 1 [ExFix "B" 2, ExAny "A"]-  , Right $ exAv "Y" 1 [ExFix "B" 1, ExFix "A" 2]-  , Right $ exAv "A" 1 []-  , Right $ exAv "A" 2 [ExAny "B"]-  , Right $ exAv "B" 1 []-  , Right $ exAv "B" 2 []-  ]--dbExts1 :: ExampleDb-dbExts1 = [-    Right $ exAv "A" 1 [ExExt (EnableExtension RankNTypes)]-  , Right $ exAv "B" 1 [ExExt (EnableExtension CPP), ExAny "A"]-  , Right $ exAv "C" 1 [ExAny "B"]-  , Right $ exAv "D" 1 [ExExt (DisableExtension CPP), ExAny "B"]-  , Right $ exAv "E" 1 [ExExt (UnknownExtension "custom"), ExAny "C"]-  ]--dbLangs1 :: ExampleDb-dbLangs1 = [-    Right $ exAv "A" 1 [ExLang Haskell2010]-  , Right $ exAv "B" 1 [ExLang Haskell98, ExAny "A"]-  , Right $ exAv "C" 1 [ExLang (UnknownLanguage "Haskell3000"), ExAny "B"]-  ]---- | cabal must set enable-lib to false in order to avoid the unavailable--- dependency. Flags are true by default. The flag choice causes "pkg" to--- depend on "false-dep".-testBuildable :: String -> ExampleDependency -> TestTree-testBuildable testName unavailableDep =-    runTest $ mkTestExtLangPC (Just []) (Just []) [] db testName ["pkg"] expected-  where-    expected = SolverSuccess [("false-dep", 1), ("pkg", 1)]-    db = [-        Right $ exAv "pkg" 1-            [ unavailableDep-            , ExFlag "enable-lib" (Buildable []) NotBuildable ]-         `withTest`-            ExTest "test" [exFlag "enable-lib"-                              [ExAny "true-dep"]-                              [ExAny "false-dep"]]-      , Right $ exAv "true-dep" 1 []-      , Right $ exAv "false-dep" 1 []-      ]---- | cabal must choose -flag1 +flag2 for "pkg", which requires packages--- "flag1-false" and "flag2-true".-dbBuildable1 :: ExampleDb-dbBuildable1 = [-    Right $ exAv "pkg" 1-        [ ExAny "unknown"-        , ExFlag "flag1" (Buildable []) NotBuildable-        , ExFlag "flag2" (Buildable []) NotBuildable]-     `withTests`-        [ ExTest "optional-test"-              [ ExAny "unknown"-              , ExFlag "flag1"-                    (Buildable [])-                    (Buildable [ExFlag "flag2" NotBuildable (Buildable [])])]-        , ExTest "test" [ exFlag "flag1" [ExAny "flag1-true"] [ExAny "flag1-false"]-                        , exFlag "flag2" [ExAny "flag2-true"] [ExAny "flag2-false"]]-        ]-  , Right $ exAv "flag1-true" 1 []-  , Right $ exAv "flag1-false" 1 []-  , Right $ exAv "flag2-true" 1 []-  , Right $ exAv "flag2-false" 1 []-  ]---- | Package databases for testing @pkg-config@ dependencies.-dbPC1 :: ExampleDb-dbPC1 = [-    Right $ exAv "A" 1 [ExPkg ("pkgA", 1)]-  , Right $ exAv "B" 1 [ExPkg ("pkgB", 1), ExAny "A"]-  , Right $ exAv "B" 2 [ExPkg ("pkgB", 2), ExAny "A"]-  , Right $ exAv "C" 1 [ExAny "B"]-  ]---- | cabal must pick B-2 to avoid the unknown dependency.-dbBuildable2 :: ExampleDb-dbBuildable2 = [-    Right $ exAv "A" 1 [ExAny "B"]-  , Right $ exAv "B" 1 [ExAny "unknown"]-  , Right $ exAv "B" 2-        [ ExAny "unknown"-        , ExFlag "disable-lib" NotBuildable (Buildable [])-        ]-  , Right $ exAv "B" 3 [ExAny "unknown"]-  ]
tests/UnitTests/Distribution/Client/FileMonitor.hs view
@@ -15,7 +15,7 @@ import Distribution.Verbosity (silent)  import Distribution.Client.FileMonitor-import Distribution.Client.Compat.Time+import Distribution.Compat.Time  import Test.Tasty import Test.Tasty.HUnit@@ -24,6 +24,7 @@ tests :: Int -> [TestTree] tests mtimeChange =   [ testCase "sanity check mtimes"   $ testFileMTimeSanity mtimeChange+  , testCase "sanity check dirs"     $ testDirChangeSanity mtimeChange   , testCase "no monitor cache"      testNoMonitorCache   , testCase "corrupt monitor cache" testCorruptMonitorCache   , testCase "empty monitor"         testEmptyMonitor@@ -34,6 +35,7 @@   , testCase "remove file"           testRemoveFile   , testCase "non-existent file"     testNonExistentFile   , testCase "changed file type"     $ testChangedFileType mtimeChange+  , testCase "several monitor kinds" $ testMultipleMonitorKinds mtimeChange    , testGroup "glob matches"     [ testCase "no change"           testGlobNoChange@@ -69,6 +71,8 @@   , testCase "value updated"         testValueUpdated   ] +-- Check the file system behaves the way we expect it to+ -- we rely on file mtimes having a reasonable resolution testFileMTimeSanity :: Int -> Assertion testFileMTimeSanity mtimeChange =@@ -81,6 +85,62 @@       t2 <- getModTime (dir </> "a")       assertBool "expected different file mtimes" (t2 > t1) +-- We rely on directories changing mtime when entries are added or removed+testDirChangeSanity :: Int -> Assertion+testDirChangeSanity mtimeChange =+  withTempDirectory silent "." "dir-mtime-" $ \dir -> do++    expectMTimeChange dir "file add" $+      IO.writeFile (dir </> "file") "content"++    expectMTimeSame dir "file content change" $+      IO.writeFile (dir </> "file") "new content"++    expectMTimeChange dir "file del" $+      IO.removeFile (dir </> "file")++    expectMTimeChange dir "subdir add" $+      IO.createDirectory (dir </> "dir")++    expectMTimeSame dir "subdir file add" $+      IO.writeFile (dir </> "dir" </> "file") "content"++    expectMTimeChange dir "subdir file move in" $+      IO.renameFile (dir </> "dir" </> "file") (dir </> "file")++    expectMTimeChange dir "subdir file move out" $+      IO.renameFile (dir </> "file") (dir </> "dir" </> "file")++    expectMTimeSame dir "subdir dir add" $+      IO.createDirectory (dir </> "dir" </> "subdir")++    expectMTimeChange dir "subdir dir move in" $+      IO.renameDirectory (dir </> "dir" </> "subdir") (dir </> "subdir")++    expectMTimeChange dir "subdir dir move out" $+      IO.renameDirectory (dir </> "subdir") (dir </> "dir" </> "subdir")++  where+    expectMTimeChange, expectMTimeSame :: FilePath -> String -> IO ()+                                       -> Assertion++    expectMTimeChange dir descr action = do+      t  <- getModTime dir+      threadDelay mtimeChange+      action+      t' <- getModTime dir+      assertBool ("expected dir mtime change on " ++ descr) (t' > t)++    expectMTimeSame dir descr action = do+      t  <- getModTime dir+      threadDelay mtimeChange+      action+      t' <- getModTime dir+      assertBool ("expected same dir mtime on " ++ descr) (t' == t)+++-- Now for the FileMonitor tests proper...+ -- first run, where we don't even call updateMonitor testNoMonitorCache :: Assertion testNoMonitorCache =@@ -327,6 +387,34 @@         touch' root "a"         reason <- expectMonitorChanged root monitor ()         reason @?= MonitoredFileChanged "a"++-- Monitoring the same file with two different kinds of monitor should work+-- both should be kept, and both checked for changes.+-- We had a bug where only one monitor kind was kept per file.+-- https://github.com/haskell/cabal/pull/3863#issuecomment-248495178+testMultipleMonitorKinds :: Int -> Assertion+testMultipleMonitorKinds mtimeChange =+  withFileMonitor $ \root monitor -> do+    touchFile root "a"+    updateMonitor root monitor [monitorFile "a", monitorFileHashed "a"] () ()+    (res, files) <- expectMonitorUnchanged root monitor ()+    res   @?= ()+    files @?= [monitorFile "a", monitorFileHashed "a"]+    threadDelay mtimeChange+    touchFile root "a" -- not changing content, just mtime+    reason <- expectMonitorChanged root monitor ()+    reason @?= MonitoredFileChanged "a"++    createDir root "dir"+    updateMonitor root monitor [monitorDirectory "dir",+                                monitorDirectoryExistence "dir"] () ()+    (res2, files2) <- expectMonitorUnchanged root monitor ()+    res2   @?= ()+    files2 @?= [monitorDirectory "dir", monitorDirectoryExistence "dir"]+    threadDelay mtimeChange+    touchFile root ("dir" </> "a") -- changing dir mtime, not existence+    reason2 <- expectMonitorChanged root monitor ()+    reason2 @?= MonitoredFileChanged "dir"   ------------------
+ tests/UnitTests/Distribution/Client/IndexUtils/Timestamp.hs view
@@ -0,0 +1,60 @@+module UnitTests.Distribution.Client.IndexUtils.Timestamp (tests) where++import Distribution.Text+import Data.Time+import Data.Time.Clock.POSIX++import Distribution.Client.IndexUtils.Timestamp++import Test.Tasty+import Test.Tasty.QuickCheck++tests :: [TestTree]+tests =+    [ testProperty "Timestamp1" prop_timestamp1+    , testProperty "Timestamp2" prop_timestamp2+    , testProperty "Timestamp3" prop_timestamp3+    , testProperty "Timestamp4" prop_timestamp4+    , testProperty "Timestamp5" prop_timestamp5+    ]++-- test unixtime format parsing+prop_timestamp1 :: Int -> Bool+prop_timestamp1 t0 = Just t == simpleParse ('@':show t0)+  where+    t = toEnum t0 :: Timestamp++-- test display/simpleParse roundtrip+prop_timestamp2 :: Int -> Bool+prop_timestamp2 t0+  | t /= nullTimestamp  = simpleParse (display t) == Just t+  | otherwise           = display t == ""+  where+    t = toEnum t0 :: Timestamp++-- test display against reference impl+prop_timestamp3 :: Int -> Bool+prop_timestamp3 t0+  | t /= nullTimestamp  = refDisp t == display t+  | otherwise           = display t == ""+  where+    t = toEnum t0 :: Timestamp++    refDisp = maybe undefined (formatTime undefined "%FT%TZ")+              . timestampToUTCTime++-- test utcTimeToTimestamp/timestampToUTCTime roundtrip+prop_timestamp4 :: Int -> Bool+prop_timestamp4 t0+  | t /= nullTimestamp  = (utcTimeToTimestamp =<< timestampToUTCTime t) == Just t+  | otherwise           = timestampToUTCTime t == Nothing+  where+    t = toEnum t0 :: Timestamp++prop_timestamp5 :: Int -> Bool+prop_timestamp5 t0+  | t /= nullTimestamp = timestampToUTCTime t == Just ut+  | otherwise          = timestampToUTCTime t == Nothing+  where+    t = toEnum t0 :: Timestamp+    ut = posixSecondsToUTCTime (fromIntegral t0)
+ tests/UnitTests/Distribution/Client/InstallPlan.hs view
@@ -0,0 +1,312 @@+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE NoMonoLocalBinds #-}+{-# LANGUAGE ConstraintKinds #-}+module UnitTests.Distribution.Client.InstallPlan (tests) where++import           Distribution.Package+import           Distribution.Version+import qualified Distribution.Client.InstallPlan as InstallPlan+import           Distribution.Client.InstallPlan (GenericInstallPlan, IsUnit)+import qualified Distribution.Compat.Graph as Graph+import           Distribution.Compat.Graph (IsNode(..))+import           Distribution.Solver.Types.Settings+import           Distribution.Solver.Types.PackageFixedDeps+import qualified Distribution.Solver.Types.ComponentDeps as CD+import           Distribution.Client.Types+import           Distribution.Client.JobControl++import Data.Graph+import Data.Array hiding (index)+import Data.List+import qualified Data.Map as Map+import qualified Data.Set as Set+import Data.Set (Set)+import Data.IORef+import Control.Monad+import Control.Concurrent (threadDelay)+import System.Random+import Test.QuickCheck++import Test.Tasty+import Test.Tasty.QuickCheck+++tests :: [TestTree]+tests =+  [ testProperty "reverseTopologicalOrder" prop_reverseTopologicalOrder+  , testProperty "executionOrder"          prop_executionOrder+  , testProperty "execute serial"          prop_execute_serial+  , testProperty "execute parallel"        prop_execute_parallel+  , testProperty "execute/executionOrder"  prop_execute_vs_executionOrder+  ]++prop_reverseTopologicalOrder :: TestInstallPlan -> Bool+prop_reverseTopologicalOrder (TestInstallPlan plan graph toVertex _) =+    isReverseTopologicalOrder+      graph+      (map (toVertex . installedUnitId)+           (InstallPlan.reverseTopologicalOrder plan))++-- | @executionOrder@ is in reverse topological order+prop_executionOrder :: TestInstallPlan -> Bool+prop_executionOrder (TestInstallPlan plan graph toVertex _) =+    isReversePartialTopologicalOrder graph (map toVertex pkgids)+ && allConfiguredPackages plan == Set.fromList pkgids+  where+    pkgids = map installedUnitId (InstallPlan.executionOrder plan)++-- | @execute@ is in reverse topological order+prop_execute_serial :: TestInstallPlan -> Property+prop_execute_serial tplan@(TestInstallPlan plan graph toVertex _) =+    ioProperty $ do+      jobCtl <- newSerialJobControl+      pkgids <- executeTestInstallPlan jobCtl tplan (\_ -> return ())+      return $ isReversePartialTopologicalOrder graph (map toVertex pkgids)+            && allConfiguredPackages plan == Set.fromList pkgids++prop_execute_parallel :: Positive (Small Int) -> TestInstallPlan -> Property+prop_execute_parallel (Positive (Small maxJobLimit))+                      tplan@(TestInstallPlan plan graph toVertex _) =+    ioProperty $ do+      jobCtl <- newParallelJobControl maxJobLimit+      pkgids <- executeTestInstallPlan jobCtl tplan $ \_ -> do+                  delay <- randomRIO (0,1000)+                  threadDelay delay+      return $ isReversePartialTopologicalOrder graph (map toVertex pkgids)+            && allConfiguredPackages plan == Set.fromList pkgids++-- | return the packages that are visited by execute, in order.+executeTestInstallPlan :: JobControl IO (UnitId, Either () ())+                       -> TestInstallPlan+                       -> (TestPkg -> IO ())+                       -> IO [UnitId]+executeTestInstallPlan jobCtl (TestInstallPlan plan _ _ _) visit = do+    resultsRef <- newIORef []+    _ <- InstallPlan.execute jobCtl False (const ())+                             plan $ \(ReadyPackage pkg) -> do+           visit pkg+           atomicModifyIORef resultsRef $ \pkgs -> (installedUnitId pkg:pkgs, ())+           return (Right ())+    fmap reverse (readIORef resultsRef)++-- | @execute@ visits the packages in the same order as @executionOrder@+prop_execute_vs_executionOrder :: TestInstallPlan -> Property+prop_execute_vs_executionOrder tplan@(TestInstallPlan plan _ _ _) =+    ioProperty $ do+      jobCtl <- newSerialJobControl+      pkgids <- executeTestInstallPlan jobCtl tplan (\_ -> return ())+      let pkgids' = map installedUnitId (InstallPlan.executionOrder plan)+      return (pkgids == pkgids')+++--------------------------+-- Property helper utils+--++-- | A graph topological ordering is a linear ordering of its vertices such+-- that for every directed edge uv from vertex u to vertex v, u comes before v+-- in the ordering.+--+-- A reverse topological ordering is the swapped: for every directed edge uv+-- from vertex u to vertex v, v comes before u in the ordering.+--+isReverseTopologicalOrder :: Graph -> [Vertex] -> Bool+isReverseTopologicalOrder g vs =+    and [ ixs ! u > ixs ! v+        | let ixs = array (bounds g) (zip vs [0::Int ..])+        , (u,v) <- edges g ]++isReversePartialTopologicalOrder :: Graph -> [Vertex] -> Bool+isReversePartialTopologicalOrder g vs =+    and [ case (ixs ! u, ixs ! v) of+            (Just ixu, Just ixv) -> ixu > ixv+            _                    -> True+        | let ixs = array (bounds g)+                          (zip (range (bounds g)) (repeat Nothing) ++ +                           zip vs (map Just [0::Int ..]))+        , (u,v) <- edges g ]++allConfiguredPackages :: HasUnitId srcpkg+                      => GenericInstallPlan ipkg srcpkg -> Set UnitId+allConfiguredPackages plan =+    Set.fromList+      [ installedUnitId pkg+      | InstallPlan.Configured pkg <- InstallPlan.toList plan ]+++--------------------+-- Test generators+--++data TestInstallPlan = TestInstallPlan+                         (GenericInstallPlan TestPkg TestPkg)+                         Graph+                         (UnitId -> Vertex)+                         (Vertex -> UnitId)++instance Show TestInstallPlan where+  show (TestInstallPlan plan _ _ _) = InstallPlan.showInstallPlan plan++data TestPkg = TestPkg PackageId UnitId [UnitId]+  deriving (Eq, Show)++instance IsNode TestPkg where+  type Key TestPkg = UnitId+  nodeKey (TestPkg _ ipkgid _) = ipkgid+  nodeNeighbors (TestPkg _ _ deps) = deps+++instance Package TestPkg where+  packageId (TestPkg pkgid _ _) = pkgid++instance HasUnitId TestPkg where+  installedUnitId (TestPkg _ ipkgid _) = ipkgid++instance PackageFixedDeps TestPkg where+  depends (TestPkg _ _ deps) = CD.singleton CD.ComponentLib deps++instance PackageInstalled TestPkg where+  installedDepends (TestPkg _ _ deps) = deps++instance Arbitrary TestInstallPlan where+  arbitrary = arbitraryTestInstallPlan++arbitraryTestInstallPlan :: Gen TestInstallPlan+arbitraryTestInstallPlan = do+    graph <- arbitraryAcyclicGraph+               (choose (2,5))+               (choose (1,5))+               0.3++    plan  <- arbitraryInstallPlan mkTestPkg mkTestPkg 0.5 graph++    let toVertexMap   = Map.fromList [ (mkUnitIdV v, v) | v <- vertices graph ]+        fromVertexMap = Map.fromList [ (v, mkUnitIdV v) | v <- vertices graph ]+        toVertex      = (toVertexMap   Map.!)+        fromVertex    = (fromVertexMap Map.!)++    return (TestInstallPlan plan graph toVertex fromVertex)+  where+    mkTestPkg pkgv depvs =+        return (TestPkg pkgid ipkgid deps)+      where+        pkgid  = mkPkgId pkgv+        ipkgid = mkUnitIdV pkgv+        deps   = map mkUnitIdV depvs+    mkUnitIdV = mkUnitId . show+    mkPkgId v = PackageIdentifier (mkPackageName ("pkg" ++ show v))+                                  (mkVersion [1])+++-- | Generate a random 'InstallPlan' following the structure of an existing+-- 'Graph'.+--+-- It takes generators for installed and source packages and the chance that+-- each package is installed (for those packages with no prerequisites).+--+arbitraryInstallPlan :: (IsUnit ipkg,+                         IsUnit srcpkg)+                     => (Vertex -> [Vertex] -> Gen ipkg)+                     -> (Vertex -> [Vertex] -> Gen srcpkg)+                     -> Float+                     -> Graph+                     -> Gen (InstallPlan.GenericInstallPlan ipkg srcpkg)+arbitraryInstallPlan mkIPkg mkSrcPkg ipkgProportion graph = do++    (ipkgvs, srcpkgvs) <-+      fmap ((\(ipkgs, srcpkgs) -> (map fst ipkgs, map fst srcpkgs))+            . partition snd) $+      sequence+        [ do isipkg <- if isRoot then pick ipkgProportion+                                 else return False+             return (v, isipkg)+        | (v,n) <- assocs (outdegree graph)+        , let isRoot = n == 0 ]++    ipkgs   <- sequence+                 [ mkIPkg pkgv depvs+                 | pkgv <- ipkgvs+                 , let depvs  = graph ! pkgv+                 ]+    srcpkgs <- sequence+                 [ mkSrcPkg pkgv depvs+                 | pkgv <- srcpkgvs+                 , let depvs  = graph ! pkgv+                 ]+    let index = Graph.fromDistinctList+                   (map InstallPlan.PreExisting ipkgs+                 ++ map InstallPlan.Configured  srcpkgs)+    return $ InstallPlan.new (IndependentGoals False) index+++-- | Generate a random directed acyclic graph, based on the algorithm presented+-- here <http://stackoverflow.com/questions/12790337/generating-a-random-dag>+--+-- It generates a DAG based on ranks of nodes. Nodes in each rank can only+-- have edges to nodes in subsequent ranks.+--+-- The generator is paramterised by a generator for the number of ranks and+-- the number of nodes within each rank. It is also paramterised by the+-- chance that each node in each rank will have an edge from each node in+-- each previous rank. Thus a higher chance will produce a more densely+-- connected graph.+--+arbitraryAcyclicGraph :: Gen Int -> Gen Int -> Float -> Gen Graph+arbitraryAcyclicGraph genNRanks genNPerRank edgeChance = do+    nranks    <- genNRanks+    rankSizes <- replicateM nranks genNPerRank+    let rankStarts = scanl (+) 0 rankSizes+        rankRanges = drop 1 (zip rankStarts (tail rankStarts))+        totalRange = sum rankSizes+    rankEdges <- mapM (uncurry genRank) rankRanges+    return $ buildG (0, totalRange-1) (concat rankEdges)+  where+    genRank :: Vertex -> Vertex -> Gen [Edge]+    genRank rankStart rankEnd =+      filterM (const (pick edgeChance))+        [ (i,j)+        | i <- [0..rankStart-1]+        , j <- [rankStart..rankEnd-1]+        ]++pick :: Float -> Gen Bool+pick chance = do+    p <- choose (0,1)+    return (p < chance)+++--------------------------------+-- Inspecting generated graphs+--++{-+-- Handy util for checking the generated graphs look sensible+writeDotFile :: FilePath -> Graph -> IO ()+writeDotFile file = writeFile file . renderDotGraph++renderDotGraph :: Graph -> String+renderDotGraph graph =+  unlines (+      [header+      ,graphDefaultAtribs+      ,nodeDefaultAtribs+      ,edgeDefaultAtribs]+    ++ map renderNode (vertices graph)+    ++ map renderEdge (edges graph)+    ++ [footer]+  )+  where+    renderNode n = "\t" ++ show n ++ " [label=\"" ++ show n ++  "\"];"++    renderEdge (n, n') = "\t" ++ show n ++ " -> " ++ show n' ++ "[];"+++header, footer, graphDefaultAtribs, nodeDefaultAtribs, edgeDefaultAtribs :: String++header = "digraph packages {"+footer = "}"++graphDefaultAtribs = "\tgraph [fontsize=14, fontcolor=black, color=black];"+nodeDefaultAtribs  = "\tnode [label=\"\\N\", width=\"0.75\", shape=ellipse];"+edgeDefaultAtribs  = "\tedge [fontsize=10];"+-}
+ tests/UnitTests/Distribution/Client/JobControl.hs view
@@ -0,0 +1,193 @@+{-# LANGUAGE DeriveDataTypeable #-}+module UnitTests.Distribution.Client.JobControl (tests) where++import Distribution.Client.JobControl++import Data.List+import Data.Maybe+import Data.IORef+import Control.Monad+import Control.Concurrent (threadDelay)+import Control.Exception  (Exception, try, throwIO)+import Data.Typeable      (Typeable)+import qualified Data.Set as Set++import Test.Tasty+import Test.Tasty.QuickCheck hiding (collect)+++tests :: [TestTree]+tests =+  [ testGroup "serial"+      [ testProperty "submit batch"       prop_submit_serial+      , testProperty "submit batch"       prop_remaining_serial+      , testProperty "submit interleaved" prop_interleaved_serial+      , testProperty "concurrent jobs"    prop_concurrent_serial+      , testProperty "cancel"             prop_cancel_serial+      , testProperty "exceptions"         prop_exception_serial+      ]+  , testGroup "parallel"+      [ testProperty "submit batch"       prop_submit_parallel+      , testProperty "submit batch"       prop_remaining_parallel+      , testProperty "submit interleaved" prop_interleaved_parallel+      , testProperty "concurrent jobs"    prop_concurrent_parallel+      , testProperty "cancel"             prop_cancel_parallel+      , testProperty "exceptions"         prop_exception_parallel+      ]+  ]+++prop_submit_serial :: [Int] -> Property+prop_submit_serial xs =+  ioProperty $ do+    jobCtl <- newSerialJobControl+    prop_submit jobCtl xs++prop_submit_parallel :: Positive (Small Int) -> [Int] -> Property+prop_submit_parallel (Positive (Small maxJobLimit)) xs =+  ioProperty $ do+    jobCtl <- newParallelJobControl maxJobLimit+    prop_submit jobCtl xs++prop_remaining_serial :: [Int] -> Property+prop_remaining_serial xs =+  ioProperty $ do+    jobCtl <- newSerialJobControl+    prop_remaining jobCtl xs++prop_remaining_parallel :: Positive (Small Int) -> [Int] -> Property+prop_remaining_parallel (Positive (Small maxJobLimit)) xs =+  ioProperty $ do+    jobCtl <- newParallelJobControl maxJobLimit+    prop_remaining jobCtl xs++prop_interleaved_serial :: [Int] -> Property+prop_interleaved_serial xs =+  ioProperty $ do+    jobCtl <- newSerialJobControl+    prop_submit_interleaved jobCtl xs++prop_interleaved_parallel :: Positive (Small Int) -> [Int] -> Property+prop_interleaved_parallel (Positive (Small maxJobLimit)) xs =+  ioProperty $ do+    jobCtl <- newParallelJobControl maxJobLimit+    prop_submit_interleaved jobCtl xs++prop_submit :: JobControl IO Int -> [Int] -> IO Bool+prop_submit jobCtl xs = do+    mapM_ (\x -> spawnJob jobCtl (return x)) xs+    xs' <- mapM (\_ -> collectJob jobCtl) xs+    return (sort xs == sort xs')++prop_remaining :: JobControl IO Int -> [Int] -> IO Bool+prop_remaining jobCtl xs = do+    mapM_ (\x -> spawnJob jobCtl (return x)) xs+    xs' <- collectRemainingJobs jobCtl+    return (sort xs == sort xs')++collectRemainingJobs :: Monad m => JobControl m a -> m [a]+collectRemainingJobs jobCtl = go []+  where+    go xs = do+      remaining <- remainingJobs jobCtl+      if remaining+        then do x <- collectJob jobCtl+                go (x:xs)+        else return xs++prop_submit_interleaved :: JobControl IO (Maybe Int) -> [Int] -> IO Bool+prop_submit_interleaved jobCtl xs = do+    xs' <- sequence+      [ spawn >> collect+      | let spawns   = map (\x -> spawnJob jobCtl (return (Just x))) xs+                    ++ repeat (return ())+            collects = replicate 5 (return Nothing)+                    ++ map (\_ -> collectJob jobCtl) xs+      , (spawn, collect) <- zip spawns collects+      ]+    return (sort xs == sort (catMaybes xs'))++prop_concurrent_serial :: NonNegative (Small Int) -> Property+prop_concurrent_serial (NonNegative (Small ntasks)) =+  ioProperty $ do+    jobCtl   <- newSerialJobControl+    countRef <- newIORef (0 :: Int)+    replicateM_ ntasks (spawnJob jobCtl (task countRef))+    counts   <- replicateM ntasks (collectJob jobCtl)+    return $ length counts == ntasks+          && all (\(n0, n1) -> n0 == 0 && n1 == 1) counts+  where+    task countRef = do+      n0 <- atomicModifyIORef countRef (\n -> (n+1, n))+      threadDelay 100+      n1 <- atomicModifyIORef countRef (\n -> (n-1, n))+      return (n0, n1)++prop_concurrent_parallel :: Positive (Small Int) -> NonNegative Int -> Property+prop_concurrent_parallel (Positive (Small maxJobLimit)) (NonNegative ntasks) =+  ioProperty $ do+    jobCtl   <- newParallelJobControl maxJobLimit+    countRef <- newIORef (0 :: Int)+    replicateM_ ntasks (spawnJob jobCtl (task countRef))+    counts   <- replicateM ntasks (collectJob jobCtl)+    return $ length counts == ntasks+          && all (\(n0, n1) -> n0 >= 0 && n0 <  maxJobLimit+                            && n1 >  0 && n1 <= maxJobLimit) counts+             -- we do hit the concurrency limit (in the right circumstances)+          && if ntasks >= maxJobLimit*2 -- give us enough of a margin+               then any (\(_,n1) -> n1 == maxJobLimit) counts+               else True+  where+    task countRef = do+      n0 <- atomicModifyIORef countRef (\n -> (n+1, n))+      threadDelay 100+      n1 <- atomicModifyIORef countRef (\n -> (n-1, n))+      return (n0, n1)++prop_cancel_serial :: [Int] -> [Int] -> Property+prop_cancel_serial xs ys =+  ioProperty $ do+    jobCtl <- newSerialJobControl+    mapM_ (\x -> spawnJob jobCtl (return x)) (xs++ys)+    xs' <- mapM (\_ -> collectJob jobCtl) xs+    cancelJobs jobCtl+    ys' <- collectRemainingJobs jobCtl+    return (sort xs == sort xs' && null ys')++prop_cancel_parallel :: Positive (Small Int) -> [Int] -> [Int] -> Property+prop_cancel_parallel (Positive (Small maxJobLimit)) xs ys = do+  ioProperty $ do+    jobCtl <- newParallelJobControl maxJobLimit+    mapM_ (\x -> spawnJob jobCtl (threadDelay 100  >> return x)) (xs++ys)+    xs' <- mapM (\_ -> collectJob jobCtl) xs+    cancelJobs jobCtl+    ys' <- collectRemainingJobs jobCtl+    return $ Set.fromList (xs'++ys') `Set.isSubsetOf` Set.fromList (xs++ys)++data TestException = TestException Int+  deriving (Typeable, Show)++instance Exception TestException++prop_exception_serial :: [Either Int Int] -> Property+prop_exception_serial xs =+  ioProperty $ do+    jobCtl <- newSerialJobControl+    prop_exception jobCtl xs++prop_exception_parallel :: Positive (Small Int) -> [Either Int Int] -> Property+prop_exception_parallel (Positive (Small maxJobLimit)) xs =+  ioProperty $ do+    jobCtl <- newParallelJobControl maxJobLimit+    prop_exception jobCtl xs++prop_exception :: JobControl IO Int -> [Either Int Int] -> IO Bool+prop_exception jobCtl xs = do+    mapM_ (\x -> spawnJob jobCtl (either (throwIO . TestException) return x)) xs+    xs' <- replicateM (length xs) $ do+             mx <- try (collectJob jobCtl)+             return $ case mx of+               Left (TestException n) -> Left  n+               Right               n  -> Right n+    return (sort xs == sort xs')+
tests/UnitTests/Distribution/Client/ProjectConfig.hs view
@@ -31,6 +31,11 @@ import Distribution.Utils.NubList import Network.URI +import Distribution.Solver.Types.PackageConstraint+import Distribution.Solver.Types.ConstraintSource+import Distribution.Solver.Types.OptionalStanza+import Distribution.Solver.Types.Settings+ import Distribution.Client.ProjectConfig import Distribution.Client.ProjectConfig.Legacy @@ -72,7 +77,7 @@   where     usingGhc76orOlder =       case buildCompilerId of-        CompilerId GHC v -> v < Version [7,7] []+        CompilerId GHC v -> v < mkVersion [7,7]         _                -> False  @@ -91,8 +96,11 @@   prop_roundtrip_legacytypes_all :: ProjectConfig -> Bool-prop_roundtrip_legacytypes_all =+prop_roundtrip_legacytypes_all config =     roundtrip_legacytypes+      config {+        projectConfigProvenance = mempty+      }  prop_roundtrip_legacytypes_packages :: ProjectConfig -> Bool prop_roundtrip_legacytypes_packages config =@@ -100,6 +108,7 @@       config {         projectConfigBuildOnly       = mempty,         projectConfigShared          = mempty,+        projectConfigProvenance      = mempty,         projectConfigLocalPackages   = mempty,         projectConfigSpecificPackage = mempty       }@@ -136,7 +145,7 @@         . showLegacyProjectConfig         . convertToLegacyProjectConfig)           config of-      ParseOk _ x -> x == config+      ParseOk _ x -> x == config { projectConfigProvenance = mempty }       _           -> False  @@ -190,12 +199,13 @@ hackProjectConfigShared :: ProjectConfigShared -> ProjectConfigShared hackProjectConfigShared config =     config {+      projectConfigProjectFile = mempty, -- not present within project files       projectConfigConstraints =       --TODO: [required eventually] parse ambiguity in constraint       -- "pkgname -any" as either any version or disabled flag "any".-        let ambiguous ((UserConstraintFlags _pkg flags), _) =-              (not . null) [ () | (FlagName name, False) <- flags-                                , "any" `isPrefixOf` name ]+        let ambiguous (UserConstraint _ (PackagePropertyFlags flags), _) =+              (not . null) [ () | (name, False) <- flags+                                , "any" `isPrefixOf` unFlagName name ]             ambiguous _ = False          in filter (not . ambiguous) (projectConfigConstraints config)     }@@ -240,19 +250,21 @@         <*> (map getPackageLocationString <$> arbitrary)         <*> shortListOf 3 arbitrary         <*> arbitrary-        <*> arbitrary <*> arbitrary         <*> arbitrary+        <*> arbitrary+        <*> arbitrary+        <*> arbitrary         <*> (MapMappend . fmap getNonMEmpty . Map.fromList                <$> shortListOf 3 arbitrary)         -- package entries with no content are equivalent to         -- the entry not existing at all, so exclude empty -    shrink (ProjectConfig x0 x1 x2 x3 x4 x5 x6 x7) =+    shrink (ProjectConfig x0 x1 x2 x3 x4 x5 x6 x7 x8) =       [ ProjectConfig x0' x1' x2' x3'-                      x4' x5' x6' (MapMappend (fmap getNonMEmpty x7'))-      | ((x0', x1', x2', x3'), (x4', x5', x6', x7'))+                      x4' x5' x6' x7' (MapMappend (fmap getNonMEmpty x8'))+      | ((x0', x1', x2', x3'), (x4', x5', x6', x7', x8'))           <- shrink ((x0, x1, x2, x3),-                     (x4, x5, x6, fmap NonMEmpty (getMapMappend x7)))+                     (x4, x5, x6, x7, fmap NonMEmpty (getMapMappend x8)))       ]  newtype PackageLocationString@@ -295,11 +307,10 @@         <*> arbitrary         <*> arbitraryNumJobs         <*> arbitrary-        <*> arbitrary                                           -- 12-        <*> (fmap getShortToken <$> arbitrary)         <*> arbitrary+        <*> arbitrary         <*> (fmap getShortToken <$> arbitrary)-        <*> (fmap getShortToken <$> arbitrary)                  -- 16+        <*> arbitrary         <*> (fmap getShortToken <$> arbitrary)         <*> (fmap getShortToken <$> arbitrary)       where@@ -308,19 +319,19 @@     shrink (ProjectConfigBuildOnly               x00 x01 x02 x03 x04 x05 x06 x07               x08 x09 x10 x11 x12 x13 x14 x15-              x16 x17) =+              x16) =       [ ProjectConfigBuildOnly           x00' x01' x02' x03' x04'           x05' x06' x07' x08' (postShrink_NumJobs x09')-          x10' x11' x12  x13' x14-          x15 x16 x17+          x10' x11' x12' x13  x14'+          x15  x16       | ((x00', x01', x02', x03', x04'),          (x05', x06', x07', x08', x09'),-         (x10', x11',       x13'))+         (x10', x11', x12',       x14'))           <- shrink                ((x00, x01, x02, x03, x04),                 (x05, x06, x07, x08, preShrink_NumJobs x09),-                (x10, x11,      x13))+                (x10, x11, x12,      x14))       ]       where         preShrink_NumJobs  = fmap (fmap Positive)@@ -329,17 +340,23 @@ instance Arbitrary ProjectConfigShared where     arbitrary =       ProjectConfigShared-        <$> arbitrary                                           --  4+        <$> arbitraryFlag arbitraryShortToken         <*> arbitraryFlag arbitraryShortToken+        <*> arbitrary         <*> arbitraryFlag arbitraryShortToken+        <*> arbitraryFlag arbitraryShortToken         <*> arbitrary         <*> arbitrary         <*> (toNubList <$> listOf arbitraryShortToken)+        <*> arbitrary         <*> arbitraryConstraints         <*> shortListOf 2 arbitrary         <*> arbitrary <*> arbitrary         <*> arbitrary <*> arbitrary         <*> arbitrary <*> arbitrary+        <*> arbitrary <*> arbitrary+        <*> arbitrary+        <*> arbitrary       where         arbitraryConstraints :: Gen [(UserConstraint, ConstraintSource)]         arbitraryConstraints =@@ -348,18 +365,24 @@     shrink (ProjectConfigShared               x00 x01 x02 x03 x04               x05 x06 x07 x08 x09-              x10 x11 x12 x13) =+              x10 x11 x12 x13 x14+              x15 x16 x17 x18 x19+              x20) =       [ ProjectConfigShared-          x00' (fmap getNonEmpty x01') (fmap getNonEmpty x02') x03' x04'-          x05' (postShrink_Constraints x06') x07' x08' x09'-          x10' x11' x12' x13'+          x00' x01' x02' (fmap getNonEmpty x03') (fmap getNonEmpty x04')+          x05' x06' x07' x08' (postShrink_Constraints x09')+          x10' x11' x12' x13' x14' x15' x16' x17' x18' x19' x20'       | ((x00', x01', x02', x03', x04'),          (x05', x06', x07', x08', x09'),-         (x10', x11', x12', x13'))+         (x10', x11', x12', x13', x14'),+         (x15', x16', x17', x18', x19'),+         x20')           <- shrink-               ((x00, fmap NonEmpty x01, fmap NonEmpty x02, x03, x04),-                (x05, preShrink_Constraints x06, x07, x08, x09),-                (x10, x11, x12, x13))+               ((x00, x01, x02, fmap NonEmpty x03, fmap NonEmpty x04),+                (x05, x06, x07, x08, preShrink_Constraints x09),+                (x10, x11, x12, x13, x14),+                (x15, x16, x17, x18, x19),+                x20)       ]       where         preShrink_Constraints  = map fst@@ -369,6 +392,9 @@ projectConfigConstraintSource =      ConstraintSourceProjectConfig "TODO" +instance Arbitrary ProjectConfigProvenance where+    arbitrary = elements [Implicit, Explicit "cabal.project"]+ instance Arbitrary PackageConfig where     arbitrary =       PackageConfig@@ -399,6 +425,7 @@         <*> arbitrary <*> arbitrary         <*> arbitraryFlag arbitraryShortToken         <*> arbitrary+        <*> arbitrary         <*> arbitrary <*> arbitrary         <*> arbitrary         <*> arbitraryFlag arbitraryShortToken@@ -418,7 +445,7 @@               x15 x16 x17 x18 x19               x20 x21 x22 x23 x24               x25 x26 x27 x28 x29-              x30 x31 x32 x33 x34+              x30 x31 x32 x33 x33_1 x34               x35 x36 x37 x38 x39               x40) =       [ PackageConfig@@ -432,7 +459,7 @@                               x19'           x20' x21' x22' x23' x24'           x25' x26' x27' x28' x29'-          x30' x31' x32' x33' x34'+          x30' x31' x32' x33' x33_1' x34'           x35' x36' (fmap getNonEmpty x37') x38'                     (fmap getNonEmpty x39')           x40'@@ -442,7 +469,7 @@           (x15', x16', x17', x18', x19')),          ((x20', x21', x22', x23', x24'),           (x25', x26', x27', x28', x29'),-          (x30', x31', x32', x33', x34'),+          (x30', x31', x32', (x33', x33_1'), x34'),           (x35', x36', x37', x38', x39'),           (x40')))           <- shrink@@ -455,7 +482,7 @@                        x19)),                 ((x20, x21, x22, x23, x24),                  (x25, x26, x27, x28, x29),-                 (x30, x31, x32, x33, x34),+                 (x30, x31, x32, (x33, x33_1), x34),                  (x35, x36, fmap NonEmpty x37, x38, fmap NonEmpty x39),                  (x40)))       ]@@ -482,7 +509,7 @@                            <*> (fmap getShortToken <$> arbitrary)                            <*> (fmap getShortToken <$> arbitrary)                            <*> (fmap getShortToken <$> arbitrary))-                `suchThat` (/= emptySourceRepo)+                `suchThat` (/= emptySourceRepo RepoThis)      shrink (SourceRepo _ x1 x2 x3 x4 x5 x6) =       [ repo@@ -499,14 +526,9 @@                               (fmap getShortToken x4')                               (fmap getShortToken x5')                               (fmap getShortToken x6')-      , repo /= emptySourceRepo+      , repo /= emptySourceRepo RepoThis       ] -emptySourceRepo :: SourceRepo-emptySourceRepo = SourceRepo RepoThis Nothing Nothing Nothing-                                      Nothing Nothing Nothing-- instance Arbitrary RepoType where     arbitrary = elements knownRepoTypes @@ -527,7 +549,7 @@         <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary --  4         <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary --  8         <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary -- 12-        <*> arbitrary <*> arbitrary                             -- 14+        <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary -- 16  instance Arbitrary PackageDB where     arbitrary = oneof [ pure GlobalPackageDB@@ -549,21 +571,36 @@           shortListOf1 5 (oneof [ choose ('0', '9')                                 , choose ('a', 'f') ]) +instance Arbitrary UserConstraintScope where+    arbitrary = oneof [ UserQualified <$> arbitrary <*> arbitrary+                      , UserAnySetupQualifier <$> arbitrary+                      , UserAnyQualifier <$> arbitrary+                      ]++instance Arbitrary UserQualifier where+    arbitrary = oneof [ pure UserQualToplevel+                      , UserQualSetup <$> arbitrary++                      -- -- TODO: Re-enable UserQualExe tests once we decide on a syntax.+                      -- , UserQualExe <$> arbitrary <*> arbitrary+                      ]+ instance Arbitrary UserConstraint where-    arbitrary =-      oneof-        [ UserConstraintVersion   <$> arbitrary <*> arbitrary-        , UserConstraintInstalled <$> arbitrary-        , UserConstraintSource    <$> arbitrary-        , UserConstraintFlags     <$> arbitrary <*> shortListOf1 3 arbitrary-        , UserConstraintStanzas   <$> arbitrary <*> ((\x->[x]) <$> arbitrary)-        ]+    arbitrary = UserConstraint <$> arbitrary <*> arbitrary +instance Arbitrary PackageProperty where+    arbitrary = oneof [ PackagePropertyVersion <$> arbitrary+                      , pure PackagePropertyInstalled+                      , pure PackagePropertySource+                      , PackagePropertyFlags <$> shortListOf1 3 arbitrary+                      , PackagePropertyStanzas . (\x->[x]) <$> arbitrary+                      ]+ instance Arbitrary OptionalStanza where     arbitrary = elements [minBound..maxBound]  instance Arbitrary FlagName where-    arbitrary = FlagName <$> flagident+    arbitrary = mkFlagName <$> flagident       where         flagident   = lowercase <$> shortListOf1 5 (elements flagChars)                       `suchThat` (("-" /=) . take 1)@@ -572,15 +609,33 @@ instance Arbitrary PreSolver where     arbitrary = elements [minBound..maxBound] +instance Arbitrary ReorderGoals where+    arbitrary = ReorderGoals <$> arbitrary++instance Arbitrary CountConflicts where+    arbitrary = CountConflicts <$> arbitrary++instance Arbitrary StrongFlags where+    arbitrary = StrongFlags <$> arbitrary++instance Arbitrary AllowBootLibInstalls where+    arbitrary = AllowBootLibInstalls <$> arbitrary+ instance Arbitrary AllowNewer where-    arbitrary = oneof [ pure AllowNewerNone-                      , AllowNewerSome <$> shortListOf1 3 arbitrary-                      , pure AllowNewerAll+    arbitrary = AllowNewer <$> arbitrary++instance Arbitrary AllowOlder where+    arbitrary = AllowOlder <$> arbitrary++instance Arbitrary RelaxDeps where+    arbitrary = oneof [ pure RelaxDepsNone+                      , RelaxDepsSome <$> shortListOf1 3 arbitrary+                      , pure RelaxDepsAll                       ] -instance Arbitrary AllowNewerDep where-    arbitrary = oneof [ AllowNewerDep       <$> arbitrary-                      , AllowNewerDepScoped <$> arbitrary <*> arbitrary+instance Arbitrary RelaxedDep where+    arbitrary = oneof [ RelaxedDep       <$> arbitrary+                      , RelaxedDepScoped <$> arbitrary <*> arbitrary                       ]  instance Arbitrary ProfDetailLevel where
tests/UnitTests/Distribution/Client/Sandbox/Timestamp.hs view
@@ -5,7 +5,7 @@ import Distribution.Simple.Utils (withTempDirectory) import Distribution.Verbosity -import Distribution.Client.Compat.Time+import Distribution.Compat.Time import Distribution.Client.Sandbox.Timestamp  import Test.Tasty@@ -44,7 +44,7 @@   withTempDirectory silent "." "cabal-timestamp-" $ \dir -> do     let fileName = dir </> "timestamp-record"     writeFile fileName fileContent-    tRec <- readTimestampFile fileName+    tRec <- readTimestampFile normal fileName     assertEqual "expected timestamp records to be equal"       expected tRec @@ -58,6 +58,6 @@   withTempDirectory silent "." "cabal-timestamp-" $ \dir -> do     let fileName = dir </> "timestamp-record"     writeTimestampFile fileName timestampRecord-    tRec <- readTimestampFile fileName+    tRec <- readTimestampFile normal fileName     assertEqual "expected timestamp records to be equal"       timestampRecord tRec
+ tests/UnitTests/Distribution/Client/Store.hs view
@@ -0,0 +1,181 @@+module UnitTests.Distribution.Client.Store (tests) where++--import Control.Monad+--import Control.Concurrent (forkIO, threadDelay)+--import Control.Concurrent.MVar+import qualified Data.Set as Set+import System.FilePath+import System.Directory+--import System.Random++import Distribution.Package (UnitId, mkUnitId)+import Distribution.Compiler (CompilerId(..), CompilerFlavor(..))+import Distribution.Version  (mkVersion)+import Distribution.Verbosity (Verbosity, silent)+import Distribution.Simple.Utils (withTempDirectory)++import Distribution.Client.Store+import Distribution.Client.RebuildMonad++import Test.Tasty+import Test.Tasty.HUnit+++tests :: [TestTree]+tests =+  [ testCase "list content empty"  testListEmpty+  , testCase "install serial"      testInstallSerial+--, testCase "install parallel"    testInstallParallel+    --TODO: figure out some way to do a parallel test, see issue below+  ]+++testListEmpty :: Assertion+testListEmpty =+  withTempDirectory verbosity "." "store-" $ \tmp -> do+    let storeDirLayout = defaultStoreDirLayout (tmp </> "store")++    assertStoreEntryExists storeDirLayout compid unitid False+    assertStoreContent tmp storeDirLayout compid        Set.empty+  where+    compid = CompilerId GHC (mkVersion [1,0])+    unitid = mkUnitId "foo-1.0-xyz"+++testInstallSerial :: Assertion+testInstallSerial =+  withTempDirectory verbosity "." "store-" $ \tmp -> do+    let storeDirLayout = defaultStoreDirLayout (tmp </> "store")+        copyFiles file content dir = do+          -- we copy into a prefix inside the tmp dir and return the prefix+          let destprefix = dir </> "prefix"+          createDirectory destprefix+          writeFile (destprefix </> file) content+          return destprefix++    assertNewStoreEntry tmp storeDirLayout compid unitid1+                        (copyFiles "file1" "content-foo") (return ())+                        UseNewStoreEntry++    assertNewStoreEntry tmp storeDirLayout compid unitid1+                        (copyFiles "file1" "content-foo") (return ())+                        UseExistingStoreEntry++    assertNewStoreEntry tmp storeDirLayout compid unitid2+                        (copyFiles "file2" "content-bar") (return ())+                        UseNewStoreEntry++    let pkgDir :: UnitId -> FilePath+        pkgDir = storePackageDirectory storeDirLayout compid+    assertFileEqual (pkgDir unitid1 </> "file1") "content-foo"+    assertFileEqual (pkgDir unitid2 </> "file2") "content-bar"+  where+    compid  = CompilerId GHC (mkVersion [1,0])+    unitid1 = mkUnitId "foo-1.0-xyz"+    unitid2 = mkUnitId "bar-2.0-xyz"+++{-+-- unfortunately a parallel test like the one below is thwarted by the normal+-- process-internal file locking. If that locking were not in place then we+-- ought to get the blocking behaviour, but due to the normal Handle locking+-- it just fails instead.++testInstallParallel :: Assertion+testInstallParallel =+  withTempDirectory verbosity "." "store-" $ \tmp -> do+    let storeDirLayout = defaultStoreDirLayout (tmp </> "store")++    sync1 <- newEmptyMVar+    sync2 <- newEmptyMVar+    outv  <- newEmptyMVar+    regv  <- newMVar (0 :: Int)++    sequence_+      [ do forkIO $ do+             let copyFiles dir = do+                   delay <- randomRIO (1,100000)+                   writeFile (dir </> "file") (show n)+                   putMVar  sync1 ()+                   readMVar sync2+                   threadDelay delay+                 register = do+                   modifyMVar_ regv (return . (+1))+                   threadDelay 200000+             o <- newStoreEntry verbosity storeDirLayout+                                compid unitid+                                copyFiles register+             putMVar outv (n, o)+      | n <- [0..9 :: Int] ]++    replicateM_ 10 (takeMVar sync1)+    -- all threads are in the copyFiles action concurrently, release them:+    putMVar  sync2 ()++    outcomes <- replicateM 10 (takeMVar outv)+    regcount <- readMVar regv+    let regcount' = length [ () | (_, UseNewStoreEntry) <- outcomes ]++    assertEqual "num registrations" 1 regcount+    assertEqual "num registrations" 1 regcount'++    assertStoreContent tmp storeDirLayout compid (Set.singleton unitid)++    let pkgDir :: UnitId -> FilePath+        pkgDir = storePackageDirectory storeDirLayout compid+    case [ n | (n, UseNewStoreEntry) <- outcomes ] of+      [n] -> assertFileEqual (pkgDir unitid </> "file") (show n)+      _   -> assertFailure "impossible"++  where+    compid  = CompilerId GHC (mkVersion [1,0])+    unitid = mkUnitId "foo-1.0-xyz"+-}++-------------+-- Utils++assertNewStoreEntry :: FilePath -> StoreDirLayout+                    -> CompilerId -> UnitId+                    -> (FilePath -> IO FilePath) -> IO ()+                    -> NewStoreEntryOutcome+                    -> Assertion+assertNewStoreEntry tmp storeDirLayout compid unitid+                    copyFiles register expectedOutcome = do+    entries <- runRebuild tmp $ getStoreEntries storeDirLayout compid+    outcome <- newStoreEntry verbosity storeDirLayout+                             compid unitid+                             copyFiles register+    assertEqual "newStoreEntry outcome" expectedOutcome outcome+    assertStoreEntryExists storeDirLayout compid unitid True+    let expected = Set.insert unitid entries+    assertStoreContent tmp storeDirLayout compid expected+++assertStoreEntryExists :: StoreDirLayout+                       -> CompilerId -> UnitId -> Bool+                       -> Assertion+assertStoreEntryExists storeDirLayout compid unitid expected = do+    actual <- doesStoreEntryExist storeDirLayout compid unitid+    assertEqual "store entry exists" expected actual+++assertStoreContent :: FilePath -> StoreDirLayout+                   -> CompilerId -> Set.Set UnitId+                   -> Assertion+assertStoreContent tmp storeDirLayout compid expected = do+    actual <- runRebuild tmp $ getStoreEntries storeDirLayout compid+    assertEqual "store content" actual expected+++assertFileEqual :: FilePath -> String -> Assertion+assertFileEqual path expected = do+    exists <- doesFileExist path+    assertBool ("file does not exist:\n" ++ path) exists+    actual <- readFile path+    assertEqual ("file content for:\n" ++ path) expected actual+++verbosity :: Verbosity+verbosity = silent+
tests/UnitTests/Distribution/Client/Targets.hs view
@@ -2,57 +2,105 @@   tests   ) where -import Distribution.Client.Targets     (UserConstraint (..), readUserConstraint)-import Distribution.Compat.ReadP       (ReadP, readP_to_S)-import Distribution.Package            (PackageName (..))+import Distribution.Client.Targets     (UserQualifier(..)+                                       ,UserConstraintScope(..)+                                       ,UserConstraint(..), readUserConstraint)+import Distribution.Compat.ReadP       (readP_to_S)+import Distribution.Package            (mkPackageName)+import Distribution.PackageDescription (mkFlagName)+import Distribution.Version            (anyVersion, thisVersion, mkVersion) import Distribution.ParseUtils         (parseCommaList) import Distribution.Text               (parse) +import Distribution.Solver.Types.PackageConstraint (PackageProperty(..))+import Distribution.Solver.Types.OptionalStanza (OptionalStanza(..))+ import Test.Tasty import Test.Tasty.HUnit  import Data.Char                       (isSpace)+import Data.List                       (intercalate) -tests :: [TestTree]-tests = [ testCase "readUserConstraint" readUserConstraintTest-        , testCase "parseUserConstraint" parseUserConstraintTest-        , testCase "readUserConstraints" readUserConstraintsTest-        ]+-- Helper function: makes a test group by mapping each element+-- of a list to a test case.+makeGroup :: String -> (a -> Assertion) -> [a] -> TestTree+makeGroup name f xs = testGroup name $+                      zipWith testCase (map show [0 :: Integer ..]) (map f xs) -readUserConstraintTest :: Assertion-readUserConstraintTest =-  assertEqual ("Couldn't read constraint: '" ++ constr ++ "'") expected actual+tests :: [TestTree]+tests =+  [ makeGroup "readUserConstraint" (uncurry readUserConstraintTest)+      exampleConstraints+    +  , makeGroup "parseUserConstraint" (uncurry parseUserConstraintTest)+      exampleConstraints+  +  , makeGroup "readUserConstraints" (uncurry readUserConstraintsTest)+      [-- First example only.+       (head exampleStrs, take 1 exampleUcs),+       -- All examples separated by commas.+       (intercalate ", " exampleStrs, exampleUcs)]+  ]   where-    pkgName  = "template-haskell"-    constr   = pkgName ++ " installed"+    (exampleStrs, exampleUcs) = unzip exampleConstraints -    expected = UserConstraintInstalled (PackageName pkgName)-    actual   = let (Right r) = readUserConstraint constr in r+exampleConstraints :: [(String, UserConstraint)]+exampleConstraints =+  [ ("template-haskell installed",+     UserConstraint (UserQualified UserQualToplevel (pn "template-haskell"))+                    PackagePropertyInstalled)+    +  , ("bytestring -any",+     UserConstraint (UserQualified UserQualToplevel (pn "bytestring"))+                    (PackagePropertyVersion anyVersion)) -parseUserConstraintTest :: Assertion-parseUserConstraintTest =-  assertEqual ("Couldn't parse constraint: '" ++ constr ++ "'") expected actual-  where-    pkgName  = "template-haskell"-    constr   = pkgName ++ " installed"+  , ("any.directory test",+     UserConstraint (UserAnyQualifier (pn "directory"))+                    (PackagePropertyStanzas [TestStanzas])) -    expected = [UserConstraintInstalled (PackageName pkgName)]-    actual   = [ x | (x, ys) <- readP_to_S parseUserConstraint constr-                   , all isSpace ys]+  , ("setup.Cabal installed",+     UserConstraint (UserAnySetupQualifier (pn "Cabal"))+                    PackagePropertyInstalled) -    parseUserConstraint :: ReadP r UserConstraint-    parseUserConstraint = parse+  , ("process:setup.bytestring ==5.2",+     UserConstraint (UserQualified (UserQualSetup (pn "process")) (pn "bytestring"))+                    (PackagePropertyVersion (thisVersion (mkVersion [5, 2]))))+    +  , ("network:setup.containers +foo -bar baz",+     UserConstraint (UserQualified (UserQualSetup (pn "network")) (pn "containers"))+                    (PackagePropertyFlags [(fn "foo", True),+                                           (fn "bar", False),+                                           (fn "baz", True)]))+    +  -- -- TODO: Re-enable UserQualExe tests once we decide on a syntax.+  --+  -- , ("foo:happy:exe.template-haskell test",+  --    UserConstraint (UserQualified (UserQualExe (pn "foo") (pn "happy")) (pn "template-haskell"))+  --                   (PackagePropertyStanzas [TestStanzas]))+  ]+  where+    pn = mkPackageName+    fn = mkFlagName -readUserConstraintsTest :: Assertion-readUserConstraintsTest =-  assertEqual ("Couldn't read constraints: '" ++ constr ++ "'") expected actual+readUserConstraintTest :: String -> UserConstraint -> Assertion+readUserConstraintTest str uc =+  assertEqual ("Couldn't read constraint: '" ++ str ++ "'") expected actual   where-    pkgName  = "template-haskell"-    constr   = pkgName ++ " installed"+    expected = uc+    actual   = let Right r = readUserConstraint str in r -    expected = [[UserConstraintInstalled (PackageName pkgName)]]-    actual   = [ x | (x, ys) <- readP_to_S parseUserConstraints constr+parseUserConstraintTest :: String -> UserConstraint -> Assertion+parseUserConstraintTest str uc =+  assertEqual ("Couldn't parse constraint: '" ++ str ++ "'") expected actual+  where+    expected = [uc]+    actual   = [ x | (x, ys) <- readP_to_S parse str                    , all isSpace ys] -    parseUserConstraints :: ReadP r [UserConstraint]-    parseUserConstraints = parseCommaList parse+readUserConstraintsTest :: String -> [UserConstraint] -> Assertion+readUserConstraintsTest str ucs =+  assertEqual ("Couldn't read constraints: '" ++ str ++ "'") expected actual+  where+    expected = [ucs]+    actual   = [ x | (x, ys) <- readP_to_S (parseCommaList parse) str+                   , all isSpace ys]
+ tests/UnitTests/Distribution/Solver/Modular/DSL.hs view
@@ -0,0 +1,688 @@+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE ScopedTypeVariables #-}+-- | DSL for testing the modular solver+module UnitTests.Distribution.Solver.Modular.DSL (+    ExampleDependency(..)+  , Dependencies(..)+  , ExTest(..)+  , ExExe(..)+  , ExConstraint(..)+  , ExPreference(..)+  , ExampleDb+  , ExampleVersionRange+  , ExamplePkgVersion+  , ExamplePkgName+  , ExampleFlagName+  , ExFlag(..)+  , ExampleAvailable(..)+  , ExampleInstalled(..)+  , ExampleQualifier(..)+  , ExampleVar(..)+  , EnableAllTests(..)+  , exAv+  , exInst+  , exFlagged+  , exResolve+  , extractInstallPlan+  , declareFlags+  , withSetupDeps+  , withTest+  , withTests+  , withExe+  , withExes+  , runProgress+  , mkVersionRange+  ) where++import Prelude ()+import Distribution.Client.Compat.Prelude++-- base+import Data.Either (partitionEithers)+import Data.List (elemIndex)+import Data.Ord (comparing)+import qualified Data.Map as Map++-- Cabal+import qualified Distribution.Compiler                  as C+import qualified Distribution.InstalledPackageInfo      as IPI+import           Distribution.License (License(..))+import qualified Distribution.ModuleName                as Module+import qualified Distribution.Package                   as C+  hiding (HasUnitId(..))+import qualified Distribution.Types.LegacyExeDependency as C+import qualified Distribution.Types.PkgconfigDependency as C+import qualified Distribution.Types.UnqualComponentName as C+import qualified Distribution.Types.CondTree            as C+import qualified Distribution.PackageDescription        as C+import qualified Distribution.PackageDescription.Check  as C+import qualified Distribution.Simple.PackageIndex       as C.PackageIndex+import           Distribution.Simple.Setup (BooleanFlag(..))+import qualified Distribution.System                    as C+import           Distribution.Text (display)+import qualified Distribution.Version                   as C+import Language.Haskell.Extension (Extension(..), Language(..))++-- cabal-install+import Distribution.Client.Dependency+import Distribution.Client.Dependency.Types+import Distribution.Client.Types+import qualified Distribution.Client.SolverInstallPlan as CI.SolverInstallPlan++import           Distribution.Solver.Types.ComponentDeps (ComponentDeps)+import qualified Distribution.Solver.Types.ComponentDeps as CD+import           Distribution.Solver.Types.ConstraintSource+import           Distribution.Solver.Types.Flag+import           Distribution.Solver.Types.LabeledPackageConstraint+import           Distribution.Solver.Types.OptionalStanza+import qualified Distribution.Solver.Types.PackageIndex      as CI.PackageIndex+import           Distribution.Solver.Types.PackageConstraint+import qualified Distribution.Solver.Types.PackagePath as P+import qualified Distribution.Solver.Types.PkgConfigDb as PC+import           Distribution.Solver.Types.Settings+import           Distribution.Solver.Types.SolverPackage+import           Distribution.Solver.Types.SourcePackage+import           Distribution.Solver.Types.Variable++{-------------------------------------------------------------------------------+  Example package database DSL++  In order to be able to set simple examples up quickly, we define a very+  simple version of the package database here explicitly designed for use in+  tests.++  The design of `ExampleDb` takes the perspective of the solver, not the+  perspective of the package DB. This makes it easier to set up tests for+  various parts of the solver, but makes the mapping somewhat awkward,  because+  it means we first map from "solver perspective" `ExampleDb` to the package+  database format, and then the modular solver internally in `IndexConversion`+  maps this back to the solver specific data structures.++  IMPLEMENTATION NOTES+  --------------------++  TODO: Perhaps these should be made comments of the corresponding data type+  definitions. For now these are just my own conclusions and may be wrong.++  * The difference between `GenericPackageDescription` and `PackageDescription`+    is that `PackageDescription` describes a particular _configuration_ of a+    package (for instance, see documentation for `checkPackage`). A+    `GenericPackageDescription` can be turned into a `PackageDescription` in+    two ways:++      a. `finalizePD` does the proper translation, by taking+         into account the platform, available dependencies, etc. and picks a+         flag assignment (or gives an error if no flag assignment can be found)+      b. `flattenPackageDescription` ignores flag assignment and just joins all+         components together.++    The slightly odd thing is that a `GenericPackageDescription` contains a+    `PackageDescription` as a field; both of the above functions do the same+    thing: they take the embedded `PackageDescription` as a basis for the result+    value, but override `library`, `executables`, `testSuites`, `benchmarks`+    and `buildDepends`.+  * The `condTreeComponents` fields of a `CondTree` is a list of triples+    `(condition, then-branch, else-branch)`, where the `else-branch` is+    optional.+-------------------------------------------------------------------------------}++type ExamplePkgName    = String+type ExamplePkgVersion = Int+type ExamplePkgHash    = String  -- for example "installed" packages+type ExampleFlagName   = String+type ExampleTestName   = String+type ExampleExeName    = String+type ExampleVersionRange = C.VersionRange++data Dependencies = NotBuildable | Buildable [ExampleDependency]+  deriving Show++data ExampleDependency =+    -- | Simple dependency on any version+    ExAny ExamplePkgName++    -- | Simple dependency on a fixed version+  | ExFix ExamplePkgName ExamplePkgVersion++    -- | Simple dependency on a range of versions, with an inclusive lower bound+    -- and an exclusive upper bound.+  | ExRange ExamplePkgName ExamplePkgVersion ExamplePkgVersion++    -- | Build-tools dependency+  | ExBuildToolAny ExamplePkgName++    -- | Build-tools dependency on a fixed version+  | ExBuildToolFix ExamplePkgName ExamplePkgVersion++    -- | Dependencies indexed by a flag+  | ExFlagged ExampleFlagName Dependencies Dependencies++    -- | Dependency on a language extension+  | ExExt Extension++    -- | Dependency on a language version+  | ExLang Language++    -- | Dependency on a pkg-config package+  | ExPkg (ExamplePkgName, ExamplePkgVersion)+  deriving Show++-- | Simplified version of D.Types.GenericPackageDescription.Flag for use in+-- example source packages.+data ExFlag = ExFlag {+    exFlagName    :: ExampleFlagName+  , exFlagDefault :: Bool+  , exFlagType    :: FlagType+  } deriving Show++data ExTest = ExTest ExampleTestName [ExampleDependency]++data ExExe = ExExe ExampleExeName [ExampleDependency]++exFlagged :: ExampleFlagName -> [ExampleDependency] -> [ExampleDependency]+          -> ExampleDependency+exFlagged n t e = ExFlagged n (Buildable t) (Buildable e)++data ExConstraint =+    ExVersionConstraint ConstraintScope ExampleVersionRange+  | ExFlagConstraint ConstraintScope ExampleFlagName Bool++data ExPreference =+    ExPkgPref ExamplePkgName ExampleVersionRange+  | ExStanzaPref ExamplePkgName [OptionalStanza]++data ExampleAvailable = ExAv {+    exAvName    :: ExamplePkgName+  , exAvVersion :: ExamplePkgVersion+  , exAvDeps    :: ComponentDeps [ExampleDependency]++  -- Setting flags here is only necessary to override the default values of+  -- the fields in C.Flag.+  , exAvFlags   :: [ExFlag]+  } deriving Show++data ExampleVar =+    P ExampleQualifier ExamplePkgName+  | F ExampleQualifier ExamplePkgName ExampleFlagName+  | S ExampleQualifier ExamplePkgName OptionalStanza++data ExampleQualifier =+    None+  | Indep Int+  | Setup ExamplePkgName+  | IndepSetup Int ExamplePkgName++-- | Whether to enable tests in all packages in a test case.+newtype EnableAllTests = EnableAllTests Bool+  deriving BooleanFlag++-- | Constructs an 'ExampleAvailable' package for the 'ExampleDb',+-- given:+--+--      1. The name 'ExamplePkgName' of the available package,+--      2. The version 'ExamplePkgVersion' available+--      3. The list of dependency constraints 'ExampleDependency'+--         that this package has.  'ExampleDependency' provides+--         a number of pre-canned dependency types to look at.+--+exAv :: ExamplePkgName -> ExamplePkgVersion -> [ExampleDependency]+     -> ExampleAvailable+exAv n v ds = ExAv { exAvName = n, exAvVersion = v+                   , exAvDeps = CD.fromLibraryDeps ds, exAvFlags = [] }++-- | Override the default settings (e.g., manual vs. automatic) for a subset of+-- a package's flags.+declareFlags :: [ExFlag] -> ExampleAvailable -> ExampleAvailable+declareFlags flags ex = ex {+      exAvFlags = flags+    }++withSetupDeps :: ExampleAvailable -> [ExampleDependency] -> ExampleAvailable+withSetupDeps ex setupDeps = ex {+      exAvDeps = exAvDeps ex <> CD.fromSetupDeps setupDeps+    }++withTest :: ExampleAvailable -> ExTest -> ExampleAvailable+withTest ex test = withTests ex [test]++withTests :: ExampleAvailable -> [ExTest] -> ExampleAvailable+withTests ex tests =+  let testCDs = CD.fromList [(CD.ComponentTest $ C.mkUnqualComponentName name, deps)+                            | ExTest name deps <- tests]+  in ex { exAvDeps = exAvDeps ex <> testCDs }++withExe :: ExampleAvailable -> ExExe -> ExampleAvailable+withExe ex exe = withExes ex [exe]++withExes :: ExampleAvailable -> [ExExe] -> ExampleAvailable+withExes ex exes =+  let exeCDs = CD.fromList [(CD.ComponentExe $ C.mkUnqualComponentName name, deps)+                           | ExExe name deps <- exes]+  in ex { exAvDeps = exAvDeps ex <> exeCDs }++-- | An installed package in 'ExampleDb'; construct me with 'exInst'.+data ExampleInstalled = ExInst {+    exInstName         :: ExamplePkgName+  , exInstVersion      :: ExamplePkgVersion+  , exInstHash         :: ExamplePkgHash+  , exInstBuildAgainst :: [ExamplePkgHash]+  } deriving Show++-- | Constructs an example installed package given:+--+--      1. The name of the package 'ExamplePkgName', i.e., 'String'+--      2. The version of the package 'ExamplePkgVersion', i.e., 'Int'+--      3. The IPID for the package 'ExamplePkgHash', i.e., 'String'+--         (just some unique identifier for the package.)+--      4. The 'ExampleInstalled' packages which this package was+--         compiled against.)+--+exInst :: ExamplePkgName -> ExamplePkgVersion -> ExamplePkgHash+       -> [ExampleInstalled] -> ExampleInstalled+exInst pn v hash deps = ExInst pn v hash (map exInstHash deps)++-- | An example package database is a list of installed packages+-- 'ExampleInstalled' and available packages 'ExampleAvailable'.+-- Generally, you want to use 'exInst' and 'exAv' to construct+-- these packages.+type ExampleDb = [Either ExampleInstalled ExampleAvailable]++type DependencyTree a = C.CondTree C.ConfVar [C.Dependency] a++type DependencyComponent a = C.CondBranch C.ConfVar [C.Dependency] a++exDbPkgs :: ExampleDb -> [ExamplePkgName]+exDbPkgs = map (either exInstName exAvName)++exAvSrcPkg :: ExampleAvailable -> UnresolvedSourcePackage+exAvSrcPkg ex =+    let pkgId = exAvPkgId ex++        flags :: [C.Flag]+        flags =+          let declaredFlags :: Map ExampleFlagName C.Flag+              declaredFlags =+                  Map.fromListWith+                      (\f1 f2 -> error $ "duplicate flag declarations: " ++ show [f1, f2])+                      [(exFlagName flag, mkFlag flag) | flag <- exAvFlags ex]++              usedFlags :: Map ExampleFlagName C.Flag+              usedFlags = Map.fromList [(fn, mkDefaultFlag fn) | fn <- names]+                where+                  names = concatMap extractFlags $+                          CD.libraryDeps (exAvDeps ex)+                           ++ concatMap snd testSuites+                           ++ concatMap snd executables+          in -- 'declaredFlags' overrides 'usedFlags' to give flags non-default settings:+             Map.elems $ declaredFlags `Map.union` usedFlags++        testSuites = [(name, deps) | (CD.ComponentTest name, deps) <- CD.toList (exAvDeps ex)]+        executables = [(name, deps) | (CD.ComponentExe name, deps) <- CD.toList (exAvDeps ex)]+        setup = case CD.setupDeps (exAvDeps ex) of+                  []   -> Nothing+                  deps -> Just C.SetupBuildInfo {+                            C.setupDepends = mkSetupDeps deps,+                            C.defaultSetupDepends = False+                          }+        package = SourcePackage {+            packageInfoId        = pkgId+          , packageSource        = LocalTarballPackage "<<path>>"+          , packageDescrOverride = Nothing+          , packageDescription   = C.GenericPackageDescription {+                C.packageDescription = C.emptyPackageDescription {+                    C.package        = pkgId+                  , C.setupBuildInfo = setup+                  , C.license = BSD3+                  , C.buildType = if isNothing setup+                                  then Just C.Simple+                                  else Just C.Custom+                  , C.category = "category"+                  , C.maintainer = "maintainer"+                  , C.description = "description"+                  , C.synopsis = "synopsis"+                  , C.licenseFiles = ["LICENSE"]+                  , C.specVersionRaw = Left $ C.mkVersion [1,12]+                  }+              , C.genPackageFlags = flags+              , C.condLibrary =+                  let mkLib bi = mempty { C.libBuildInfo = bi }+                  in Just $ mkCondTree defaultLib mkLib $ mkBuildInfoTree $+                     Buildable (CD.libraryDeps (exAvDeps ex))+              , C.condSubLibraries = []+              , C.condForeignLibs = []+              , C.condExecutables =+                  let mkTree = mkCondTree defaultExe mkExe . mkBuildInfoTree . Buildable+                      mkExe bi = mempty { C.buildInfo = bi }+                  in map (\(t, deps) -> (t, mkTree deps)) executables+              , C.condTestSuites =+                  let mkTree = mkCondTree defaultTest mkTest . mkBuildInfoTree . Buildable+                      mkTest bi = mempty { C.testBuildInfo = bi }+                  in map (\(t, deps) -> (t, mkTree deps)) testSuites+              , C.condBenchmarks  = []+              }+            }+        pkgCheckErrors =+          -- We ignore these warnings because some unit tests test that the+          -- solver allows unknown extensions/languages when the compiler+          -- supports them.+          let ignore = ["Unknown extensions:", "Unknown languages:"]+          in [ err | err <- C.checkPackage (packageDescription package) Nothing+             , not $ any (`isPrefixOf` C.explanation err) ignore ]+    in if null pkgCheckErrors+       then package+       else error $ "invalid GenericPackageDescription for package "+                 ++ display pkgId ++ ": " ++ show pkgCheckErrors+  where+    defaultTopLevelBuildInfo :: C.BuildInfo+    defaultTopLevelBuildInfo = mempty { C.defaultLanguage = Just Haskell98 }++    defaultLib :: C.Library+    defaultLib = mempty { C.exposedModules = [Module.fromString "Module"] }++    defaultExe :: C.Executable+    defaultExe = mempty { C.modulePath = "Main.hs" }++    defaultTest :: C.TestSuite+    defaultTest = mempty {+        C.testInterface = C.TestSuiteExeV10 (C.mkVersion [1,0]) "Test.hs"+      }++    -- Split the set of dependencies into the set of dependencies of the library,+    -- the dependencies of the test suites and extensions.+    splitTopLevel :: [ExampleDependency]+                  -> ( [ExampleDependency]+                     , [Extension]+                     , Maybe Language+                     , [(ExamplePkgName, ExamplePkgVersion)] -- pkg-config+                     , [(ExamplePkgName, C.VersionRange)] -- build tools+                     )+    splitTopLevel [] =+        ([], [], Nothing, [], [])+    splitTopLevel (ExBuildToolAny p:deps) =+      let (other, exts, lang, pcpkgs, exes) = splitTopLevel deps+      in (other, exts, lang, pcpkgs, (p, C.anyVersion):exes)+    splitTopLevel (ExBuildToolFix p v:deps) =+      let (other, exts, lang, pcpkgs, exes) = splitTopLevel deps+      in (other, exts, lang, pcpkgs, (p, C.thisVersion (mkVersion v)):exes)+    splitTopLevel (ExExt ext:deps) =+      let (other, exts, lang, pcpkgs, exes) = splitTopLevel deps+      in (other, ext:exts, lang, pcpkgs, exes)+    splitTopLevel (ExLang lang:deps) =+        case splitTopLevel deps of+            (other, exts, Nothing, pcpkgs, exes) -> (other, exts, Just lang, pcpkgs, exes)+            _ -> error "Only 1 Language dependency is supported"+    splitTopLevel (ExPkg pkg:deps) =+      let (other, exts, lang, pcpkgs, exes) = splitTopLevel deps+      in (other, exts, lang, pkg:pcpkgs, exes)+    splitTopLevel (dep:deps) =+      let (other, exts, lang, pcpkgs, exes) = splitTopLevel deps+      in (dep:other, exts, lang, pcpkgs, exes)++    -- Extract the total set of flags used+    extractFlags :: ExampleDependency -> [ExampleFlagName]+    extractFlags (ExAny _)            = []+    extractFlags (ExFix _ _)          = []+    extractFlags (ExRange _ _ _)      = []+    extractFlags (ExBuildToolAny _)   = []+    extractFlags (ExBuildToolFix _ _) = []+    extractFlags (ExFlagged f a b)    =+        f : concatMap extractFlags (deps a ++ deps b)+      where+        deps :: Dependencies -> [ExampleDependency]+        deps NotBuildable = []+        deps (Buildable ds) = ds+    extractFlags (ExExt _)      = []+    extractFlags (ExLang _)     = []+    extractFlags (ExPkg _)      = []++    -- Convert a tree of BuildInfos into a tree of a specific component type.+    -- 'defaultTopLevel' contains the default values for the component, and+    -- 'mkComponent' creates a component from a 'BuildInfo'.+    mkCondTree :: forall a. Semigroup a =>+                  a -> (C.BuildInfo -> a)+               -> DependencyTree C.BuildInfo+               -> DependencyTree a+    mkCondTree defaultTopLevel mkComponent (C.CondNode topData topConstraints topComps) =+        C.CondNode {+            C.condTreeData =+                defaultTopLevel <> mkComponent (defaultTopLevelBuildInfo <> topData)+          , C.condTreeConstraints = topConstraints+          , C.condTreeComponents = goComponents topComps+          }+      where+        go :: DependencyTree C.BuildInfo -> DependencyTree a+        go (C.CondNode ctData constraints comps) =+            C.CondNode (mkComponent ctData) constraints (goComponents comps)++        goComponents :: [DependencyComponent C.BuildInfo]+                     -> [DependencyComponent a]+        goComponents comps = [C.CondBranch cond (go t) (go <$> me) | C.CondBranch cond t me <- comps]++    mkBuildInfoTree :: Dependencies -> DependencyTree C.BuildInfo+    mkBuildInfoTree NotBuildable =+      C.CondNode {+             C.condTreeData        = mempty { C.buildable = False }+           , C.condTreeConstraints = []+           , C.condTreeComponents  = []+           }+    mkBuildInfoTree (Buildable deps) =+      let (libraryDeps, exts, mlang, pcpkgs, buildTools) = splitTopLevel deps+          (directDeps, flaggedDeps) = splitDeps libraryDeps+          bi = mempty {+                  C.otherExtensions = exts+                , C.defaultLanguage = mlang+                , C.buildTools = [ C.LegacyExeDependency n vr+                                 | (n,vr) <- buildTools ]+                , C.pkgconfigDepends = [ C.PkgconfigDependency n' v'+                                       | (n,v) <- pcpkgs+                                       , let n' = C.mkPkgconfigName n+                                       , let v' = C.thisVersion (mkVersion v) ]+              }+      in C.CondNode {+             C.condTreeData        = bi -- Necessary for language extensions+           -- TODO: Arguably, build-tools dependencies should also+           -- effect constraints on conditional tree. But no way to+           -- distinguish between them+           , C.condTreeConstraints = map mkDirect directDeps+           , C.condTreeComponents  = map mkFlagged flaggedDeps+           }++    mkDirect :: (ExamplePkgName, C.VersionRange) -> C.Dependency+    mkDirect (dep, vr) = C.Dependency (C.mkPackageName dep) vr++    mkFlagged :: (ExampleFlagName, Dependencies, Dependencies)+              -> DependencyComponent C.BuildInfo+    mkFlagged (f, a, b) =+        C.CondBranch (C.Var (C.Flag (C.mkFlagName f)))+                     (mkBuildInfoTree a)+                     (Just (mkBuildInfoTree b))++    -- Split a set of dependencies into direct dependencies and flagged+    -- dependencies. A direct dependency is a tuple of the name of package and+    -- its version range meant to be converted to a 'C.Dependency' with+    -- 'mkDirect' for example. A flagged dependency is the set of dependencies+    -- guarded by a flag.+    splitDeps :: [ExampleDependency]+              -> ( [(ExamplePkgName, C.VersionRange)]+                 , [(ExampleFlagName, Dependencies, Dependencies)]+                 )+    splitDeps [] =+      ([], [])+    splitDeps (ExAny p:deps) =+      let (directDeps, flaggedDeps) = splitDeps deps+      in ((p, C.anyVersion):directDeps, flaggedDeps)+    splitDeps (ExFix p v:deps) =+      let (directDeps, flaggedDeps) = splitDeps deps+      in ((p, C.thisVersion $ mkVersion v):directDeps, flaggedDeps)+    splitDeps (ExRange p v1 v2:deps) =+      let (directDeps, flaggedDeps) = splitDeps deps+      in ((p, mkVersionRange v1 v2):directDeps, flaggedDeps)+    splitDeps (ExFlagged f a b:deps) =+      let (directDeps, flaggedDeps) = splitDeps deps+      in (directDeps, (f, a, b):flaggedDeps)+    splitDeps (dep:_) = error $ "Unexpected dependency: " ++ show dep++    -- custom-setup only supports simple dependencies+    mkSetupDeps :: [ExampleDependency] -> [C.Dependency]+    mkSetupDeps deps =+      let (directDeps, []) = splitDeps deps in map mkDirect directDeps++mkVersion :: ExamplePkgVersion -> C.Version+mkVersion n = C.mkVersion [n, 0, 0]++mkVersionRange :: ExamplePkgVersion -> ExamplePkgVersion -> C.VersionRange+mkVersionRange v1 v2 =+    C.intersectVersionRanges (C.orLaterVersion $ mkVersion v1)+                             (C.earlierVersion $ mkVersion v2)++mkFlag :: ExFlag -> C.Flag+mkFlag flag = C.MkFlag {+    C.flagName        = C.mkFlagName $ exFlagName flag+  , C.flagDescription = ""+  , C.flagDefault     = exFlagDefault flag+  , C.flagManual      =+      case exFlagType flag of+        Manual    -> True+        Automatic -> False+  }++mkDefaultFlag :: ExampleFlagName -> C.Flag+mkDefaultFlag flag = C.MkFlag {+    C.flagName        = C.mkFlagName flag+  , C.flagDescription = ""+  , C.flagDefault     = True+  , C.flagManual      = False+  }++exAvPkgId :: ExampleAvailable -> C.PackageIdentifier+exAvPkgId ex = C.PackageIdentifier {+      pkgName    = C.mkPackageName (exAvName ex)+    , pkgVersion = C.mkVersion [exAvVersion ex, 0, 0]+    }++exInstInfo :: ExampleInstalled -> IPI.InstalledPackageInfo+exInstInfo ex = IPI.emptyInstalledPackageInfo {+      IPI.installedUnitId    = C.mkUnitId (exInstHash ex)+    , IPI.sourcePackageId    = exInstPkgId ex+    , IPI.depends            = map C.mkUnitId (exInstBuildAgainst ex)+    }++exInstPkgId :: ExampleInstalled -> C.PackageIdentifier+exInstPkgId ex = C.PackageIdentifier {+      pkgName    = C.mkPackageName (exInstName ex)+    , pkgVersion = C.mkVersion [exInstVersion ex, 0, 0]+    }++exAvIdx :: [ExampleAvailable] -> CI.PackageIndex.PackageIndex UnresolvedSourcePackage+exAvIdx = CI.PackageIndex.fromList . map exAvSrcPkg++exInstIdx :: [ExampleInstalled] -> C.PackageIndex.InstalledPackageIndex+exInstIdx = C.PackageIndex.fromList . map exInstInfo++exResolve :: ExampleDb+          -- List of extensions supported by the compiler, or Nothing if unknown.+          -> Maybe [Extension]+          -- List of languages supported by the compiler, or Nothing if unknown.+          -> Maybe [Language]+          -> PC.PkgConfigDb+          -> [ExamplePkgName]+          -> Solver+          -> Maybe Int+          -> IndependentGoals+          -> ReorderGoals+          -> AllowBootLibInstalls+          -> EnableBackjumping+          -> Maybe [ExampleVar]+          -> [ExConstraint]+          -> [ExPreference]+          -> EnableAllTests+          -> Progress String String CI.SolverInstallPlan.SolverInstallPlan+exResolve db exts langs pkgConfigDb targets solver mbj indepGoals reorder+          allowBootLibInstalls enableBj vars constraints prefs enableAllTests+    = resolveDependencies C.buildPlatform compiler pkgConfigDb solver params+  where+    defaultCompiler = C.unknownCompilerInfo C.buildCompilerId C.NoAbiTag+    compiler = defaultCompiler { C.compilerInfoExtensions = exts+                               , C.compilerInfoLanguages  = langs+                               }+    (inst, avai) = partitionEithers db+    instIdx      = exInstIdx inst+    avaiIdx      = SourcePackageDb {+                       packageIndex       = exAvIdx avai+                     , packagePreferences = Map.empty+                     }+    enableTests+        | asBool enableAllTests = fmap (\p -> PackageConstraint+                                              (scopeToplevel (C.mkPackageName p))+                                              (PackagePropertyStanzas [TestStanzas]))+                                       (exDbPkgs db)+        | otherwise             = []+    targets'     = fmap (\p -> NamedPackage (C.mkPackageName p) []) targets+    params       =   addConstraints (fmap toConstraint constraints)+                   $ addConstraints (fmap toLpc enableTests)+                   $ addPreferences (fmap toPref prefs)+                   $ setIndependentGoals indepGoals+                   $ setReorderGoals reorder+                   $ setMaxBackjumps mbj+                   $ setAllowBootLibInstalls allowBootLibInstalls+                   $ setEnableBackjumping enableBj+                   $ setGoalOrder goalOrder+                   $ standardInstallPolicy instIdx avaiIdx targets'+    toLpc     pc = LabeledPackageConstraint pc ConstraintSourceUnknown++    toConstraint (ExVersionConstraint scope v) =+        toLpc $ PackageConstraint scope (PackagePropertyVersion v)+    toConstraint (ExFlagConstraint scope fn b) =+        toLpc $ PackageConstraint scope (PackagePropertyFlags [(C.mkFlagName fn, b)])++    toPref (ExPkgPref n v)          = PackageVersionPreference (C.mkPackageName n) v+    toPref (ExStanzaPref n stanzas) = PackageStanzasPreference (C.mkPackageName n) stanzas++    goalOrder :: Maybe (Variable P.QPN -> Variable P.QPN -> Ordering)+    goalOrder = (orderFromList . map toVariable) `fmap` vars++    -- Sort elements in the list ahead of elements not in the list. Otherwise,+    -- follow the order in the list.+    orderFromList :: Eq a => [a] -> a -> a -> Ordering+    orderFromList xs =+        comparing $ \x -> let i = elemIndex x xs in (isNothing i, i)++    toVariable :: ExampleVar -> Variable P.QPN+    toVariable (P q pn)        = PackageVar (toQPN q pn)+    toVariable (F q pn fn)     = FlagVar    (toQPN q pn) (C.mkFlagName fn)+    toVariable (S q pn stanza) = StanzaVar  (toQPN q pn) stanza++    toQPN :: ExampleQualifier -> ExamplePkgName -> P.QPN+    toQPN q pn = P.Q pp (C.mkPackageName pn)+      where+        pp = case q of+               None           -> P.PackagePath P.DefaultNamespace P.QualToplevel+               Indep x        -> P.PackagePath (P.Independent x) P.QualToplevel+               Setup p        -> P.PackagePath P.DefaultNamespace (P.QualSetup (C.mkPackageName p))+               IndepSetup x p -> P.PackagePath (P.Independent x) (P.QualSetup (C.mkPackageName p))++extractInstallPlan :: CI.SolverInstallPlan.SolverInstallPlan+                   -> [(ExamplePkgName, ExamplePkgVersion)]+extractInstallPlan = catMaybes . map confPkg . CI.SolverInstallPlan.toList+  where+    confPkg :: CI.SolverInstallPlan.SolverPlanPackage -> Maybe (String, Int)+    confPkg (CI.SolverInstallPlan.Configured pkg) = Just $ srcPkg pkg+    confPkg _                               = Nothing++    srcPkg :: SolverPackage UnresolvedPkgLoc -> (String, Int)+    srcPkg cpkg =+      let C.PackageIdentifier pn ver = packageInfoId (solverPkgSource cpkg)+      in (C.unPackageName pn, head (C.versionNumbers ver))++{-------------------------------------------------------------------------------+  Auxiliary+-------------------------------------------------------------------------------}++-- | Run Progress computation+runProgress :: Progress step e a -> ([step], Either e a)+runProgress = go+  where+    go (Step s p) = let (ss, result) = go p in (s:ss, result)+    go (Fail e)   = ([], Left e)+    go (Done a)   = ([], Right a)
+ tests/UnitTests/Distribution/Solver/Modular/DSL/TestCaseUtils.hs view
@@ -0,0 +1,204 @@+{-# LANGUAGE RecordWildCards #-}+-- | Utilities for creating HUnit test cases with the solver DSL.+module UnitTests.Distribution.Solver.Modular.DSL.TestCaseUtils (+    SolverTest+  , SolverResult(..)+  , independentGoals+  , allowBootLibInstalls+  , disableBackjumping+  , goalOrder+  , constraints+  , preferences+  , enableAllTests+  , solverSuccess+  , solverFailure+  , anySolverFailure+  , mkTest+  , mkTestExts+  , mkTestLangs+  , mkTestPCDepends+  , mkTestExtLangPC+  , runTest+  ) where++-- test-framework+import Test.Tasty as TF+import Test.Tasty.HUnit (testCase, assertEqual, assertBool)++-- Cabal+import Language.Haskell.Extension (Extension(..), Language(..))++-- cabal-install+import Distribution.Solver.Types.PkgConfigDb (PkgConfigDb, pkgConfigDbFromList)+import Distribution.Solver.Types.Settings+import Distribution.Client.Dependency (foldProgress)+import Distribution.Client.Dependency.Types+         ( Solver(Modular) )+import UnitTests.Distribution.Solver.Modular.DSL+import UnitTests.Options++-- | Combinator to turn on --independent-goals behavior, i.e. solve+-- for the goals as if we were solving for each goal independently.+independentGoals :: SolverTest -> SolverTest+independentGoals test = test { testIndepGoals = IndependentGoals True }++allowBootLibInstalls :: SolverTest -> SolverTest+allowBootLibInstalls test =+    test { testAllowBootLibInstalls = AllowBootLibInstalls True }++disableBackjumping :: SolverTest -> SolverTest+disableBackjumping test =+    test { testEnableBackjumping = EnableBackjumping False }++goalOrder :: [ExampleVar] -> SolverTest -> SolverTest+goalOrder order test = test { testGoalOrder = Just order }++constraints :: [ExConstraint] -> SolverTest -> SolverTest+constraints cs test = test { testConstraints = cs }++preferences :: [ExPreference] -> SolverTest -> SolverTest+preferences prefs test = test { testSoftConstraints = prefs }++enableAllTests :: SolverTest -> SolverTest+enableAllTests test = test { testEnableAllTests = EnableAllTests True }++{-------------------------------------------------------------------------------+  Solver tests+-------------------------------------------------------------------------------}++data SolverTest = SolverTest {+    testLabel          :: String+  , testTargets        :: [String]+  , testResult         :: SolverResult+  , testIndepGoals     :: IndependentGoals+  , testAllowBootLibInstalls :: AllowBootLibInstalls+  , testEnableBackjumping :: EnableBackjumping+  , testGoalOrder      :: Maybe [ExampleVar]+  , testConstraints    :: [ExConstraint]+  , testSoftConstraints :: [ExPreference]+  , testDb             :: ExampleDb+  , testSupportedExts  :: Maybe [Extension]+  , testSupportedLangs :: Maybe [Language]+  , testPkgConfigDb    :: PkgConfigDb+  , testEnableAllTests :: EnableAllTests+  }++-- | Expected result of a solver test.+data SolverResult = SolverResult {+    -- | The solver's log should satisfy this predicate. Note that we also print+    -- the log, so evaluating a large log here can cause a space leak.+    resultLogPredicate            :: [String] -> Bool,++    -- | Fails with an error message satisfying the predicate, or succeeds with+    -- the given plan.+    resultErrorMsgPredicateOrPlan :: Either (String -> Bool) [(String, Int)]+  }++solverSuccess :: [(String, Int)] -> SolverResult+solverSuccess = SolverResult (const True) . Right++solverFailure :: (String -> Bool) -> SolverResult+solverFailure = SolverResult (const True) . Left++-- | Can be used for test cases where we just want to verify that+-- they fail, but do not care about the error message.+anySolverFailure :: SolverResult+anySolverFailure = solverFailure (const True)++-- | Makes a solver test case, consisting of the following components:+--+--      1. An 'ExampleDb', representing the package database (both+--         installed and remote) we are doing dependency solving over,+--      2. A 'String' name for the test,+--      3. A list '[String]' of package names to solve for+--      4. The expected result, either 'Nothing' if there is no+--         satisfying solution, or a list '[(String, Int)]' of+--         packages to install, at which versions.+--+-- See 'UnitTests.Distribution.Solver.Modular.DSL' for how+-- to construct an 'ExampleDb', as well as definitions of 'db1' etc.+-- in this file.+mkTest :: ExampleDb+       -> String+       -> [String]+       -> SolverResult+       -> SolverTest+mkTest = mkTestExtLangPC Nothing Nothing []++mkTestExts :: [Extension]+           -> ExampleDb+           -> String+           -> [String]+           -> SolverResult+           -> SolverTest+mkTestExts exts = mkTestExtLangPC (Just exts) Nothing []++mkTestLangs :: [Language]+            -> ExampleDb+            -> String+            -> [String]+            -> SolverResult+            -> SolverTest+mkTestLangs langs = mkTestExtLangPC Nothing (Just langs) []++mkTestPCDepends :: [(String, String)]+                -> ExampleDb+                -> String+                -> [String]+                -> SolverResult+                -> SolverTest+mkTestPCDepends pkgConfigDb = mkTestExtLangPC Nothing Nothing pkgConfigDb++mkTestExtLangPC :: Maybe [Extension]+                -> Maybe [Language]+                -> [(String, String)]+                -> ExampleDb+                -> String+                -> [String]+                -> SolverResult+                -> SolverTest+mkTestExtLangPC exts langs pkgConfigDb db label targets result = SolverTest {+    testLabel          = label+  , testTargets        = targets+  , testResult         = result+  , testIndepGoals     = IndependentGoals False+  , testAllowBootLibInstalls = AllowBootLibInstalls False+  , testEnableBackjumping = EnableBackjumping True+  , testGoalOrder      = Nothing+  , testConstraints    = []+  , testSoftConstraints = []+  , testDb             = db+  , testSupportedExts  = exts+  , testSupportedLangs = langs+  , testPkgConfigDb    = pkgConfigDbFromList pkgConfigDb+  , testEnableAllTests = EnableAllTests False+  }++runTest :: SolverTest -> TF.TestTree+runTest SolverTest{..} = askOption $ \(OptionShowSolverLog showSolverLog) ->+    testCase testLabel $ do+      let progress = exResolve testDb testSupportedExts+                     testSupportedLangs testPkgConfigDb testTargets+                     Modular Nothing testIndepGoals (ReorderGoals False)+                     testAllowBootLibInstalls testEnableBackjumping testGoalOrder+                     testConstraints testSoftConstraints testEnableAllTests+          printMsg msg = if showSolverLog+                         then putStrLn msg+                         else return ()+          msgs = foldProgress (:) (const []) (const []) progress+      assertBool ("Unexpected solver log:\n" ++ unlines msgs) $+                 resultLogPredicate testResult $ concatMap lines msgs+      result <- foldProgress ((>>) . printMsg) (return . Left) (return . Right) progress+      case result of+        Left  err  -> assertBool ("Unexpected error:\n" ++ err)+                                 (checkErrorMsg testResult err)+        Right plan -> assertEqual "" (toMaybe testResult) (Just (extractInstallPlan plan))+  where+    toMaybe :: SolverResult -> Maybe [(String, Int)]+    toMaybe = either (const Nothing) Just . resultErrorMsgPredicateOrPlan++    checkErrorMsg :: SolverResult -> String -> Bool+    checkErrorMsg result msg =+        case resultErrorMsgPredicateOrPlan result of+          Left f  -> f msg+          Right _ -> False
+ tests/UnitTests/Distribution/Solver/Modular/MemoryUsage.hs view
@@ -0,0 +1,97 @@+-- | Tests for detecting space leaks in the dependency solver.+module UnitTests.Distribution.Solver.Modular.MemoryUsage (tests) where++import Test.Tasty (TestTree)++import UnitTests.Distribution.Solver.Modular.DSL+import UnitTests.Distribution.Solver.Modular.DSL.TestCaseUtils++tests :: [TestTree]+tests = [+      runTest $ basicTest "basic space leak test"+    , runTest $ flagsTest "package with many flags"+    , runTest $ issue2899 "issue #2899"+    ]++-- | This test solves for n packages that each have two versions. There is no+-- solution, because the nth package depends on another package that doesn't fit+-- its version constraint. Backjumping is disabled, so the solver must explore a+-- search tree of size 2^n. It should fail if memory usage is proportional to+-- the size of the tree.+basicTest :: String -> SolverTest+basicTest name =+    disableBackjumping $ mkTest pkgs name ["target"] anySolverFailure+  where+    n :: Int+    n = 18++    pkgs :: ExampleDb+    pkgs = map Right $+           [ exAv "target" 1 [ExAny $ pkgName 1]]+        ++ [ exAv (pkgName i) v [ExRange (pkgName $ i + 1) 2 4]+           | i <- [1..n], v <- [2, 3]]+        ++ [exAv (pkgName $ n + 1) 1 []]++    pkgName :: Int -> ExamplePkgName+    pkgName x = "pkg-" ++ show x++-- | This test is similar to 'basicTest', except that it has one package with n+-- flags, flag-1 through flag-n. The solver assigns flags in order, so it+-- doesn't discover the unknown dependencies under flag-n until it has assigned+-- all of the flags. It has to explore the whole search tree.+flagsTest :: String -> SolverTest+flagsTest name =+    disableBackjumping $+    goalOrder orderedFlags $ mkTest pkgs name ["pkg"] anySolverFailure+  where+    n :: Int+    n = 16++    pkgs :: ExampleDb+    pkgs = [Right $ exAv "pkg" 1 $+                [exFlagged (flagName n) [ExAny "unknown1"] [ExAny "unknown2"]]++                -- The remaining flags have no effect:+             ++ [exFlagged (flagName i) [] [] | i <- [1..n - 1]]+           ]++    flagName :: Int -> ExampleFlagName+    flagName x = "flag-" ++ show x++    orderedFlags :: [ExampleVar]+    orderedFlags = [F None "pkg" (flagName i) | i <- [1..n]]++-- | Test for a space leak caused by sharing of search trees under packages with+-- link choices (issue #2899).+--+-- The goal order is fixed so that the solver chooses setup-dep and then+-- target-setup.setup-dep at the top of the search tree. target-setup.setup-dep+-- has two choices: link to setup-dep, and don't link to setup-dep. setup-dep+-- has a long chain of dependencies (pkg-1 through pkg-n). However, pkg-n+-- depends on pkg-n+1, which doesn't exist, so there is no solution. Since each+-- dependency has two versions, the solver must try 2^n combinations when+-- backjumping is disabled. These combinations create large search trees under+-- each of the two choices for target-setup.setup-dep. Although the choice to+-- not link is disallowed by the Single Instance Restriction, the solver doesn't+-- know that until it has explored (and evaluated) the whole tree under the+-- choice to link. If the two trees are shared, memory usage spikes.+issue2899 :: String -> SolverTest+issue2899 name =+    disableBackjumping $+    goalOrder goals $ mkTest pkgs name ["target"] anySolverFailure+  where+    n :: Int+    n = 16++    pkgs :: ExampleDb+    pkgs = map Right $+           [ exAv "target" 1 [ExAny "setup-dep"] `withSetupDeps` [ExAny "setup-dep"]+           , exAv "setup-dep" 1 [ExAny $ pkgName 1]]+        ++ [ exAv (pkgName i) v [ExAny $ pkgName (i + 1)]+           | i <- [1..n], v <- [1, 2]]++    pkgName :: Int -> ExamplePkgName+    pkgName x = "pkg-" ++ show x++    goals :: [ExampleVar]+    goals = [P None "setup-dep", P (Setup "target") "setup-dep"]
+ tests/UnitTests/Distribution/Solver/Modular/PSQ.hs view
@@ -0,0 +1,22 @@+module UnitTests.Distribution.Solver.Modular.PSQ (+  tests+  ) where++import Distribution.Solver.Modular.PSQ++import Test.Tasty+import Test.Tasty.QuickCheck++tests :: [TestTree]+tests = [ testProperty "splitsAltImplementation" splitsTest+        ]++-- | Original splits implementation+splits' :: PSQ k a -> PSQ k (a, PSQ k a)+splits' xs =+  casePSQ xs+    (PSQ [])+    (\ k v ys -> cons k (v, ys) (fmap (\ (w, zs) -> (w, cons k v zs)) (splits' ys)))++splitsTest :: [(Int, Int)] -> Bool+splitsTest psq = splits' (PSQ psq) == splits (PSQ psq)
+ tests/UnitTests/Distribution/Solver/Modular/QuickCheck.hs view
@@ -0,0 +1,369 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DeriveGeneric #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}++module UnitTests.Distribution.Solver.Modular.QuickCheck (tests) where++import Control.DeepSeq (NFData, force)+import Control.Monad (foldM)+import Data.Either (lefts)+import Data.Function (on)+import Data.List (groupBy, isInfixOf, nub, nubBy, sort)+import Data.Maybe (isJust)+import GHC.Generics (Generic)++#if !MIN_VERSION_base(4,8,0)+import Control.Applicative ((<$>), (<*>))+import Data.Monoid (Monoid)+#endif++import Text.Show.Pretty (parseValue, valToStr)++import Test.Tasty (TestTree)+import Test.Tasty.QuickCheck++import Distribution.Client.Dependency.Types+         ( Solver(..) )+import Distribution.Client.Setup (defaultMaxBackjumps)++import           Distribution.Types.UnqualComponentName++import qualified Distribution.Solver.Types.ComponentDeps as CD+import           Distribution.Solver.Types.ComponentDeps+                   ( Component(..), ComponentDep, ComponentDeps )+import           Distribution.Solver.Types.PkgConfigDb+                   (pkgConfigDbFromList)+import           Distribution.Solver.Types.Settings++import UnitTests.Distribution.Solver.Modular.DSL++tests :: [TestTree]+tests = [+      -- This test checks that certain solver parameters do not affect the+      -- existence of a solution. It runs the solver twice, and only sets those+      -- parameters on the second run. The test also applies parameters that+      -- can affect the existence of a solution to both runs.+      testProperty "target order and --reorder-goals do not affect solvability" $+          \(SolverTest db targets) targetOrder reorderGoals indepGoals solver ->+            let r1 = solve' (ReorderGoals False) targets  db+                r2 = solve' reorderGoals         targets2 db+                solve' reorder = solve (EnableBackjumping True) reorder+                                       indepGoals solver+                targets2 = case targetOrder of+                             SameOrder -> targets+                             ReverseOrder -> reverse targets+            in counterexample (showResults r1 r2) $+               noneReachedBackjumpLimit [r1, r2] ==>+               isRight (resultPlan r1) === isRight (resultPlan r2)++    , testProperty+          "solvable without --independent-goals => solvable with --independent-goals" $+          \(SolverTest db targets) reorderGoals solver ->+            let r1 = solve' (IndependentGoals False) targets db+                r2 = solve' (IndependentGoals True)  targets db+                solve' indep = solve (EnableBackjumping True)+                                     reorderGoals indep solver+             in counterexample (showResults r1 r2) $+                noneReachedBackjumpLimit [r1, r2] ==>+                isRight (resultPlan r1) `implies` isRight (resultPlan r2)++    , testProperty "backjumping does not affect solvability" $+          \(SolverTest db targets) reorderGoals indepGoals ->+            let r1 = solve' (EnableBackjumping True)  targets db+                r2 = solve' (EnableBackjumping False) targets db+                solve' enableBj = solve enableBj reorderGoals indepGoals Modular+             in counterexample (showResults r1 r2) $+                noneReachedBackjumpLimit [r1, r2] ==>+                isRight (resultPlan r1) === isRight (resultPlan r2)+    ]+  where+    noneReachedBackjumpLimit :: [Result] -> Bool+    noneReachedBackjumpLimit =+        not . any (\r -> resultPlan r == Left BackjumpLimitReached)++    showResults :: Result -> Result -> String+    showResults r1 r2 = showResult 1 r1 ++ showResult 2 r2++    showResult :: Int -> Result -> String+    showResult n result =+        unlines $ ["", "Run " ++ show n ++ ":"]+               ++ resultLog result+               ++ ["result: " ++ show (resultPlan result)]++    implies :: Bool -> Bool -> Bool+    implies x y = not x || y++    isRight :: Either a b -> Bool+    isRight (Right _) = True+    isRight _         = False++solve :: EnableBackjumping -> ReorderGoals -> IndependentGoals+      -> Solver -> [PN] -> TestDb -> Result+solve enableBj reorder indep solver targets (TestDb db) =+  let (lg, result) =+        runProgress $ exResolve db Nothing Nothing+                  (pkgConfigDbFromList [])+                  (map unPN targets)+                  solver+                  -- The backjump limit prevents individual tests from using+                  -- too much time and memory.+                  (Just defaultMaxBackjumps)+                  indep reorder (AllowBootLibInstalls False) enableBj Nothing [] []+                  (EnableAllTests True)++      failure :: String -> Failure+      failure msg+        | "Backjump limit reached" `isInfixOf` msg = BackjumpLimitReached+        | otherwise                                = OtherFailure+  in Result {+       resultLog = lg+     , resultPlan =+         -- Force the result so that we check for internal errors when we check+         -- for success or failure. See D.C.Dependency.validateSolverResult.+         force $ either (Left . failure) (Right . extractInstallPlan) result+     }++-- | How to modify the order of the input targets.+data TargetOrder = SameOrder | ReverseOrder+  deriving Show++instance Arbitrary TargetOrder where+  arbitrary = elements [SameOrder, ReverseOrder]++  shrink SameOrder = []+  shrink ReverseOrder = [SameOrder]++data Result = Result {+    resultLog :: [String]+  , resultPlan :: Either Failure [(ExamplePkgName, ExamplePkgVersion)]+  }++data Failure = BackjumpLimitReached | OtherFailure+  deriving (Eq, Generic, Show)++instance NFData Failure++-- | Package name.+newtype PN = PN { unPN :: String }+  deriving (Eq, Ord, Show)++instance Arbitrary PN where+  arbitrary = PN <$> elements ("base" : [[pn] | pn <- ['A'..'G']])++-- | Package version.+newtype PV = PV { unPV :: Int }+  deriving (Eq, Ord, Show)++instance Arbitrary PV where+  arbitrary = PV <$> elements [1..10]++type TestPackage = Either ExampleInstalled ExampleAvailable++getName :: TestPackage -> PN+getName = PN . either exInstName exAvName++getVersion :: TestPackage -> PV+getVersion = PV . either exInstVersion exAvVersion++data SolverTest = SolverTest {+    testDb :: TestDb+  , testTargets :: [PN]+  }++-- | Pretty-print the test when quickcheck calls 'show'.+instance Show SolverTest where+  show test =+    let str = "SolverTest {testDb = " ++ show (testDb test)+                     ++ ", testTargets = " ++ show (testTargets test) ++ "}"+    in maybe str valToStr $ parseValue str++instance Arbitrary SolverTest where+  arbitrary = do+    db <- arbitrary+    let pkgs = nub $ map getName (unTestDb db)+    Positive n <- arbitrary+    targets <- randomSubset n pkgs+    return (SolverTest db targets)++  shrink test =+         [test { testDb = db } | db <- shrink (testDb test)]+      ++ [test { testTargets = targets } | targets <- shrink (testTargets test)]++-- | Collection of source and installed packages.+newtype TestDb = TestDb { unTestDb :: ExampleDb }+  deriving Show++instance Arbitrary TestDb where+  arbitrary = do+      -- Avoid cyclic dependencies by grouping packages by name and only+      -- allowing each package to depend on packages in the groups before it.+      groupedPkgs <- shuffle . groupBy ((==) `on` fst) . nub . sort =<<+                     boundedListOf 10 arbitrary+      db <- foldM nextPkgs (TestDb []) groupedPkgs+      TestDb <$> shuffle (unTestDb db)+    where+      nextPkgs :: TestDb -> [(PN, PV)] -> Gen TestDb+      nextPkgs db pkgs = TestDb . (++ unTestDb db) <$> mapM (nextPkg db) pkgs++      nextPkg :: TestDb -> (PN, PV) -> Gen TestPackage+      nextPkg db (pn, v) = do+        installed <- arbitrary+        if installed+        then Left <$> arbitraryExInst pn v (lefts $ unTestDb db)+        else Right <$> arbitraryExAv pn v db++  shrink (TestDb pkgs) = map TestDb $ shrink pkgs++arbitraryExAv :: PN -> PV -> TestDb -> Gen ExampleAvailable+arbitraryExAv pn v db =+    (\cds -> ExAv (unPN pn) (unPV v) cds []) <$> arbitraryComponentDeps db++arbitraryExInst :: PN -> PV -> [ExampleInstalled] -> Gen ExampleInstalled+arbitraryExInst pn v pkgs = do+  hash <- vectorOf 10 $ elements $ ['a'..'z'] ++ ['A'..'Z'] ++ ['0'..'9']+  numDeps <- min 3 <$> arbitrary+  deps <- randomSubset numDeps pkgs+  return $ ExInst (unPN pn) (unPV v) hash (map exInstHash deps)++arbitraryComponentDeps :: TestDb -> Gen (ComponentDeps [ExampleDependency])+arbitraryComponentDeps (TestDb []) = return $ CD.fromList []+arbitraryComponentDeps db =+    -- dedupComponentNames removes components with duplicate names, for example,+    -- 'ComponentExe x' and 'ComponentTest x', and then CD.fromList combines+    -- duplicate unnamed components.+    CD.fromList . dedupComponentNames <$>+    boundedListOf 5 (arbitraryComponentDep db)+  where+    dedupComponentNames =+        nubBy ((\x y -> isJust x && isJust y && x == y) `on` componentName . fst)++    componentName :: Component -> Maybe UnqualComponentName+    componentName ComponentLib        = Nothing+    componentName ComponentSetup      = Nothing+    componentName (ComponentSubLib n) = Just n+    componentName (ComponentFLib   n) = Just n+    componentName (ComponentExe    n) = Just n+    componentName (ComponentTest   n) = Just n+    componentName (ComponentBench  n) = Just n++arbitraryComponentDep :: TestDb -> Gen (ComponentDep [ExampleDependency])+arbitraryComponentDep db = do+  comp <- arbitrary+  deps <- case comp of+            ComponentSetup -> smallListOf (arbitraryExDep db SetupDep)+            _              -> boundedListOf 5 (arbitraryExDep db NonSetupDep)+  return (comp, deps)++-- | Location of an 'ExampleDependency'. It determines which values are valid.+data ExDepLocation = SetupDep | NonSetupDep++arbitraryExDep :: TestDb -> ExDepLocation -> Gen ExampleDependency+arbitraryExDep db@(TestDb pkgs) level =+  let flag = ExFlagged <$> arbitraryFlagName+                       <*> arbitraryDeps db+                       <*> arbitraryDeps db+      other =+          -- Package checks require dependencies on "base" to have bounds.+        let notBase = filter ((/= PN "base") . getName) pkgs+        in  [ExAny . unPN <$> elements (map getName notBase) | not (null notBase)]+         ++ [+              -- existing version+              let fixed pkg = ExFix (unPN $ getName pkg) (unPV $ getVersion pkg)+              in fixed <$> elements pkgs++              -- random version of an existing package+            , ExFix . unPN . getName <$> elements pkgs <*> (unPV <$> arbitrary)+            ]+  in oneof $+      case level of+        NonSetupDep -> flag : other+        SetupDep -> other++arbitraryDeps :: TestDb -> Gen Dependencies+arbitraryDeps db = frequency+    [ (1, return NotBuildable)+    , (20, Buildable <$> smallListOf (arbitraryExDep db NonSetupDep))+    ]++arbitraryFlagName :: Gen String+arbitraryFlagName = (:[]) <$> elements ['A'..'E']++instance Arbitrary ReorderGoals where+  arbitrary = ReorderGoals <$> arbitrary++  shrink (ReorderGoals reorder) = [ReorderGoals False | reorder]++instance Arbitrary IndependentGoals where+  arbitrary = IndependentGoals <$> arbitrary++  shrink (IndependentGoals indep) = [IndependentGoals False | indep]++instance Arbitrary Solver where+  arbitrary = return Modular++  shrink Modular = []++instance Arbitrary UnqualComponentName where+  arbitrary = mkUnqualComponentName <$> (:[]) <$> elements "ABC"++instance Arbitrary Component where+  arbitrary = oneof [ return ComponentLib+                    , ComponentSubLib <$> arbitrary+                    , ComponentExe <$> arbitrary+                    , ComponentFLib <$> arbitrary+                    , ComponentTest <$> arbitrary+                    , ComponentBench <$> arbitrary+                    , return ComponentSetup+                    ]++  shrink ComponentLib = []+  shrink _ = [ComponentLib]++instance Arbitrary ExampleInstalled where+  arbitrary = error "arbitrary not implemented: ExampleInstalled"++  shrink ei = [ ei { exInstBuildAgainst = deps }+              | deps <- shrinkList shrinkNothing (exInstBuildAgainst ei)]++instance Arbitrary ExampleAvailable where+  arbitrary = error "arbitrary not implemented: ExampleAvailable"++  shrink ea = [ea { exAvDeps = deps } | deps <- shrink (exAvDeps ea)]++instance (Arbitrary a, Monoid a) => Arbitrary (ComponentDeps a) where+  arbitrary = error "arbitrary not implemented: ComponentDeps"++  shrink = map CD.fromList . shrink . CD.toList++instance Arbitrary ExampleDependency where+  arbitrary = error "arbitrary not implemented: ExampleDependency"++  shrink (ExAny _) = []+  shrink (ExFix "base" _) = [] -- preserve bounds on base+  shrink (ExFix pn _) = [ExAny pn]+  shrink (ExFlagged flag th el) =+         deps th ++ deps el+      ++ [ExFlagged flag th' el | th' <- shrink th]+      ++ [ExFlagged flag th el' | el' <- shrink el]+    where+      deps NotBuildable = []+      deps (Buildable ds) = ds+  shrink dep = error $ "Dependency not handled: " ++ show dep++instance Arbitrary Dependencies where+  arbitrary = error "arbitrary not implemented: Dependencies"++  shrink NotBuildable = [Buildable []]+  shrink (Buildable deps) = map Buildable (shrink deps)++randomSubset :: Int -> [a] -> Gen [a]+randomSubset n xs = take n <$> shuffle xs++boundedListOf :: Int -> Gen a -> Gen [a]+boundedListOf n gen = take n <$> listOf gen++-- | Generates lists with average length less than 1.+smallListOf :: Gen a -> Gen [a]+smallListOf gen =+    frequency [ (fr, vectorOf n gen)+              | (fr, n) <- [(3, 0), (5, 1), (2, 2)]]
+ tests/UnitTests/Distribution/Solver/Modular/RetryLog.hs view
@@ -0,0 +1,72 @@+{-# LANGUAGE StandaloneDeriving #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}+module UnitTests.Distribution.Solver.Modular.RetryLog (+  tests+  ) where++import Distribution.Solver.Modular.Message+import Distribution.Solver.Modular.RetryLog+import Distribution.Solver.Types.Progress++import Test.Tasty (TestTree)+import Test.Tasty.HUnit (testCase, (@?=))+import Test.Tasty.QuickCheck+         ( Arbitrary(..), Blind(..), listOf, oneof, testProperty, (===))++type Log a = Progress a String String++tests :: [TestTree]+tests = [+    testProperty "'toProgress . fromProgress' is identity" $ \p ->+        toProgress (fromProgress p) === (p :: Log Int)++  , testProperty "'mapFailure f' is like 'foldProgress Step (Fail . f) Done'" $+        let mapFailureProgress f = foldProgress Step (Fail . f) Done+        in \(Blind f) p ->+               toProgress (mapFailure f (fromProgress p))+               === mapFailureProgress (f :: String -> Int) (p :: Log Int)++  , testProperty "'retry p f' is like 'foldProgress Step f Done p'" $+      \p (Blind f) ->+        toProgress (retry (fromProgress p) (fromProgress . f))+        === (foldProgress Step f Done (p :: Log Int) :: Log Int)++  , testProperty "failWith" $ \step failure ->+        toProgress (failWith step failure)+        === (Step step (Fail failure) :: Log Int)++  , testProperty "succeedWith" $ \step success ->+        toProgress (succeedWith step success)+        === (Step step (Done success) :: Log Int)++  , testProperty "continueWith" $ \step p ->+        toProgress (continueWith step (fromProgress p))+        === (Step step p :: Log Int)++  , testCase "tryWith with failure" $+        let failure = Fail "Error"+            s = Step Success+        in toProgress (tryWith Success $ fromProgress (s (s failure)))+           @?= (s (Step Enter (s (s (Step Leave failure)))) :: Log Message)++  , testCase "tryWith with success" $+        let done = Done "Done"+            s = Step Success+        in toProgress (tryWith Success $ fromProgress (s (s done)))+           @?= (s (Step Enter (s (s done))) :: Log Message)+  ]++instance (Arbitrary step, Arbitrary fail, Arbitrary done)+    => Arbitrary (Progress step fail done) where+  arbitrary = do+    steps <- listOf arbitrary+    end <- oneof [Fail `fmap` arbitrary, Done `fmap` arbitrary]+    return $ foldr Step end steps++deriving instance (Eq step, Eq fail, Eq done) => Eq (Progress step fail done)++deriving instance (Show step, Show fail, Show done)+    => Show (Progress step fail done)++deriving instance Eq Message+deriving instance Show Message
+ tests/UnitTests/Distribution/Solver/Modular/Solver.hs view
@@ -0,0 +1,1231 @@+{-# LANGUAGE OverloadedStrings #-}+-- | This is a set of unit tests for the dependency solver,+-- which uses the solver DSL ("UnitTests.Distribution.Solver.Modular.DSL")+-- to more conveniently create package databases to run the solver tests on.+module UnitTests.Distribution.Solver.Modular.Solver (tests)+       where++-- base+import Data.List (isInfixOf)++import qualified Distribution.Version as V++-- test-framework+import Test.Tasty as TF++-- Cabal+import Language.Haskell.Extension ( Extension(..)+                                  , KnownExtension(..), Language(..))++-- cabal-install+import Distribution.Solver.Types.Flag+import Distribution.Solver.Types.OptionalStanza+import Distribution.Solver.Types.PackageConstraint+import Distribution.Solver.Types.PackagePath+import UnitTests.Distribution.Solver.Modular.DSL+import UnitTests.Distribution.Solver.Modular.DSL.TestCaseUtils++tests :: [TF.TestTree]+tests = [+      testGroup "Simple dependencies" [+          runTest $         mkTest db1 "alreadyInstalled"   ["A"]      (solverSuccess [])+        , runTest $         mkTest db1 "installLatest"      ["B"]      (solverSuccess [("B", 2)])+        , runTest $         mkTest db1 "simpleDep1"         ["C"]      (solverSuccess [("B", 1), ("C", 1)])+        , runTest $         mkTest db1 "simpleDep2"         ["D"]      (solverSuccess [("B", 2), ("D", 1)])+        , runTest $         mkTest db1 "failTwoVersions"    ["C", "D"] anySolverFailure+        , runTest $ indep $ mkTest db1 "indepTwoVersions"   ["C", "D"] (solverSuccess [("B", 1), ("B", 2), ("C", 1), ("D", 1)])+        , runTest $ indep $ mkTest db1 "aliasWhenPossible1" ["C", "E"] (solverSuccess [("B", 1), ("C", 1), ("E", 1)])+        , runTest $ indep $ mkTest db1 "aliasWhenPossible2" ["D", "E"] (solverSuccess [("B", 2), ("D", 1), ("E", 1)])+        , runTest $ indep $ mkTest db2 "aliasWhenPossible3" ["C", "D"] (solverSuccess [("A", 1), ("A", 2), ("B", 1), ("B", 2), ("C", 1), ("D", 1)])+        , runTest $         mkTest db1 "buildDepAgainstOld" ["F"]      (solverSuccess [("B", 1), ("E", 1), ("F", 1)])+        , runTest $         mkTest db1 "buildDepAgainstNew" ["G"]      (solverSuccess [("B", 2), ("E", 1), ("G", 1)])+        , runTest $ indep $ mkTest db1 "multipleInstances"  ["F", "G"] anySolverFailure+        , runTest $         mkTest db21 "unknownPackage1"   ["A"]      (solverSuccess [("A", 1), ("B", 1)])+        , runTest $         mkTest db22 "unknownPackage2"   ["A"]      (solverFailure (isInfixOf "unknown package: C"))+        , runTest $         mkTest db23 "unknownPackage3"   ["A"]      (solverFailure (isInfixOf "unknown package: B"))+        ]+    , testGroup "Flagged dependencies" [+          runTest $         mkTest db3 "forceFlagOn"  ["C"]      (solverSuccess [("A", 1), ("B", 1), ("C", 1)])+        , runTest $         mkTest db3 "forceFlagOff" ["D"]      (solverSuccess [("A", 2), ("B", 1), ("D", 1)])+        , runTest $ indep $ mkTest db3 "linkFlags1"   ["C", "D"] anySolverFailure+        , runTest $ indep $ mkTest db4 "linkFlags2"   ["C", "D"] anySolverFailure+        , runTest $ indep $ mkTest db18 "linkFlags3"  ["A", "B"] (solverSuccess [("A", 1), ("B", 1), ("C", 1), ("D", 1), ("D", 2), ("F", 1)])+        ]+    , testGroup "Manual flags" [+          runTest $ mkTest dbManualFlags "Use default value for manual flag" ["pkg"] $+          solverSuccess [("pkg", 1), ("true-dep", 1)]++        , let checkFullLog =+                  any $ isInfixOf "rejecting: pkg-1.0.0:-flag (manual flag can only be changed explicitly)"+          in runTest $ constraints [ExVersionConstraint (ScopeAnyQualifier "true-dep") V.noVersion] $+             mkTest dbManualFlags "Don't toggle manual flag to avoid conflict" ["pkg"] $+             -- TODO: We should check the summarized log instead of the full log+             -- for the manual flags error message, but it currently only+             -- appears in the full log.+             SolverResult checkFullLog (Left $ const True)++        , let cs = [ExFlagConstraint (ScopeAnyQualifier "pkg") "flag" False]+          in runTest $ constraints cs $+             mkTest dbManualFlags "Toggle manual flag with flag constraint" ["pkg"] $+             solverSuccess [("false-dep", 1), ("pkg", 1)]+        ]+    , testGroup "Qualified manual flag constraints" [+          let name = "Top-level flag constraint does not constrain setup dep's flag"+              cs = [ExFlagConstraint (ScopeQualified QualToplevel "B") "flag" False]+          in runTest $ constraints cs $ mkTest dbSetupDepWithManualFlag name ["A"] $+             solverSuccess [ ("A", 1), ("B", 1), ("B", 2)+                           , ("b-1-false-dep", 1), ("b-2-true-dep", 1) ]++        , let name = "Solver can toggle setup dep's flag to match top-level constraint"+              cs = [ ExFlagConstraint (ScopeQualified QualToplevel "B") "flag" False+                   , ExVersionConstraint (ScopeAnyQualifier "b-2-true-dep") V.noVersion ]+          in runTest $ constraints cs $ mkTest dbSetupDepWithManualFlag name ["A"] $+             solverSuccess [ ("A", 1), ("B", 1), ("B", 2)+                           , ("b-1-false-dep", 1), ("b-2-false-dep", 1) ]++        , let name = "User can constrain flags separately with qualified constraints"+              cs = [ ExFlagConstraint (ScopeQualified QualToplevel    "B") "flag" True+                   , ExFlagConstraint (ScopeQualified (QualSetup "A") "B") "flag" False ]+          in runTest $ constraints cs $ mkTest dbSetupDepWithManualFlag name ["A"] $+             solverSuccess [ ("A", 1), ("B", 1), ("B", 2)+                           , ("b-1-true-dep", 1), ("b-2-false-dep", 1) ]++          -- Regression test for #4299+        , let name = "Solver can link deps when only one has constrained manual flag"+              cs = [ExFlagConstraint (ScopeQualified QualToplevel "B") "flag" False]+          in runTest $ constraints cs $ mkTest dbLinkedSetupDepWithManualFlag name ["A"] $+             solverSuccess [ ("A", 1), ("B", 1), ("b-1-false-dep", 1) ]++        , let name = "Solver cannot link deps that have conflicting manual flag constraints"+              cs = [ ExFlagConstraint (ScopeQualified QualToplevel    "B") "flag" True+                   , ExFlagConstraint (ScopeQualified (QualSetup "A") "B") "flag" False ]+              failureReason = "(constraint from unknown source requires opposite flag selection)"+              checkFullLog lns =+                  all (\msg -> any (msg `isInfixOf`) lns)+                  [ "rejecting: B-1.0.0:-flag "         ++ failureReason+                  , "rejecting: A:setup.B-1.0.0:+flag " ++ failureReason ]+          in runTest $ constraints cs $+             mkTest dbLinkedSetupDepWithManualFlag name ["A"] $+             SolverResult checkFullLog (Left $ const True)+        ]+    , testGroup "Stanzas" [+          runTest $         enableAllTests $ mkTest db5 "simpleTest1" ["C"]      (solverSuccess [("A", 2), ("C", 1)])+        , runTest $         enableAllTests $ mkTest db5 "simpleTest2" ["D"]      anySolverFailure+        , runTest $         enableAllTests $ mkTest db5 "simpleTest3" ["E"]      (solverSuccess [("A", 1), ("E", 1)])+        , runTest $         enableAllTests $ mkTest db5 "simpleTest4" ["F"]      anySolverFailure -- TODO+        , runTest $         enableAllTests $ mkTest db5 "simpleTest5" ["G"]      (solverSuccess [("A", 2), ("G", 1)])+        , runTest $         enableAllTests $ mkTest db5 "simpleTest6" ["E", "G"] anySolverFailure+        , runTest $ indep $ enableAllTests $ mkTest db5 "simpleTest7" ["E", "G"] (solverSuccess [("A", 1), ("A", 2), ("E", 1), ("G", 1)])+        , runTest $         enableAllTests $ mkTest db6 "depsWithTests1" ["C"]      (solverSuccess [("A", 1), ("B", 1), ("C", 1)])+        , runTest $ indep $ enableAllTests $ mkTest db6 "depsWithTests2" ["C", "D"] (solverSuccess [("A", 1), ("B", 1), ("C", 1), ("D", 1)])+        ]+    , testGroup "Setup dependencies" [+          runTest $         mkTest db7  "setupDeps1" ["B"] (solverSuccess [("A", 2), ("B", 1)])+        , runTest $         mkTest db7  "setupDeps2" ["C"] (solverSuccess [("A", 2), ("C", 1)])+        , runTest $         mkTest db7  "setupDeps3" ["D"] (solverSuccess [("A", 1), ("D", 1)])+        , runTest $         mkTest db7  "setupDeps4" ["E"] (solverSuccess [("A", 1), ("A", 2), ("E", 1)])+        , runTest $         mkTest db7  "setupDeps5" ["F"] (solverSuccess [("A", 1), ("A", 2), ("F", 1)])+        , runTest $         mkTest db8  "setupDeps6" ["C", "D"] (solverSuccess [("A", 1), ("B", 1), ("B", 2), ("C", 1), ("D", 1)])+        , runTest $         mkTest db9  "setupDeps7" ["F", "G"] (solverSuccess [("A", 1), ("B", 1), ("B",2 ), ("C", 1), ("D", 1), ("E", 1), ("E", 2), ("F", 1), ("G", 1)])+        , runTest $         mkTest db10 "setupDeps8" ["C"] (solverSuccess [("C", 1)])+        , runTest $ indep $ mkTest dbSetupDeps "setupDeps9" ["A", "B"] (solverSuccess [("A", 1), ("B", 1), ("C", 1), ("D", 1), ("D", 2)])+        ]+    , testGroup "Base shim" [+          runTest $ mkTest db11 "baseShim1" ["A"] (solverSuccess [("A", 1)])+        , runTest $ mkTest db12 "baseShim2" ["A"] (solverSuccess [("A", 1)])+        , runTest $ mkTest db12 "baseShim3" ["B"] (solverSuccess [("B", 1)])+        , runTest $ mkTest db12 "baseShim4" ["C"] (solverSuccess [("A", 1), ("B", 1), ("C", 1)])+        , runTest $ mkTest db12 "baseShim5" ["D"] anySolverFailure+        , runTest $ mkTest db12 "baseShim6" ["E"] (solverSuccess [("E", 1), ("syb", 2)])+        ]+    , testGroup "Base" [+          runTest $ mkTest dbBase "Refuse to install base without --allow-boot-library-installs" ["base"] $+                      solverFailure (isInfixOf "only already installed instances can be used")+        , runTest $ allowBootLibInstalls $ mkTest dbBase "Install base with --allow-boot-library-installs" ["base"] $+                      solverSuccess [("base", 1), ("ghc-prim", 1), ("integer-gmp", 1), ("integer-simple", 1)]+        ]+    , testGroup "Cycles" [+          runTest $ mkTest db14 "simpleCycle1"          ["A"]      anySolverFailure+        , runTest $ mkTest db14 "simpleCycle2"          ["A", "B"] anySolverFailure+        , runTest $ mkTest db14 "cycleWithFlagChoice1"  ["C"]      (solverSuccess [("C", 1), ("E", 1)])+        , runTest $ mkTest db15 "cycleThroughSetupDep1" ["A"]      anySolverFailure+        , runTest $ mkTest db15 "cycleThroughSetupDep2" ["B"]      anySolverFailure+        , runTest $ mkTest db15 "cycleThroughSetupDep3" ["C"]      (solverSuccess [("C", 2), ("D", 1)])+        , runTest $ mkTest db15 "cycleThroughSetupDep4" ["D"]      (solverSuccess [("D", 1)])+        , runTest $ mkTest db15 "cycleThroughSetupDep5" ["E"]      (solverSuccess [("C", 2), ("D", 1), ("E", 1)])+        , runTest $ testCyclicDependencyErrorMessages "cyclic dependency error messages"+        ]+    , testGroup "Extensions" [+          runTest $ mkTestExts [EnableExtension CPP] dbExts1 "unsupported" ["A"] anySolverFailure+        , runTest $ mkTestExts [EnableExtension CPP] dbExts1 "unsupportedIndirect" ["B"] anySolverFailure+        , runTest $ mkTestExts [EnableExtension RankNTypes] dbExts1 "supported" ["A"] (solverSuccess [("A",1)])+        , runTest $ mkTestExts (map EnableExtension [CPP,RankNTypes]) dbExts1 "supportedIndirect" ["C"] (solverSuccess [("A",1),("B",1), ("C",1)])+        , runTest $ mkTestExts [EnableExtension CPP] dbExts1 "disabledExtension" ["D"] anySolverFailure+        , runTest $ mkTestExts (map EnableExtension [CPP,RankNTypes]) dbExts1 "disabledExtension" ["D"] anySolverFailure+        , runTest $ mkTestExts (UnknownExtension "custom" : map EnableExtension [CPP,RankNTypes]) dbExts1 "supportedUnknown" ["E"] (solverSuccess [("A",1),("B",1),("C",1),("E",1)])+        ]+    , testGroup "Languages" [+          runTest $ mkTestLangs [Haskell98] dbLangs1 "unsupported" ["A"] anySolverFailure+        , runTest $ mkTestLangs [Haskell98,Haskell2010] dbLangs1 "supported" ["A"] (solverSuccess [("A",1)])+        , runTest $ mkTestLangs [Haskell98] dbLangs1 "unsupportedIndirect" ["B"] anySolverFailure+        , runTest $ mkTestLangs [Haskell98, Haskell2010, UnknownLanguage "Haskell3000"] dbLangs1 "supportedUnknown" ["C"] (solverSuccess [("A",1),("B",1),("C",1)])+        ]+     , testGroup "Qualified Package Constraints" [+          runTest $ mkTest dbConstraints "install latest versions without constraints" ["A", "B", "C"] $+          solverSuccess [("A", 7), ("B", 8), ("C", 9), ("D", 7), ("D", 8), ("D", 9)]++        , let cs = [ ExVersionConstraint (ScopeAnyQualifier "D") $ mkVersionRange 1 4 ]+          in runTest $ constraints cs $+             mkTest dbConstraints "force older versions with unqualified constraint" ["A", "B", "C"] $+             solverSuccess [("A", 1), ("B", 2), ("C", 3), ("D", 1), ("D", 2), ("D", 3)]++        , let cs = [ ExVersionConstraint (ScopeQualified QualToplevel    "D") $ mkVersionRange 1 4+                   , ExVersionConstraint (ScopeQualified (QualSetup "B") "D") $ mkVersionRange 4 7+                   ]+          in runTest $ constraints cs $+             mkTest dbConstraints "force multiple versions with qualified constraints" ["A", "B", "C"] $+             solverSuccess [("A", 1), ("B", 5), ("C", 9), ("D", 1), ("D", 5), ("D", 9)]++        , let cs = [ ExVersionConstraint (ScopeAnySetupQualifier "D") $ mkVersionRange 1 4 ]+          in runTest $ constraints cs $+             mkTest dbConstraints "constrain package across setup scripts" ["A", "B", "C"] $+             solverSuccess [("A", 7), ("B", 2), ("C", 3), ("D", 2), ("D", 3), ("D", 7)]+        ]+     , testGroup "Package Preferences" [+          runTest $ preferences [ ExPkgPref "A" $ mkvrThis 1]      $ mkTest db13 "selectPreferredVersionSimple" ["A"] (solverSuccess [("A", 1)])+        , runTest $ preferences [ ExPkgPref "A" $ mkvrOrEarlier 2] $ mkTest db13 "selectPreferredVersionSimple2" ["A"] (solverSuccess [("A", 2)])+        , runTest $ preferences [ ExPkgPref "A" $ mkvrOrEarlier 2+                                , ExPkgPref "A" $ mkvrOrEarlier 1] $ mkTest db13 "selectPreferredVersionMultiple" ["A"] (solverSuccess [("A", 1)])+        , runTest $ preferences [ ExPkgPref "A" $ mkvrOrEarlier 1+                                , ExPkgPref "A" $ mkvrOrEarlier 2] $ mkTest db13 "selectPreferredVersionMultiple2" ["A"] (solverSuccess [("A", 1)])+        , runTest $ preferences [ ExPkgPref "A" $ mkvrThis 1+                                , ExPkgPref "A" $ mkvrThis 2] $ mkTest db13 "selectPreferredVersionMultiple3" ["A"] (solverSuccess [("A", 2)])+        , runTest $ preferences [ ExPkgPref "A" $ mkvrThis 1+                                , ExPkgPref "A" $ mkvrOrEarlier 2] $ mkTest db13 "selectPreferredVersionMultiple4" ["A"] (solverSuccess [("A", 1)])+        ]+     , testGroup "Stanza Preferences" [+          runTest $+          mkTest dbStanzaPreferences1 "disable tests by default" ["pkg"] $+          solverSuccess [("pkg", 1)]++        , runTest $ preferences [ExStanzaPref "pkg" [TestStanzas]] $+          mkTest dbStanzaPreferences1 "enable tests with testing preference" ["pkg"] $+          solverSuccess [("pkg", 1), ("test-dep", 1)]++        , runTest $ preferences [ExStanzaPref "pkg" [TestStanzas]] $+          mkTest dbStanzaPreferences2 "disable testing when it's not possible" ["pkg"] $+          solverSuccess [("pkg", 1)]+        ]+     , testGroup "Buildable Field" [+          testBuildable "avoid building component with unknown dependency" (ExAny "unknown")+        , testBuildable "avoid building component with unknown extension" (ExExt (UnknownExtension "unknown"))+        , testBuildable "avoid building component with unknown language" (ExLang (UnknownLanguage "unknown"))+        , runTest $ mkTest dbBuildable1 "choose flags that set buildable to false" ["pkg"] (solverSuccess [("flag1-false", 1), ("flag2-true", 1), ("pkg", 1)])+        , runTest $ mkTest dbBuildable2 "choose version that sets buildable to false" ["A"] (solverSuccess [("A", 1), ("B", 2)])+         ]+    , testGroup "Pkg-config dependencies" [+          runTest $ mkTestPCDepends [] dbPC1 "noPkgs" ["A"] anySolverFailure+        , runTest $ mkTestPCDepends [("pkgA", "0")] dbPC1 "tooOld" ["A"] anySolverFailure+        , runTest $ mkTestPCDepends [("pkgA", "1.0.0"), ("pkgB", "1.0.0")] dbPC1 "pruneNotFound" ["C"] (solverSuccess [("A", 1), ("B", 1), ("C", 1)])+        , runTest $ mkTestPCDepends [("pkgA", "1.0.0"), ("pkgB", "2.0.0")] dbPC1 "chooseNewest" ["C"] (solverSuccess [("A", 1), ("B", 2), ("C", 1)])+        ]+    , testGroup "Independent goals" [+          runTest $ indep $ mkTest db16 "indepGoals1" ["A", "B"] (solverSuccess [("A", 1), ("B", 1), ("C", 1), ("D", 1), ("D", 2), ("E", 1)])+        , runTest $ testIndepGoals2 "indepGoals2"+        , runTest $ testIndepGoals3 "indepGoals3"+        , runTest $ testIndepGoals4 "indepGoals4"+        , runTest $ testIndepGoals5 "indepGoals5 - fixed goal order" FixedGoalOrder+        , runTest $ testIndepGoals5 "indepGoals5 - default goal order" DefaultGoalOrder+        , runTest $ testIndepGoals6 "indepGoals6 - fixed goal order" FixedGoalOrder+        , runTest $ testIndepGoals6 "indepGoals6 - default goal order" DefaultGoalOrder+        ]+      -- Tests designed for the backjumping blog post+    , testGroup "Backjumping" [+          runTest $         mkTest dbBJ1a "bj1a" ["A"]      (solverSuccess [("A", 1), ("B",  1)])+        , runTest $         mkTest dbBJ1b "bj1b" ["A"]      (solverSuccess [("A", 1), ("B",  1)])+        , runTest $         mkTest dbBJ1c "bj1c" ["A"]      (solverSuccess [("A", 1), ("B",  1)])+        , runTest $         mkTest dbBJ2  "bj2"  ["A"]      (solverSuccess [("A", 1), ("B",  1), ("C", 1)])+        , runTest $         mkTest dbBJ3  "bj3"  ["A"]      (solverSuccess [("A", 1), ("Ba", 1), ("C", 1)])+        , runTest $         mkTest dbBJ4  "bj4"  ["A"]      (solverSuccess [("A", 1), ("B",  1), ("C", 1)])+        , runTest $         mkTest dbBJ5  "bj5"  ["A"]      (solverSuccess [("A", 1), ("B",  1), ("D", 1)])+        , runTest $         mkTest dbBJ6  "bj6"  ["A"]      (solverSuccess [("A", 1), ("B",  1)])+        , runTest $         mkTest dbBJ7  "bj7"  ["A"]      (solverSuccess [("A", 1), ("B",  1), ("C", 1)])+        , runTest $ indep $ mkTest dbBJ8  "bj8"  ["A", "B"] (solverSuccess [("A", 1), ("B",  1), ("C", 1)])+        ]+    -- Build-tools dependencies+    , testGroup "build-tools" [+          runTest $ mkTest dbBuildTools1 "bt1" ["A"] (solverSuccess [("A", 1), ("alex", 1)])+        , runTest $ mkTest dbBuildTools2 "bt2" ["A"] (solverSuccess [("A", 1)])+        , runTest $ mkTest dbBuildTools3 "bt3" ["C"] (solverSuccess [("A", 1), ("B", 1), ("C", 1), ("alex", 1), ("alex", 2)])+        , runTest $ mkTest dbBuildTools4 "bt4" ["B"] (solverSuccess [("A", 1), ("A", 2), ("B", 1), ("alex", 1)])+        , runTest $ mkTest dbBuildTools5 "bt5" ["A"] (solverSuccess [("A", 1), ("alex", 1), ("happy", 1)])+        , runTest $ mkTest dbBuildTools6 "bt6" ["B"] (solverSuccess [("A", 2), ("B", 2), ("warp", 1)])+        ]+      -- Tests for the contents of the solver's log+    , testGroup "Solver log" [+          -- See issue #3203. The solver should only choose a version for A once.+          runTest $+              let db = [Right $ exAv "A" 1 []]++                  p :: [String] -> Bool+                  p lg =    elem "targets: A" lg+                         && length (filter ("trying: A" `isInfixOf`) lg) == 1+              in mkTest db "deduplicate targets" ["A", "A"] $+                 SolverResult p $ Right [("A", 1)]+        , runTest $+              let db = [Right $ exAv "A" 1 [ExAny "B"]]+                  msg = "After searching the rest of the dependency tree exhaustively, "+                     ++ "these were the goals I've had most trouble fulfilling: A, B"+              in mkTest db "exhaustive search failure message" ["A"] $+                 solverFailure (isInfixOf msg)+        ]+    ]+  where+    indep           = independentGoals+    mkvrThis        = V.thisVersion . makeV+    mkvrOrEarlier   = V.orEarlierVersion . makeV+    makeV v         = V.mkVersion [v,0,0]++data GoalOrder = FixedGoalOrder | DefaultGoalOrder++{-------------------------------------------------------------------------------+  Specific example database for the tests+-------------------------------------------------------------------------------}++db1 :: ExampleDb+db1 =+    let a = exInst "A" 1 "A-1" []+    in [ Left a+       , Right $ exAv "B" 1 [ExAny "A"]+       , Right $ exAv "B" 2 [ExAny "A"]+       , Right $ exAv "C" 1 [ExFix "B" 1]+       , Right $ exAv "D" 1 [ExFix "B" 2]+       , Right $ exAv "E" 1 [ExAny "B"]+       , Right $ exAv "F" 1 [ExFix "B" 1, ExAny "E"]+       , Right $ exAv "G" 1 [ExFix "B" 2, ExAny "E"]+       , Right $ exAv "Z" 1 []+       ]++-- In this example, we _can_ install C and D as independent goals, but we have+-- to pick two diferent versions for B (arbitrarily)+db2 :: ExampleDb+db2 = [+    Right $ exAv "A" 1 []+  , Right $ exAv "A" 2 []+  , Right $ exAv "B" 1 [ExAny "A"]+  , Right $ exAv "B" 2 [ExAny "A"]+  , Right $ exAv "C" 1 [ExAny "B", ExFix "A" 1]+  , Right $ exAv "D" 1 [ExAny "B", ExFix "A" 2]+  ]++db3 :: ExampleDb+db3 = [+     Right $ exAv "A" 1 []+   , Right $ exAv "A" 2 []+   , Right $ exAv "B" 1 [exFlagged "flagB" [ExFix "A" 1] [ExFix "A" 2]]+   , Right $ exAv "C" 1 [ExFix "A" 1, ExAny "B"]+   , Right $ exAv "D" 1 [ExFix "A" 2, ExAny "B"]+   ]++-- | Like db3, but the flag picks a different package rather than a+-- different package version+--+-- In db3 we cannot install C and D as independent goals because:+--+-- * The multiple instance restriction says C and D _must_ share B+-- * Since C relies on A-1, C needs B to be compiled with flagB on+-- * Since D relies on A-2, D needs B to be compiled with flagB off+-- * Hence C and D have incompatible requirements on B's flags.+--+-- However, _even_ if we don't check explicitly that we pick the same flag+-- assignment for 0.B and 1.B, we will still detect the problem because+-- 0.B depends on 0.A-1, 1.B depends on 1.A-2, hence we cannot link 0.A to+-- 1.A and therefore we cannot link 0.B to 1.B.+--+-- In db4 the situation however is trickier. We again cannot install+-- packages C and D as independent goals because:+--+-- * As above, the multiple instance restriction says that C and D _must_ share B+-- * Since C relies on Ax-2, it requires B to be compiled with flagB off+-- * Since D relies on Ay-2, it requires B to be compiled with flagB on+-- * Hence C and D have incompatible requirements on B's flags.+--+-- But now this requirement is more indirect. If we only check dependencies+-- we don't see the problem:+--+-- * We link 0.B to 1.B+-- * 0.B relies on Ay-1+-- * 1.B relies on Ax-1+--+-- We will insist that 0.Ay will be linked to 1.Ay, and 0.Ax to 1.Ax, but since+-- we only ever assign to one of these, these constraints are never broken.+db4 :: ExampleDb+db4 = [+     Right $ exAv "Ax" 1 []+   , Right $ exAv "Ax" 2 []+   , Right $ exAv "Ay" 1 []+   , Right $ exAv "Ay" 2 []+   , Right $ exAv "B"  1 [exFlagged "flagB" [ExFix "Ax" 1] [ExFix "Ay" 1]]+   , Right $ exAv "C"  1 [ExFix "Ax" 2, ExAny "B"]+   , Right $ exAv "D"  1 [ExFix "Ay" 2, ExAny "B"]+   ]++-- | Simple database containing one package with a manual flag.+dbManualFlags :: ExampleDb+dbManualFlags = [+    Right $ declareFlags [ExFlag "flag" True Manual] $+        exAv "pkg" 1 [exFlagged "flag" [ExAny "true-dep"] [ExAny "false-dep"]]+  , Right $ exAv "true-dep"  1 []+  , Right $ exAv "false-dep" 1 []+  ]++-- | Database containing a setup dependency with a manual flag. A's library and+-- setup script depend on two different versions of B. B's manual flag can be+-- set to different values in the two places where it is used.+dbSetupDepWithManualFlag :: ExampleDb+dbSetupDepWithManualFlag =+  let bFlags = [ExFlag "flag" True Manual]+  in [+      Right $ exAv "A" 1 [ExFix "B" 1] `withSetupDeps` [ExFix "B" 2]+    , Right $ declareFlags bFlags $+          exAv "B" 1 [exFlagged "flag" [ExAny "b-1-true-dep"]+                                       [ExAny "b-1-false-dep"]]+    , Right $ declareFlags bFlags $+          exAv "B" 2 [exFlagged "flag" [ExAny "b-2-true-dep"]+                                       [ExAny "b-2-false-dep"]]+    , Right $ exAv "b-1-true-dep"  1 []+    , Right $ exAv "b-1-false-dep" 1 []+    , Right $ exAv "b-2-true-dep"  1 []+    , Right $ exAv "b-2-false-dep" 1 []+    ]++-- | A database similar to 'dbSetupDepWithManualFlag', except that the library+-- and setup script both depend on B-1. B must be linked because of the Single+-- Instance Restriction, and its flag can only have one value.+dbLinkedSetupDepWithManualFlag :: ExampleDb+dbLinkedSetupDepWithManualFlag = [+    Right $ exAv "A" 1 [ExFix "B" 1] `withSetupDeps` [ExFix "B" 1]+  , Right $ declareFlags [ExFlag "flag" True Manual] $+        exAv "B" 1 [exFlagged "flag" [ExAny "b-1-true-dep"]+                                     [ExAny "b-1-false-dep"]]+  , Right $ exAv "b-1-true-dep"  1 []+  , Right $ exAv "b-1-false-dep" 1 []+  ]++-- | Some tests involving testsuites+--+-- Note that in this test framework test suites are always enabled; if you+-- want to test without test suites just set up a test database without+-- test suites.+--+-- * C depends on A (through its test suite)+-- * D depends on B-2 (through its test suite), but B-2 is unavailable+-- * E depends on A-1 directly and on A through its test suite. We prefer+--     to use A-1 for the test suite in this case.+-- * F depends on A-1 directly and on A-2 through its test suite. In this+--     case we currently fail to install F, although strictly speaking+--     test suites should be considered independent goals.+-- * G is like E, but for version A-2. This means that if we cannot install+--     E and G together, unless we regard them as independent goals.+db5 :: ExampleDb+db5 = [+    Right $ exAv "A" 1 []+  , Right $ exAv "A" 2 []+  , Right $ exAv "B" 1 []+  , Right $ exAv "C" 1 [] `withTest` ExTest "testC" [ExAny "A"]+  , Right $ exAv "D" 1 [] `withTest` ExTest "testD" [ExFix "B" 2]+  , Right $ exAv "E" 1 [ExFix "A" 1] `withTest` ExTest "testE" [ExAny "A"]+  , Right $ exAv "F" 1 [ExFix "A" 1] `withTest` ExTest "testF" [ExFix "A" 2]+  , Right $ exAv "G" 1 [ExFix "A" 2] `withTest` ExTest "testG" [ExAny "A"]+  ]++-- Now the _dependencies_ have test suites+--+-- * Installing C is a simple example. C wants version 1 of A, but depends on+--   B, and B's testsuite depends on an any version of A. In this case we prefer+--   to link (if we don't regard test suites as independent goals then of course+--   linking here doesn't even come into it).+-- * Installing [C, D] means that we prefer to link B -- depending on how we+--   set things up, this means that we should also link their test suites.+db6 :: ExampleDb+db6 = [+    Right $ exAv "A" 1 []+  , Right $ exAv "A" 2 []+  , Right $ exAv "B" 1 [] `withTest` ExTest "testA" [ExAny "A"]+  , Right $ exAv "C" 1 [ExFix "A" 1, ExAny "B"]+  , Right $ exAv "D" 1 [ExAny "B"]+  ]++-- Packages with setup dependencies+--+-- Install..+-- * B: Simple example, just make sure setup deps are taken into account at all+-- * C: Both the package and the setup script depend on any version of A.+--      In this case we prefer to link+-- * D: Variation on C.1 where the package requires a specific (not latest)+--      version but the setup dependency is not fixed. Again, we prefer to+--      link (picking the older version)+-- * E: Variation on C.2 with the setup dependency the more inflexible.+--      Currently, in this case we do not see the opportunity to link because+--      we consider setup dependencies after normal dependencies; we will+--      pick A.2 for E, then realize we cannot link E.setup.A to A.2, and pick+--      A.1 instead. This isn't so easy to fix (if we want to fix it at all);+--      in particular, considering setup dependencies _before_ other deps is+--      not an improvement, because in general we would prefer to link setup+--      setups to package deps, rather than the other way around. (For example,+--      if we change this ordering then the test for D would start to install+--      two versions of A).+-- * F: The package and the setup script depend on different versions of A.+--      This will only work if setup dependencies are considered independent.+db7 :: ExampleDb+db7 = [+    Right $ exAv "A" 1 []+  , Right $ exAv "A" 2 []+  , Right $ exAv "B" 1 []            `withSetupDeps` [ExAny "A"]+  , Right $ exAv "C" 1 [ExAny "A"  ] `withSetupDeps` [ExAny "A"  ]+  , Right $ exAv "D" 1 [ExFix "A" 1] `withSetupDeps` [ExAny "A"  ]+  , Right $ exAv "E" 1 [ExAny "A"  ] `withSetupDeps` [ExFix "A" 1]+  , Right $ exAv "F" 1 [ExFix "A" 2] `withSetupDeps` [ExFix "A" 1]+  ]++-- If we install C and D together (not as independent goals), we need to build+-- both B.1 and B.2, both of which depend on A.+db8 :: ExampleDb+db8 = [+    Right $ exAv "A" 1 []+  , Right $ exAv "B" 1 [ExAny "A"]+  , Right $ exAv "B" 2 [ExAny "A"]+  , Right $ exAv "C" 1 [] `withSetupDeps` [ExFix "B" 1]+  , Right $ exAv "D" 1 [] `withSetupDeps` [ExFix "B" 2]+  ]++-- Extended version of `db8` so that we have nested setup dependencies+db9 :: ExampleDb+db9 = db8 ++ [+    Right $ exAv "E" 1 [ExAny "C"]+  , Right $ exAv "E" 2 [ExAny "D"]+  , Right $ exAv "F" 1 [] `withSetupDeps` [ExFix "E" 1]+  , Right $ exAv "G" 1 [] `withSetupDeps` [ExFix "E" 2]+  ]++-- Multiple already-installed packages with inter-dependencies, and one package+-- (C) that depends on package A-1 for its setup script and package A-2 as a+-- library dependency.+db10 :: ExampleDb+db10 =+  let rts         = exInst "rts"         1 "rts-inst"         []+      ghc_prim    = exInst "ghc-prim"    1 "ghc-prim-inst"    [rts]+      base        = exInst "base"        1 "base-inst"        [rts, ghc_prim]+      a1          = exInst "A"           1 "A1-inst"          [base]+      a2          = exInst "A"           2 "A2-inst"          [base]+  in [+      Left rts+    , Left ghc_prim+    , Left base+    , Left a1+    , Left a2+    , Right $ exAv "C" 1 [ExFix "A" 2] `withSetupDeps` [ExFix "A" 1]+    ]++-- | This database tests that a package's setup dependencies are correctly+-- linked when the package is linked. See pull request #3268.+--+-- When A and B are installed as independent goals, their dependencies on C must+-- be linked, due to the single instance restriction. Since C depends on D, 0.D+-- and 1.D must be linked. C also has a setup dependency on D, so 0.C-setup.D+-- and 1.C-setup.D must be linked. However, D's two link groups must remain+-- independent. The solver should be able to choose D-1 for C's library and D-2+-- for C's setup script.+dbSetupDeps :: ExampleDb+dbSetupDeps = [+    Right $ exAv "A" 1 [ExAny "C"]+  , Right $ exAv "B" 1 [ExAny "C"]+  , Right $ exAv "C" 1 [ExFix "D" 1] `withSetupDeps` [ExFix "D" 2]+  , Right $ exAv "D" 1 []+  , Right $ exAv "D" 2 []+  ]++-- | Tests for dealing with base shims+db11 :: ExampleDb+db11 =+  let base3 = exInst "base" 3 "base-3-inst" [base4]+      base4 = exInst "base" 4 "base-4-inst" []+  in [+      Left base3+    , Left base4+    , Right $ exAv "A" 1 [ExFix "base" 3]+    ]++-- | Slightly more realistic version of db11 where base-3 depends on syb+-- This means that if a package depends on base-3 and on syb, then they MUST+-- share the version of syb+--+-- * Package A relies on base-3 (which relies on base-4)+-- * Package B relies on base-4+-- * Package C relies on both A and B+-- * Package D relies on base-3 and on syb-2, which is not possible because+--     base-3 has a dependency on syb-1 (non-inheritance of the Base qualifier)+-- * Package E relies on base-4 and on syb-2, which is fine.+db12 :: ExampleDb+db12 =+  let base3 = exInst "base" 3 "base-3-inst" [base4, syb1]+      base4 = exInst "base" 4 "base-4-inst" []+      syb1  = exInst "syb" 1 "syb-1-inst" [base4]+  in [+      Left base3+    , Left base4+    , Left syb1+    , Right $ exAv "syb" 2 [ExFix "base" 4]+    , Right $ exAv "A" 1 [ExFix "base" 3, ExAny "syb"]+    , Right $ exAv "B" 1 [ExFix "base" 4, ExAny "syb"]+    , Right $ exAv "C" 1 [ExAny "A", ExAny "B"]+    , Right $ exAv "D" 1 [ExFix "base" 3, ExFix "syb" 2]+    , Right $ exAv "E" 1 [ExFix "base" 4, ExFix "syb" 2]+    ]++dbBase :: ExampleDb+dbBase = [+      Right $ exAv "base" 1+              [ExAny "ghc-prim", ExAny "integer-simple", ExAny "integer-gmp"]+    , Right $ exAv "ghc-prim" 1 []+    , Right $ exAv "integer-simple" 1 []+    , Right $ exAv "integer-gmp" 1 []+    ]++db13 :: ExampleDb+db13 = [+    Right $ exAv "A" 1 []+  , Right $ exAv "A" 2 []+  , Right $ exAv "A" 3 []+  ]++-- | A, B, and C have three different dependencies on D that can be set to+-- different versions with qualified constraints. Each version of D can only+-- be depended upon by one version of A, B, or C, so that the versions of A, B,+-- and C in the install plan indicate which version of D was chosen for each+-- dependency. The one-to-one correspondence between versions of A, B, and C and+-- versions of D also prevents linking, which would complicate the solver's+-- behavior.+dbConstraints :: ExampleDb+dbConstraints =+    [Right $ exAv "A" v [ExFix "D" v] | v <- [1, 4, 7]]+ ++ [Right $ exAv "B" v [] `withSetupDeps` [ExFix "D" v] | v <- [2, 5, 8]]+ ++ [Right $ exAv "C" v [] `withSetupDeps` [ExFix "D" v] | v <- [3, 6, 9]]+ ++ [Right $ exAv "D" v [] | v <- [1..9]]++dbStanzaPreferences1 :: ExampleDb+dbStanzaPreferences1 = [+    Right $ exAv "pkg" 1 [] `withTest` ExTest "test" [ExAny "test-dep"]+  , Right $ exAv "test-dep" 1 []+  ]++dbStanzaPreferences2 :: ExampleDb+dbStanzaPreferences2 = [+    Right $ exAv "pkg" 1 [] `withTest` ExTest "test" [ExAny "unknown"]+  ]++-- | Database with some cycles+--+-- * Simplest non-trivial cycle: A -> B and B -> A+-- * There is a cycle C -> D -> C, but it can be broken by picking the+--   right flag assignment.+db14 :: ExampleDb+db14 = [+    Right $ exAv "A" 1 [ExAny "B"]+  , Right $ exAv "B" 1 [ExAny "A"]+  , Right $ exAv "C" 1 [exFlagged "flagC" [ExAny "D"] [ExAny "E"]]+  , Right $ exAv "D" 1 [ExAny "C"]+  , Right $ exAv "E" 1 []+  ]++-- | Cycles through setup dependencies+--+-- The first cycle is unsolvable: package A has a setup dependency on B,+-- B has a regular dependency on A, and we only have a single version available+-- for both.+--+-- The second cycle can be broken by picking different versions: package C-2.0+-- has a setup dependency on D, and D has a regular dependency on C-*. However,+-- version C-1.0 is already available (perhaps it didn't have this setup dep).+-- Thus, we should be able to break this cycle even if we are installing package+-- E, which explictly depends on C-2.0.+db15 :: ExampleDb+db15 = [+    -- First example (real cycle, no solution)+    Right $ exAv   "A" 1            []            `withSetupDeps` [ExAny "B"]+  , Right $ exAv   "B" 1            [ExAny "A"]+    -- Second example (cycle can be broken by picking versions carefully)+  , Left  $ exInst "C" 1 "C-1-inst" []+  , Right $ exAv   "C" 2            []            `withSetupDeps` [ExAny "D"]+  , Right $ exAv   "D" 1            [ExAny "C"  ]+  , Right $ exAv   "E" 1            [ExFix "C" 2]+  ]++-- | Packages pkg-A, pkg-B, and pkg-C form a cycle. The solver should backtrack+-- as soon as it chooses the last package in the cycle, to avoid searching parts+-- of the tree that have no solution. Since there is no way to break the cycle,+-- it should fail with an error message describing the cycle.+testCyclicDependencyErrorMessages :: String -> SolverTest+testCyclicDependencyErrorMessages name =+    goalOrder goals $+    mkTest db name ["pkg-A"] $+    SolverResult checkFullLog $ Left checkSummarizedLog+  where+    db :: ExampleDb+    db = [+        Right $ exAv "pkg-A" 1 [ExAny "pkg-B"]+      , Right $ exAv "pkg-B" 1 [ExAny "pkg-C"]+      , Right $ exAv "pkg-C" 1 [ExAny "pkg-A", ExAny "pkg-D"]+      , Right $ exAv "pkg-D" 1 [ExAny "pkg-E"]+      , Right $ exAv "pkg-E" 1 []+      ]++    -- The solver should backtrack as soon as pkg-A, pkg-B, and pkg-C form a+    -- cycle. It shouldn't try pkg-D or pkg-E.+    checkFullLog :: [String] -> Bool+    checkFullLog =+        not . any (\l -> "pkg-D" `isInfixOf` l || "pkg-E" `isInfixOf` l)++    checkSummarizedLog :: String -> Bool+    checkSummarizedLog =+        isInfixOf "rejecting: pkg-C-1.0.0 (cyclic dependencies; conflict set: pkg-A, pkg-B, pkg-C)"++    -- Solve for pkg-D and pkg-E last.+    goals :: [ExampleVar]+    goals = [P None ("pkg-" ++ [c]) | c <- ['A'..'E']]++-- | Check that the solver can backtrack after encountering the SIR (issue #2843)+--+-- When A and B are installed as independent goals, the single instance+-- restriction prevents B from depending on C.  This database tests that the+-- solver can backtrack after encountering the single instance restriction and+-- choose the only valid flag assignment (-flagA +flagB):+--+-- > flagA flagB  B depends on+-- >  On    _     C-*+-- >  Off   On    E-*               <-- only valid flag assignment+-- >  Off   Off   D-2.0, C-*+--+-- Since A depends on C-* and D-1.0, and C-1.0 depends on any version of D,+-- we must build C-1.0 against D-1.0. Since B depends on D-2.0, we cannot have+-- C in the transitive closure of B's dependencies, because that would mean we+-- would need two instances of C: one built against D-1.0 and one built against+-- D-2.0.+db16 :: ExampleDb+db16 = [+    Right $ exAv "A" 1 [ExAny "C", ExFix "D" 1]+  , Right $ exAv "B" 1 [ ExFix "D" 2+                       , exFlagged "flagA"+                             [ExAny "C"]+                             [exFlagged "flagB"+                                 [ExAny "E"]+                                 [ExAny "C"]]]+  , Right $ exAv "C" 1 [ExAny "D"]+  , Right $ exAv "D" 1 []+  , Right $ exAv "D" 2 []+  , Right $ exAv "E" 1 []+  ]++-- | This test checks that when the solver discovers a constraint on a+-- package's version after choosing to link that package, it can backtrack to+-- try alternative versions for the linked-to package. See pull request #3327.+--+-- When A and B are installed as independent goals, their dependencies on C+-- must be linked. Since C depends on D, A and B's dependencies on D must also+-- be linked. This test fixes the goal order so that the solver chooses D-2 for+-- both 0.D and 1.D before it encounters the test suites' constraints. The+-- solver must backtrack to try D-1 for both 0.D and 1.D.+testIndepGoals2 :: String -> SolverTest+testIndepGoals2 name =+    goalOrder goals $ independentGoals $+    enableAllTests $ mkTest db name ["A", "B"] $+    solverSuccess [("A", 1), ("B", 1), ("C", 1), ("D", 1)]+  where+    db :: ExampleDb+    db = [+        Right $ exAv "A" 1 [ExAny "C"] `withTest` ExTest "test" [ExFix "D" 1]+      , Right $ exAv "B" 1 [ExAny "C"] `withTest` ExTest "test" [ExFix "D" 1]+      , Right $ exAv "C" 1 [ExAny "D"]+      , Right $ exAv "D" 1 []+      , Right $ exAv "D" 2 []+      ]++    goals :: [ExampleVar]+    goals = [+        P (Indep 0) "A"+      , P (Indep 0) "C"+      , P (Indep 0) "D"+      , P (Indep 1) "B"+      , P (Indep 1) "C"+      , P (Indep 1) "D"+      , S (Indep 1) "B" TestStanzas+      , S (Indep 0) "A" TestStanzas+      ]++-- | Issue #2834+-- When both A and B are installed as independent goals, their dependencies on+-- C must be linked. The only combination of C's flags that is consistent with+-- A and B's dependencies on D is -flagA +flagB. This database tests that the+-- solver can backtrack to find the right combination of flags (requiring F, but+-- not E or G) and apply it to both 0.C and 1.C.+--+-- > flagA flagB  C depends on+-- >  On    _     D-1, E-*+-- >  Off   On    F-*        <-- Only valid choice+-- >  Off   Off   D-2, G-*+--+-- The single instance restriction means we cannot have one instance of C+-- built against D-1 and one instance built against D-2; since A depends on+-- D-1, and B depends on C-2, it is therefore important that C cannot depend+-- on any version of D.+db18 :: ExampleDb+db18 = [+    Right $ exAv "A" 1 [ExAny "C", ExFix "D" 1]+  , Right $ exAv "B" 1 [ExAny "C", ExFix "D" 2]+  , Right $ exAv "C" 1 [exFlagged "flagA"+                           [ExFix "D" 1, ExAny "E"]+                           [exFlagged "flagB"+                               [ExAny "F"]+                               [ExFix "D" 2, ExAny "G"]]]+  , Right $ exAv "D" 1 []+  , Right $ exAv "D" 2 []+  , Right $ exAv "E" 1 []+  , Right $ exAv "F" 1 []+  , Right $ exAv "G" 1 []+  ]++-- | Tricky test case with independent goals (issue #2842)+--+-- Suppose we are installing D, E, and F as independent goals:+--+-- * D depends on A-* and C-1, requiring A-1 to be built against C-1+-- * E depends on B-* and C-2, requiring B-1 to be built against C-2+-- * F depends on A-* and B-*; this means we need A-1 and B-1 both to be built+--     against the same version of C, violating the single instance restriction.+--+-- We can visualize this DB as:+--+-- >    C-1   C-2+-- >    /|\   /|\+-- >   / | \ / | \+-- >  /  |  X  |  \+-- > |   | / \ |   |+-- > |   |/   \|   |+-- > |   +     +   |+-- > |   |     |   |+-- > |   A     B   |+-- >  \  |\   /|  /+-- >   \ | \ / | /+-- >    \|  V  |/+-- >     D  F  E+testIndepGoals3 :: String -> SolverTest+testIndepGoals3 name =+    goalOrder goals $ independentGoals $+    mkTest db name ["D", "E", "F"] anySolverFailure+  where+    db :: ExampleDb+    db = [+        Right $ exAv "A" 1 [ExAny "C"]+      , Right $ exAv "B" 1 [ExAny "C"]+      , Right $ exAv "C" 1 []+      , Right $ exAv "C" 2 []+      , Right $ exAv "D" 1 [ExAny "A", ExFix "C" 1]+      , Right $ exAv "E" 1 [ExAny "B", ExFix "C" 2]+      , Right $ exAv "F" 1 [ExAny "A", ExAny "B"]+      ]++    goals :: [ExampleVar]+    goals = [+        P (Indep 0) "D"+      , P (Indep 0) "C"+      , P (Indep 0) "A"+      , P (Indep 1) "E"+      , P (Indep 1) "C"+      , P (Indep 1) "B"+      , P (Indep 2) "F"+      , P (Indep 2) "B"+      , P (Indep 2) "C"+      , P (Indep 2) "A"+      ]++-- | This test checks that the solver correctly backjumps when dependencies+-- of linked packages are not linked. It is an example where the conflict set+-- from enforcing the single instance restriction is not sufficient. See pull+-- request #3327.+--+-- When A, B, and C are installed as independent goals with the specified goal+-- order, the first choice that the solver makes for E is 0.E-2. Then, when it+-- chooses dependencies for B and C, it links both 1.E and 2.E to 0.E. Finally,+-- the solver discovers C's test's constraint on E. It must backtrack to try+-- 1.E-1 and then link 2.E to 1.E. Backjumping all the way to 0.E does not lead+-- to a solution, because 0.E's version is constrained by A and cannot be+-- changed.+testIndepGoals4 :: String -> SolverTest+testIndepGoals4 name =+    goalOrder goals $ independentGoals $+    enableAllTests $ mkTest db name ["A", "B", "C"] $+    solverSuccess [("A",1), ("B",1), ("C",1), ("D",1), ("E",1), ("E",2)]+  where+    db :: ExampleDb+    db = [+        Right $ exAv "A" 1 [ExFix "E" 2]+      , Right $ exAv "B" 1 [ExAny "D"]+      , Right $ exAv "C" 1 [ExAny "D"] `withTest` ExTest "test" [ExFix "E" 1]+      , Right $ exAv "D" 1 [ExAny "E"]+      , Right $ exAv "E" 1 []+      , Right $ exAv "E" 2 []+      ]++    goals :: [ExampleVar]+    goals = [+        P (Indep 0) "A"+      , P (Indep 0) "E"+      , P (Indep 1) "B"+      , P (Indep 1) "D"+      , P (Indep 1) "E"+      , P (Indep 2) "C"+      , P (Indep 2) "D"+      , P (Indep 2) "E"+      , S (Indep 2) "C" TestStanzas+      ]++-- | Test the trace messages that we get when a package refers to an unknown pkg+--+-- TODO: Currently we don't actually test the trace messages, and this particular+-- test still suceeds. The trace can only be verified by hand.+db21 :: ExampleDb+db21 = [+    Right $ exAv "A" 1 [ExAny "B"]+  , Right $ exAv "A" 2 [ExAny "C"] -- A-2.0 will be tried first, but C unknown+  , Right $ exAv "B" 1 []+  ]++-- | A variant of 'db21', which actually fails.+db22 :: ExampleDb+db22 = [+    Right $ exAv "A" 1 [ExAny "B"]+  , Right $ exAv "A" 2 [ExAny "C"]+  ]++-- | Another test for the unknown package message.  This database tests that+-- filtering out redundant conflict set messages in the solver log doesn't+-- interfere with generating a message about a missing package (part of issue+-- #3617). The conflict set for the missing package is {A, B}. That conflict set+-- is propagated up the tree to the level of A. Since the conflict set is the+-- same at both levels, the solver only keeps one of the backjumping messages.+db23 :: ExampleDb+db23 = [+    Right $ exAv "A" 1 [ExAny "B"]+  ]++-- | Database for (unsuccessfully) trying to expose a bug in the handling+-- of implied linking constraints. The question is whether an implied linking+-- constraint should only have the introducing package in its conflict set,+-- or also its link target.+--+-- It turns out that as long as the Single Instance Restriction is in place,+-- it does not matter, because there will aways be an option that is failing+-- due to the SIR, which contains the link target in its conflict set.+--+-- Even if the SIR is not in place, if there is a solution, one will always+-- be found, because without the SIR, linking is always optional, but never+-- necessary.+--+testIndepGoals5 :: String -> GoalOrder -> SolverTest+testIndepGoals5 name fixGoalOrder =+    case fixGoalOrder of+      FixedGoalOrder   -> goalOrder goals test+      DefaultGoalOrder -> test+  where+    test :: SolverTest+    test = independentGoals $ mkTest db name ["X", "Y"] $+           solverSuccess+           [("A", 1), ("A", 2), ("B", 1), ("C", 1), ("C", 2), ("X", 1), ("Y", 1)]++    db :: ExampleDb+    db = [+        Right $ exAv "X" 1 [ExFix "C" 2, ExAny "A"]+      , Right $ exAv "Y" 1 [ExFix "C" 1, ExFix "A" 2]+      , Right $ exAv "A" 1 []+      , Right $ exAv "A" 2 [ExAny "B"]+      , Right $ exAv "B" 1 [ExAny "C"]+      , Right $ exAv "C" 1 []+      , Right $ exAv "C" 2 []+      ]++    goals :: [ExampleVar]+    goals = [+        P (Indep 0) "X"+      , P (Indep 0) "A"+      , P (Indep 0) "B"+      , P (Indep 0) "C"+      , P (Indep 1) "Y"+      , P (Indep 1) "A"+      , P (Indep 1) "B"+      , P (Indep 1) "C"+      ]++-- | A simplified version of 'testIndepGoals5'.+testIndepGoals6 :: String -> GoalOrder -> SolverTest+testIndepGoals6 name fixGoalOrder =+    case fixGoalOrder of+      FixedGoalOrder   -> goalOrder goals test+      DefaultGoalOrder -> test+  where+    test :: SolverTest+    test = independentGoals $ mkTest db name ["X", "Y"] $+           solverSuccess+           [("A", 1), ("A", 2), ("B", 1), ("B", 2), ("X", 1), ("Y", 1)]++    db :: ExampleDb+    db = [+        Right $ exAv "X" 1 [ExFix "B" 2, ExAny "A"]+      , Right $ exAv "Y" 1 [ExFix "B" 1, ExFix "A" 2]+      , Right $ exAv "A" 1 []+      , Right $ exAv "A" 2 [ExAny "B"]+      , Right $ exAv "B" 1 []+      , Right $ exAv "B" 2 []+      ]++    goals :: [ExampleVar]+    goals = [+        P (Indep 0) "X"+      , P (Indep 0) "A"+      , P (Indep 0) "B"+      , P (Indep 1) "Y"+      , P (Indep 1) "A"+      , P (Indep 1) "B"+      ]++dbExts1 :: ExampleDb+dbExts1 = [+    Right $ exAv "A" 1 [ExExt (EnableExtension RankNTypes)]+  , Right $ exAv "B" 1 [ExExt (EnableExtension CPP), ExAny "A"]+  , Right $ exAv "C" 1 [ExAny "B"]+  , Right $ exAv "D" 1 [ExExt (DisableExtension CPP), ExAny "B"]+  , Right $ exAv "E" 1 [ExExt (UnknownExtension "custom"), ExAny "C"]+  ]++dbLangs1 :: ExampleDb+dbLangs1 = [+    Right $ exAv "A" 1 [ExLang Haskell2010]+  , Right $ exAv "B" 1 [ExLang Haskell98, ExAny "A"]+  , Right $ exAv "C" 1 [ExLang (UnknownLanguage "Haskell3000"), ExAny "B"]+  ]++-- | cabal must set enable-exe to false in order to avoid the unavailable+-- dependency. Flags are true by default. The flag choice causes "pkg" to+-- depend on "false-dep".+testBuildable :: String -> ExampleDependency -> TestTree+testBuildable testName unavailableDep =+    runTest $+    mkTestExtLangPC (Just []) (Just [Haskell98]) [] db testName ["pkg"] expected+  where+    expected = solverSuccess [("false-dep", 1), ("pkg", 1)]+    db = [+        Right $ exAv "pkg" 1 [exFlagged "enable-exe"+                                 [ExAny "true-dep"]+                                 [ExAny "false-dep"]]+         `withExe`+            ExExe "exe" [ unavailableDep+                        , ExFlagged "enable-exe" (Buildable []) NotBuildable ]+      , Right $ exAv "true-dep" 1 []+      , Right $ exAv "false-dep" 1 []+      ]++-- | cabal must choose -flag1 +flag2 for "pkg", which requires packages+-- "flag1-false" and "flag2-true".+dbBuildable1 :: ExampleDb+dbBuildable1 = [+    Right $ exAv "pkg" 1+        [ exFlagged "flag1" [ExAny "flag1-true"] [ExAny "flag1-false"]+        , exFlagged "flag2" [ExAny "flag2-true"] [ExAny "flag2-false"]]+     `withExes`+        [ ExExe "exe1"+            [ ExAny "unknown"+            , ExFlagged "flag1" (Buildable []) NotBuildable+            , ExFlagged "flag2" (Buildable []) NotBuildable]+        , ExExe "exe2"+            [ ExAny "unknown"+            , ExFlagged "flag1"+                  (Buildable [])+                  (Buildable [ExFlagged "flag2" NotBuildable (Buildable [])])]+         ]+  , Right $ exAv "flag1-true" 1 []+  , Right $ exAv "flag1-false" 1 []+  , Right $ exAv "flag2-true" 1 []+  , Right $ exAv "flag2-false" 1 []+  ]++-- | cabal must pick B-2 to avoid the unknown dependency.+dbBuildable2 :: ExampleDb+dbBuildable2 = [+    Right $ exAv "A" 1 [ExAny "B"]+  , Right $ exAv "B" 1 [ExAny "unknown"]+  , Right $ exAv "B" 2 []+     `withExe`+        ExExe "exe"+        [ ExAny "unknown"+        , ExFlagged "disable-exe" NotBuildable (Buildable [])+        ]+  , Right $ exAv "B" 3 [ExAny "unknown"]+  ]++-- | Package databases for testing @pkg-config@ dependencies.+dbPC1 :: ExampleDb+dbPC1 = [+    Right $ exAv "A" 1 [ExPkg ("pkgA", 1)]+  , Right $ exAv "B" 1 [ExPkg ("pkgB", 1), ExAny "A"]+  , Right $ exAv "B" 2 [ExPkg ("pkgB", 2), ExAny "A"]+  , Right $ exAv "C" 1 [ExAny "B"]+  ]++{-------------------------------------------------------------------------------+  Simple databases for the illustrations for the backjumping blog post+-------------------------------------------------------------------------------}++-- | Motivate conflict sets+dbBJ1a :: ExampleDb+dbBJ1a = [+    Right $ exAv "A" 1 [ExFix "B" 1]+  , Right $ exAv "A" 2 [ExFix "B" 2]+  , Right $ exAv "B" 1 []+  ]++-- | Show that we can skip some decisions+dbBJ1b :: ExampleDb+dbBJ1b = [+    Right $ exAv "A" 1 [ExFix "B" 1]+  , Right $ exAv "A" 2 [ExFix "B" 2, ExAny "C"]+  , Right $ exAv "B" 1 []+  , Right $ exAv "C" 1 []+  , Right $ exAv "C" 2 []+  ]++-- | Motivate why both A and B need to be in the conflict set+dbBJ1c :: ExampleDb+dbBJ1c = [+    Right $ exAv "A" 1 [ExFix "B" 1]+  , Right $ exAv "B" 1 []+  , Right $ exAv "B" 2 []+  ]++-- | Motivate the need for accumulating conflict sets while we walk the tree+dbBJ2 :: ExampleDb+dbBJ2 = [+    Right $ exAv "A"  1 [ExFix "B" 1]+  , Right $ exAv "A"  2 [ExFix "B" 2]+  , Right $ exAv "B"  1 [ExFix "C" 1]+  , Right $ exAv "B"  2 [ExFix "C" 2]+  , Right $ exAv "C"  1 []+  ]++-- | Motivate the need for `QGoalReason`+dbBJ3 :: ExampleDb+dbBJ3 = [+    Right $ exAv "A"  1 [ExAny "Ba"]+  , Right $ exAv "A"  2 [ExAny "Bb"]+  , Right $ exAv "Ba" 1 [ExFix "C" 1]+  , Right $ exAv "Bb" 1 [ExFix "C" 2]+  , Right $ exAv "C"  1 []+  ]++-- | `QGOalReason` not unique+dbBJ4 :: ExampleDb+dbBJ4 = [+    Right $ exAv "A" 1 [ExAny "B", ExAny "C"]+  , Right $ exAv "B" 1 [ExAny "C"]+  , Right $ exAv "C" 1 []+  ]++-- | Flags are represented somewhat strangely in the tree+--+-- This example probably won't be in the blog post itself but as a separate+-- bug report (#3409)+dbBJ5 :: ExampleDb+dbBJ5 = [+    Right $ exAv "A" 1 [exFlagged "flagA" [ExFix "B" 1] [ExFix "C" 1]]+  , Right $ exAv "B" 1 [ExFix "D" 1]+  , Right $ exAv "C" 1 [ExFix "D" 2]+  , Right $ exAv "D" 1 []+  ]++-- | Conflict sets for cycles+dbBJ6 :: ExampleDb+dbBJ6 = [+    Right $ exAv "A" 1 [ExAny "B"]+  , Right $ exAv "B" 1 []+  , Right $ exAv "B" 2 [ExAny "C"]+  , Right $ exAv "C" 1 [ExAny "A"]+  ]++-- | Conflicts not unique+dbBJ7 :: ExampleDb+dbBJ7 = [+    Right $ exAv "A" 1 [ExAny "B", ExFix "C" 1]+  , Right $ exAv "B" 1 [ExFix "C" 1]+  , Right $ exAv "C" 1 []+  , Right $ exAv "C" 2 []+  ]++-- | Conflict sets for SIR (C shared subgoal of independent goals A, B)+dbBJ8 :: ExampleDb+dbBJ8 = [+    Right $ exAv "A" 1 [ExAny "C"]+  , Right $ exAv "B" 1 [ExAny "C"]+  , Right $ exAv "C" 1 []+  ]++{-------------------------------------------------------------------------------+  Databases for build-tools+-------------------------------------------------------------------------------}+dbBuildTools1 :: ExampleDb+dbBuildTools1 = [+    Right $ exAv "alex" 1 [],+    Right $ exAv "A" 1 [ExBuildToolAny "alex"]+  ]++-- Test that build-tools on a random thing doesn't matter (only+-- the ones we recognize need to be in db)+dbBuildTools2 :: ExampleDb+dbBuildTools2 = [+    Right $ exAv "A" 1 [ExBuildToolAny "otherdude"]+  ]++-- Test that we can solve for different versions of executables+dbBuildTools3 :: ExampleDb+dbBuildTools3 = [+    Right $ exAv "alex" 1 [],+    Right $ exAv "alex" 2 [],+    Right $ exAv "A" 1 [ExBuildToolFix "alex" 1],+    Right $ exAv "B" 1 [ExBuildToolFix "alex" 2],+    Right $ exAv "C" 1 [ExAny "A", ExAny "B"]+  ]++-- Test that exe is not related to library choices+dbBuildTools4 :: ExampleDb+dbBuildTools4 = [+    Right $ exAv "alex" 1 [ExFix "A" 1],+    Right $ exAv "A" 1 [],+    Right $ exAv "A" 2 [],+    Right $ exAv "B" 1 [ExBuildToolFix "alex" 1, ExFix "A" 2]+  ]++-- Test that build-tools on build-tools works+dbBuildTools5 :: ExampleDb+dbBuildTools5 = [+    Right $ exAv "alex" 1 [],+    Right $ exAv "happy" 1 [ExBuildToolAny "alex"],+    Right $ exAv "A" 1 [ExBuildToolAny "happy"]+  ]++-- Test that build-depends on library/executable package works.+-- Extracted from https://github.com/haskell/cabal/issues/3775+dbBuildTools6 :: ExampleDb+dbBuildTools6 = [+    Right $ exAv "warp" 1 [],+    -- NB: the warp build-depends refers to the package, not the internal+    -- executable!+    Right $ exAv "A" 2 [ExFix "warp" 1] `withExe` ExExe "warp" [ExAny "A"],+    Right $ exAv "B" 2 [ExAny "A", ExAny "warp"]+  ]
+ tests/UnitTests/Distribution/Solver/Modular/WeightedPSQ.hs view
@@ -0,0 +1,54 @@+{-# LANGUAGE ParallelListComp #-}+module UnitTests.Distribution.Solver.Modular.WeightedPSQ (+  tests+  ) where++import qualified Distribution.Solver.Modular.WeightedPSQ as W++import Data.List (sort)++import Test.Tasty (TestTree)+import Test.Tasty.HUnit (testCase, (@?=))+import Test.Tasty.QuickCheck (Blind(..), testProperty)++tests :: [TestTree]+tests = [+    testProperty "'toList . fromList' preserves elements" $ \xs ->+        sort (xs :: [(Int, Char, Bool)]) == sort (W.toList (W.fromList xs))++  , testProperty "'toList . fromList' sorts stably" $ \xs ->+        let indexAsValue :: [(Int, (), Int)]+            indexAsValue = [(x, (), i) | x <- xs | i <- [0..]]+        in isSorted $ W.toList $ W.fromList indexAsValue++  , testProperty "'mapWeightsWithKey' sorts by weight" $ \xs (Blind f) ->+        isSorted $ W.weights $+        W.mapWeightsWithKey (f :: Int -> Int -> Int) $+        W.fromList (xs :: [(Int, Int, Int)])++  , testCase "applying 'mapWeightsWithKey' twice sorts twice" $+        let indexAsKey :: [((), Int, ())]+            indexAsKey = [((), i, ()) | i <- [0..10]]+            actual = W.toList $+                     W.mapWeightsWithKey (\_ _ -> ()) $+                     W.mapWeightsWithKey (\i _ -> -i) $ -- should not be ignored+                     W.fromList indexAsKey+        in reverse indexAsKey @?= actual++  , testProperty "'union' sorts by weight" $ \xs ys ->+        isSorted $ W.weights $+        W.union (W.fromList xs) (W.fromList (ys :: [(Int, Int, Int)]))++  , testProperty "'union' preserves elements" $ \xs ys ->+        let union = W.union (W.fromList xs)+                            (W.fromList (ys :: [(Int, Int, Int)]))+        in sort (xs ++ ys) == sort (W.toList union)++  , testCase "'lookup' returns first occurrence" $+        let xs = W.fromList [((), False, 'A'), ((), True, 'C'), ((), True, 'B')]+        in Just 'C' @?= W.lookup True xs+  ]++isSorted :: Ord a => [a] -> Bool+isSorted (x1 : xs@(x2 : _)) = x1 <= x2 && isSorted xs+isSorted _ = True