-
Notifications
You must be signed in to change notification settings - Fork 2.8k
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
[RFC] Switch from stack
to cabal-install
for building Haskell code
#3280
Comments
This has been on my mind too.
Yeah, I agree, this is super annoying, I just figured out how to live with it using
If I'm not wrong, we can use stackage snapshots with cabal? So we won't lose out any reproducibility that The last time I tried
Instead, if we could specify these customisations in separate files say cabal new-build --project-file-local cabal.project.local.fast for fast builds with cabal new-build --project-file-local cabal.project.local.profile to enable profiling. I think this can be done with |
I agree that it would be nice if Personally, I actually kind of like the |
I think we can do this, but to be entirely honest I don’t see the point. When I’ve built One of the nice things about the existence of Stackage is that we still benefit from it even if we do not consume it directly. Stackage effectively provides a kind of ecosystem-wide CI that helps to ensure packages are kept up to date and version bounds are properly maintained. This is good, as it makes it more likely that |
👍 I guess the workflow that I want can be easily handled by a shell script. I agree, with |
Depending on the details, it might also be very easy to just add to
I think that all we have to do to update dependencies is either update the freeze file directly (which is easy—it just outputs an extra We might need to add some version bounds to some of our dependencies if we ever end up with a build plan that gives us a newer version of a library than we can actually build against, but that’s pretty easy and provides value, anyway. And if we want to upgrade absolutely everything to the newest possible version, then yes, we can always use |
I'll open an issue with cabal-install folks to see if they are interested in this. I have nothing more to add, but I think folks who are using |
I was considering modifying my I also feel confident we could (maybe even more easily) maintain graphql-engine under cabal install by just manually upgrading and freezing dependencies. But I also want a little more clarity on what that would look like: do we expect developers to always be building/testing/benchmarking against the frozen dependencies (I think that's a good idea)? How often do we plan on bumping them, and what is that process like (hopefully not too often or for no reason since that simply cause a lot of rebuilding for folks, and the process should maybe involve some benchmarks... not sure)? |
...and also:
|
I’ve been meaning to open a PR like this for a while, but I haven’t quite gotten around to it. Personally, I’d like to make the switch all in one go—I’d rather not have some things (e.g. CI,
Yes, my intent was to always build using the frozen dependencies.
I don’t think there needs to be too much ceremony around this if someone wants to bump a particular dependency simply because they want a newer feature or something like that. As for bumping them because we want to pull in bugfixes and things like that, I’m not sure, but to be honest I’m not super worried about that, either—the ecosystem is usually pretty stable. We haven’t been bumping our LTS very often and that seems to have been fine.
I think we should probably all build with the same GHC version, and we should probably pin |
Yeah that makes sense. How about an initial PR with just
Okay yeah I think you're right. It shouldn't disrupt normal local development cycle anyway. @0x777 sort of obvious I guess but it might work well and follow a unixy convention to have many |
Yeah, I think that makes sense. It could even serve as a collaborative branch if others want to help move it along so it doesn’t have to be entirely one person. I’d definitely be willing to pitch in a little time, and I can share my current |
The idea here is to start the process of moving away from stack (potentially), but making this move official if we decie to, will require more coordination (moving CI, dev.sh and docs all at once). I've added a "fast" variation of cabal.project.local mostly as a demonstration. I haven't found a "prof" variant that really works for me. All this is pretty open to change, but it would be nice to have some local cabal.project files that encode good/useful stuff. The freeze file should be version-exact to our stackage LTS snapshot.
Cool feel free to comment or contribute to that branch ^ I think I'm just using what you posted above. I don't feel like I have a great idea yet of what a good development workflow looks like using cabal-install for this project. I seem to be triggering a lot of rebuilds of dependencies as I tweak the |
The idea here is to start the process of moving away from stack (potentially), but making this move official if we decie to, will require more coordination (moving CI, dev.sh and docs all at once). I've added a "fast" variation of cabal.project.local mostly as a demonstration. I haven't found a "prof" variant that really works for me. All this is pretty open to change, but it would be nice to have some local cabal.project files that encode good/useful stuff. The freeze file should be version-exact to our stackage LTS snapshot.
…hasura#3280) (hasura#3558) Co-authored-by: Alexis King <[email protected]>
What
I propose we switch away from using
stack
as our Haskell build tool tocabal-install
(aka thecabal
command-line executable).Why
Why bother switching build tools? Generally, the reasons fall into two categories: reasons
stack
is insufficient and reasonscabal-install
has gotten better.Problems with
stack
First and foremost, the number one reason to switch away from
stack
is the way it handles profiling. I do not know how to properly profile Haskell code usingstack
, as it forcibly compiles all dependencies with-fprof-auto
(stack --profile shouldn't force --ghc-options='-auto-all' commercialhaskell/stack#2853), which is simply untenable. To me, this is a dealbreaker: when I have needed to profilegraphql-engine
, I have needed to rebuild it withcabal-install
anyway.My experience with
stack
2.x has been poor. It uses a new caching system calledpantry
, which is similar in spirit tocabal-install
’s Nix-style build caching. However,pantry
’s caching model is worse—it does not properly account for different sets of build flags in dependencies (which is related to the above point on why-fprof-auto
cannot be disabled)—and it relies on a global SQLite database that provides zero options from recovering from an invalid caching state.Also related to the above, there is no way to build library dependencies with different optimization levels, so it is not possible to reliably link against libraries compiled with
-O2
.Getting access to new versions of GHC and libraries with
stack
is slow. The use of Stackage LTSes means reliable builds, but it also means you’re locked into those packages unless you want to do a lot of constraint solving yourself.stack
has also recently removed thestack solver
escape hatch, leaving it with no constraint solver capabilities whatsoever.Various other minor infelicities, some of which I wrote about here.
Improvements in
cabal-install
cabal-install
’s support for Nix-style local builds (akacabal new-build
) combined with support for per-project configuration (in the form ofcabal.project
files) is a total game-changer.cabal-install
now supports essentially all the flexibility provided bystack.yaml
files and much more:cabal.project
files support multi-project builds, as well as using packages directly from git repositories, just likestack.yaml
files do.cabal.project
files allow fine-grained control over the build options for every dependency in your project, and it actually caches them properly. If you modify the optimization or profiling settings for a dependency,cabal new-build
will rebuild the minimal set of things necessary, caching the build results globally.cabal new-freeze
allows pinning the results of build plan construction, just like package lockfiles in other ecosystems likenpm
, ensuring reliable builds without needing to rely on a Stackage snapshot.Improvements in infrastructure like Stackage, Hackage CI, and HEAD.hackage have made
cabal-install
build plans more consistent than ever. Error reporting for plan construction failure is still not ideal, but it is readable, and unlikestack
,cabal-install
allows manually weakening individual constraints of individual dependencies to allow fine-grained control over the constraint solver, if it really comes to that.It is hard to overstate how pleasant I have found using
cabal new-build
to build Haskell projects recently compared tostack
. One of the coolest features ofcabal new-build
is that it’s entirely configuration file driven, so you can create acabal.project.local
file that is.gitignore
d, and you can use that to control whatcabal new-build
will do on your local machine: you can change optimization levels, modify profiling options, tweak settings for building documentation, and more, all without needing to muck with any command-line flags. Furthermore, the available options are fairly well-documented, and I found getting started withcabal new-build
to be pretty easy.How
Even if you’re sold by my sales pitch for
cabal-install
, perhaps you’re worried that switching away fromstack
will require a ton of work and will screw up your workflow. Everyone’s workflow is a little different, so I can’t say for certain what your experience will be, but mine has been complete ease. I have already been regularly buildinggraphql-engine
withcabal-install
on my local machine, and it’s so easy to do that it really hasn’t even involved any significant overhead despite the fact that everything in the project is set up forstack
.Here’s what changes:
We swap out the
stack.yaml
file for acabal.project
file. This is thecabal.project
file I’ve been using:This basically just works.
Instead of running
stack build
to buildgraphql-engine
, you runcabal new-build
. Instead of passing command-line options tocabal new-build
, create acabal.project.local
file that specifies any changes to how the project should be built on your machine. For example, mycabal.project.local
file usually looks like this:As the options imply, this enables building docs for all my dependencies and building
graphql-engine
with-O0
. When I want to build with profiling enabled, I change mycabal.project.local
file to this:This automatically rebuilds everything with profiling, and
profiling-detail: none
means GHC will not automatically insert any cost centers in my dependencies, whileprofiling-detail: toplevel-functions
means it will add cost centers for all top-level functions ingraphql-engine
. It’s easy to adjust these settings per-package as necessary while doing performance debugging.The one major downside to using
cabal-install
is that it does not manage GHC versions automatically, so you have to install GHC yourself. Fortunately, there are two tools to do this easily and reliably:ghcup
andstack
.Yes, that’s right: I have been using
stack
to manage my installed versions of GHC even without using it to actually build anything. I just runstack --resolver=ghc-<some_ghc_version> exec -- bash
, and I get dropped into a shell withghc
in my path. If you want a lighter-weight solution,ghcup
is also available, but I’ve found this to work totally fine.Everything else should basically still work the same way it currently does. CI scripts and contributing guides will have to change, but the changes are very small.
When
I do not want to force a different workflow on anyone, so if nobody has any drastic objections to this plan of action, I will prepare a branch with the necessary changes to swap out
stack
forcabal-install
. Anyone who wants to verify their workflow is not impacted by the change should take that time to try building everything on the branch, and after some period of time, I’ll merge it in.If anyone does have any major objections or concerns, please voice them! There are probably ways we can make things work.
The text was updated successfully, but these errors were encountered: