packages feed

hix 0.7.0 → 0.7.1

raw patch · 26 files changed

+494/−103 lines, 26 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

- Hix.Managed.Cabal.Repo: indexOutdated :: Path Abs File -> IO Bool
- Hix.Managed.Cabal.Repo: matchIndexState :: Verbosity -> RepoContext -> Repo -> HackageIndexState -> M (Maybe IndexProblem)
+ Hix.Data.VersionBounds: inclusiveRange :: VersionBounds -> VersionRange
+ Hix.Data.VersionBounds: maybeInclusiveRange :: VersionBounds -> Maybe VersionRange
+ Hix.Managed.Cabal.Print: resolverPackageId :: ResolverPackage a -> PackageId
+ Hix.Managed.Cabal.Repo: IndexCorrupt :: Timestamp -> IndexProblem
+ Hix.Managed.Cabal.Repo: IndexMatch :: HackageIndexState -> ValidIndex
+ Hix.Managed.Cabal.Repo: IndexRecent :: NominalDiffTime -> ValidIndex
+ Hix.Managed.Cabal.Repo: currentIndexState :: Verbosity -> RepoContext -> Repo -> M (Maybe HackageIndexState)
+ Hix.Managed.Cabal.Repo: data ValidIndex
+ Hix.Managed.Cabal.Repo: instance GHC.Classes.Eq Hix.Managed.Cabal.Repo.ValidIndex
+ Hix.Managed.Cabal.Repo: instance GHC.Generics.Generic Hix.Managed.Cabal.Repo.ValidIndex
+ Hix.Managed.Cabal.Repo: instance GHC.Show.Show Hix.Managed.Cabal.Repo.ValidIndex
+ Hix.Managed.Cabal.Repo: logValid :: ValidIndex -> M ()
+ Hix.Managed.Cabal.Repo: maxIndexAge :: NominalDiffTime
+ Hix.Managed.Cabal.Repo: maxIndexAgeDays :: Int
+ Hix.Managed.Cabal.Sort: sortDeps :: forall a. SolveResources -> (a -> PackageName) -> [a] -> M [a]
+ Hix.Managed.Cabal.Sort: sortMutations :: forall a. SolveResources -> [DepMutation a] -> M [DepMutation a]
+ Hix.Managed.Data.BuildConfig: [$sel:timeout:BuildConfig] :: BuildConfig -> Maybe BuildTimeout
+ Hix.Managed.Data.BuildConfig: [$sel:toposortMutations:BuildConfig] :: BuildConfig -> Bool
+ Hix.Managed.Data.StageState: Finished :: BuildStatus -> BuildResult
+ Hix.Managed.Data.StageState: TimedOut :: BuildResult
+ Hix.Managed.Data.StageState: buildUnsuccessful :: BuildResult -> Bool
+ Hix.Managed.Data.StageState: data BuildResult
+ Hix.Managed.Data.StageState: instance GHC.Classes.Eq Hix.Managed.Data.StageState.BuildResult
+ Hix.Managed.Data.StageState: instance GHC.Generics.Generic Hix.Managed.Data.StageState.BuildResult
+ Hix.Managed.Data.StageState: instance GHC.Show.Show Hix.Managed.Data.StageState.BuildResult
+ Hix.Managed.Data.Targets: firstMTargets :: Monad m => a -> (a -> Bool) -> (LocalPackage -> m a) -> Targets -> m a
+ Hix.Managed.Handlers.Build: BuildTimeout :: Int -> BuildTimeout
+ Hix.Managed.Handlers.Build: instance Data.Aeson.Types.FromJSON.FromJSON Hix.Managed.Handlers.Build.BuildTimeout
+ Hix.Managed.Handlers.Build: instance GHC.Classes.Eq Hix.Managed.Handlers.Build.BuildTimeout
+ Hix.Managed.Handlers.Build: instance GHC.Classes.Ord Hix.Managed.Handlers.Build.BuildTimeout
+ Hix.Managed.Handlers.Build: instance GHC.Enum.Enum Hix.Managed.Handlers.Build.BuildTimeout
+ Hix.Managed.Handlers.Build: instance GHC.Generics.Generic Hix.Managed.Handlers.Build.BuildTimeout
+ Hix.Managed.Handlers.Build: instance GHC.Num.Num Hix.Managed.Handlers.Build.BuildTimeout
+ Hix.Managed.Handlers.Build: instance GHC.Real.Integral Hix.Managed.Handlers.Build.BuildTimeout
+ Hix.Managed.Handlers.Build: instance GHC.Real.Real Hix.Managed.Handlers.Build.BuildTimeout
+ Hix.Managed.Handlers.Build: instance GHC.Show.Show Hix.Managed.Handlers.Build.BuildTimeout
+ Hix.Managed.Handlers.Build: newtype BuildTimeout
+ Hix.Managed.Handlers.Build.Prod: [$sel:buildTimeout:BuilderResources] :: BuilderResources -> Maybe BuildTimeout
+ Hix.Managed.Handlers.Cabal: [$sel:sortMutations:CabalHandlers] :: CabalHandlers -> forall a. [DepMutation a] -> M [DepMutation a]
- Hix.Managed.Build: logBuildResult :: Text -> BuildStatus -> M ()
+ Hix.Managed.Build: logBuildResult :: Text -> BuildResult -> M ()
- Hix.Managed.Cabal.Repo: IndexOutdated :: IndexProblem
+ Hix.Managed.Cabal.Repo: IndexOutdated :: NominalDiffTime -> IndexProblem
- Hix.Managed.Cabal.Repo: indexProblem :: SolveConfig -> RepoContext -> Repo -> Path Abs File -> M (Maybe IndexProblem)
+ Hix.Managed.Cabal.Repo: indexProblem :: SolveConfig -> RepoContext -> Repo -> Path Abs File -> M (Either IndexProblem ValidIndex)
- Hix.Managed.Data.BuildConfig: BuildConfig :: Natural -> Natural -> Natural -> Bool -> Bool -> Bool -> BuildConfig
+ Hix.Managed.Data.BuildConfig: BuildConfig :: Natural -> Natural -> Natural -> Bool -> Bool -> Bool -> Bool -> Maybe BuildTimeout -> BuildConfig
- Hix.Managed.Data.StageState: buildStatus :: Bool -> BuildStatus
+ Hix.Managed.Data.StageState: buildStatus :: BuildResult -> BuildStatus
- Hix.Managed.Handlers.Build: EnvBuilder :: CabalHandlers -> (Versions -> [PackageId] -> M (Overrides, BuildStatus)) -> EnvBuilder
+ Hix.Managed.Handlers.Build: EnvBuilder :: CabalHandlers -> (Versions -> [PackageId] -> M (Overrides, BuildResult)) -> EnvBuilder
- Hix.Managed.Handlers.Build: [$sel:buildWithState:EnvBuilder] :: EnvBuilder -> Versions -> [PackageId] -> M (Overrides, BuildStatus)
+ Hix.Managed.Handlers.Build: [$sel:buildWithState:EnvBuilder] :: EnvBuilder -> Versions -> [PackageId] -> M (Overrides, BuildResult)
- Hix.Managed.Handlers.Build: testBuilder :: (Versions -> [PackageId] -> M (Overrides, BuildStatus)) -> (Builder -> M a) -> M a
+ Hix.Managed.Handlers.Build: testBuilder :: (Versions -> [PackageId] -> M (Overrides, BuildResult)) -> (Builder -> M a) -> M a
- Hix.Managed.Handlers.Build: versionsBuilder :: HackageHandlers -> (Versions -> M BuildStatus) -> (Builder -> M a) -> M a
+ Hix.Managed.Handlers.Build: versionsBuilder :: HackageHandlers -> (Versions -> M BuildResult) -> (Builder -> M a) -> M a
- Hix.Managed.Handlers.Build.Prod: BuilderResources :: HackageHandlers -> StateFileHandlers -> Envs EnvConfig -> Maybe BuildOutputsPrefix -> Path Abs Dir -> BuilderResources
+ Hix.Managed.Handlers.Build.Prod: BuilderResources :: HackageHandlers -> StateFileHandlers -> Envs EnvConfig -> Maybe BuildOutputsPrefix -> Path Abs Dir -> Maybe BuildTimeout -> BuilderResources
- Hix.Managed.Handlers.Build.Prod: buildPackage :: Path Abs Dir -> EnvName -> LocalPackage -> M Bool
+ Hix.Managed.Handlers.Build.Prod: buildPackage :: Maybe BuildTimeout -> Path Abs Dir -> EnvName -> LocalPackage -> M BuildResult
- Hix.Managed.Handlers.Build.Prod: buildWithState :: EnvBuilderResources -> Versions -> [PackageId] -> M (Overrides, BuildStatus)
+ Hix.Managed.Handlers.Build.Prod: buildWithState :: EnvBuilderResources -> Versions -> [PackageId] -> M (Overrides, BuildResult)
- Hix.Managed.Handlers.Build.Prod: handlersProd :: MonadIO m => StateFileConfig -> Envs EnvConfig -> Maybe BuildOutputsPrefix -> CabalConfig -> Bool -> m BuildHandlers
+ Hix.Managed.Handlers.Build.Prod: handlersProd :: MonadIO m => StateFileConfig -> Envs EnvConfig -> Maybe BuildOutputsPrefix -> Maybe BuildTimeout -> CabalConfig -> Bool -> m BuildHandlers
- Hix.Managed.Handlers.Build.Prod: withBuilder :: HackageHandlers -> StateFileHandlers -> StateFileConfig -> Envs EnvConfig -> Maybe BuildOutputsPrefix -> (Builder -> M a) -> M a
+ Hix.Managed.Handlers.Build.Prod: withBuilder :: HackageHandlers -> StateFileHandlers -> StateFileConfig -> Envs EnvConfig -> Maybe BuildOutputsPrefix -> Maybe BuildTimeout -> (Builder -> M a) -> M a
- Hix.Managed.Handlers.Build.Test: chooseHandlers :: MonadIO m => StateFileConfig -> Envs EnvConfig -> Maybe BuildOutputsPrefix -> CabalConfig -> Maybe SpecialBuildHandlers -> m BuildHandlers
+ Hix.Managed.Handlers.Build.Test: chooseHandlers :: MonadIO m => StateFileConfig -> Envs EnvConfig -> Maybe BuildOutputsPrefix -> Maybe BuildTimeout -> CabalConfig -> Maybe SpecialBuildHandlers -> m BuildHandlers
- Hix.Managed.Handlers.Build.Test: handlersBumpTest :: MonadIO m => StateFileConfig -> Envs EnvConfig -> Maybe BuildOutputsPrefix -> CabalConfig -> Bool -> m BuildHandlers
+ Hix.Managed.Handlers.Build.Test: handlersBumpTest :: MonadIO m => StateFileConfig -> Envs EnvConfig -> Maybe BuildOutputsPrefix -> Maybe BuildTimeout -> CabalConfig -> Bool -> m BuildHandlers
- Hix.Managed.Handlers.Build.Test: handlersUnitTest :: MonadIO m => GhcPackages -> (Versions -> M BuildStatus) -> m (BuildHandlers, IORef [Expr], IORef [FailedMutation])
+ Hix.Managed.Handlers.Build.Test: handlersUnitTest :: MonadIO m => GhcPackages -> (Versions -> M BuildResult) -> m (BuildHandlers, IORef [Expr], IORef [FailedMutation])
- Hix.Managed.Handlers.Cabal: CabalHandlers :: (SolverState -> M (Maybe SolverPlan)) -> (PackageName -> Maybe Version) -> CabalHandlers
+ Hix.Managed.Handlers.Cabal: CabalHandlers :: (SolverState -> M (Maybe SolverPlan)) -> (PackageName -> Maybe Version) -> (forall a. [DepMutation a] -> M [DepMutation a]) -> CabalHandlers

Files

hix.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack  name:           hix-version:        0.7.0+version:        0.7.1 synopsis:       Haskell/Nix development build tools description:    See https://hackage.haskell.org/package/hix/docs/Hix.html category:       Build@@ -90,6 +90,7 @@       Hix.Managed.Cabal.Repo       Hix.Managed.Cabal.Resources       Hix.Managed.Cabal.Solve+      Hix.Managed.Cabal.Sort       Hix.Managed.Cabal.Targets       Hix.Managed.Constraints       Hix.Managed.Data.BuildConfig@@ -337,9 +338,11 @@       Hix.Test.Managed.LowerNativeTest       Hix.Test.Managed.LowerOptimize.CandidatesTest       Hix.Test.Managed.LowerOptimize.MutationTest+      Hix.Test.Managed.LowerOptimize.OrderTest       Hix.Test.Managed.LowerStabilize.MutationTest       Hix.Test.Managed.ProjectContextProtoTest       Hix.Test.Managed.Run+      Hix.Test.Managed.SolveTest       Hix.Test.Managed.UnsafeIsString       Hix.Test.ManagedTest       Hix.Test.NewTest
lib/Hix/Data/VersionBounds.hs view
@@ -8,6 +8,7 @@   VersionRange,   earlierVersion,   intersectVersionRanges,+  orEarlierVersion,   orLaterVersion,   simplifyVersionRange,   thisVersion,@@ -117,6 +118,15 @@  instance Pretty VersionBounds where   pretty = pretty . majorRange++maybeInclusiveRange :: VersionBounds -> Maybe VersionRange+maybeInclusiveRange =+  maybeRange \case+    BoundLower -> orLaterVersion+    BoundUpper -> orEarlierVersion++inclusiveRange :: VersionBounds -> VersionRange+inclusiveRange = fromMaybe range0 . maybeInclusiveRange  anyBounds :: VersionBounds anyBounds =
lib/Hix/Managed/Build.hs view
@@ -33,8 +33,10 @@ import Hix.Managed.Data.StageContext (StageContext (StageContext)) import qualified Hix.Managed.Data.StageState import Hix.Managed.Data.StageState (+  BuildResult (Finished, TimedOut),   BuildStatus (Failure, Success),   StageState,+  buildStatus,   failed,   initStageState,   iterations,@@ -42,6 +44,8 @@   ) import qualified Hix.Managed.Handlers.Build import Hix.Managed.Handlers.Build (EnvBuilder)+import qualified Hix.Managed.Handlers.Cabal+import Hix.Managed.Handlers.Cabal (CabalHandlers (CabalHandlers)) import qualified Hix.Managed.Handlers.Mutation import Hix.Managed.Handlers.Mutation (MutationHandlers) import Hix.Managed.StageState (updateStageState)@@ -56,13 +60,14 @@   Log.info [exon|Building targets in '##{env}' with #{description}...|]   Log.debugP (vcat ["Overrides:", prettyL overrides]) -logBuildResult :: Text -> BuildStatus -> M ()-logBuildResult description status =-  Log.info [exon|Build with ##{description} #{result status}|]+logBuildResult :: Text -> BuildResult -> M ()+logBuildResult description result =+  Log.info [exon|Build with ##{description} #{describeResult result}|]   where-    result = \case-      Success -> "succeeded"-      Failure -> "failed"+    describeResult = \case+      Finished Success -> "succeeded"+      Finished Failure -> "failed"+      TimedOut -> "timed out"  updateMutationState ::   (Version -> VersionBounds -> VersionBounds) ->@@ -92,7 +97,7 @@   logBuildInputs context.env description reinstallable   (overrides, status) <- builder.buildWithState versions reinstallable   logBuildResult description status-  pure (overrides, status)+  pure (overrides, buildStatus status)   where     reinstallable = filter isReinstallableId overrideVersions @@ -210,7 +215,10 @@   s ->   M (StageState a s) processQuery candidates handlers conf StageContext {env, builder, state, query = query} ext = do-  mutations <- reinstallableCandidates candidates query+  mutations <- postprocess =<< reinstallableCandidates candidates query   convergeMutations handlers conf builder env stageState mutations   where+    postprocess | conf.toposortMutations = sortMutations+                | otherwise = pure     stageState = initStageState state ext+    CabalHandlers {sortMutations} = builder.cabal
lib/Hix/Managed/Bump/App.hs view
@@ -6,6 +6,7 @@ import Hix.Data.Options (BumpOptions (BumpOptions)) import Hix.Json (jsonConfigE) import Hix.Managed.Bump.Optimize (bumpOptimizeMain)+import qualified Hix.Managed.Data.BuildConfig import qualified Hix.Managed.Data.ProjectContextProto import Hix.Managed.Handlers.Build.Test (chooseHandlers) import Hix.Managed.ProjectContext (withProjectContext)@@ -13,5 +14,7 @@ bumpCli :: BumpOptions -> M () bumpCli BumpOptions {common = opts} = do   context <- jsonConfigE Client opts.context-  handlers <- liftIO (chooseHandlers opts.stateFile context.envs context.buildOutputsPrefix opts.cabal opts.handlers)+  handlers <- liftIO $+    chooseHandlers opts.stateFile context.envs context.buildOutputsPrefix opts.project.build.timeout opts.cabal+      opts.handlers   withProjectContext handlers opts.project context (bumpOptimizeMain handlers)
lib/Hix/Managed/Cabal/Print.hs view
@@ -3,21 +3,31 @@ import qualified Distribution.Client.SolverInstallPlan as SolverInstallPlan import Distribution.Client.SolverInstallPlan (ResolverPackage (..), SolverInstallPlan, SolverPlanPackage) import Distribution.InstalledPackageInfo (InstalledPackageInfo (InstalledPackageInfo, sourcePackageId))-import Distribution.Pretty (pretty) import Distribution.Solver.Types.InstSolverPackage (InstSolverPackage (InstSolverPackage, instSolverPkgIPI)) import Distribution.Solver.Types.SolverPackage (SolverPackage (..)) import Distribution.Solver.Types.SourcePackage (SourcePackage (..)) import Exon (exon)  import Hix.Data.Monad (M)+import qualified Hix.Data.PackageId as PackageId+import Hix.Data.PackageId (PackageId) import qualified Hix.Log as Log+import Hix.Pretty (showP) -showPackage :: SolverPlanPackage -> String-showPackage = \case+resolverPackageId :: ResolverPackage a -> PackageId+resolverPackageId = \case   PreExisting InstSolverPackage {instSolverPkgIPI = InstalledPackageInfo {sourcePackageId}} ->-    "existing: " <> show (pretty sourcePackageId)+    PackageId.fromCabal sourcePackageId   Configured SolverPackage {solverPkgSource = SourcePackage {srcpkgPackageId}} ->-    "configured: " <> show (pretty srcpkgPackageId)+    PackageId.fromCabal srcpkgPackageId++showPackage :: SolverPlanPackage -> String+showPackage pkg =+  [exon|#{desc}: #{showP (resolverPackageId pkg)}|]+  where+    desc = case pkg of+      PreExisting {} -> "existing"+      Configured {} -> "configured"  printPlan :: SolverInstallPlan -> M () printPlan plan = do
lib/Hix/Managed/Cabal/Repo.hs view
@@ -1,20 +1,26 @@ module Hix.Managed.Cabal.Repo where -import Data.Time (UTCTime (..), showGregorian, timeToTimeOfDay)+import Data.Time (+  NominalDiffTime,+  UTCTime (..),+  diffUTCTime,+  getCurrentTime,+  nominalDay,+  showGregorian,+  timeToTimeOfDay,+  ) import Distribution.Client.CmdUpdate (updateAction) import qualified Distribution.Client.GlobalFlags import Distribution.Client.GlobalFlags (GlobalFlags (..)) import Distribution.Client.IndexUtils (currentIndexTimestamp, indexBaseName)-import Distribution.Client.IndexUtils.Timestamp (timestampToUTCTime)+import Distribution.Client.IndexUtils.Timestamp (Timestamp, timestampToUTCTime) import Distribution.Client.NixStyleOptions (NixStyleFlags (..)) import Distribution.Client.Setup (RepoContext, withRepoContext) import Distribution.Client.Types (RemoteRepo (..), Repo (RepoSecure), RepoName (RepoName)) import qualified Distribution.Client.Types.Repo-import Distribution.Compat.Time (getFileAge) import Distribution.Verbosity (Verbosity) import Exon (exon)-import Path (Abs, File, Path, addExtension, parseAbsFile, toFilePath)-import Path.IO (doesFileExist)+import Path (Abs, File, Path, addExtension, parseAbsFile)  import Hix.Data.Error (Error (Fatal)) import Hix.Error (pathText)@@ -26,8 +32,8 @@   HackageRepoName (HackageRepoName),   SolveConfig (SolveConfig),   )-import Hix.Maybe (justIf) import Hix.Monad (M, catchIOM, eitherFatalShow, noteFatal, tryIOM, tryIOMWith, withLower)+import Hix.Pretty (showP)  withRepoContextM ::   SolveConfig ->@@ -51,14 +57,18 @@ data IndexProblem =   IndexMissing   |-  IndexOutdated+  IndexOutdated NominalDiffTime   |   IndexMismatch+  |+  IndexCorrupt Timestamp   deriving stock (Eq, Show, Generic) -indexOutdated :: Path Abs File -> IO Bool-indexOutdated (toFilePath -> index) =-  (> 7) <$> getFileAge index+data ValidIndex =+  IndexMatch HackageIndexState+  |+  IndexRecent NominalDiffTime+  deriving stock (Eq, Show, Generic)  updateRequest ::   SolveConfig ->@@ -72,6 +82,12 @@      HackageRepoName (toString -> hackage) = hackageRepoName +maxIndexAgeDays :: Int+maxIndexAgeDays = 7++maxIndexAge :: NominalDiffTime+maxIndexAge = nominalDay * fromIntegral maxIndexAgeDays+ updateIndex ::   SolveConfig ->   GlobalFlags ->@@ -80,13 +96,14 @@   M () updateIndex conf global main problem = do   Log.verbose [exon|Hackage snapshot #{message}, fetching...|]-  tryIOMWith (\ err -> Fatal [exon|Fetching hackage snapshot failed: #{err}|]) do+  tryIOMWith (\ err -> Fatal [exon|Fetching Hackage snapshot failed: #{err}|]) do     updateAction main [updateRequest conf] global   where     message = case problem of       IndexMissing -> "doesn't exist"-      IndexOutdated -> "is older than 7 days"+      IndexOutdated age -> [exon|is older than #{show maxIndexAgeDays} days (#{show age})|]       IndexMismatch -> "differs from requested index state"+      IndexCorrupt stamp -> [exon|has corrupt timestamp (#{show stamp})|]  indexPath :: Repo -> M (Path Abs File) indexPath repo =@@ -96,34 +113,59 @@   where     err = "Bad Hackage repo config" -matchIndexState ::+currentIndexState ::   Verbosity ->   RepoContext ->   Repo ->-  HackageIndexState ->-  M (Maybe IndexProblem)-matchIndexState verbosity ctx repo (HackageIndexState target) =-  catchIOM (match <$> currentIndexTimestamp verbosity ctx repo) (\ _ -> pure (Just IndexMissing))-  where-    match current | current == target = Nothing-                  | otherwise = Just IndexMismatch+  M (Maybe HackageIndexState)+currentIndexState verbosity ctx repo =+  catchIOM (Just . HackageIndexState <$> currentIndexTimestamp verbosity ctx repo) (const (pure Nothing))  indexProblem ::   SolveConfig ->   RepoContext ->   Repo ->   Path Abs File ->-  M (Maybe IndexProblem)+  M (Either IndexProblem ValidIndex) indexProblem SolveConfig {verbosity, cabal} ctx repo path = do-  Log.debug [exon|Checking hackage snapshot at #{pathText path}|]-  tryIOM (doesFileExist path) >>= \case-    False -> pure (Just IndexMissing)-    True+  now <- tryIOM getCurrentTime+  Log.debug [exon|Checking Hackage snapshot at #{pathText path}|]+  currentIndexState verbosity ctx repo <&> \case+    -- There is no index, so the target is irrelevant.+    Nothing -> Left IndexMissing+    Just currentState+      -- If a target index state was specified, we only care whether it matches exactly.+      -- The later conditions are irrelevant.       | Just target <- cabal.indexState-      -> matchIndexState verbosity ctx repo target+      -> if currentState == target+         then Right (IndexMatch target)+         else Left IndexMismatch+      -- If no target was specified, the age of the current index must be below the threshold.+      -- The guard expresses that the timestamp could be parsed.+      | Just current <- currentUTC+      , let age = diffUTCTime now current+      -> if diffUTCTime now current > maxIndexAge+         then Left (IndexOutdated age)+         else Right (IndexRecent age)+      -- If the current state's timestamp can't be converted to UTCTime, it may be corrupt, so we update.       | otherwise-      -> flip justIf IndexOutdated <$> tryIOM (indexOutdated path)+      -> Left (IndexCorrupt (coerce currentState))+      where+        currentUTC = timestampToUTCTime (coerce currentState) +logValid ::+  ValidIndex ->+  M ()+logValid = \case+  IndexMatch (HackageIndexState ts) -> Log.debug [exon|Snapshot matches target: #{showP ts}|]+  IndexRecent age ->+    Log.debug [exon|Snapshot is less than #{desc} old (maximum #{show maxIndexAgeDays}).|]+    where+      desc | days == 1 = "a day"+           | otherwise = [exon|#{show days} days|]+      days :: Int+      days = maybe 0 ceiling (age / nominalDay)+ -- TODO currentIndexTimestamp has a different signature in later versions ensureHackageIndex ::   SolveConfig ->@@ -134,4 +176,4 @@   withRepoContextM conf global \ ctx -> do     repo <- fullHackageRepo conf ctx     path <- indexPath repo-    traverse_ (updateIndex conf global main) =<< indexProblem conf ctx repo path+    either (updateIndex conf global main) logValid =<< indexProblem conf ctx repo path
+ lib/Hix/Managed/Cabal/Sort.hs view
@@ -0,0 +1,45 @@+module Hix.Managed.Cabal.Sort where++import qualified Data.Map.Strict as Map+import Data.Map.Strict ((!?))+import Distribution.Client.SolverInstallPlan (topologicalOrder)+import Exon (exon)++import Hix.Data.Monad (M)+import qualified Hix.Data.PackageId+import qualified Hix.Log as Log+import Hix.Managed.Cabal.Data.SolveResources (SolveResources)+import Hix.Managed.Cabal.Print (resolverPackageId)+import Hix.Managed.Cabal.Solve (solveForTargets)+import Hix.Managed.Cabal.Targets (solveTarget)+import Hix.Managed.Data.Mutable (MutableDep (MutableDep))+import Hix.Managed.Data.Mutation (DepMutation (..))+import Hix.Data.PackageName (PackageName)++sortDeps ::+  ∀ a .+  SolveResources ->+  (a -> PackageName) ->+  [a] ->+  M [a]+sortDeps resources name mutations =+  solveForTargets resources id targets >>= \case+    Right plan ->+      pure (mapMaybe original (topologicalOrder plan))+    Left err -> do+      Log.error [exon|No plan for mutations: ##{err}|]+      pure mutations+  where+    original k = byName !? (resolverPackageId k).name+    byName = Map.fromList [(name m, m) | m <- mutations]+    targets = [solveTarget (name m) mempty | m <- mutations]++sortMutations ::+  ∀ a .+  SolveResources ->+  [DepMutation a] ->+  M [DepMutation a]+sortMutations resources =+  sortDeps resources name+  where+    name DepMutation {package = MutableDep package} = package
lib/Hix/Managed/Data/BuildConfig.hs view
@@ -1,5 +1,7 @@ module Hix.Managed.Data.BuildConfig where +import Hix.Managed.Handlers.Build (BuildTimeout)+ data BuildConfig =   BuildConfig {     maxIterations :: Natural,@@ -7,7 +9,9 @@     maxFailedPost :: Natural,     lookup :: Bool,     validate :: Bool,-    buildOutput :: Bool+    buildOutput :: Bool,+    toposortMutations :: Bool,+    timeout :: Maybe BuildTimeout   }   deriving stock (Eq, Show, Generic) @@ -18,5 +22,7 @@     maxFailedPost = 0,     lookup = False,     validate = False,-    buildOutput = False+    buildOutput = False,+    toposortMutations = True,+    timeout = Nothing   }
lib/Hix/Managed/Data/Constraints.hs view
@@ -8,7 +8,7 @@  import Hix.Class.Map (LookupMaybe, NMap, nPretty) import Hix.Data.PackageName (PackageName)-import Hix.Data.VersionBounds (VersionBounds, anyBounds)+import Hix.Data.VersionBounds (VersionBounds, anyBounds, inclusiveRange) import Hix.Pretty (prettyL)  -- | Left-biased semigroup op.@@ -37,7 +37,7 @@  instance Pretty MutationConstraints where   pretty MutationConstraints {mutation, oldest, installed, force, prefer} =-    pretty mutation <+>+    pretty (inclusiveRange mutation) <+>     flags [("oldest", oldest), ("installed", installed)] <+>     foldMap pref prefer <+>     foldMap spec force
lib/Hix/Managed/Data/Query.hs view
@@ -5,11 +5,15 @@ import Hix.Data.PackageName (PackageName) import Hix.Managed.Data.QueryDep (QueryDep) +-- | User-selected restriction of packages that should be processed.+-- Given as positional arguments on the command line.+-- If empty, all packages are processed. newtype RawQuery =   RawQuery [PackageName]   deriving stock (Eq, Show, Generic)   deriving newtype (IsList) +-- | Stage-specific subset of the validated environment subset of 'RawQuery', adorned with metadata. newtype Query =   Query (NonEmpty QueryDep)   deriving stock (Eq, Show, Generic)
lib/Hix/Managed/Data/StageState.hs view
@@ -18,10 +18,22 @@   Success -> Just a   Failure -> Nothing -buildStatus :: Bool -> BuildStatus+data BuildResult =+  Finished BuildStatus+  |+  TimedOut+  deriving stock (Eq, Show, Generic)++buildUnsuccessful :: BuildResult -> Bool+buildUnsuccessful = \case+  Finished Success -> False+  Finished Failure -> True+  TimedOut -> True++buildStatus :: BuildResult -> BuildStatus buildStatus = \case-  True -> Success-  False -> Failure+  Finished s -> s+  TimedOut -> Failure  data BuildSuccess =   CandidateBuilt MutableId
lib/Hix/Managed/Data/Targets.hs view
@@ -6,6 +6,7 @@   singleTarget,   sortTargets,   allMTargets,+  firstMTargets,   overTargets, ) where @@ -78,6 +79,22 @@     simple :: Packages [LocalPackage]     simple = nMap (onlyFrom targetSet) (nRestrictKeys targetSet deps)     targetSet = Set.fromList targets++firstMTargets ::+  Monad m =>+  a ->+  (a -> Bool) ->+  (LocalPackage -> m a) ->+  Targets ->+  m a+firstMTargets success cond f (Targets targets) =+  foldr chain (pure success) targets+  where+    chain a z = do+      res <- f a+      if cond res+      then pure res+      else z  allMTargets ::   Monad m =>
lib/Hix/Managed/Handlers/Build.hs view
@@ -16,7 +16,7 @@ import Hix.Managed.Data.Initial (Initial) import Hix.Managed.Data.ManagedPackage (ManagedPackage) import Hix.Managed.Data.Packages (Packages)-import Hix.Managed.Data.StageState (BuildStatus (Failure))+import Hix.Managed.Data.StageState (BuildResult (Finished), BuildStatus (Failure)) import qualified Hix.Managed.Handlers.Cabal as Solve import qualified Hix.Managed.Handlers.Cabal as Cabal import Hix.Managed.Handlers.Cabal (CabalHandlers)@@ -32,10 +32,15 @@   deriving stock (Eq, Show, Generic)   deriving newtype (IsString, Ord, FromJSON) +newtype BuildTimeout =+  BuildTimeout Int+  deriving stock (Eq, Show, Generic)+  deriving newtype (Num, Real, Enum, Integral, Ord, FromJSON)+ data EnvBuilder =   EnvBuilder {     cabal :: CabalHandlers,-    buildWithState :: Versions -> [PackageId] -> M (Overrides, BuildStatus)+    buildWithState :: Versions -> [PackageId] -> M (Overrides, BuildResult)   }  data Builder =@@ -56,11 +61,11 @@     latestVersion :: PackageName -> M (Maybe Version)   } -testBuilder :: (Versions -> [PackageId] -> M (Overrides, BuildStatus)) -> (Builder -> M a) -> M a+testBuilder :: (Versions -> [PackageId] -> M (Overrides, BuildResult)) -> (Builder -> M a) -> M a testBuilder buildWithState use =   use Builder {withEnvBuilder = \ cabal _ _ useE -> useE EnvBuilder {cabal, buildWithState}} -versionsBuilder :: HackageHandlers -> (Versions -> M BuildStatus) -> (Builder -> M a) -> M a+versionsBuilder :: HackageHandlers -> (Versions -> M BuildResult) -> (Builder -> M a) -> M a versionsBuilder hackage build =   testBuilder \ versions overrideVersions -> do     overrides <- packageOverrides hackage overrideVersions@@ -73,7 +78,7 @@     stateFile = StateFileHandlers.handlersNull,     report = Report.handlersNull,     cabal = \ _ _ -> pure Solve.handlersNull,-    withBuilder = testBuilder \ _ _ -> pure (mempty, Failure),+    withBuilder = testBuilder \ _ _ -> pure (mempty, Finished Failure),     versions = \ _ -> pure [],     latestVersion = \ _ -> pure Nothing   }
lib/Hix/Managed/Handlers/Build/Prod.hs view
@@ -14,11 +14,13 @@   inherit,   nullStream,   proc,-  runProcess,   setStderr,   setStdout,   setWorkingDir,+  waitExitCode,+  withProcessTerm,   )+import System.Timeout (timeout)  import Hix.Data.EnvName (EnvName) import Hix.Data.Error (Error (Fatal))@@ -38,12 +40,18 @@ import Hix.Managed.Data.EnvState (EnvState) import Hix.Managed.Data.Envs (Envs) import Hix.Managed.Data.Initial (Initial)-import Hix.Managed.Data.StageState (BuildStatus, buildStatus)+import Hix.Managed.Data.StageState (BuildResult (Finished, TimedOut), BuildStatus (Failure, Success), buildUnsuccessful) import qualified Hix.Managed.Data.StateFileConfig import Hix.Managed.Data.StateFileConfig (StateFileConfig)-import Hix.Managed.Data.Targets (allMTargets)+import Hix.Managed.Data.Targets (firstMTargets) import qualified Hix.Managed.Handlers.Build-import Hix.Managed.Handlers.Build (BuildHandlers (..), BuildOutputsPrefix, Builder (Builder), EnvBuilder (EnvBuilder))+import Hix.Managed.Handlers.Build (+  BuildHandlers (..),+  BuildOutputsPrefix,+  BuildTimeout,+  Builder (Builder),+  EnvBuilder (EnvBuilder),+  ) import Hix.Managed.Handlers.Cabal (CabalHandlers) import qualified Hix.Managed.Handlers.Cabal.Prod as CabalHandlers import Hix.Managed.Handlers.Hackage (HackageHandlers)@@ -62,7 +70,8 @@     stateFileHandlers :: StateFileHandlers,     envsConf :: Envs EnvConfig,     buildOutputsPrefix :: Maybe BuildOutputsPrefix,-    root :: Path Abs Dir+    root :: Path Abs Dir,+    buildTimeout :: Maybe BuildTimeout   }  data EnvBuilderResources =@@ -103,17 +112,22 @@     args = toString <$> cmd ++ [exon|path:#{".#"}#{installable}|] : extra  buildPackage ::+  Maybe BuildTimeout ->   Path Abs Dir ->   EnvName ->   LocalPackage ->-  M Bool-buildPackage root env target = do+  M BuildResult+buildPackage processTimeout root env target = do   debug <- M (asks (.debug))   conf <- nixProc root ["-L", "build"] [exon|env.##{env}.##{target}|] []-  tryIOM (runProcess (err debug conf)) <&> \case-    ExitSuccess -> True-    ExitFailure _ -> False+  tryIOM (withProcessTerm (err debug conf) (limit . waitExitCode)) <&> \case+    Just ExitSuccess -> Finished Success+    Just (ExitFailure _) -> Finished Failure+    Nothing -> TimedOut   where+    limit | Just t <- processTimeout = timeout (coerce t * 1_000_000)+          | otherwise = fmap Just+     err = \case       True -> setStdout inherit       False -> id@@ -122,11 +136,11 @@   EnvBuilderResources ->   Versions ->   [PackageId] ->-  M (Overrides, BuildStatus)+  M (Overrides, BuildResult) buildWithState EnvBuilderResources {global, context = context@EnvContext {env, targets}} _ overrideVersions = do   overrides <- packageOverrides global.hackage overrideVersions   writeBuildStateFor "current build" global.stateFileHandlers global.root context overrides-  status <- buildStatus <$> allMTargets (buildPackage global.root env) targets+  status <- firstMTargets (Finished Success) buildUnsuccessful (buildPackage global.buildTimeout global.root env) targets   pure (overrides, status)  -- | This used to have the purpose of reading an updated GHC package db using the current managed state, but this has@@ -159,9 +173,10 @@   StateFileConfig ->   Envs EnvConfig ->   Maybe BuildOutputsPrefix ->+  Maybe BuildTimeout ->   (Builder -> M a) ->   M a-withBuilder hackage stateFileHandlers stateFileConf envsConf buildOutputsPrefix use =+withBuilder hackage stateFileHandlers stateFileConf envsConf buildOutputsPrefix buildTimeout use =   withTempProject stateFileConf.projectRoot \ root -> do     let resources = BuilderResources {..}     use Builder {withEnvBuilder = withEnvBuilder resources}@@ -171,10 +186,11 @@   StateFileConfig ->   Envs EnvConfig ->   Maybe BuildOutputsPrefix ->+  Maybe BuildTimeout ->   CabalConfig ->   Bool ->   m BuildHandlers-handlersProd stateFileConf envsConf buildOutputsPrefix cabalConf oldest = do+handlersProd stateFileConf envsConf buildOutputsPrefix buildTimeout cabalConf oldest = do   manager <- liftIO (newManager tlsManagerSettings)   hackage <- HackageHandlers.handlersProd   let stateFile = StateFileHandlers.handlersProd stateFileConf@@ -182,7 +198,7 @@     stateFile,     report = ReportHandlers.handlersProd,     cabal = CabalHandlers.handlersProd cabalConf oldest,-    withBuilder = withBuilder hackage stateFile stateFileConf envsConf buildOutputsPrefix,+    withBuilder = withBuilder hackage stateFile stateFileConf envsConf buildOutputsPrefix buildTimeout,     versions = versionsHackage manager,     latestVersion = latestVersionHackage manager   }
lib/Hix/Managed/Handlers/Build/Test.hs view
@@ -16,11 +16,12 @@ import Hix.Managed.Data.EnvConfig (EnvConfig) import Hix.Managed.Data.Envs (Envs) import Hix.Managed.Data.Mutation (FailedMutation)-import Hix.Managed.Data.StageState (BuildStatus)+import Hix.Managed.Data.StageState (BuildResult) import Hix.Managed.Data.StateFileConfig (StateFileConfig) import Hix.Managed.Handlers.Build (   BuildHandlers (..),   BuildOutputsPrefix,+  BuildTimeout,   SpecialBuildHandlers (TestBumpHandlers),   versionsBuilder,   )@@ -35,7 +36,7 @@ handlersUnitTest ::   MonadIO m =>   GhcPackages ->-  (Versions -> M BuildStatus) ->+  (Versions -> M BuildResult) ->   m (BuildHandlers, IORef [Expr], IORef [FailedMutation]) handlersUnitTest ghcPackages builder = do   (stateFile, stateFileRef) <- StateFile.handlersUnitTest@@ -67,11 +68,12 @@   StateFileConfig ->   Envs EnvConfig ->   Maybe BuildOutputsPrefix ->+  Maybe BuildTimeout ->   CabalConfig ->   Bool ->   m BuildHandlers-handlersBumpTest stateFileConf envsConf buildOutputsPrefix cabalConf oldest = do-  handlers <- handlersProd stateFileConf envsConf buildOutputsPrefix cabalConf oldest+handlersBumpTest stateFileConf envsConf buildOutputsPrefix buildTimeout cabalConf oldest = do+  handlers <- handlersProd stateFileConf envsConf buildOutputsPrefix buildTimeout cabalConf oldest   pure handlers {     cabal = CabalHandlers.handlersTest cabalConf oldest,     latestVersion = latestVersionNixTestBump@@ -82,11 +84,12 @@   StateFileConfig ->   Envs EnvConfig ->   Maybe BuildOutputsPrefix ->+  Maybe BuildTimeout ->   CabalConfig ->   Maybe SpecialBuildHandlers ->   m BuildHandlers-chooseHandlers stateFileConf envsConf buildOutputsPrefix cabalConf = \case-  Just TestBumpHandlers -> handlersBumpTest stateFileConf envsConf buildOutputsPrefix cabalConf oldest-  Nothing -> handlersProd stateFileConf envsConf buildOutputsPrefix cabalConf oldest+chooseHandlers stateFileConf envsConf buildOutputsPrefix buildTimeout cabalConf = \case+  Just TestBumpHandlers -> handlersBumpTest stateFileConf envsConf buildOutputsPrefix buildTimeout cabalConf oldest+  Nothing -> handlersProd stateFileConf envsConf buildOutputsPrefix buildTimeout cabalConf oldest   where     oldest = False
lib/Hix/Managed/Handlers/Cabal.hs view
@@ -10,18 +10,21 @@ import Hix.Managed.Cabal.Data.SolverState (SolverState) import Hix.Managed.Data.Constraints (EnvConstraints) import Hix.Managed.Data.Mutable (MutableDep, MutableVersions, depName)+import Hix.Managed.Data.Mutation (DepMutation)  data CabalHandlers =   CabalHandlers {     solveForVersion :: SolverState -> M (Maybe SolverPlan),-    installedVersion :: PackageName -> Maybe Version+    installedVersion :: PackageName -> Maybe Version,+    sortMutations :: ∀ a . [DepMutation a] -> M [DepMutation a]   }  handlersNull :: CabalHandlers handlersNull =   CabalHandlers {     solveForVersion = \ _ -> pure Nothing,-    installedVersion = const Nothing+    installedVersion = const Nothing,+    sortMutations = pure   }  installedVersions ::
lib/Hix/Managed/Handlers/Cabal/Prod.hs view
@@ -13,12 +13,13 @@ import Hix.Data.PackageId (PackageId (PackageId)) import qualified Hix.Data.PackageName as PackageName import Hix.Data.PackageName (PackageName)-import Hix.Managed.Cabal.Data.Config (GhcDb, CabalConfig)+import Hix.Managed.Cabal.Data.Config (CabalConfig, GhcDb) import qualified Hix.Managed.Cabal.Data.SolveResources as SolveResources import Hix.Managed.Cabal.Data.SolveResources (SolveResources (SolveResources), solverParams) import Hix.Managed.Cabal.Installed (installedVersion) import qualified Hix.Managed.Cabal.Resources as SolveResources import Hix.Managed.Cabal.Solve (solveWithCabal)+import Hix.Managed.Cabal.Sort (sortMutations) import Hix.Managed.Data.ManagedPackage (ManagedPackage) import Hix.Managed.Data.Packages (Packages) import Hix.Managed.Handlers.Cabal (CabalHandlers (..))@@ -35,7 +36,8 @@   solveResources <- trans <$> SolveResources.acquire packages cabalConf ghc   pure CabalHandlers {     solveForVersion = solveWithCabal solveResources {solverParams},-    installedVersion = installedVersion solveResources.installedPkgIndex+    installedVersion = installedVersion solveResources.installedPkgIndex,+    sortMutations = sortMutations solveResources   }   where     solverParams | oldest = setPreferenceDefault PreferAllOldest
lib/Hix/Managed/Lower/App.hs view
@@ -5,6 +5,7 @@ import qualified Hix.Data.Options import Hix.Data.Options (LowerOptions (LowerOptions)) import Hix.Json (jsonConfigE)+import qualified Hix.Managed.Data.BuildConfig import Hix.Managed.Data.LowerConfig (LowerConfig, lowerConfig) import Hix.Managed.Data.ProjectContext (ProjectContext) import qualified Hix.Managed.Data.ProjectContextProto@@ -23,7 +24,8 @@   M () lowerCli main opts@LowerOptions {common} = do   context <- jsonConfigE Client common.context-  handlers <- chooseHandlers common.stateFile context.envs context.buildOutputsPrefix common.cabal common.handlers+  handlers <- chooseHandlers common.stateFile context.envs context.buildOutputsPrefix common.project.build.timeout+    common.cabal common.handlers   withProjectContext handlers common.project context (main conf handlers)   where     conf = lowerConfig opts
lib/Hix/Managed/Lower/Stabilize.hs view
@@ -60,12 +60,11 @@   execStatelessStage "stabilize-initial" \ StageContext {env, initial, builder} ->     buildVersions builder env "initial lower bounds" initial <&> \case       Success -> StageNoAction (Just "Env builds successfully with the initial bounds.")-      Failure ->-        StageFailure (FailedPrecondition msg)+      Failure -> StageFailure (FailedPrecondition msg)   where     msg =       [-        "Cannot stabilize since the build with initial bounds failed.",+        [exon|Cannot stabilize since the build with initial bounds failed.|],         "Please run 'lower.init --reset' or fix the build manually."       ] @@ -136,7 +135,7 @@ lowerStabilizeStages handlers conf =   validateCurrent >>= \case     Success -> unit-    Failure -> stabilizeIfPossible handlers conf+    _ -> stabilizeIfPossible handlers conf  lowerStabilizeMain ::   BuildHandlers ->
lib/Hix/Options.hs view
@@ -77,6 +77,7 @@ import Hix.Managed.Data.Query (RawQuery (RawQuery)) import qualified Hix.Managed.Data.StateFileConfig import Hix.Managed.Data.StateFileConfig (StateFileConfig (StateFileConfig))+import Hix.Managed.Handlers.Build (BuildTimeout (BuildTimeout)) import Hix.Optparse (   JsonConfig,   absDirOption,@@ -256,12 +257,15 @@   lookup <- switch (long "lookup" <> short 'n' <> help "Only print latest versions (bump)")   validate <- switch (long "validate" <> help "Validate new versions, but don't update the project state")   buildOutput <- switch (long "build-output" <> help "Show output from Nix builds")+  timeout <- fmap BuildTimeout <$> optional (option auto (long "build-timeout" <> help buildTimeoutHelp))   pure BuildConfig {..}   where     maxIterationsHelp = "Number of restarts when some dependencies fail"     maxFailedPreHelp = maxFailedHelp "prior to the first"     maxFailedPostHelp = maxFailedHelp "after the last"     maxFailedHelp variant = [exon|Number of majors that may fail before aborting, #{variant} success|]+    buildTimeoutHelp = "Kill Nix builds after this duration in seconds"+    toposortMutations = True  projectOptionsParser :: Parser ProjectOptions projectOptionsParser = do
test/Hix/Test/Managed/LowerInit/MutationTest.hs view
@@ -27,7 +27,7 @@ import Hix.NixExpr (renderRootExpr) import Hix.Pretty (showP) import Hix.Test.Hedgehog (eqLines, listEqZip)-import Hix.Test.Managed.Run (Result (..), TestParams (..), lowerTest, testParams)+import Hix.Test.Managed.Run (Result (..), TestParams (..), lowerTest, testParams, nosortOptions) import Hix.Test.Utils (UnitTest)  packages :: Packages ManagedPackageProto@@ -433,6 +433,6 @@         log = True,         ghcPackages,         state,-        projectOptions = def {envs = ["lower-main", "lower-special"], readUpperBounds = True},+        projectOptions = nosortOptions {envs = ["lower-main", "lower-special"], readUpperBounds = True},         build       }
test/Hix/Test/Managed/LowerNativeTest.hs view
@@ -233,7 +233,7 @@       projectRoot = Just root     }     envsConfig = [("lower", EnvConfig {targets = ["root"], ghc = GhcDbSystem Nothing})]-  handlers <- Build.handlersProd stateFileConf envsConfig Nothing def False+  handlers <- Build.handlersProd stateFileConf envsConfig Nothing Nothing def False   let     opts = projectOptions ["lower"] 
+ test/Hix/Test/Managed/LowerOptimize/OrderTest.hs view
@@ -0,0 +1,133 @@+module Hix.Test.Managed.LowerOptimize.OrderTest where++import Hix.Data.Monad (M)+import Hix.Data.Version (Versions)+import Hix.Managed.Cabal.Changes (SolverPlan (..))+import qualified Hix.Managed.Cabal.Data.Packages+import Hix.Managed.Cabal.Data.Packages (GhcPackages (GhcPackages))+import Hix.Managed.Cabal.Data.SourcePackage (SourcePackages)+import Hix.Managed.Data.Constraints (EnvConstraints)+import Hix.Managed.Data.ManagedPackageProto (ManagedPackageProto, managedPackages)+import Hix.Managed.Data.Packages (Packages)+import qualified Hix.Managed.Data.ProjectStateProto+import Hix.Managed.Data.ProjectStateProto (ProjectStateProto (ProjectStateProto))+import Hix.Managed.Data.StageState (BuildStatus (Success))+import Hix.Managed.Lower.Optimize (lowerOptimizeMain)+import Hix.Test.Hedgehog (listEqZip)+import Hix.Test.Managed.Run (Result (..), TestParams (..), lowerTest, testParams)+import Hix.Test.Utils (UnitTest)++packages :: Packages ManagedPackageProto+packages =+  managedPackages [(("local1", "1.0"), ["dep1", "dep2", "dep3"])]++packageDb :: SourcePackages+packageDb =+  [+    ("dep1", [+      ([1, 8], []),+      ([1, 9], []),+      ([2, 0], []),+      ([2, 1], [])+    ]),+    ("dep2", [+      ([1, 9], ["dep1 <1.9"]),+      ([2, 0], []),+      ([2, 1], [])+    ]),+    ("dep3", [+      ([1, 9], ["dep2 <2"]),+      ([2, 0], ["dep2 <2"]),+      ([2, 1], [])+    ])+  ]++ghcPackages :: GhcPackages+ghcPackages = GhcPackages {installed = [], available = packageDb}++state :: ProjectStateProto+state =+  ProjectStateProto {+    bounds = [+      ("local1", [+        ("dep1", ">=2.1"),+        ("dep2", ">=2.1"),+        ("dep3", ">=2.1")+      ])+    ],+    versions = [+      ("lower", [+        ("dep1", [2, 1]),+        ("dep2", [2, 1]),+        ("dep3", [2, 1])+      ])+    ],+    overrides = [("lower", [])],+    initial = [+      ("lower", [+        ("dep1", [2, 1]),+        ("dep2", [2, 1]),+        ("dep3", [2, 1])+      ])+    ],+    resolving = False+  }++build :: Versions -> M BuildStatus+build _ = pure Success++cabalTarget :: [(EnvConstraints, Maybe SolverPlan)]+cabalTarget =+  [+    ([+      "dep1 <=2.1",+      "dep2 <=2.1",+      "dep3 ==2.0"+    ], plan ["dep1-1.8", "dep2-1.9", "dep3-2.0"]),+    ([+      "dep1 <=2.1",+      "dep2 <=2.1",+      "dep3 ==1.9"+    ], plan ["dep1-1.8", "dep2-1.9", "dep3-1.9"]),+    ([+      "dep1 <=1.8",+      "dep2 ==2.0",+      "dep3 <=1.9"+    ], Nothing),+    ([+      "dep1 <=1.8",+      "dep2 ==1.9",+      "dep3 <=1.9"+    ], plan ["dep1-1.8", "dep2-1.9", "dep3-1.9"]),+    ([+      "dep1 ==2.0",+      "dep2 <=1.9",+      "dep3 <=1.9"+    ], Nothing),+    ([+      "dep1 ==1.9",+      "dep2 <=1.9",+      "dep3 <=1.9"+    ], Nothing),+    ([+      "dep1 ==1.8",+      "dep2 <=1.9",+      "dep3 <=1.9"+    ], plan ["dep1-1.8", "dep2-1.9", "dep3-1.9"])+  ]+  where+    plan changes = Just SolverPlan {changes, matching = [], nonReinstallable = Nothing}++test_mutationOrder :: UnitTest+test_mutationOrder = do+  Result {cabalLog} <- lowerTest params lowerOptimizeMain+  listEqZip cabalTarget cabalLog+  where+    params =+      (testParams False packages) {+        cabalLog = True,+        ghcPackages,+        state,+        build,+        projectOptions = def+      }
test/Hix/Test/Managed/Run.hs view
@@ -7,6 +7,7 @@ import Hix.Data.EnvName (EnvName) import Hix.Data.Monad (M) import Hix.Data.NixExpr (Expr)+import qualified Hix.Data.Options as ProjectOptions import Hix.Data.Options (ProjectOptions) import Hix.Data.PackageName (LocalPackage) import Hix.Data.Version (Versions)@@ -14,6 +15,7 @@ import Hix.Managed.Cabal.Data.Config (GhcDb (GhcDbSynthetic)) import qualified Hix.Managed.Cabal.Data.Packages import Hix.Managed.Cabal.Data.Packages (GhcPackages (GhcPackages))+import Hix.Managed.Data.BuildConfig (BuildConfig (toposortMutations)) import Hix.Managed.Data.Constraints (EnvConstraints) import qualified Hix.Managed.Data.EnvConfig import Hix.Managed.Data.EnvConfig (EnvConfig (EnvConfig))@@ -24,7 +26,7 @@ import Hix.Managed.Data.ProjectContextProto (ProjectContextProto (..)) import Hix.Managed.Data.ProjectResult (ProjectResult) import Hix.Managed.Data.ProjectStateProto (ProjectStateProto)-import Hix.Managed.Data.StageState (BuildStatus (Failure))+import Hix.Managed.Data.StageState (BuildResult (Finished), BuildStatus (Failure)) import qualified Hix.Managed.Handlers.Build as BuildHandlers import Hix.Managed.Handlers.Build (BuildHandlers (..)) import qualified Hix.Managed.Handlers.Build.Test as BuildHandlers@@ -45,6 +47,9 @@     build :: Versions -> M BuildStatus   } +nosortOptions :: ProjectOptions+nosortOptions = def {ProjectOptions.build = def {toposortMutations = False}}+ testParams ::   Bool ->   Packages ManagedPackageProto ->@@ -58,15 +63,16 @@     packages,     ghcPackages = GhcPackages {installed = [], available = []},     state = def,-    projectOptions = def,+    projectOptions = nosortOptions,     build = const (pure Failure)   } -data Result =+data Result a =   Result {     stateFile :: Expr,     cabalLog :: [(EnvConstraints, Maybe SolverPlan)],-    log :: [Text]+    log :: [Text],+    result :: a   }  testProjectContext ::@@ -85,14 +91,14 @@      defaultEnv = [(defaultEnvName, nKeys params.packages)] -managedTest ::+managedTest' ::   EnvName ->   TestParams ->-  (BuildHandlers -> ProjectContext -> M ProjectResult) ->-  TestT IO Result-managedTest defaultEnvName params main =+  (BuildHandlers -> ProjectOptions -> ProjectContextProto -> M a) ->+  TestT IO (Result a)+managedTest' defaultEnvName params main =   withFrozenCallStack do-    (handlers0, stateFileRef, _) <- BuildHandlers.handlersUnitTest params.ghcPackages params.build+    (handlers0, stateFileRef, _) <- BuildHandlers.handlersUnitTest params.ghcPackages (fmap Finished . params.build)     (cabalRef, handlers1) <-       if params.cabalLog       then first Just <$> BuildHandlers.logCabal handlers0@@ -102,8 +108,8 @@         if params.log         then handlers1 {report = ReportHandlers.handlersProd}         else handlers1-    (log, result) <- liftIO (runner (withProjectContext handlers params.projectOptions context (main handlers)))-    evalEither result+    (log, e_result) <- liftIO (runner (main handlers params.projectOptions context))+    result <- evalEither e_result     stateFile <- evalMaybe . head =<< liftIO (readIORef stateFileRef)     cabalLog <- fold <$> for cabalRef \ ref -> reverse <$> liftIO (readIORef ref)     pure Result {..}@@ -113,10 +119,20 @@      context = testProjectContext defaultEnvName params +managedTest ::+  EnvName ->+  TestParams ->+  (BuildHandlers -> ProjectContext -> M ProjectResult) ->+  TestT IO (Result ())+managedTest defaultEnvName params main =+  withFrozenCallStack do+    managedTest' defaultEnvName params \ handlers options context ->+      withProjectContext handlers options context (main handlers)+ lowerTest ::   TestParams ->   (BuildHandlers -> ProjectContext -> M ProjectResult) ->-  TestT IO Result+  TestT IO (Result ()) lowerTest params main =   withFrozenCallStack do     managedTest "lower" params main@@ -124,7 +140,7 @@ bumpTest ::   TestParams ->   (BuildHandlers -> ProjectContext -> M ProjectResult) ->-  TestT IO Result+  TestT IO (Result ()) bumpTest params main =   withFrozenCallStack do     managedTest "latest" params main
+ test/Hix/Test/Managed/SolveTest.hs view
@@ -0,0 +1,46 @@+module Hix.Test.Managed.SolveTest where++import qualified Data.Set as Set+import Hedgehog (evalEither)++import Hix.Class.Map (nKeys)+import qualified Hix.Managed.Cabal.Changes+import Hix.Managed.Cabal.Data.Config (GhcDb (GhcDbSystem))+import Hix.Managed.Cabal.Data.SolverState (solverState)+import qualified Hix.Managed.Cabal.Resources as SolveResources+import Hix.Managed.Cabal.Solve (solveWithCabal)+import Hix.Managed.Cabal.Sort (sortDeps)+import Hix.Managed.Data.EnvContext (EnvDeps (EnvDeps))+import Hix.Managed.Data.Mutable (unsafeMutableDep)+import Hix.Pretty (prettyL, showP, showPL)+import Hix.Test.Managed.UnsafeIsString ()+import Hix.Test.Utils (UnitTest, runMTest)++test_solve :: UnitTest+test_solve =+  evalEither =<< liftIO do+    runMTest True do+      res <- SolveResources.acquire mempty def (GhcDbSystem Nothing)+      result <- solveWithCabal res state+      sorted <- sortDeps res id (nKeys constraints)+      liftIO do+        putStrLn ""+        putStrLn "Constraints:"+        putStrLn (showP constraints)+        putStrLn ""+        putStrLn "Changes:"+        putStrLn (maybe "no plan" (show . prettyL . (.changes)) result)+        putStrLn ""+        putStrLn "Sorted:"+        putStrLn (showPL sorted)+  where+    state = solverState [] (EnvDeps (Set.fromList (unsafeMutableDep <$> nKeys constraints)) mempty) constraints def+    constraints =+      [+        ("ansi-terminal", mempty),+        ("async", mempty),+        ("polysemy-conc", mempty),+        ("polysemy-time", mempty),+        ("stm", mempty),+        ("time", mempty)+      ]
test/Hix/Test/ManagedTest.hs view
@@ -12,6 +12,7 @@ import Hix.Test.Managed.LowerNativeTest () import Hix.Test.Managed.LowerOptimize.CandidatesTest (test_candidatesOptimize) import Hix.Test.Managed.LowerOptimize.MutationTest (test_lowerOptimizeMutation)+import Hix.Test.Managed.LowerOptimize.OrderTest (test_mutationOrder) import Hix.Test.Managed.LowerStabilize.MutationTest (test_lowerStabilizeMutation) import Hix.Test.Managed.ProjectContextProtoTest (test_parseProjectContextProto) import Hix.Test.Utils (unitTest)@@ -29,7 +30,8 @@         unitTest "lower.optimize" test_lowerOptimizeMutation,         unitTest "lower.stabilize" test_lowerStabilizeMutation,         unitTest "lower.auto stabilize" test_lowerAutoMutationStabilize,-        test_lowerAutoMutationOptimize+        test_lowerAutoMutationOptimize,+        unitTest "mutation ordering" test_mutationOrder       ],       unitTest "parse ProjectContext" test_parseProjectContextProto,       test_buildOutput,