Correct. A freeze file that’s out of sync will just result in a non deterministic build.
However, a cabal build plan that’s out of sync should fail. We just lack a good interface to execute build plans from a file.
Correct. A freeze file that’s out of sync will just result in a non deterministic build.
However, a cabal build plan that’s out of sync should fail. We just lack a good interface to execute build plans from a file.
Note that if you both freeze the dependencies and also the repo-state, this is not true! The same solver will solve things the same way deterministically with regards to a given hackage repo state. And cabal freeze will record that repo state as well! This is because the freeze file contains not only the constraints stanza, but also something like index-state: hackage.haskell.org 2025-01-09T18:52:11Z – as such, running against A1, even if A1 has been edited to contain a dependency not listed in the constraints, will still be reproducible.
I don’t think this is correct in regards to automatic flags and pkg-config handling of cabal.
Ah fair point. If the system itself is differently configured – i.e. has different packages available via pkg-config, then that will potentially affect the solver. So if I add a package to dependencies, and that package is unconstrained but one version depends on a c library that is installed and another does not, then the solver may pick different versions depending on that. So I think what I wrote typically holds, but this is a good exception to note. Or rather, what I wrote holds under the assumption of the same c packages being available. (and of course this is an issue for reproducibility more generally – if we don’t reproduce the larger system environment we will still link against different versions of glibc or the like, even if we lock all haskell dependencies firmly).
how?? if you set a different specific version in your cabal file and attempt to update (which is how you’d update your lock file in any standard package manager), it won’t work, because that violates the freeze file. it doesn’t allow updates.
copilot actually recommended i delete the freeze file, but that’s obviously deeply problematic because then it has no idea what packages i want to change versions for. it could easily update any update-able package.
of course. i didn’t say anything like that. I’m just talking about normal lock behavior, where you update one or more dependencies and your lock file updates to reflect that.
you can produce a new lock file to reflect the updated dependencies. just run cabal freeze again. the issue here which i think is real is that you aren’t prompted to do this, and also that it would be nice to see an auto-diff of what changed in the rerun.
These would both be good ui improvements. Potentially we could just add a hash of the cabal file to the freeze file, and check it on ingestion.
Cabal already takes the newest package which your constraints are valid for. If you want even newer then the set of constraints might change. You could argue that in the cases in which the constraints don’t change cabal isn’t allowed to bump any of the packages but that seems like it’s easily accommodated by using ^>= constraints in your package definitions
That doesn’t work.
If you update, say, resourcet ^>=1.2.2 to resourcet ^>=1.3 in your *.cabal file and then run cabal freeze over an existing freeze file that contains any.resourcet ==1.2.6… you will get a solver error.
You either have to delete the entire freeze file or the resourcet line of it. And when you delete the whole file, you also lose the index state, potentially ending up with a rather different build plan.
fair. “just delete the file save index state then run freeze again” would be a better instruction.
Yes, but is that a good user interface?
its certainly one that could be improved, should somebody wish to do so!
i will note that since whenever i’ve had to work with freeze files they’ve been in version control, the lack of a sophisticated cabal interface has been compensated for by an adequate diff interface in git/github. and more to the point, at work, we don’t use freeze files because we maintain whole system control of our deployment with nix, which is a much more general solution.
how can i update package “foo” to the latest version (that satisfies my cabal version constraints)? like bundle update foo would do this (and update the lockfile of course). is there a cabal freeze foo?
copilot says: **Cabal's freeze command *cannot* update specific dependencies in isolation.** It always re-resolves and updates *all* dependencies based on the constraints.
so utterly painful.
literally just run cabal freeze again, as the message says. as the prior discussion says, you may want to move the freeze file or edit it first to prevent a conflict.
indeed you can never – in general – update specific dependencies in isolation because any change may invalidate other choices of versions of other packages.
if you want to update a specific package to a specific version, and hold all else constant, then you don’t need to run cabal freeze at all, just edit the freeze file manually.
here’s how bundler does it for example:
bundle update foo: updates foo to the latest version allowed by the Gemfile.lock, potentially updating other gems if required.bundle update foo --conservative: updates foo to the latest version allowed by the Gemfile.lock, without updating any other gems, even if required. if foo cannot be updated without also updating bar, then it will tell you, giving you the option to explicitly update both via bundle update foo bar --conservative.this is a dream to use, but i always just took it for granted.
bundle update installs updated versions of gems. it does not modify the lock, but as you say, it makes use of the lock.
cabal does not have or need this, because it is transparent to the use of cabal build which always ensures you are using updated dependencies relative to the choices made in the lock file.
there may be a feature that auto-updates lock files, but the feature you’re describing is not such a feature, nor does it address a workflow not present in cabal – rather its another ui to a core workflow that already exists in cabal. Maybe that’s the source of confusion?
the point is to not do that, but only update the package(s) you specify.
it absolutely does update the lock file. that’s the whole point.
i find it quite painful not having this feature. the amount of manual effort required to work around this is pretty significant.
but this could end up inadvertently updating a dependency you didn’t want to update.
ah i misread the comment you gave that it “updates foo to the latest version allowed by the Gemfile.lock, potentially updating other gems if required.” – taken literally that would mean that you must alter the lock before updating the gem. perhaps you meant “allowed by the Gemfile” not by the lock? Additionally, the docs seem to say it updates the specified gem and all dependencies of that gem, whether or not updating them is “required”? The docs say specifically: " In short, when you update a gem using bundle update, bundler will update all dependencies of that gem, including those that are also dependencies of another gem."
i know we’re going in circles – but i think i have a remaining question. we seem to have an “xy” problem (XY problem - Wikipedia) – you’ve stated the workflow you want. but not why. the reasons i know for wanting tight control over dependency versions are reproducible builds or whole-system audits. in the former case, i don’t see why updating one dependency at a time is better than just using version constraints and reproducing the whole freeze file. in the latter case, one wants more than a freeze file.
So what is the use case for automatic programmatic update of a single dependency while attempting to hold others constant?