Package managers, toolchain managers, and _distrobox exports_: what are YOU using?!

Curious where people are on this:

I personally feel a little burned by linuxbrew, having used it on Arch Linux in the past, and $PATH eventually confusing toolchains while building packages from AUR. I imagine that’s not nearly as significant of a problem on an immutable distro, so I do concede that, but regardless of context, it still kinda gives me the willies.

It’d be easy to package a Fedora container with an alias for dnf to install packages in the container, and export a link to the host, wouldn’t it? Is there a case to be made these packages would have the most seamless compatibility with the host?

There’s also nixpacks, which I imagine is probably less user-friendly (haven’t tried it yet), but does have the advantage of being extremely reproducible.

How is pixi on the arm64 builds working out? Does its ecosystem encompass much besides python packages? It’s basically like pipx for conda, amirite?

Is anybody else using mise …?

I personally prefer using containers and Nixpkgs.

You can see some of my containers here: GitHub - karypid/box-apps: Containerized apps I use with atomic Linux distros

My Nixpgs are dispersed within projects, but my home folder is managed with Nix’s home manager here: GitHub - karypid/dotfiles: My Nix-based home configuration

In effect, I’ve replaced linuxbrew with nixpkgs, but nix is a tough language so I wouldn’t recommend it for most people…

brew works great for me, no conflicts with the host libraries, etc.
And graphical apps not available in brew, like Tana, I install in a Fedora distrobox , then distrobox-export --app /path/to/tana.
If it’s a binary you need, should be just as easy to distrobox-export --bin /path/to/binary.

Maybe my needs are more basic than yours, but I find the “batteries included” brew to be much simpler than any of the alternatives.

Or maybe you meant automating the process of installing in a container and exporting, rather than using brew?

1 Like

Yeah, I generally prefer nixpkgs, using this guide. It’s not the simplest thing, but once you understand it, it’s actually much easier to keep track of.

dnf will be the command to use for layering after bootc is implemented. apx is pretty cool though in that it automates the distrobox export of binary and app’s .desktop file, but it can still be done manually like what @JohnAtl said.

I pretty much never use brew – I always go with distrobox or nixpkgs. Used to use Conty as well, but don’t need it as much these days.

1 Like

Thanks for the mention of apx. I remember it from my trial of VanillaOS, and I didn’t realize it is distro-agnostic.
I might give it a try if brew doesn’t work out sometime.

I use brew for a few CLI tools that I don’t care too much about.

For most toolchains I’m trying to get in the habit of using Dev Containers which I’m enjoying learning. For Rust projects I trust cargo enough that I have it manually installed in my home directory.

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"
}

That sounds cool, does nixpkgs provide any isolation for its toolchain?

Kinda? I know there are two ways of using nixpkgs: imperative and declarative.

Imperative works like a normal package manager, but what is cool is that you can temporarily install it and enter a temporary environment for it. I think I had one or two alias where it basically just installs the package temporarily, enter the environment, execute the actual command, exit, then remove the package.

For declarative, you still have the basic nixpkg where the executable is really just a wrapper file that wraps the correct versions of packages in the nixstore directories before running the actual executable package. But at the same time, they also add it to a ~/.nix-something directory (forgot the name) that IS added to PATH. Strictly speaking, you can remove it from PATH, and you just need to manually reference the directory or add it to your alias list, but that’s not much difference from using brew or distrobox-export.

It IS internally isolated. If you install package-a and it references package-x-22.10 and package-b references package-x-24.10, each packages are isolated and run with the correct dependencies. But the directory on PATH can be a source of conflict. Both due to different version of packages from the rest of the system, and due to conflicts when they choose what stuff ultimately made it to the directory (which the home-manager do detect and tell you if there’s a build failure, but it is an annoyance).

I think you can work around it by using… I forgot the name, it’s either devbox, devpod, or devenv that isolates things properly unlike home-manager which is, well, meant to integrate on each user’s home folder.

1 Like

Yeah, sounds like what tools like mise, virtualenv, etc. use to isolate build environments from one another, which I think are also akin to grandaddy direnv. I have a hard time keeping them straight all the time, especially nowadays when I put all my build deps almost exclusively in userspace but also use them for global context (I’ll figure out a decent workflow one of these days).

I think the biggest thing is just having a way to isolate them from one another to start with. Being able to do it properly is entirely another story, but at least it’s something people considered when designing the tooling. I don’t see any of that with homebrew (or pkgsrc, which is what it reminds me of most, if anyone remembers that - it was NetBSD’s package manager that was ported to OmniOS by Joyent because the Solaris FOSS package selection was so booty. I think I’m dating myself…)

Nix replaces the dynamic linker with nix-ld which takes care of resolving within the confines of nix-compiled packages.

To override nix-compiled packages set NIX_LD_LIBRARY_PATH instead, and then nix-ld will use it to set the regular LD_LIBRARY_PATH to locations in the nix store.

I wish nix was not as complicated as a language, because from this perspective it is an amazing way to create siloed build/execution environments. I love using it alongside Bluefin.

1 Like

Nix sounds awesome. I’ve been using mise, myself, which has a smaller ecosystem for a package manager, but has a huge ecosystem for a dev env manager. It might make sense for some people. Given that nix has packages, though, it may be a better overall fit. That, and mise is not declarative (AFAICT).

Am definitely going to check out Nixpkgs