This is a Haskell stack package dependency configuration question.
I created a Haskell library git:LibA
I created another Haskell library git:LibB depending on git:LibA
I created a Haskell application AppC depending on git:LibB
To compile LibB, I need to specify git:LibA in extra-deps section of LibB's stack.yaml with a commit checksum, which is reasonable.
To compile AppC, it seems that I need to specify both the following packages in extra-deps section of AppC's stack.yaml
git:LibB with a commit checksum
git:LibA with a commit checksum
Is there any way to only specify git:LibB in AppC or configure LibB to hide git:LibA information to applications?
Motivation of the question: my current AppC's stack.yaml is error-prone: if I update LibA and the commit checksum in LibB but forget to update the checksum in AppC, then I will not get the new LibA in AppC. In my opinion, the newer checksum of LibB should already contain the information of a newer LibA, and the developer of AppC should not need to update LibA's checksum in AppC but only LibB's checksum.
No, stack doesn't "see" the stack.yml of any dependencies, only their respective .cabal descriptors.
If you're building multiple internal packages you can just put them all under the same source tree and list them under the packages list in your stack.yaml:
packages:
- LibA
- LibB
- AppC
Note that that doesn't mean you all have to put them into the same VCS if you don't want to - you could use git submodules; Or you could even list git locations/hashes pretty much the same way you do with extra-deps directly.
That's of course not an option if your packages are supposed to be entirely independent, but at that point you'll probably want to look into a more structured solution like making custom snapshots anyway.
No, this is not possible. By design, stack uses only the stack.yaml of the project you are building. It does not read or use any stack.yaml file which may exist in any of your dependencies. One advantage of this design is that there is a single place to specify package versions. Otherwise it's not clear how to handle different stack.yaml requesting different versions of the same package.
There are a couple of options if you want more convenience when developing these packages together, and don't care as much about keeping them independent. You can specify relative paths in your stack.yaml, and always build with whatever versions you have locally checked out. Or you can bring all three into a single VCS repo, and use the VCS to manage which changes to A should be linked to which in C.
Related
I have two Haskell libraries lib-a and lib-b, both hosted on private git repos. lib-b depends on lib-a, both build with no problem.
Now i want to import lib-b into another project and thus add it to the stack configuration with the git directive, like this:
- git: git#github.com:dataO1/lib-b.git
commit: deadbeef102958393127912734
Stack still seems to need a specific version for lib-a:
In the dependencies for application-0.1.0.0:
lib-a needed, but the stack configuration has no specified version (no package with that name found,
perhaps there is a typo in a package's build-depends or an omission from the stack.yaml packages
list?)
needed due to application-0.1.0.0 -> lib-b-0.1.0.0
The question now is, can stack somehow figure out specific versions for nested git dependencies without explicitely specifying them? If the project grows i dont want to manually adjust this every time i update lib-a.
Sidenote: I'm using nixOS and the nix directive for all three stack projects.
Stack follows the snapshot-based model of package management, meaning that it has some "globally" specified set of packages (of fixed version) that you can use. In Stack's case this set of packages is called Stackage. The core idea is to have this clearly specified set of packages that you're working with.
So the short answer is no it cannot figure it out by itself, you have to add them by hand.
But! you need to specify only packages that are not in the snapshot. e.g. package lib-a is likely to depend on mostly packages that are commonly used in Haskell (e.g. base, aeson, ...) and those will already be in Stackage. so even if the project grows you will be adding just "a few" git refs.
So this doesn't generally tend to be a problem.
Problem: I'm working on a Haskell project that uses stack (+ nix). We have a dependency that takes 10+ minutes to compile. Every time we clean our .stack-work, we have to wait for this huge package to compile, and it's really hurting our project's efficiency. The package name is godot-haskell, and here is how the package is depended upon in our stack.yaml:
extra-deps:
- godot-haskell-0.1.0.0#sha256:9d92ff27c7b6c6d2155286f04ba2c432f96460f448fd976654ef26a84f0e35a6,26290
Question: Is there a way for us to somehow cache this package (in stack, or even in nix) so that it locally never has to get compiled (or has to get compiled at most once, even if the .stack-work directory is deleted)?
For the currently released Stack, the best way to make this happen is to put the extra-dep into a custom snapshot file instead of the extra-deps in the stack.yaml file. (The upcoming Stack release has a feature referred to as "implicit snapshots" which sidesteps this.) You can see an example of this in the Stack repo itself:
https://github.com/commercialhaskell/stack/blob/master/stack.yaml#L1
https://github.com/commercialhaskell/stack/blob/master/snapshot.yaml
Normally when adding a new dependency to a .cabal file I specify the version of the new library I'm depending on. However stack works with a curated set of libraries, and I'm wondering whether it makes sense to specify the package versions in the .cabal file. My guess would be that specifying the lts version in the stack.yaml is enough.
I'm wondering whether it makes sense to specify the package versions in the .cabal file.
That depends completely upon you. If you specify a package version
which is not in that particular stackage resolver, then Stack will
throw an error saying you to adjust the versioning.
My guess would be that specifying the lts version in the stack.yaml is enough.
For private packages, it doesn't matter and I prefer not putting any version bounds there. But if it's something I ultimately plan to publish on Hackage, I usually use a CI system like Travis and get the bounds right for it with some testing. In fact, I think the Stack guide recommends something like that.
I have recently started using stack for Haskell, when specifying external dependencies for your project. Sometimes you place it in the .cabal file while other times you place it in the .yaml file.
Am I right in thinking that when you put it in the cabal file it only looks in the stackage repository for your packages. However when you place it in your .yaml file it also searches in the Hackage server, if it cannot find it in any of the snapshots?
All of the dependencies for your project go into the .cabal file. You are correct, though, that sometimes you also list packages in the stack.yaml file, which can be understandably confusing. Why is that?
Well, the .cabal file always expresses your dependencies upon packages, but the stack.yaml file effectively configures where those packages come from. Usually, when using stack, packages come from Stackage based on the resolver you specify in the stack.yaml file. However, Stackage does not include all the packages in Hackage, and it is not intended to—when you need packages that live outside of Stackage, you have to specify them in the stack.yaml file.
Why is this? Well, the resolver automatically couples two important pieces of information together: package names and package versions. Stackage resolvers provide a (weak) guarantee that all of the packages within a single resolver will work together, so when a package comes from a resolver, there is no need to manually pick which version you want. Instead, Stackage will decide for you.
When pulling packages from Hackage, you do not have this luxury, so you need to specify packages and their versions using extra-deps. For example, you might have something like this:
extra-deps:
- crypto-pubkey-openssh-0.2.7
- data-bword-0.1
- data-dword-0.3
This entry determines specifically which versions of which packages should be pulled from Hackage rather than Stackage.
When building an application, this might seem a little redundant—you can specify version constraints in the .cabal file, too, so why duplicate them in the stack.yaml file? However, when building a library, the distinction is a little more significant: the .cabal file expresses the actual version constraints of your library (if any), but the stack.yaml file specifies precisely which versions to actually install when developing locally.
In this sense, the stack.yaml file serves a purpose similar to the Gemfile.lock or npm-shrinkwrap.json files of other package managers, though the responsibilities are not nearly as clear-cut with stack (in part due to historical reasons around how Haskell’s package system works and some of the problems it’s had in the past).
I'm quite new to stack and wondering whether to git commit or .gitignore that file.
What are the implications of either of these choices?
I'd say you should commit stack.yaml, as that makes it much easier to build your package in a reproducible way. That is particularly relevant if your repository is public, and if you use the more exotic kinds of extra-deps in stack.yaml (pointers to Git repositories, secondary cabal packages within your source tree, etc.).
A complementary observation is that we should still provide reasonable version bounds for dependencies in the .cabal file even if we are using stack, as doing otherwise would make life harder for people who don't use stack or have a set of packages different than the specified by stack.yaml.
Yep. stack.yaml has a whole bunch of (not always necessary) fields such as the extra dependencies that matter for consistent builds. Check it in.