From e8742a5cc477318aeb6dd3046d0dd239b0447e4a Mon Sep 17 00:00:00 2001 From: Mikhail Glushenkov <mikhail.glushenkov@gmail.com> Date: Fri, 17 May 2013 17:51:34 +0200 Subject: [PATCH] Make newly-added add-source deps override previously installed versions. Fixes #1197. This patch is a bit large because it includes several related changes: 1) Remove 'installUseSandbox' from 'InstallFlags' and pass 'useSandbox' as an additional argument instead. 2) Instead of calling 'reinstallAddSourceDeps' from 'installAction', always pass 'SandboxPackageInfo' to 'install'. 3) Set the timestamps of newly-added add-source deps to 0 in the timestamp file. 4) Move the timestamp file update to 'postInstallActions' from 'withModifiedDeps'. This way, the timestamps are updated even when the user runs 'install --only-dependencies' or 'install some-add-source-dep-package-id'. --- .../Distribution/Client/Dependency.hs | 2 +- cabal-install/Distribution/Client/Install.hs | 50 ++++-- cabal-install/Distribution/Client/Sandbox.hs | 167 ++++++++++-------- .../Distribution/Client/Sandbox/Timestamp.hs | 68 +++---- .../Distribution/Client/Sandbox/Types.hs | 6 +- cabal-install/Distribution/Client/Setup.hs | 14 +- cabal-install/Main.hs | 45 ++--- 7 files changed, 207 insertions(+), 145 deletions(-) diff --git a/cabal-install/Distribution/Client/Dependency.hs b/cabal-install/Distribution/Client/Dependency.hs index 679e40a110..36d3fa1c14 100644 --- a/cabal-install/Distribution/Client/Dependency.hs +++ b/cabal-install/Distribution/Client/Dependency.hs @@ -325,7 +325,7 @@ applySandboxInstallPolicy :: SandboxPackageInfo -> DepResolverParams -> DepResolverParams applySandboxInstallPolicy - (SandboxPackageInfo modifiedDeps otherDeps allSandboxPkgs) + (SandboxPackageInfo modifiedDeps otherDeps allSandboxPkgs _allDeps) params = addPreferences [ PackageInstalledPreference n PreferInstalled diff --git a/cabal-install/Distribution/Client/Install.hs b/cabal-install/Distribution/Client/Install.hs index ea276e740b..26d4924ac6 100644 --- a/cabal-install/Distribution/Client/Install.hs +++ b/cabal-install/Distribution/Client/Install.hs @@ -29,6 +29,7 @@ module Distribution.Client.Install ( import Data.List ( unfoldr, nub, sort, (\\) ) +import qualified Data.Set as S import Data.Maybe ( isJust, fromMaybe, maybeToList ) import Control.Exception as Exception @@ -66,7 +67,10 @@ import Distribution.Client.Setup , ConfigExFlags(..), InstallFlags(..) ) import Distribution.Client.Config ( defaultCabalDir ) -import Distribution.Client.Sandbox.Types ( SandboxPackageInfo(..), isUseSandbox ) +import Distribution.Client.Sandbox.Timestamp + ( withUpdateTimestamps ) +import Distribution.Client.Sandbox.Types + ( SandboxPackageInfo(..), UseSandbox(..), isUseSandbox ) import Distribution.Client.Tar (extractTarGzFile) import Distribution.Client.Types as Source import Distribution.Client.BuildReports.Types @@ -120,7 +124,8 @@ import Distribution.Version import Distribution.Simple.Utils as Utils ( notice, info, warn, debugNoWrap, die, intercalate, withTempDirectory ) import Distribution.Client.Utils - ( numberOfProcessors, inDir, mergeBy, MergeResult(..) ) + ( numberOfProcessors, inDir, mergeBy, MergeResult(..) + , tryCanonicalizePath ) import Distribution.System ( Platform, OS(Windows), buildOS ) import Distribution.Text @@ -154,6 +159,7 @@ install -> Compiler -> Platform -> ProgramConfiguration + -> UseSandbox -> Maybe SandboxPackageInfo -> GlobalFlags -> ConfigFlags @@ -162,7 +168,7 @@ install -> HaddockFlags -> [UserTarget] -> IO () -install verbosity packageDBs repos comp platform conf mSandboxPkgInfo +install verbosity packageDBs repos comp platform conf useSandbox mSandboxPkgInfo globalFlags configFlags configExFlags installFlags haddockFlags userTargets0 = do @@ -173,7 +179,7 @@ install verbosity packageDBs repos comp platform conf mSandboxPkgInfo processInstallPlan verbosity args installContext installPlan where args :: InstallArgs - args = (packageDBs, repos, comp, platform, conf, mSandboxPkgInfo, + args = (packageDBs, repos, comp, platform, conf, useSandbox, mSandboxPkgInfo, globalFlags, configFlags, configExFlags, installFlags, haddockFlags) @@ -192,6 +198,7 @@ type InstallArgs = ( PackageDBStack , Compiler , Platform , ProgramConfiguration + , UseSandbox , Maybe SandboxPackageInfo , GlobalFlags , ConfigFlags @@ -203,7 +210,7 @@ type InstallArgs = ( PackageDBStack makeInstallContext :: Verbosity -> InstallArgs -> Maybe [UserTarget] -> IO InstallContext makeInstallContext verbosity - (packageDBs, repos, comp, _, conf,_, + (packageDBs, repos, comp, _, conf,_,_, globalFlags, _, _, _, _) mUserTargets = do installedPkgIndex <- getInstalledPackages verbosity comp packageDBs conf @@ -233,7 +240,7 @@ makeInstallContext verbosity makeInstallPlan :: Verbosity -> InstallArgs -> InstallContext -> IO (Progress String String InstallPlan) makeInstallPlan verbosity - (_, _, comp, platform, _, mSandboxPkgInfo, + (_, _, comp, platform, _, _, mSandboxPkgInfo, _, configFlags, configExFlags, installFlags, _) (installedPkgIndex, sourcePkgDb, @@ -251,7 +258,7 @@ processInstallPlan :: Verbosity -> InstallArgs -> InstallContext -> InstallPlan -> IO () processInstallPlan verbosity - args@(_,_, _, _, _, _, _, _, _, installFlags, _) + args@(_,_, _, _, _, _, _, _, _, _, installFlags, _) (installedPkgIndex, sourcePkgDb, userTargets, pkgSpecifiers) installPlan = do checkPrintPlan verbosity installedPkgIndex installPlan sourcePkgDb @@ -618,8 +625,8 @@ postInstallActions :: Verbosity -> InstallPlan -> IO () postInstallActions verbosity - (packageDBs, _, comp, platform, conf, _, globalFlags, configFlags - , _, installFlags, _) + (packageDBs, _, comp, platform, conf, useSandbox, mSandboxPkgInfo + ,globalFlags, configFlags, _, installFlags, _) targets installPlan = do unless oneShot $ @@ -643,6 +650,9 @@ postInstallActions verbosity printBuildFailures installPlan + updateSandboxTimestampsFile useSandbox mSandboxPkgInfo + comp platform installPlan + where reportingLevel = fromFlag (installBuildReports installFlags) logsDir = fromFlag (globalLogsDir globalFlags) @@ -795,6 +805,24 @@ printBuildFailures plan = InstallFailed e -> " failed during the final install step." ++ " The exception was:\n " ++ show e +-- | If we're working inside a sandbox and some add-source deps were installed, +-- update the timestamps of those deps. +updateSandboxTimestampsFile :: UseSandbox -> Maybe SandboxPackageInfo + -> Compiler -> Platform -> InstallPlan + -> IO () +updateSandboxTimestampsFile (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 | ConfiguredPackage pkg _ _ _ <- allInstalled ] + allPaths = [ pth | LocalUnpackedPackage pth + <- map packageSource allSrcPkgs] + allPathsCanonical <- mapM tryCanonicalizePath allPaths + return $! filter (`S.member` allAddSourceDeps) allPathsCanonical + +updateSandboxTimestampsFile _ _ _ _ _ = return () -- ------------------------------------------------------------ -- * Actually do the installations @@ -815,7 +843,7 @@ performInstallations :: Verbosity -> InstallPlan -> IO InstallPlan performInstallations verbosity - (packageDBs, _, comp, _, conf,_, + (packageDBs, _, comp, _, conf, useSandbox, _, globalFlags, configFlags, configExFlags, installFlags, haddockFlags) installedPkgIndex installPlan = do @@ -921,7 +949,7 @@ performInstallations verbosity miscOptions = InstallMisc { rootCmd = if fromFlag (configUserInstall configFlags) - || isUseSandbox (installUseSandbox installFlags) + || (isUseSandbox useSandbox) then Nothing -- ignore --root-cmd if --user -- or working inside a sandbox. else flagToMaybe (installRootCmd installFlags), diff --git a/cabal-install/Distribution/Client/Sandbox.hs b/cabal-install/Distribution/Client/Sandbox.hs index 2e9a570c2a..77af2752f2 100644 --- a/cabal-install/Distribution/Client/Sandbox.hs +++ b/cabal-install/Distribution/Client/Sandbox.hs @@ -27,6 +27,9 @@ module Distribution.Client.Sandbox ( maybeReinstallAddSourceDeps, maybeUpdateSandboxConfig, + SandboxPackageInfo(..), + maybeWithSandboxPackageInfo, + tryGetIndexFilePath, sandboxBuildDir, getInstalledPackagesInSandbox, @@ -39,10 +42,10 @@ import Distribution.Client.Setup ( SandboxFlags(..), ConfigFlags(..), ConfigExFlags(..), InstallFlags(..) , GlobalFlags(..), defaultConfigExFlags, defaultInstallFlags , defaultSandboxLocation, globalRepos ) -import Distribution.Client.Sandbox.Timestamp ( maybeAddCompilerTimestampRecord +import Distribution.Client.Sandbox.Timestamp ( listModifiedDeps + , maybeAddCompilerTimestampRecord , withAddTimestamps - , withRemoveTimestamps - , withModifiedDeps ) + , withRemoveTimestamps ) import Distribution.Client.Config ( SavedConfig(..), loadConfig ) import Distribution.Client.Dependency ( foldProgress ) import Distribution.Client.IndexUtils ( BuildTreeRefType(..) ) @@ -90,12 +93,13 @@ import qualified Distribution.Client.Sandbox.Index as Index import qualified Distribution.Simple.PackageIndex as InstalledPackageIndex import qualified Distribution.Simple.Register as Register import qualified Data.Map as M +import qualified Data.Set as S import Control.Exception ( assert, bracket_ ) import Control.Monad ( forM, liftM2, unless, when ) import Data.Bits ( shiftL, shiftR, xor ) import Data.Char ( ord ) import Data.IORef ( newIORef, writeIORef, readIORef ) -import Data.List ( (\\), delete, foldl' ) +import Data.List ( delete, foldl' ) import Data.Monoid ( mempty, mappend ) import Data.Word ( Word32 ) import Numeric ( showHex ) @@ -167,8 +171,13 @@ tryLoadSandboxConfig verbosity configFileFlag = do -- | Return the name of the package index file for this package environment. tryGetIndexFilePath :: SavedConfig -> IO FilePath -tryGetIndexFilePath config = do - let paths = globalLocalRepos . savedGlobalFlags $ config +tryGetIndexFilePath config = tryGetIndexFilePath' (savedGlobalFlags config) + +-- | The same as 'tryGetIndexFilePath', but takes 'GlobalFlags' instead of +-- 'SavedConfig'. +tryGetIndexFilePath' :: GlobalFlags -> IO FilePath +tryGetIndexFilePath' globalFlags = do + let paths = globalLocalRepos globalFlags case paths of [] -> die $ "Distribution.Client.Sandbox.tryGetIndexFilePath: " ++ "no local repos found. " ++ checkConfiguration @@ -461,55 +470,42 @@ data WereDepsReinstalled = ReinstalledSomeDeps | NoDepsReinstalled -- | Reinstall those add-source dependencies that have been modified since -- we've last installed them. Assumes that we're working inside a sandbox. reinstallAddSourceDeps :: Verbosity - -> SavedConfig -> ConfigFlags -> ConfigExFlags -> InstallFlags -> GlobalFlags -> FilePath -> IO WereDepsReinstalled -reinstallAddSourceDeps verbosity config configFlags' configExFlags +reinstallAddSourceDeps verbosity configFlags' configExFlags installFlags globalFlags sandboxDir = topHandler' $ do - let sandboxDistPref = sandboxBuildDir sandboxDir - configFlags = configFlags' - { configDistPref = Flag sandboxDistPref } - haddockFlags = mempty - { haddockDistPref = Flag sandboxDistPref } - indexFile <- tryGetIndexFilePath config - buildTreeRefs <- Index.listBuildTreeRefs verbosity - Index.DontListIgnored Index.OnlyLinks indexFile - retVal <- newIORef NoDepsReinstalled - - unless (null buildTreeRefs) $ do - (comp, platform, conf) <- configCompilerAux' configFlags - let compId = compilerId comp - - withModifiedDeps verbosity sandboxDir compId platform $ \modifiedDeps -> do - assert (null $ modifiedDeps \\ buildTreeRefs) (return ()) - unless (null modifiedDeps) $ do - sandboxPkgInfo <- makeSandboxPackageInfo verbosity configFlags - comp conf buildTreeRefs modifiedDeps - let modifiedAndInstalledDeps = modifiedAddSourceDependencies - sandboxPkgInfo - - unless (null modifiedAndInstalledDeps) $ do - notice verbosity "Installing add-source dependencies..." - - let args :: InstallArgs - args = ((configPackageDB' configFlags) - ,(globalRepos globalFlags) - ,comp, platform, conf, 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 extend 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 + let sandboxDistPref = sandboxBuildDir sandboxDir + configFlags = configFlags' + { configDistPref = Flag sandboxDistPref } + haddockFlags = mempty + { haddockDistPref = Flag sandboxDistPref } + (comp, platform, conf) <- configCompilerAux' configFlags + retVal <- newIORef NoDepsReinstalled + + withSandboxPackageInfo verbosity configFlags globalFlags + comp platform conf sandboxDir $ \sandboxPkgInfo -> + unless (null $ modifiedAddSourceDependencies sandboxPkgInfo) $ do + + let args :: InstallArgs + args = ((configPackageDB' configFlags) + ,(globalRepos globalFlags) + ,comp, platform, conf + ,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 + + processInstallPlan verbosity args installContext installPlan + writeIORef retVal ReinstalledSomeDeps readIORef retVal @@ -522,24 +518,34 @@ reinstallAddSourceDeps verbosity config configFlags' configExFlags -- to be conservative. return ReinstalledSomeDeps --- | Given a list of all add-source deps and a list of modified add-source deps, --- produce a 'SandboxPackageInfo'. -makeSandboxPackageInfo :: Verbosity -> ConfigFlags - -> Compiler -> ProgramConfiguration - -> [FilePath] -> [FilePath] - -> IO SandboxPackageInfo -makeSandboxPackageInfo verbosity configFlags comp conf - allAddSourceDeps modifiedAddSourceDeps = do +-- | Produce a 'SandboxPackageInfo' and feed it to the given action. Note that +-- we don't update the timestamp file here - this is done in +-- 'postInstallActions'. +withSandboxPackageInfo :: Verbosity -> ConfigFlags -> GlobalFlags + -> Compiler -> Platform -> ProgramConfiguration + -> FilePath + -> (SandboxPackageInfo -> IO ()) + -> IO () +withSandboxPackageInfo verbosity configFlags globalFlags + comp platform conf sandboxDir cont = do + -- List all add-source deps. + indexFile <- tryGetIndexFilePath' 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 - -- Get the package descriptions of all add-source deps. - depsCabalFiles <- mapM findPackageDesc allAddSourceDeps + -- Get the package descriptions for all add-source deps. + depsCabalFiles <- mapM findPackageDesc buildTreeRefs depsPkgDescs <- mapM (readPackageDescription verbosity) depsCabalFiles - let depsMap = M.fromList (zip allAddSourceDeps depsPkgDescs) + let depsMap = M.fromList (zip buildTreeRefs depsPkgDescs) -- Get the package ids of modified (and installed) add-source deps. + modifiedAddSourceDeps <- listModifiedDeps verbosity sandboxDir + (compilerId comp) platform let isInstalled pkgid = not . null . InstalledPackageIndex.lookupSourcePackageId installedPkgIndex $ pkgid isModified path = path `elem` modifiedAddSourceDeps @@ -548,16 +554,38 @@ makeSandboxPackageInfo verbosity configFlags comp conf depsMap modifiedDeps = M.assocs modifiedDepsMap + assert (all (`S.member` allAddSourceDepsSet) modifiedAddSourceDeps) (return ()) + unless (null modifiedDeps) $ + notice verbosity $ "Some add-source dependencies have been modified. " + ++ "They will be reinstalled..." + -- Get the package ids of the remaining add-source deps (some are possibly not -- installed). - let otherDeps = M.assocs (depsMap `M.difference` modifiedDepsMap) + let otherDeps = M.assocs (depsMap `M.difference` modifiedDepsMap) - return $ SandboxPackageInfo (map toSourcePackage modifiedDeps) - (map toSourcePackage otherDeps) installedPkgIndex + -- Finally, assemble a 'SandboxPackageInfo'. + cont $ SandboxPackageInfo (map toSourcePackage modifiedDeps) + (map toSourcePackage otherDeps) installedPkgIndex allAddSourceDepsSet - where - toSourcePackage (path, pkgDesc) = SourcePackage - (packageId pkgDesc) pkgDesc (LocalUnpackedPackage path) Nothing + where + toSourcePackage (path, pkgDesc) = SourcePackage + (packageId pkgDesc) pkgDesc (LocalUnpackedPackage path) Nothing + +-- | Same as 'withSandboxPackageInfo' if we're inside a sandbox and a no-op +-- otherwise. +maybeWithSandboxPackageInfo :: Verbosity -> ConfigFlags -> GlobalFlags + -> Compiler -> Platform -> ProgramConfiguration + -> UseSandbox + -> (Maybe SandboxPackageInfo -> IO ()) + -> IO () +maybeWithSandboxPackageInfo verbosity configFlags globalFlags + comp platform conf useSandbox cont = + case useSandbox of + NoSandbox -> cont Nothing + UseSandbox sandboxDir -> withSandboxPackageInfo verbosity + configFlags globalFlags + comp platform conf sandboxDir + (\spi -> cont (Just spi)) -- | Check if a sandbox is present and call @reinstallAddSourceDeps@ in that -- case. @@ -598,8 +626,7 @@ maybeReinstallAddSourceDeps verbosity numJobsFlag configFlags' globalFlags' = do `mappend` savedInstallFlags config installFlags = installFlags' { installNumJobs = installNumJobs installFlags' - `mappend` numJobsFlag, - installUseSandbox = useSandbox + `mappend` numJobsFlag } globalFlags = savedGlobalFlags config -- This makes it possible to override things like @@ -608,7 +635,7 @@ maybeReinstallAddSourceDeps verbosity numJobsFlag configFlags' globalFlags' = do -- fine. `mappend` globalFlags' - depsReinstalled <- reinstallAddSourceDeps verbosity config + depsReinstalled <- reinstallAddSourceDeps verbosity configFlags configExFlags installFlags globalFlags sandboxDir return (useSandbox, depsReinstalled) diff --git a/cabal-install/Distribution/Client/Sandbox/Timestamp.hs b/cabal-install/Distribution/Client/Sandbox/Timestamp.hs index da6e3bbca2..28fee8e7ff 100644 --- a/cabal-install/Distribution/Client/Sandbox/Timestamp.hs +++ b/cabal-install/Distribution/Client/Sandbox/Timestamp.hs @@ -14,7 +14,7 @@ module Distribution.Client.Sandbox.Timestamp ( withUpdateTimestamps, maybeAddCompilerTimestampRecord, isDepModified, - withModifiedDeps, + listModifiedDeps, ) where import Control.Exception (finally) @@ -73,13 +73,16 @@ timestampRecordKey compId platform = display platform ++ "-" ++ display compId timestampFileName :: FilePath timestampFileName = "add-source-timestamps" --- | Read the timestamp file. Returns an empty list if the file doesn't exist. -readTimestampFile :: FilePath -> IO (Maybe [TimestampFileRecord]) +-- | 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 timestampString <- readFile timestampFile `catchIO` \_ -> return "[]" case reads timestampString of - [(timestamps, s)] | all isSpace s -> return (Just timestamps) - _ -> return Nothing + [(timestamps, s)] | all isSpace s -> return timestamps + _ -> + die $ "The timestamps file is corrupted. " + ++ "Please delete & recreate the sandbox." -- | Write the timestamp file, atomically. writeTimestampFile :: FilePath -> [TimestampFileRecord] -> IO () @@ -94,25 +97,21 @@ withTimestampFile :: FilePath -> ([TimestampFileRecord] -> IO [TimestampFileRecord]) -> IO () withTimestampFile sandboxDir process = do - let timestampFile = sandboxDir </> timestampFileName - mTimestampRecords <- readTimestampFile timestampFile - case mTimestampRecords of - Nothing -> die $ "The timestamps file is corrupted. " - ++ "Please delete & recreate the sandbox." - Just timestampRecords -> do - timestampRecords' <- process timestampRecords - writeTimestampFile timestampFile timestampRecords' + let timestampFile = sandboxDir </> timestampFileName + timestampRecords <- readTimestampFile timestampFile >>= process + writeTimestampFile timestampFile timestampRecords -- | Given a list of 'AddSourceTimestamp's, a list of paths to add-source deps --- we've added and the current time, add an 'AddSourceTimestamp' to the list for --- each path that isn't already included. +-- we've added and an initial timestamp, add an 'AddSourceTimestamp' to the list +-- for each path. If a timestamp for a given path already exists in the list, +-- update it. addTimestamps :: EpochTime -> [AddSourceTimestamp] -> [FilePath] -> [AddSourceTimestamp] -addTimestamps now timestamps paths = - map (\p -> (p, now)) newPaths ++ timestamps +addTimestamps initial timestamps newPaths = + [ (p, initial) | p <- newPaths ] ++ oldTimestamps where - oldPaths = map fst timestamps - (_, newPaths) = partition (flip elem oldPaths) paths + (oldTimestamps, _toBeUpdated) = + partition (\(path, _) -> path `notElem` newPaths) timestamps -- | Given a list of 'AddSourceTimestamp's, a list of paths to add-source deps -- we've reinstalled and a new timestamp value, update the timestamp value for @@ -156,8 +155,8 @@ maybeAddCompilerTimestampRecord verbosity sandboxDir indexFile -- build tree refs to the timestamps file (for all compilers). withAddTimestamps :: FilePath -> IO [FilePath] -> IO () withAddTimestamps sandboxDir act = do - now <- getCurTime - withActionOnAllTimestamps (addTimestamps now) sandboxDir act + let initialTimestamp = 0 + withActionOnAllTimestamps (addTimestamps initialTimestamp) sandboxDir act -- | Given an IO action that returns a list of build tree refs, remove those -- build tree refs from the timestamps file (for all compilers). @@ -256,14 +255,19 @@ isDepModified verbosity now (packageDir, timestamp) = do return True else go rest --- | Given an IO action, feed to it the list of modified add-source deps and --- set their timestamps to the current time in the timestamps file. -withModifiedDeps :: Verbosity -> FilePath -> CompilerId -> Platform - -> ([FilePath] -> IO ()) -> IO () -withModifiedDeps verbosity sandboxDir compId platform act = do - withUpdateTimestamps sandboxDir compId platform $ \timestamps -> do - now <- getCurTime - modified <- fmap (map fst) . filterM (isDepModified verbosity now) - $ timestamps - act modified - return modified +-- | List all modified dependencies. +listModifiedDeps :: Verbosity -> FilePath -> CompilerId -> Platform + -> IO [FilePath] +listModifiedDeps verbosity sandboxDir compId platform = do + timestampRecords <- readTimestampFile (sandboxDir </> timestampFileName) + let needle = timestampRecordKey compId platform + timestamps <- maybe noTimestampRecord return + (lookup needle timestampRecords) + now <- getCurTime + fmap (map fst) . filterM (isDepModified verbosity now) $ timestamps + + where + noTimestampRecord = die $ "С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 ." diff --git a/cabal-install/Distribution/Client/Sandbox/Types.hs b/cabal-install/Distribution/Client/Sandbox/Types.hs index b0b6726ff3..17f0d46a2d 100644 --- a/cabal-install/Distribution/Client/Sandbox/Types.hs +++ b/cabal-install/Distribution/Client/Sandbox/Types.hs @@ -16,6 +16,7 @@ import qualified Distribution.Simple.PackageIndex as InstalledPackageIndex import Distribution.Client.Types (SourcePackage) import Data.Monoid +import qualified Data.Set as S -- | Are we using a sandbox? data UseSandbox = UseSandbox FilePath | NoSandbox @@ -50,8 +51,11 @@ data SandboxPackageInfo = SandboxPackageInfo { -- ^ Remaining add-source deps. Some of these may be not installed in the -- sandbox. - otherInstalledSandboxPackages :: InstalledPackageIndex.PackageIndex + otherInstalledSandboxPackages :: InstalledPackageIndex.PackageIndex, -- ^ All packages installed in the sandbox. Intersection with -- 'modifiedAddSourceDependencies' and/or 'otherAddSourceDependencies' can be -- non-empty. + + allAddSourceDependencies :: S.Set FilePath + -- ^ A set of paths to all add-source dependencies, for convenience. } diff --git a/cabal-install/Distribution/Client/Setup.hs b/cabal-install/Distribution/Client/Setup.hs index 6188902df6..ee732c4cf1 100644 --- a/cabal-install/Distribution/Client/Setup.hs +++ b/cabal-install/Distribution/Client/Setup.hs @@ -45,8 +45,6 @@ import Distribution.Client.BuildReports.Types ( ReportLevel(..) ) import Distribution.Client.Dependency.Types ( PreSolver(..) ) -import Distribution.Client.Sandbox.Types - ( UseSandbox(..) ) import qualified Distribution.Client.Init.Types as IT ( InitFlags(..), PackageType(..) ) import Distribution.Client.Targets @@ -778,8 +776,7 @@ data InstallFlags = InstallFlags { installBuildReports :: Flag ReportLevel, installSymlinkBinDir :: Flag FilePath, installOneShot :: Flag Bool, - installNumJobs :: Flag (Maybe Int), - installUseSandbox :: UseSandbox + installNumJobs :: Flag (Maybe Int) } defaultInstallFlags :: InstallFlags @@ -803,8 +800,7 @@ defaultInstallFlags = InstallFlags { installBuildReports = Flag NoReports, installSymlinkBinDir = mempty, installOneShot = Flag False, - installNumJobs = mempty, - installUseSandbox = mempty + installNumJobs = mempty } where docIndexFile = toPathTemplate ("$datadir" </> "doc" </> "index.html") @@ -996,8 +992,7 @@ instance Monoid InstallFlags where installBuildReports = mempty, installSymlinkBinDir = mempty, installOneShot = mempty, - installNumJobs = mempty, - installUseSandbox = mempty + installNumJobs = mempty } mappend a b = InstallFlags { installDocumentation = combine installDocumentation, @@ -1019,8 +1014,7 @@ instance Monoid InstallFlags where installBuildReports = combine installBuildReports, installSymlinkBinDir = combine installSymlinkBinDir, installOneShot = combine installOneShot, - installNumJobs = combine installNumJobs, - installUseSandbox = combine installUseSandbox + installNumJobs = combine installNumJobs } where combine field = field a `mappend` field b diff --git a/cabal-install/Main.hs b/cabal-install/Main.hs index 3119c7d0e2..5b12b30842 100644 --- a/cabal-install/Main.hs +++ b/cabal-install/Main.hs @@ -77,9 +77,9 @@ import Distribution.Client.Sandbox (sandboxInit ,loadConfigOrSandboxConfig ,initPackageDBIfNeeded ,maybeWithSandboxDirOnSearchPath + ,maybeWithSandboxPackageInfo ,WereDepsReinstalled(..) ,maybeReinstallAddSourceDeps - ,reinstallAddSourceDeps ,maybeUpdateSandboxConfig ,tryGetIndexFilePath ,sandboxBuildDir @@ -471,7 +471,6 @@ installAction (configFlags, configExFlags, installFlags, haddockFlags) savedConfigureExFlags config `mappend` configExFlags installFlags' = defaultInstallFlags `mappend` savedInstallFlags config `mappend` installFlags - { installUseSandbox = useSandbox } globalFlags' = savedGlobalFlags config `mappend` globalFlags haddockFlags' = haddockFlags { haddockDistPref = sandboxDistPref } (comp, platform, conf) <- configCompilerAux' configFlags' @@ -493,26 +492,32 @@ installAction (configFlags, configExFlags, installFlags, haddockFlags) maybeAddCompilerTimestampRecord verbosity sandboxDir indexFile (compilerId comp) platform - -- If "." is among the targets, we should reinstall add-source dependencies - -- for this compiler and maybe rewrite the 'with-compiler' and 'package-db' - -- fields in the 'cabal.sandbox.config' file. - when (null targets || (UserTargetLocalDir ".") `elem` targets) $ do + -- If "." is among the targets, we may need to rewrite the 'with-compiler' + -- and 'package-db' fields in the 'cabal.sandbox.config' file (since the + -- current package will be configured with the new compiler). + when (containsCurrentDir targets) $ maybeUpdateSandboxConfig verbosity config configFlags'' - -- 'install .' always runs 'configure', so we don't need to force - -- reconfigure ourselves. - _ <- reinstallAddSourceDeps verbosity config configFlags'' configExFlags' - installFlags' globalFlags' - sandboxDir - return () - maybeWithSandboxDirOnSearchPath useSandbox $ - install verbosity - (configPackageDB' configFlags'') - (globalRepos globalFlags') - comp platform conf - Nothing -- FIXME - globalFlags' configFlags'' configExFlags' installFlags' haddockFlags' - targets + -- FIXME: Passing 'SandboxPackageInfo' unconditionally means that 'install' + -- will sometimes reinstall modified add-source deps. Probably not a big + -- problem since 'build', 'test' etc are already doing it. + maybeWithSandboxPackageInfo verbosity configFlags'' globalFlags' + comp platform conf useSandbox $ \mSandboxPkgInfo -> + maybeWithSandboxDirOnSearchPath useSandbox $ + install verbosity + (configPackageDB' configFlags'') + (globalRepos globalFlags') + comp platform conf + useSandbox mSandboxPkgInfo + globalFlags' configFlags'' configExFlags' + installFlags' haddockFlags' + targets + + where + -- FIXME: Should also check for absolute path and UserTargetLocalCabalFile. + containsCurrentDir targets = null targets + || (UserTargetLocalDir ".") `elem` targets + testAction :: (TestFlags, BuildExFlags) -> [String] -> GlobalFlags -> IO () testAction (testFlags, buildExFlags) extraArgs globalFlags = do -- GitLab