rules_haskell icon indicating copy to clipboard operation
rules_haskell copied to clipboard

cabal: Add support for empty libraries

Open iteratee opened this issue 1 year ago • 3 comments

Previously, an empty library that only exported modules from another library failed to build. Bazel would error out when the .a file wasn't created. These libraries are becoming more common in the Haskell ecosystem, so we need to support them.

There is a straightforward way to handle these libraries. We make the package database the default output. We avoid creating an output file for any libraries if the library is marked as empty. This is all straightforward bazel dependency/output management.

Adjust the cabal wrapper to fix up the package database even in the case that there is no library. Otherwise we end up with an empty package database, which isn't what we're looking for.

iteratee avatar Feb 13 '25 17:02 iteratee

Thank you, that looks good so far.

It would be nice if this would be usable from stack_snapshot and also from the module extension.

With this in place we should be able to remove the workaround for empty packages here, right?

Could you also add a test case (I was using the nats package to test this after removing that from the empty packages blacklist) please?

This is related to https://github.com/tweag/rules_haskell/pull/1458, https://github.com/tweag/rules_haskell/issues/1302 and (partially) also https://github.com/tweag/rules_haskell/issues/2274 and https://github.com/tweag/rules_haskell/issues/2200.

For the last one, we would need to pass through the haddock attribute to the haskell_cabal_libary call from stack_snapshot / the module extension for specific packages, similarly to the empty_library attribute.

This gets a bit tedious over time, especially since Bazel's attr is a bit limited and does not support arbitrary values in dicts, just strings. We would have to add empty_libraries and a disable_haddock list attributes for these... and maybe more in the future.

I was thinking about adding a rule which mimics the cabal attributes and returns a Provider that can be used to pass additional information to the haskell_cabal_library rule. Something along the lines of:

cabal_args = provider(...)

haskell_cabal_args = rule(
  ...
  attrs = {
    "is_empty": "if package is empty",
    ...
  },
  provides = [cabal_args],
)

haskell_cabal_library = rule(
   ...
   attrs = {
     ...

    "args": attr.label(providers = [cabal_args]),
  },
)

_stack_snapshot = repository_rule(
   ....
   attrs = {
     ...

    "package_args": attr.string_keyed_label_dict(providers = [cabal_args]),
  },
)

One could then pass any additional args to the stack_snapshot macro:

stack_snapshot(
   ...
   package_args = {
     "nats": { "is_empty": True },
     "zlib-clib": { "haddock": False },
  }
   ...
)

... which would generate haskell_cabal_args targets for each entry of the given dict and pass the appropriate label to the args attribute of the internal _stack_snapshot repository rule.

WDYT?

avdv avatar Mar 04 '25 17:03 avdv

Sorry for the delay. I'll pick this back up again this week.

iteratee avatar Mar 23 '25 06:03 iteratee

I have a first pass at using a provider to handle this. I only handled empty libraries with a provider.

Let me know if this along the lines that you were thinking.

With the provider, it was easier to see how to handle this for a stack snapshot. I added to the component parsing with empty_lib and empty_lib:<sublib>. This makes it easy to add a package with an empty (sub)library without needing to completely vendor the package.

This allowed me to remove the longstanding empty package blacklist and just add those packages to the list of known defaults for stack snapshots.

iteratee avatar May 15 '25 22:05 iteratee

Was hoping to get some progress on this.

This fixes one of the biggest weaknesses that rules_haskell has dealing with cabal packages. There's even a hardcoded list in order to make it work at all.

iteratee avatar Oct 15 '25 00:10 iteratee

Also, some of the test cases currently fail with

Sorry, this was missed from one of the commits. It's now added. One thing down, several more to go.

iteratee avatar Oct 20 '25 19:10 iteratee

Also, it would be nice to wire this up in the module, so it can be used from a MODULE.bazel file.

I added a commit that I think does the right thing. I'll try and test it with modules. I hadn't yet converted over because ghc from nixpkgs wasn't working with modules for a while. I'll double check and try and switch over.

iteratee avatar Oct 20 '25 19:10 iteratee

Can I get access to the buildbuddy secret so that my CI runs will be cached?

iteratee avatar Oct 20 '25 22:10 iteratee

Can I get access to the buildbuddy secret so that my CI runs will be cached?

A workflow run from a forked PR does not have access to the secrets... I would have to make the secret public I guess. 🤔

No, I could move my PR to a branch in this repository. I would just need to be a contributor. But it isn't urgent.

iteratee avatar Oct 22 '25 18:10 iteratee

No, I could move my PR to a branch in this repository. I would just need to be a contributor. But it isn't urgent.

Oh sure, we can do that. I sent you an invite.

avdv avatar Oct 27 '25 13:10 avdv

I'm going to close this. I'll add a comment to the new PR with a link to this one.

I can't change the base branch for this PR, but as discussed above, CI will be happier if I create a PR from a branch in this repo instead of a fork.

iteratee avatar Oct 30 '25 22:10 iteratee