Skip to content

Allow build-depends on executable as though it were an internal library. #5415

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

Closed
kindaro opened this issue Jul 7, 2018 · 11 comments
Closed

Comments

@kindaro
Copy link

kindaro commented Jul 7, 2018

The community were inventive in their hacks to permit the testing of modules that belong to executable. The three most common solutions:

  • Test with shell scripts, similarly to the way it is done in JavaScript's npm culture. Cabal would (hopefully) never support that. Example: hdevtools.
  • Specify the same hs-source-dirs for executable and test-suite stanzas. Everything builds twice but you get to import the same modules as in executable. Kind of example: Test suite fails to see executable modules #3242.
  • Move everything from executable to a (possibly internal) library and depend on this library from the test suite. Leave executable trivial. Example: pandoc.

To my understanding, all three solutions listed above are not nice. A better solution that I can think of is to consider an executable stanza as a named, internal library for the purpose of referring to from a test suite with build-depends. The potentially problematic downside is that there may then be multiple, overlapping Main.main names to be imported, but this is easily solved with a combination of Cabal's main-is, ghc's -main-is and some naming discipline.

If consensus emerges, I can take it on myself to offer a patch as a summer project.

@hvr
Copy link
Member

hvr commented Jul 7, 2018

What's so bad about the 3rd option,

  • Move everything from executable to a (possibly internal) library and depend on this library from the test suite. Leave executable trivial.

I have done this quite often myself in projects, and tbh, I don't see what's so inconvenient about it. It's easy to understand as it uses obvious & explicit constructs/verbs.

What you suggest sounds to me a bit too magical and non-obvious (as e.g. it'd blur the distinction between exposed-modules and other-modules; and there'd be an exception where build-depends refers to executable components rather than library components), and would add implementation complexity (which directly translates into maintenance cost).

So I'm afraid I have to say I don't think this is a good idea.

@kindaro
Copy link
Author

kindaro commented Jul 8, 2018

@hvr I do not understand the details you bring up:

  • How would it blur the distinction between exposed-modules and other-modules?
  • Why would there be an exception where build-depends refers to executable components rather than library components?
  • What would actually break if we allow importing modules from executables into test suites?

Can you help me see the problem?

@ghost
Copy link

ghost commented Sep 2, 2018

Without also having the same understanding as @hvr, I think it would be nice and user friendly to be able to reference executable projects as libraries. Shifting UI related logic out of the UI project and adding an entire other library sounds tedious and annoying. The manuever of including the executable path in hs-source-dirs adds redundancy to the cabal file because it needs the dependencies twice.

To establish some precedent, .NET allows you to reference executables in your solution and use them as if they were libraries. This is personally one of the things I expected to just work.

@phadej
Copy link
Collaborator

phadej commented Sep 2, 2018

How the OP proposal is different than simply using Cabal's internal libraries?

@23Skidoo
Copy link
Member

23Skidoo commented Sep 2, 2018

I agree with @phadej and @hvr. Just use internal libraries, this exactly what they are for.

@kindaro
Copy link
Author

kindaro commented Sep 2, 2018

@23Skidoo @hvr @phadej So what is the point of having an executable stanza if the code therein best be as trivial as possible? This is clearly not in any way different than having an internal library with a pointer to the main function, while encouraging practices that are not the best.

It looks to me as though you are defending the status quo by means of your authority and a priori consensus, rather than logical argument. The reasons given by Herbert are exposed too concisely to be easy to grasp (I asked for a clarification, but none was given), but the parts I can understand relate to implementation details and inertia, which are both unrelated to design itself. The other two of you did not put forward any arguments at all. So, it appears to me as though you are not admitting that there is a problem, and this is why I re-stated it in the first lines of this post. I appreciate if you would take a moment to actually address it.


So, let me state the problem again, in the clearest way I could.

  1. A best practice concerning testing projects is to move as much code as possible to a library, leaving the executable trivial.

    In this we have consensus, as expressed in the response of Herbert.

    The detailed motivation for this best practice is in the following syllogism:

    • It is considered good practice to have tests approaching 100% coverage.
    • Tests cannot cover modules that belong to an executable, as opposed to a library.
    • Therefore, it is desirable to move as much code as possible to a library, leaving the executable trivial.
  2. Driven to the extreme, this approach gets us to an executable so trivial that it is isomorphic to a pointer to the actual code located in a library: main = Library.main. Some projects we may observe in the wild approach this ideal very closely. (pandoc, haddock, doctest — by any measure, prominent projects.)

  3. The current arrangement of Cabal stanzas does not even hint to this best practice.

    Quite the opposite, the intuitive way when making an executable is to use the executable stanza!

  4. Therefore:

    • The executable stanza does not serve any real purpose other than providing a needlessly customizable pointer.
    • The executable stanza is misleading and promotes practices that are contrary to the best.

I also offered a solution, that I think is obvious and nice: make executable a special case of an internal library with a pointer to main. Maybe this solution is problematic in some way. That does not cancel the problem.


The pool of maintainers is not some universal constant. If we design a great tool, more people will want to lend a hand in development, and the maintenance cost would be divided. If we choose to fence off and be obscure, less people will come to help, and it will be harder to actually find time to improve things. From this, it follows that the argument to maintenance cost, given by Herbert, is based on a pessimistic assumption that may not be correct. If the majority of maintainers choose to be pessimistic, just say so, and I will not bother you further.

@gbaz
Copy link
Collaborator

gbaz commented Sep 17, 2021

I'm closing this as won't fix, sorry. Its a design decision about how stanzas are organized that's pretty core to cabal. This complains of a pattern that "Driven to the extreme, this approach gets us to an executable so trivial that it is isomorphic to a pointer to the actual code located in a library". But there's nothing wrong with this pattern, as illustrated by the many projects that use it just fine.

@kindaro
Copy link
Author

kindaro commented Sep 18, 2021

Of course there is something wrong with this pattern. As I said above, it is unnecessary and misleading.

You have not even engaged with my argument. Your sole argument («… illustrated by many projects …») does not address the point of the issue — it merely re-states what I already said in the opening.

You can make decisions because you hold power over this domain, but you do not get to declare falsity true by fiat. Your core design decision is a mistake — or so it appears from the argument. You should either admit that or show how the argument is wrong.

Not that I expect you to. You had what, 3 years to think about this issue? My explanation is that you simply do not care. Maintenance cost and all that.

@jneira
Copy link
Member

jneira commented Sep 19, 2021

I think @kindaro raised a valid argument, which reveals some flaws in the design of how cabal organizes the code.
If you end suggesting a component should be almost empty, that suggestion reveals that the component, in that case, should not ever exist. It becomes a wrapper to make the valid use case fit the overall design.
And private libraries has been problematic in the past (here due to bugs and in stack) so you had to create entire packages to extract the common code in a reliable way. Not good.

But i think the solution proposed, make the executables behave like a library in some cases or contexts will add more complexity and will have unwanted consequences in cabal components like the the solver. So not sure if it woths it, given the amount of work to be done in several fronts.

Maybe it would need a complete redesign of components, removing the differences between them and make them all equal and uniform: libraries which can be executed or run as part of test suites or benchmarks depending on optional information in the package metadata and/or outside of it. But it would suppose a radical redesign wich would need lot of work.

@eyeinsky
Copy link

Related: for proprietary packages Gabriella recommends the The “God” library stanza for interactive development, as cabal doesn't reload the library when you have either an executable or test suit loaded into the repl.

I wonder if a solution would be to expose modules in an executable to test suites (and perhaps to benchmarks as well)? It does sound reasonable to want to test code in executables. (And having internal libraries starting with library executable (though they work) also points at the issue.)

@Mikolaj
Copy link
Member

Mikolaj commented Sep 8, 2022

@eyeinsky: thank you for your input. May come handy if somebody decides to invest in overhauling these features (and their continuous maintenance). I'm afraid, for now, due to cabal workforce constraints, we have to do with internal libraries and minimal executable stanzas as suggested above. I hope they work for you fine?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants