New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Recommended way to use cabal freeze
for an executable?
#8047
Comments
cabal freeze
for an executablecabal freeze
for an executable?
When moving from stack to cabal, I grabbed the constraints from stackage and stuffed those into In this screenshot, The docs say that with freeze files "all users see a consistent set of dependencies". Does that imply a reproducible build if using the same compiler version? The |
Some information is also present in https://cabal.readthedocs.io/en/latest/cabal-project.html#cabal-project-reference -> it says that So I would assume from this that But maybe trick is in this "appendable" part? I don't know hm. But for some reason I just tried following: I had
in my .cabal file. So then I modified .cabal to have more relaxed upper bound (latest version of template-haskell is 2.18):
When I ran
Which I guess is not so bad -> it did some work, but it didn't really install any new dependencies, or change any dependencies at all, it just recompiled the targets in the package. Probably it does that because it sees that cabal file changed but isn't aware what changed. So then I modified .cabal file to contain:
and this resulted in failure
As seen in the error message, it says that freeze file demanded template haskell to be of version 2.16.0.0 exactly, and that is why the resolution failed. So this is great actually, it seems from this that freeze file is used! So I guess I answered my own question, but I conclude that freeze file is automatically for all/most cabal commands, and effectively results in reproducible builds. And yes, the workflow I described sounds to be a reasonable workflow. And no, there is no way to run And yes, it seems the docs are wrong at that place where they mention Does this make sense all together? |
I can imagine one scenario that causes build to not be reproducible any more, and that is adding a new dependency to .cabal but forgetting to run In that case I guess that, since .cabal and freeze file are combined together, that new dependency will just retain the version bounds from .cabal and that is it, therefore leaving it un-pinned. I am not sure what is a solution to this, if there is a way to ensure that people run There is one more thing I just realized: should I be deleting existing freeze file before running |
The rebuild happens because modifying the cabal file invalidates the local/project cache, but due to the freeze file the same reproducible build plan will be constructed, and the previously built dependencies will be reused |
Thanks, that is what I also concluded at the end! |
Just to be clear, I still have questions outstanding:
I am guessing for each of these what the answer might be, and I described those above, but it would be great to hear from somebody experienced what are the actual answers. |
There exists something similar in npm for example? dont you have to run some npm command (or a file watcher that run the command) to update its lock file? or you are thinking in make |
That is where I got the idea: in npm, package.lock.json is updated automatically.
This means that if you do smth like So yes, it would come down to cabal freeze file most likely being updated on |
thanks, the npm example is really useful yeah, there would be other involved cabal command, like install f.e. however I think it would be hard make it the default, as quite cabal users don't use freeze files in local development by default |
It could be a flag that if turned on automatically regenerates freeze file. Library devs wouldn't use it probalby, while application devs would use it. I don't yet know how important this automatic feature is though. It sounds nice, and I know in |
Regarding ensuring that freeze file is up to date: what I just did in our project was add this check in the CI that checks if freeze file is up to date, so I will share it here in case it is interesting for the discussion. If freeze file is not up to date, CI fails. So although there is no automatic updating of freeze file by cabal, this ensures that it is indeed up to date. It is a stronger/safer mechanism than automatic updating by cabal, but on the other hand most people probably won't think of doing it hm. PR: https://github.com/wasp-lang/wasp/pull/508/files Crucial code (Github Actions):
One thing I believe I learned from this, while thinking about regenerating freeze file, is that in most cases, the best thing to do is to first delete freeze file and then run So if cabal was to implement automatic regeneration of freeze file, it would almost certainly have to consist of first deleting existing freeze file and only then generating a new one (or using some other way to generate a new one while ignoring the existing one). I believe npm uses the same approach when regenerating package.lock.json -> it ignores existing one while doing it, and doesn't just add to it, but regenerates it whole, as if it never existed. |
Having used Javascript's NPM in the past, I like it that
cabal
hasfreeze
option, to create a freeze file, which from what I understand, is similar to package.lock.json in npm world.Since I have a Haskell executable that has multiple team members working on it + gets built in the CI, I would like to use freeze file to make build reproducible -> meaning that when same git commit is executed on CI or on machine of developer A or machine of developer B, they are all guaranteed to get the same result. This is how package.lock.json is used also. So it doesn't really matter which dependency bounds are specified in myproject.cabal, nor does it matter if new patches were released for the packages we use in the project -> all that matters is the freeze file.
From what I understood, good workflow for this would be:
cabal freeze
once you are done. So for example, you will modify the version boundaries for some package in myproject.cabal, or you will add a new dependency, or remove an existing one, or docabal update
and want to get patches -> whatever you do, at the end you should docabal freeze
, so that these changes are reflected in the freeze file.One thing I wasn't sure about though is: does
cabal
actually use onlycabal.project.freeze
whencabal.project.freeze
exists, does that happen by default? Testing it out locally, and googling about it, I developed a notion that it doesn't use exclusively freeze file, or maybe doesn't use it at all, at least not by default. But if that is so, what is the purpose of freeze file?When I say testing locally, I mean that I generate freeze file with
cabal freeze
, runcabal build
, it says it is up to date, but then if I update some dependency bound in myproject.cabal and runcabal build
again, it will rebuild! While I would expect it to ignore the change, due to using freeze file.Another question: is there a way to run
cabal freeze
automatically on any change in dependencies, the same waypackage.lock.json
is generated automatically?Also, https://cabal.readthedocs.io/en/3.6/cabal-package.html?highlight=freeze#freezing-dependency-versions
-> it says output is cabal.config, but that is not correct is it? Isn't output cabal.project.freeze ?
EDIT:
TODO (for the PR I would love to create at the end of this):
cabal build
will do a bit of work if you modify .cabal, it will not produce a new build, it will still be the same old build (due to the freeze file). It just looks like it is doing smth new, because cabal file got touched.cabal.project.freeze
.cabal.project
.cabal freeze
, when to delete freeze file, should it be committed, ...). Consider discussing executable vs library use cases (how it is not that important for a library) -> not sure if that is too opinionated though, but I have seen that opinion reiterated on multiple blog posts.The text was updated successfully, but these errors were encountered: