Yeah, my point is homebrew has its own separate toolchain that exists on the same filesystem as the OS, while a container created for building binaries could share the same toolchain (e.g. a Fedora 41
distrobox for Bluefin
) and have a lot fewer potential incompatibilities, while also being isolated inside a container - basically a dev container with the intention of exporting the apps you build.
HomeBrew can pretty easily “contaminate” any base OS toolchain because it depends on alternatives to build dependencies used by the base OS during the build process. This contamination happens when $PATH
hierarchy inadvertently begins pointing to brew
’s dependencies rather than the OS-installed dependencies by accident. It’s not hard for that to happen, given that most people don’t examine their $PATH
all that frequently (speaking from experience).
It’s a bigger issue when building packages on a non-immutable system - I don’t think the immutable workflow often includes using base OS dependencies to build packages, especially when compared to a distro like Arch Linux (for the AUR), etc. But it also happens a lot to people who use dev toolchain managers like mise
, pyenv
, pdm
, rust
, etc. etc.
If you have two copies of the same executable binary, the easiest way to see which one will be called first by default is by using the which
command. If it points to the version installed by HomeBrew, congrats - HomeBrew’s instance is earlier in your $PATH
environment, and will be executed when attempting to compile or run software, regardless if whether you meant to use the brew
toolchain, or not.
It’s even more complicated when you get into issues like which instance of, say, ldd
is being called first, where $ENV
is specifying you have tools like CC
, GCC
, CLANG
, LD_LIBRARY_PATH
, having both pkgconf
and pkg-conf
on your system, or even potentially multiples of one, the other, or both, or basically inummerable other potential clusterfucks. It can turn into a mess pretty easily, and it’s not particularly easy to unwind if/when it happens.
These are some bash functions I actually started using to keep my $PATH
straight after the scenario I’m describing happened to me while using HomeBrew on another distro. But really the $PATH
issue dawned on me a long time ago trying to keep versions of node
and related packages installed via nvm
and npm
straight, because, as I mentioned, it’s a real issue using version managers or any other dev toolchain manager. These functions work both in bash
and zsh
:
# this will list your $PATH environment in a way that's
# a lot easier to read than `echo $PATH`
function path_list () {
COLONS=$(echo "$(echo $PATH | tr -cd ':' | wc -c) + 1" | bc -l)
printf "there are $COLONS paths in \$PATH variable \n"
for (( SECTION = 1; SECTION <= $COLONS; SECTION++ ))
do
PATH_EXISTS=$(echo "$PATH" | cut -d ':' -f"$SECTION")
if [ "$PATH_EXISTS" ]; then
echo "$PATH_EXISTS"; else
echo "number too high: COLON variable -eq $COLONS"
fi
done
}
# same as above using `rainbowpath`, only with pretty colors
# requires installing `rainbowpath`: https://github.com/Soft/rainbowpath
# not in homebrew, unfortunately
function pretty_path () {
for i in $(echo "$PATH" | tr : '\n'); do
rainbowpath "$i"; done
# Three easier/safer functions to add or remove paths from $PATH env
function path_remove () { export PATH=$(echo -n "${PATH}" | awk -v RS=: -v ORS=: '$0 != "'"$1"'"' | sed 's|:$||'); }
function path_append () { path_remove "$1"; export PATH="${PATH}:$1"; }
function path_prepend () { path_remove "$1"; export PATH="$1:${PATH}"; }
# curtail your $PATH env if it gets out of control
# Removes _any_ extraneous paths and puts /bin before $HOME/bin
# you may want to modify this one for your own installation
function conservative_path () {
unset PATH
export PATH="/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:$HOME/bin:$HOME/.local/bin:$HOME/.config/zsh/bin"
}