9

Cabal-newstyle has a habit to occasionally install, like, all dependencies from scratch for no apparent reason. For a project using lens and other common packages, this can take upwards of ½ hour, which is annoying especially when the purpose is just to quickly compile a small change to an executable that built fine only a month ago.

Cabal does offer the option to specify as a constraint that a version of, say, lens that's already installed globally should be used. However, whenever I try that I just get

$ cabal new-run --constraint 'lens installed'
Resolving dependencies...
cabal: Could not resolve dependencies:
[__0] trying: my-example-project-0.1.0.0 (user goal)
[__1] next goal: lens (dependency of dat103-Justus-lectures)
[__1] rejecting: lens-5.2, lens-5.1.1, lens-5.1, lens-5.0.1, lens-5,
lens-4.19.2, lens-4.19.1, lens-4.19, lens-4.18.1, lens-4.18, lens-4.17.1,
lens-4.17, lens-4.16.1, lens-4.16, lens-4.15.4, lens-4.15.3, lens-4.15.2,
lens-4.15.1, lens-4.15, lens-4.14, lens-4.13.2.1, lens-4.13.2, lens-4.13.1,
...
lens-0.4, lens-0.3, lens-0.2, lens-0.1 (constraint from command line flag
requires installed instance)
[__1] fail (backjumping, conflict set: my-example-project, lens)
After searching the rest of the dependency tree exhaustively

This is in spite of the fact that plenty of lens versions can be found in

$ ls -d ~/.cabal/store/ghc-8.6.4/lens*
/home/js/.cabal/store/ghc-8.6.4/lens-4.19.2-1392f624ae052009275d5902a574e3f1804a66406a28222f0221cc2211da4f78
/home/js/.cabal/store/ghc-8.6.4/lens-4.19.2-4854f213f63b67c0e1ea871303590def7a79bce4cc742f658e41dc4c76b56022
/home/js/.cabal/store/ghc-8.6.4/lens-4.19.2-4caff99c8da3869df1dcfb67181d1be33d98316c3f458f93caedae05279c15e5
/home/js/.cabal/store/ghc-8.6.4/lens-4.19.2-76531bf275303c15a9e6ae5160ca29f8fb15782d230b2576724103d67a4020d9
/home/js/.cabal/store/ghc-8.6.4/lens-4.19.2-76a66d74e9cb85066009b345289db97d78bcf441beb12e6b762eb4d197000f19
/home/js/.cabal/store/ghc-8.6.4/lens-4.19.2-813faecc9a5593fc14ef1677e82b9b384b66de9fff7843635cd7f4e5f4993d16
/home/js/.cabal/store/ghc-8.6.4/lens-4.19.2-8c724c6d1fcc65ea38dc13fde30d86c4856151b235ef63628b23cee76d2feac5
/home/js/.cabal/store/ghc-8.6.4/lens-4.19.2-90ec9224b09b828c948a6a1a310a085edc28f92a269bb839a2ac575dc60d579a
/home/js/.cabal/store/ghc-8.6.4/lens-4.19.2-a157954a3d903fbf6444c054f9b704cab3fc23b36debc16fdc9ee8c0f5c0d5bd
/home/js/.cabal/store/ghc-8.6.4/lens-4.19.2-c3bf086b6e081a91c7085b3c645652de0c4097edc4c88069203c5f741b14b97f
/home/js/.cabal/store/ghc-8.6.4/lens-4.19.2-d5dca17dd7dddf6f324313f363ef74ff15d5717011dd750f415ce39942fc703b
/home/js/.cabal/store/ghc-8.6.4/lens-4.19.2-e4a1a8c9f3867cc6e6dc4dc059cc7fc0e4912d9aa4ce5476f63cdea9d3c8535e
/home/js/.cabal/store/ghc-8.6.4/lens-5.0.1-0956efd5ac393e6034c76c4dcc30a40ad1b12ad2b7a708e0f980648bac6287f3
/home/js/.cabal/store/ghc-8.6.4/lens-5.0.1-bfba2b0d59ebb884a2f32a960694c0e44af4fd29a21d8021ebdc42ce994fd055
/home/js/.cabal/store/ghc-8.6.4/lens-5.0.1-ce6c5a7496a674d60f93fae50caa4eb58acc26c6536937cce0f5243238cd6215
/home/js/.cabal/store/ghc-8.6.4/lens-5.0.1-d951ac24c700a8ae7f708a46a084e8fb4b0b008af29e5527f4d8986399248c62
/home/js/.cabal/store/ghc-8.6.4/lens-5.1-08384468083ebbc9d39619bfb59a32b97333acbf703c24bcf3b0bd22357a0115
...

So why are these not used? How to find out what Cabal even considers an “installed version”, and how to prevent that an installed version stops being usable?

sjakobi
  • 3,546
  • 1
  • 25
  • 43
leftaroundabout
  • 117,950
  • 5
  • 174
  • 319
  • 1
    I don't know if there are tools to diagnose this, but the problem is certainly the package keys. Package keys change every time the dependencies change, including their flags. This is likely caused by the solver choosing a version of a dependency that none of those installed versions are built against, and the solver not being able to backtrack to resolve that. The primary way I prevent this sort of thing is by not running `cabal update`. – Carl Sep 02 '22 at 17:47
  • The question as it stands is a bit vague imo. General advice that comes to mind: --dry-run may be helpful (https://cabal.readthedocs.io/en/stable/cabal-project.html?highlight=dry#cmdoption-dry-run), as well as cabal-plan (https://hackage.haskell.org/package/cabal-plan) for inspecting the current plan. I don't know of anything that would get the solver to "explain" why something does not qualify but, again, generally speaking, there are many parameters at play, not just a version number (profiling, package flags, optimization levels) – Artem Pelenitsyn Sep 03 '22 at 02:27
  • Also, you may get a more authoritative answer if you ask on the cabal bug tracker and maybe cook a minimal example of what you find confusing. – Artem Pelenitsyn Sep 03 '22 at 02:28
  • Over on #hackage (libera.chat) people say: what may help in forcing particular versions is `cabal freeze`. – Artem Pelenitsyn Sep 03 '22 at 02:32
  • I believe the [`index-state`](https://cabal.readthedocs.io/en/latest/cabal-project.html?highlight=index-state#cfg-field-index-state) option by itself might be sufficient to prevent this "update churn", but I might be mistaken. – sjakobi Sep 03 '22 at 11:02

1 Answers1

2

As mentioned in the comments, creating a "freeze file" using cabal freeze is a way to avoid "update churn":

cabal freeze writes out a freeze file which records all of the versions and flags that are picked by the solver under the current index and flags.

What if you didn't create such a file back in the day, how to avoid update churn now? cabal freeze seems to support the following command-line option:

 --index-state=STATE            Use source package index state as it existed
                                at a previous time. Accepts unix-timestamps
                                (e.g. '@1474732068'), ISO8601 UTC timestamps
                                (e.g. '2016-09-24T17:47:48Z'), or 'HEAD'
                                (default: 'HEAD').

So you could supply a date of around a month ago, and hopefully the freeze file will be generated without taking into account any versions of the project's dependencies released after that date.

danidiaz
  • 26,936
  • 4
  • 45
  • 95
  • A simpler way to "freeze" the build plan is to record the [`index-state`](https://cabal.readthedocs.io/en/latest/cabal-project.html#cfg-field-index-state) in the `cabal.project` file. A simple way to get the timestamp for the current index state is to run `cabal update`: The output includes the timestamps for both the new and the old index states. – sjakobi Sep 03 '22 at 11:24
  • Ok, but can this in any way be combined with updating _some_ packages, to versions that weren't published yet at the time of the old index state? If not, then this is a highly impractical solution. – leftaroundabout Sep 12 '22 at 12:10