4

I'm contributing to an open source project which defines its project setup in a cabal file that looks like this (omitting lots of properties that are not relevant for this problem):

library
  hs-source-dirs:    src
  build-depends:
                     base >= 4.9 && < 5,
                     some-other-deps
  exposed-modules:
                     Data.Foo.Bar,
                     Data.Foo.Baz
  other-modules:
                     Data.Foo.Bar.Internal

test-suite test
  hs-source-dirs:
                     tests
  build-depends:
                     foo-library
  other-modules:
                     Foo.Bar.Tests,
                     Foo.Baz.Tests

Now, in order to test the feature I'm adding, I want the tests to have access to the Data.Foo.Bar.Internal module, but since it's hidden in the library, I can't access it from the tests.

I looked at the cabal docs which suggested adding a third component - an internal library (unfortunately not linkable, but search for "internal libraries"). If I understand the documentation correctly, I should be able to do something like this:

library foo-internal
  hs-source-dirs:    src
  build-depends:
                     base,
                     some-other-deps
  exposed-modules:
                     Data.Foo.Bar.Internal

library
  hs-source-dirs:    src
  build-depends:
                     base >= 4.9 && < 5,
                     foo-internal,
                     some-other-deps
  exposed-modules:
                     Data.Foo.Bar,
                     Data.Foo.Baz

test-suite test
  hs-source-dirs:
                     tests
  build-depends:
                     foo-library,
                     foo-internal
  other-modules:
                     Foo.Bar.Tests,
                     Foo.Baz.Tests

but running stack build here gives me warnings that Data.Foo.Bar.Internal "should be added to exposed-modules or other-modules in ./foo.cabal", and then a build error in the test that seems to be caused by failed unification (it points to a function argument for a function defined in the library, says that its type must be Data.Foo.Bar.Internal.Qux defined in package foo, and that the type foo:Data.Foo.Bar.Internal.Qux don't match).

How can I expose internal modules to the test suite, without exposing them also to consumers of the library?

Tomas Aschan
  • 58,548
  • 56
  • 243
  • 402
  • Does it help if you move the `.hs` files of `foo-internal` to another `hs-source-dirs`, say, `src-internal`? – danidiaz Nov 06 '20 at 21:15
  • @danidiaz Possibly; I'm not sure I can do that, though, since it's not my project (and the main point here is to get a PR accepted) but I'll try it out locally to see if it helps. – Tomas Aschan Nov 06 '20 at 21:21
  • @danidiaz: Yes, moving the files to a different root seems to work. – Tomas Aschan Nov 06 '20 at 21:28
  • I've had the same problem, but I haven't found the exact place in the Cabal user guide that says that different libraries should have different source folders. https://cabal.readthedocs.io/en/3.4/cabal-package.html?highlight=internal%20libraries#pkg-field-hs-source-dirs – danidiaz Nov 06 '20 at 21:32

1 Answers1

2

As mentioned in the comments, different components (internal libraries, executables) should have modules under different roots.

Li-yao Xia
  • 31,896
  • 2
  • 33
  • 56