Homebrew / homebrew-core

🍻 Default formulae for the missing package manager for macOS (or Linux)
https://brew.sh
BSD 2-Clause "Simplified" License
13.41k stars 12.18k forks source link

Symbolic link from `man` to `gnuman`, etc. does not work #176037

Closed injust closed 2 days ago

injust commented 1 week ago

brew gist-logs <formula> link OR brew config AND brew doctor output

HOMEBREW_VERSION: 4.3.7
ORIGIN: https://github.com/Homebrew/brew
HEAD: 43eaeca50fe3b6c755f3fc9bc6f43669e0db1039
Last commit: 7 days ago
Core tap: N/A
Core cask tap: N/A
HOMEBREW_PREFIX: /usr/local
HOMEBREW_CASK_OPTS: []
HOMEBREW_EDITOR: nano
HOMEBREW_MAKE_JOBS: 8
Homebrew Ruby: 3.3.3 => /usr/local/Homebrew/Library/Homebrew/vendor/portable-ruby/3.3.3/bin/ruby
CPU: octa-core 64-bit kabylake
Clang: 15.0.0 build 1500
Git: 2.45.2 => /usr/local/bin/git
Curl: 8.6.0 => /usr/bin/curl
macOS: 14.5-x86_64
CLT: 15.3.0.0.1.1708646388
Xcode: N/A

Your system is ready to brew.

Verification

What were you trying to do (and why)?

I want to install coreutils, follow the caveats, and:

  1. Run GNU ls with ls
  2. Read the GNU ls man page with man ls

What happened (include all command output)?

  1. ls runs GNU ls
  2. man -w ls prints /usr/share/man/man1/ls.1 and man ls displays the macOS ls man page instead of the GNU ls man page

What did you expect to happen?

man ls should display the GNU ls man page.

In #35874, this caveat was removed:

Additionally, you can access their man pages with normal names if you add the "gnuman" directory to your MANPATH from your bashrc as well: MANPATH="#{opt_libexec}/gnuman:$MANPATH"

But man ls still displays the macOS ls man page, i.e. the changes in #35874 just don't work on my system. I'm not sure how the default manual path is constructed, but macOS man pages are being preferred over coreutils man pages, even if I prepend /usr/local/opt/coreutils/libexec/gnubin to PATH.

I had to prepend /usr/local/opt/coreutils/libexec/gnuman to MANPATH to get man ls working.

coreutils is just an example here. This also goes for other Homebrew formulae, e.g. findutils, gnu-sed, grep, and uutils-coreutils.

Step-by-step reproduction instructions (by running brew commands)

# This should work according to #35874, but it doesn't
brew install coreutils
fish_add_path /usr/local/opt/coreutils/libexec/gnubin

# Prints /usr/share/man/man1/ls.1
man -w ls

# Prepending to MANPATH fixes it
set -gx MANPATH /usr/local/opt/coreutils/libexec/gnuman:

# Prints /usr/local/opt/coreutils/libexec/gnuman/man1/ls.1
man -w ls
injust commented 1 week ago

cc @moonfruit

gromgit commented 1 week ago

I'm not sure how #35874 managed to work. Perhaps macOS changed their MANPATH construction logic after that PR was committed, because the current manpath man page reads:

The manpath utility constructs the manual path from two sources:

  1. From each component of the user's PATH for the first of:
    • pathname/man
    • pathname/MAN
    • If pathname ends with /bin: pathname/../share/man and pathname/../man

Since /gnubin != /bin, the corresponding /man would never be added, so #35874 is quite useless now.

I've confirmed this by reading the man and manpath sources (they're the same shell script).

To fix, one of the following needs to be done:

  1. Rename /gnubin to /bin across the board.
  2. Revert #35874.
moonfruit commented 1 week ago

Yes, #35874 is not working properly now. I also create a PR #135272 to fix this. But this latter PR was not accepted and has been closed. So I had to add some scripts on my own computer to automate what was in this PR. @gromgit @injust

gromgit commented 1 week ago

@Homebrew/maintainers, what was the rationale for using libexec/gnu{bin,man} instead of the conventional libexec/{bin,man} for, say, Homebrew-wrapped binaries?

cho-m commented 1 week ago

@Homebrew/maintainers, what was the rationale for using libexec/gnu{bin,man} instead of the conventional libexec/{bin,man} for, say, Homebrew-wrapped binaries?

It looks like gnubin was copied from MacPorts. Ref: https://github.com/Homebrew/legacy-homebrew/commit/c53e35ed6560fe2074c0a5e9ca2ddbec11a7d155

 - libexec/gnubin directory contains symlinks to all the coreutils
   commands without its program-prefix "g". (Inspired by MacPorts'
   coreutils)

gnuman was probably created based on gnubin name (https://github.com/Homebrew/legacy-homebrew/commit/372c0d76849aaa18282e1841021588c74f2bf3a3). MacPorts instead used libexec/gnubin/man/ (https://github.com/macports/macports-ports/blob/master/sysutils/coreutils/Portfile#L94).

gromgit commented 1 week ago

MacPorts instead used libexec/gnubin/man/ (https://github.com/macports/macports-ports/blob/master/sysutils/coreutils/Portfile#L94).

Yeah, that would trigger the current macOS man path logic, so that's a third option, that's probably the least disruptive of all:

~3. Move the man symlink created in #35874, from libexec/man to libexec/gnubin/MAN (to avoid confusion with the man command).~

Turns out the standard Linux man implementation does the same thing as macOS, only one of the default directories it adds is binpath/../man, which is covered by #35874, so...

  1. Add another symlink from libexec/gnuman to libexec/gnubin/MAN (to avoid confusion with the man command).

However, there's another issue: if $MANPATH is not empty (and brew shellenv ensures it's not), man only adds $PATH-derived man directories under the following circumstances:-

  1. $MANPATH == :* --> prepend derived directories
  2. $MANPATH == *: --> append derived directories
  3. $MANPATH == *::* --> insert derived directories between the two colons

Now, brew shellenv adds a trailing colon to MANPATH (case 2), so system man pages will always shadow Homebrew pages of the same name, which I think defeats the whole purpose of deriving man dirs dynamically, particularly since people would logically prepend gnubin directories to their PATH.

Ironically, having brew shellenv not set MANPATH at all (or simply prepending a colon if it's already been set) seems to be the Right Thing going forward.

Is there a flaw in my logic?

moonfruit commented 1 week ago

gnuman was probably created based on gnubin name (Homebrew/legacy-homebrew@372c0d7). MacPorts instead used libexec/gnubin/man/ (macports/macports-ports@master/sysutils/coreutils/Portfile#L94).

Yes, this is indeed a viable option. This solution gets around the problem of confusing directory names: bin vs gnubin. Just kind of breaks the regular Linux directory layout.

Ironically, having brew shellenv not set MANPATH at all (or simply prepending a colon if it's already been set) seems to be the Right Thing going forward.

Couldn't agree more.

MikeMcQuaid commented 1 week ago

Ironically, having brew shellenv not set MANPATH at all (or simply prepending a colon if it's already been set) seems to be the Right Thing going forward.

Yes, agreed. Could you open a PR @gromgit? No worries if not.

MacPorts instead used libexec/gnubin/man/ (macports/macports-ports@master/sysutils/coreutils/Portfile#L94).

Yeah, that would trigger the current macOS man path logic, so that's a third option, that's probably the least disruptive of all

This also seems like a good idea. I'd be in favour of doing both.

@homebrew/core any thoughts on the above?

gromgit commented 6 days ago

Yes, agreed. Could you open a PR @gromgit? No worries if not.

I'll take care of that in a few hours.