Skip to content

Commit a46658e

Browse files
authored
Merge pull request #3985 from ezyang/pr/nix-sdist-replacement
For non-Custom packages, replace sdist with hand-rolled rebuild checking
2 parents d8c1017 + 19b8c68 commit a46658e

File tree

6 files changed

+272
-38
lines changed

6 files changed

+272
-38
lines changed

Cabal/Distribution/Simple/SrcDist.hs

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -177,12 +177,7 @@ listPackageSourcesOrdinary verbosity pkg_descr pps =
177177
case testInterface t of
178178
TestSuiteExeV10 _ mainPath -> do
179179
biSrcs <- allSourcesBuildInfo bi pps []
180-
srcMainFile <- do
181-
ppFile <- findFileWithExtension (ppSuffixes pps)
182-
(hsSourceDirs bi) (dropExtension mainPath)
183-
case ppFile of
184-
Nothing -> findFile (hsSourceDirs bi) mainPath
185-
Just pp -> return pp
180+
srcMainFile <- findMainExeFile bi pps mainPath
186181
return (srcMainFile:biSrcs)
187182
TestSuiteLibV09 _ m ->
188183
allSourcesBuildInfo bi pps [m]
@@ -196,12 +191,7 @@ listPackageSourcesOrdinary verbosity pkg_descr pps =
196191
case benchmarkInterface bm of
197192
BenchmarkExeV10 _ mainPath -> do
198193
biSrcs <- allSourcesBuildInfo bi pps []
199-
srcMainFile <- do
200-
ppFile <- findFileWithExtension (ppSuffixes pps)
201-
(hsSourceDirs bi) (dropExtension mainPath)
202-
case ppFile of
203-
Nothing -> findFile (hsSourceDirs bi) mainPath
204-
Just pp -> return pp
194+
srcMainFile <- findMainExeFile bi pps mainPath
205195
return (srcMainFile:biSrcs)
206196
BenchmarkUnsupported tp -> die $ "Unsupported benchmark type: "
207197
++ show tp
@@ -427,6 +417,9 @@ allSourcesBuildInfo bi pps modules = do
427417
let searchDirs = hsSourceDirs bi
428418
sources <- fmap concat $ sequenceA $
429419
[ let file = ModuleName.toFilePath module_
420+
-- NB: *Not* findFileWithExtension, because the same source
421+
-- file may show up in multiple paths due to a conditional;
422+
-- we need to package all of them. See #367.
430423
in findAllFilesWithExtension suffixes searchDirs file
431424
>>= nonEmpty (notFound module_) return
432425
| module_ <- modules ++ otherModules bi ]

cabal-install/Distribution/Client/FileMonitor.hs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ module Distribution.Client.FileMonitor (
1515
monitorFile,
1616
monitorFileHashed,
1717
monitorNonExistentFile,
18+
monitorFileExistence,
1819
monitorDirectory,
1920
monitorNonExistentDirectory,
2021
monitorDirectoryExistence,
@@ -123,6 +124,12 @@ monitorFileHashed = MonitorFile FileHashed DirNotExists
123124
monitorNonExistentFile :: FilePath -> MonitorFilePath
124125
monitorNonExistentFile = MonitorFile FileNotExists DirNotExists
125126

127+
-- | Monitor a single file for existence only. The monitored file is
128+
-- considered to have changed if it no longer exists.
129+
--
130+
monitorFileExistence :: FilePath -> MonitorFilePath
131+
monitorFileExistence = MonitorFile FileExists DirNotExists
132+
126133
-- | Monitor a single directory for changes, based on its modification
127134
-- time. The monitored directory is considered to have changed if it no
128135
-- longer exists or if its modification time has changed.

cabal-install/Distribution/Client/ProjectBuilding.hs

Lines changed: 32 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,15 @@ import Distribution.Client.FetchUtils
5656
import Distribution.Client.GlobalFlags (RepoContext)
5757
import qualified Distribution.Client.Tar as Tar
5858
import Distribution.Client.Setup (filterConfigureFlags)
59+
import Distribution.Client.SourceFiles
5960
import Distribution.Client.SrcDist (allPackageSourceFiles)
6061
import Distribution.Client.Utils (removeExistingFile)
6162

6263
import Distribution.Package hiding (InstalledPackageId, installedPackageId)
64+
import qualified Distribution.PackageDescription as PD
6365
import Distribution.InstalledPackageInfo (InstalledPackageInfo)
6466
import qualified Distribution.InstalledPackageInfo as Installed
67+
import Distribution.Types.BuildType
6568
import Distribution.Simple.Program
6669
import qualified Distribution.Simple.Setup as Cabal
6770
import Distribution.Simple.Command (CommandUI)
@@ -85,7 +88,6 @@ import qualified Data.ByteString.Lazy as LBS
8588

8689
import Control.Monad
8790
import Control.Exception
88-
import Data.List
8991
import Data.Maybe
9092

9193
import System.FilePath
@@ -452,15 +454,14 @@ updatePackageBuildFileMonitor :: PackageFileMonitor
452454
-> MonitorTimestamp
453455
-> ElaboratedConfiguredPackage
454456
-> BuildStatusRebuild
455-
-> [FilePath]
457+
-> [MonitorFilePath]
456458
-> BuildResultMisc
457459
-> IO ()
458460
updatePackageBuildFileMonitor PackageFileMonitor{pkgFileMonitorBuild}
459461
srcdir timestamp pkg pkgBuildStatus
460-
allSrcFiles buildResult =
462+
monitors buildResult =
461463
updateFileMonitor pkgFileMonitorBuild srcdir (Just timestamp)
462-
(map monitorFileHashed allSrcFiles)
463-
buildComponents' buildResult
464+
monitors buildComponents' buildResult
464465
where
465466
(_pkgconfig, buildComponents) = packageFileMonitorKeyValues pkg
466467

@@ -1041,29 +1042,35 @@ buildInplaceUnpackedPackage verbosity
10411042
annotateFailureNoLog BuildFailed $
10421043
setup buildCommand buildFlags buildArgs
10431044

1044-
--TODO: [required eventually] this doesn't track file
1045-
--non-existence, so we could fail to rebuild if someone
1046-
--adds a new file which changes behavior.
1047-
allSrcFiles <-
1048-
let trySdist = allPackageSourceFiles verbosity scriptOptions srcdir
1049-
-- This is just a hack, to get semi-reasonable file
1050-
-- listings for the monitor
1051-
tryFallback = do
1052-
warn verbosity $
1053-
"Couldn't use sdist to compute source files; falling " ++
1054-
"back on recursive file scan."
1055-
filter (not . ("dist" `isPrefixOf`))
1056-
`fmap` getDirectoryContentsRecursive srcdir
1057-
in if elabSetupScriptCliVersion pkg >= mkVersion [1,17]
1058-
then do r <- trySdist
1059-
if null r
1060-
then tryFallback
1061-
else return r
1062-
else tryFallback
1045+
let listSimple =
1046+
execRebuild srcdir (needElaboratedConfiguredPackage pkg)
1047+
listSdist =
1048+
fmap (map monitorFileHashed) $
1049+
allPackageSourceFiles verbosity scriptOptions srcdir
1050+
ifNullThen m m' = do xs <- m
1051+
if null xs then m' else return xs
1052+
monitors <- case PD.buildType (elabPkgDescription pkg) of
1053+
Just Simple -> listSimple
1054+
-- If a Custom setup was used, AND the Cabal is recent
1055+
-- enough to have sdist --list-sources, use that to
1056+
-- determine the files that we need to track. This can
1057+
-- cause unnecessary rebuilding (for example, if README
1058+
-- is edited, we will try to rebuild) but there isn't
1059+
-- a more accurate Custom interface we can use to get
1060+
-- this info. We prefer not to use listSimple here
1061+
-- as it can miss extra source files that are considered
1062+
-- by the Custom setup.
1063+
_ | elabSetupScriptCliVersion pkg >= mkVersion [1,17]
1064+
-- However, sometimes sdist --list-sources will fail
1065+
-- and return an empty list. In that case, fall
1066+
-- back on the (inaccurate) simple tracking.
1067+
-> listSdist `ifNullThen` listSimple
1068+
| otherwise
1069+
-> listSimple
10631070

10641071
updatePackageBuildFileMonitor packageFileMonitor srcdir timestamp
10651072
pkg buildStatus
1066-
allSrcFiles buildResult
1073+
monitors buildResult
10671074

10681075
-- PURPOSELY omitted: no copy!
10691076

cabal-install/Distribution/Client/RebuildMonad.hs

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ module Distribution.Client.RebuildMonad (
1212
-- * Rebuild monad
1313
Rebuild,
1414
runRebuild,
15+
execRebuild,
1516
askRoot,
1617

1718
-- * Setting up file monitoring
@@ -44,6 +45,12 @@ module Distribution.Client.RebuildMonad (
4445
getDirectoryContentsMonitored,
4546
createDirectoryMonitored,
4647
monitorDirectoryStatus,
48+
doesFileExistMonitored,
49+
need,
50+
needIfExists,
51+
findFileWithExtensionMonitored,
52+
findFirstFileMonitored,
53+
findFileMonitored,
4754
) where
4855

4956
import Prelude ()
@@ -58,7 +65,7 @@ import Distribution.Verbosity (Verbosity)
5865

5966
import Control.Monad.State as State
6067
import Control.Monad.Reader as Reader
61-
import System.FilePath (takeFileName)
68+
import System.FilePath
6269
import System.Directory
6370

6471

@@ -88,6 +95,10 @@ unRebuild rootDir (Rebuild action) = runStateT (runReaderT action rootDir) []
8895
runRebuild :: FilePath -> Rebuild a -> IO a
8996
runRebuild rootDir (Rebuild action) = evalStateT (runReaderT action rootDir) []
9097

98+
-- | Run a 'Rebuild' IO action.
99+
execRebuild :: FilePath -> Rebuild a -> IO [MonitorFilePath]
100+
execRebuild rootDir (Rebuild action) = execStateT (runReaderT action rootDir) []
101+
91102
-- | The root that relative paths are interpreted as being relative to.
92103
askRoot :: Rebuild FilePath
93104
askRoot = Rebuild Reader.ask
@@ -166,3 +177,58 @@ monitorDirectoryStatus dir = do
166177
then monitorDirectory dir
167178
else monitorNonExistentDirectory dir]
168179

180+
-- | Like 'doesFileExist', but in the 'Rebuild' monad. This does
181+
-- NOT track the contents of 'FilePath'; use 'need' in that case.
182+
doesFileExistMonitored :: FilePath -> Rebuild Bool
183+
doesFileExistMonitored f = do
184+
root <- askRoot
185+
exists <- liftIO $ doesFileExist (root </> f)
186+
monitorFiles [if exists
187+
then monitorFileExistence f
188+
else monitorNonExistentFile f]
189+
return exists
190+
191+
-- | Monitor a single file
192+
need :: FilePath -> Rebuild ()
193+
need f = monitorFiles [monitorFileHashed f]
194+
195+
-- | Monitor a file if it exists; otherwise check for when it
196+
-- gets created. This is a bit better for recompilation avoidance
197+
-- because sometimes users give bad package metadata, and we don't
198+
-- want to repeatedly rebuild in this case (which we would if we
199+
-- need'ed a non-existent file).
200+
needIfExists :: FilePath -> Rebuild ()
201+
needIfExists f = do
202+
root <- askRoot
203+
exists <- liftIO $ doesFileExist (root </> f)
204+
monitorFiles [if exists
205+
then monitorFileHashed f
206+
else monitorNonExistentFile f]
207+
208+
-- | Like 'findFileWithExtension', but in the 'Rebuild' monad.
209+
findFileWithExtensionMonitored
210+
:: [String]
211+
-> [FilePath]
212+
-> FilePath
213+
-> Rebuild (Maybe FilePath)
214+
findFileWithExtensionMonitored extensions searchPath baseName =
215+
findFirstFileMonitored id
216+
[ path </> baseName <.> ext
217+
| path <- nub searchPath
218+
, ext <- nub extensions ]
219+
220+
-- | Like 'findFirstFile', but in the 'Rebuild' monad.
221+
findFirstFileMonitored :: (a -> FilePath) -> [a] -> Rebuild (Maybe a)
222+
findFirstFileMonitored file = findFirst
223+
where findFirst [] = return Nothing
224+
findFirst (x:xs) = do exists <- doesFileExistMonitored (file x)
225+
if exists
226+
then return (Just x)
227+
else findFirst xs
228+
229+
-- | Like 'findFile', but in the 'Rebuild' monad.
230+
findFileMonitored :: [FilePath] -> FilePath -> Rebuild (Maybe FilePath)
231+
findFileMonitored searchPath fileName =
232+
findFirstFileMonitored id
233+
[ path </> fileName
234+
| path <- nub searchPath]

0 commit comments

Comments
 (0)