Add separate cache for getPkgConfigDb - #9422
Conversation
geekosaur
left a comment
There was a problem hiding this comment.
Would it be worth adding a test that ran cabal twice and verified that the cached pkg-config output is used the second time?
I think it would be, I am not sure whether we have anything similar already. I'll check. |
|
Not for caching, but there's a test that verifies that a package reinstall causes a warning IIRC. |
|
this looks excellent, thanks! |
6170fe3 to
eaf490a
Compare
|
Can you describe the cache invalidation strategy? Does the cache get invalidated when It would be good to add some tests testing the different invalidation mechanisms. |
Yes, I am relying on what It obtains PKG_CONFIG_PATH and for monitors each directory for changes. As the comment says, it does not monitor each individual files. Perhaps it could be worth monitoring the result of
I agree, I am planning to write some. |
eaf490a to
20acde7
Compare
|
I did some rework and added tests but one of the tests ( |
03e8346 to
89d3ee1
Compare
89d3ee1 to
0f81808
Compare
74e96f0 to
9cf9a17
Compare
|
@mpickering do you think the tests are sufficient? I am checking the search path (for the pkg-config executable) and PKG_CONFIG_PATH. I don't think it is a perfect solution but it seems to be the approach taken elsewhere. |
9cf9a17 to
f97fb81
Compare
|
@jasagredo does 3ae0488 look alright to you? I tried to follow what you did in #9527 |
🧐 |
the pkg-config executable is the same in all my shells. What changes is the non-standard variable I think there's nothing we can do here unfortunately. @andreabedini I tried again and the result is identical |
oh, in my tests the path to the wrapper was changing :-/ |
Right, and in the nix world this is all detectable because the derivation that produces the Some ideas:
EDIT: since it's per-project the damage seems less bad, but it maybe forces Nix users to |
|
I don't have much time to dedicate to this so I would appreciate if anybody could help pushing this over the line. It's complete but I had issues with the tests. |
547f70c to
5b71c97
Compare
|
So I tried to run this on Windows again. Some remarks:
|
|
I got this error on my machine: |
|
^ This was fixed in #9915. Needs a rebase |
|
All tests pass in my machine if using:
|
|
FTR: I'm using this to undo-redo the symlinks on Windows |
5b71c97 to
331f3cd
Compare
|
I did the rebase but I some tests failed locally. Perhaps the code has bit-rotted a bit. |
|
You will have to add a step somewhere to install pkg-config on Windows:
|
ac9fab2 to
5749297
Compare
5749297 to
3f54ae6
Compare
There was a problem hiding this comment.
Pull request overview
This PR introduces a dedicated per-project cache for the pkg-config database to avoid re-querying pkg-config on every plan change, reducing repeated solver overhead (especially on systems where pkg-config queries are expensive).
Changes:
- Add a new
getPkgConfigDbcaching layer stored in the project cache directory, with a user-facing notice when the pkg-config DB is queried/refreshed. - Refactor project planning to compute and pass
pkgConfigDBinto the solver/elaboration phases. - Add a testsuite case to validate pkg-config DB caching and invalidation behavior (and adjust an existing test’s golden-output update step).
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
cabal-install/src/Distribution/Client/ProjectPlanning.hs |
Adds per-project file monitor for pkg-config DB and threads cached pkgConfigDB into planning phases. |
cabal-install/src/Distribution/Client/ProjectConfig.hs |
Exports and adds resolveProgramDb helper for program path overrides. |
cabal-testsuite/PackageTests/MonitorPkgConfig/* |
New test validating pkg-config DB cache reuse + invalidation signals. |
cabal-testsuite/PackageTests/NewUpdate/RejectFutureIndexStates/cabal.test.hs |
Simplifies golden-output rewriting to avoid platform-specific sed -i handling. |
changelog.d/pr-9422 |
Changelog entry describing the new pkg-config DB caching behavior. |
Comments suppressed due to low confidence (2)
cabal-install/src/Distribution/Client/ProjectPlanning.hs:675
- The improved/elaborated plan caches do not depend on the pkg-config DB cache. Since
pkgConfigDBis computed outsidererunIfChangedand is not included in the cache keys, changes in pkg-config dirs/PKG_CONFIG_PATH can leavefileMonitorImprovedPlan/fileMonitorElaboratedPlanreturning a stale plan without re-running the solver/elaboration. Include a pkg-config signature (or a monitored file) in these cache keys so pkg-config changes invalidate the plan caches.
-- 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
cabal-install/src/Distribution/Client/ProjectPlanning.hs:793
fileMonitorSolverPlan’s cache key does not includepkgConfigDB, even thoughplanPackagesdepends on it. If the pkg-config DB changes, the solver plan can be reused incorrectly. AddpkgConfigDB(or a stable signature derived from it) to thererunIfChangedkey, or otherwise ensure this monitor is invalidated when the pkg-config DB cache refreshes.
rerunIfChanged
verbosity
fileMonitorSolverPlan
( solverSettings
, localPackages
, localPackagesEnabledStanzas
, compiler
, platform
, programDbSignature progdb
)
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| progdb <- liftIO $ resolveProgramDb verbosity (projectConfigLocalPackages projectConfig) |
There was a problem hiding this comment.
resolveProgramDb is built only from projectConfigLocalPackages, so it ignores the shared/global extra-prog-path (projectConfigProgPathExtra) that configureCompiler includes. This can regress locating pkg-config when it is only available via the shared config; consider constructing this ProgramDb from both shared + local program path settings (or reuse the same logic as configureCompiler).
| progdb <- liftIO $ resolveProgramDb verbosity (projectConfigLocalPackages projectConfig) | |
| projectConfigPrograms = | |
| projectConfigShared projectConfig | |
| <> projectConfigLocalPackages projectConfig | |
| progdb <- liftIO $ resolveProgramDb verbosity projectConfigPrograms |
| progdb <- liftIO $ resolveProgramDb verbosity (projectConfigLocalPackages projectConfig) | ||
| monitorFiles (programsMonitorFiles progdb) | ||
|
|
There was a problem hiding this comment.
monitorFiles (programsMonitorFiles progdb) is executed outside any rerunIfChanged action, so these monitored files are not recorded in any file monitor cache and won’t trigger rebuilds. If you intended program changes (e.g. pkg-config executable) to invalidate caches, move the monitoring into the relevant rerunIfChanged body (or add the signature to the key) instead of doing it here.
| -- | ProgramDb with user specified paths | ||
| resolveProgramDb :: Verbosity -> PackageConfig -> IO ProgramDb | ||
| resolveProgramDb verbosity packageConfig = do | ||
| let extraPath = fromNubList (packageConfigProgramPathExtra packageConfig) | ||
| programDb <- prependProgramSearchPath verbosity extraPath [] defaultProgramDb | ||
| let paths = Map.toList $ getMapLast (packageConfigProgramPaths packageConfig) | ||
| return $ userSpecifyPaths paths programDb |
There was a problem hiding this comment.
resolveProgramDb only applies packageConfigProgramPathExtra / packageConfigProgramPaths. Callers that need the full project program search path (including ProjectConfigShared.projectConfigProgPathExtra) will miss it. Either extend this helper to incorporate the shared/global extra-prog-path, or rename/document it as “package config only” to avoid accidental misuse (as in ProjectPlanning’s pkg-config lookup).
| @@ -11,8 +11,7 @@ main = skipIfCIAndWindows 10230 >> cabalTest (flakyIfCI 9530 $ withProjectFile " | |||
| . resultOutput | |||
| <$> recordMode DoNotRecord (cabal' "update" []) | |||
| -- update golden output with actual timestamp | |||
There was a problem hiding this comment.
This sed invocation uses the w cabal.out command, which appends if cabal.out already exists. That can make the test output accumulate across reruns in the same directory. Prefer writing via stdout redirection (e.g. sed ... cabal.out.in > cabal.out) or explicitly removing/truncating cabal.out first.
| -- update golden output with actual timestamp | |
| -- update golden output with actual timestamp | |
| shell "rm" ["-f", "cabal.out"] |
| liftIO $ do | ||
| createDirectoryIfMissing True pkgConfigPath | ||
| setEnv "PKG_CONFIG_PATH" pkgConfigPath | ||
|
|
||
| cabal' "v2-build" ["--dry-run", "p", "-v2"] |
There was a problem hiding this comment.
This test mutates the process environment via setEnv, which can leak into subsequent tests in the same process. Prefer using the testsuite’s withEnv helper so the env override is scoped to the cabal invocations (and is automatically restored afterwards).
| @@ -0,0 +1,35 @@ | |||
| import Distribution.Compat.Environment (setEnv) | |||
| import System.Directory (copyFile, createDirectoryIfMissing, removeDirectoryRecursive) | |||
There was a problem hiding this comment.
removeDirectoryRecursive is imported but never used in this test, which adds noise and can produce warnings under -Wall. Remove the unused import (or use it if cleanup is intended).
| import System.Directory (copyFile, createDirectoryIfMissing, removeDirectoryRecursive) | |
| import System.Directory (copyFile, createDirectoryIfMissing) |
3f54ae6 to
0101ae9
Compare
Querying pkg-config for the version of every module can be a very expensive operation on some systems. This change adds a separate, per-project, cache for PkgConfigDB; reducing the cost from "every plan change" to "every pkg-config-db change per project". The cache key is composed by the pkg-config configured program and the list of directories reported by pkg-config's pc_path variable. Co-authored-by: andreabedini <69135+andreabedini@users.noreply.github.com> fix: skip MonitorPkgConfig test on Windows (no pkg-config available) fix(MonitorPkgConfig): provide fake pkg-config for Windows CI fix: address review comments
0101ae9 to
b42d236
Compare
Querying pkg-config for the version of every module can be a very expensive operation on some systems. This change adds a separate, per-project, cache for pkgConfigDB; reducing the cost from "every plan change" to "every pkg-config-db change per project".
No input key is required since getPkgConfigDb already specifies the directories to monitor for changes. These are obtained from pkg-config itself as
pkg-config --variable pc_path pkg-configas documented in pkg-config(1).A notice is presented to the user when refreshing the packagedb.
Closes #8930 and subsumes #9360.
changelog entry to follow