The layout was a deliberate design decision on Homebrew’s side to keep the balance between usability, maintainer sanity, and not breaking things.
A comment in Homebrew’s source code says:
Installing JARs to "lib" can cause conflicts between packages.
For Java software, it is typically better for the formula to
install to "libexec" and then symlink or wrap binaries into "bin".
Why conflicts?
To explain what the specific conflict is and what causes it, let’s have a look at (a gross simplification of) what Homebrew does when you run brew install foo
:
Homebrew downloads and unpacks everything foo
needs according to the formula.
Homebrew follows the installation instruction according to the formula. For example, it copies files around, applies patches, and so on. In the end, you get a keg, the folder containing the fresh installation.
For your convenience, Homebrew looks at the bin
and lib
directories of your keg, then symlinks everything it finds into /usr/local/bin
and /usr/local/lib
so you don’t have to.
Note how in #3, the formula does not have a say. This helps keep complexity out of the formula definitions. A formula maintainer can never forget to create a symlink because she or he doesn’t have to; Homebrew does it automatically for everything that is in bin
or lib
.
The layout policy was (AFAIK) first codified in issue #678 of Homebrew’s legacy GitHub repository, which says:
This is expected behavior; libexec is assumed to be private-use stuff for that formula.
Basically, Homebrew considers the contents of bin
and lib
public artifacts, i. e. things that are supposed to be called from outside the formula.
Everything else, including the whole content of libexec
, is considered an implementation detail, i. e. things for private use by the formula itself.
But what is the big deal with that? Could you, as an author of a formula like maven
, not simply ignore those principles and instead live with a few unnecessary symlinks in /usr/local/lib
, and give the user the same layout as the upstream project has?
Turns out that yes, you could, but it’s a horrible idea, and Homebrew maintainers would rightfully refuse such a formula until fixed.
Publishing private stuff to /usr/local/lib
is a typical cause of JAR hell. (Imagine a Java-based Homebrew formula foo
which uses mylib.jar
v1, and another Homebrew formula bar
which uses mylib.jar
v2, with the version number not being part of the filename mylib.jar
. Now go ahead and try to figure out why your business-critical application – which happens to use /usr/local/lib/mylib.jar
because it’s in the path – only works on that 50% of your machines where foo
has been installed after bar
but not vice versa. This is an example of the conflict mentioned at the beginning.)
tl;dr Whatever you put into lib
, Homebrew will symlink to /usr/local/lib
for you. Putting a JAR you depend on into /usr/local/lib
is a bad thing to do and leads to JAR hell.