Meta(
    title: "Dependencies",
    summary: Some("Questioning whether embracing Rust was the right path in light of how developers are so quick to add extra dependencies"),
    published: Some(Time(
        year: 2023,
        month: 7,
        day: 17,
        hour: 2,
        minute: 57,
        second: 43,
    )),
    tags: [
        "programming",
        "rants",
    ],
)
---
I switched from using packages on my FreeBSD system to using ports a few weeks back in order to get some features which were missing in libraries that I have been using in a few development projects. Other than the long compile times for certain projects (most notably Firefox) that has been mostly painless. There is one pain point that is annoying me to no end though.

I use git to keep the ports tree up to date (so happy that they switched from svn to git) and portupgrade to rebuild anything which has updated. By default, portupgrade refuses to build anything with a security vulnerability. This is a good thing, but it shows you just how intertwined package dependencies are on a modern Unix system. When portupgrade finishes, it gives you a list of which packages were updated, which failed and which were skipped, and I have a growing list of skipped packages which I decided I needed to investigate this morning as the newest Lagrange was affected. Very annoying after waiting for weeks for the port maintainer to review the patches I sent.

So here's the deal. Lagrange depends on sdl2, which depends on mesa-libs, which has a build dependency on py39-mako, which depends on py39-beaker. Py-beaker has a three year old security vulnerability for a remote code execution cve. I looked it up and skimmed the conversation in the bug report on the project's tracker, and the last entries are from the middle of last year, where the maintainer had proposed a fix which doesn't sound like it will actually fix the problem, which the reporter of the bug told him in so many words. Further, considering there's two years of discussion on this bug report, it's an awful short read. I don't get the impression that this is a well maintained library that anyone should be relying on as "infrastructure", and yet there it sits, propping up some obscure corner of the stack.

That leads me to a recent Rust experience. I wound up settling on i3 for my desktop in FreeBSD, as my laptop's video card doesn't seem to be new enough to get Wayland working on FreeBSD. It's nice though, because i3 is so similar to Sway that the setup looks and works the same as what I'd gotten used to in Void. Not surprising when you consider that Sway was written to be a straight up clone functionally of i3, just using Wayland instead of X. Anyway, I had gone back to using the standard top bar that comes with sway/i3 rather than the fancier gtk one I switched to for a few weeks, and had been experimenting with i3status-rust to show things in the bar on Void (I'm still dual booting and take my other laptop when I'm on the go, which I haven't switched to FreeBSD - yet). It's got some nice features compared with i3status, and I wanted to try it out on FreeBSD if possible. There's no port for it, so I pulled down the source and tried to compile it - unsuccessfully.

So the problem wound up being that the i3status-rust crate depends on the swayipc-async crate, which depends on the async-pidfd crate. The async-pidfd crate is Linux only, as it's completely based around the Linux pidfd interface. This is an interface which allows you to get a file descriptor for a process, which will be persistent for the life of that process and is a much more reliable way to track a process than just a process id. FreeBSD obviously doesn't have this interface. They do, however, have a similar interface, pdfork, which of course predates the Linux pidfd interface by a number of years. In a brief skim of the source it's obvious that they're not using any features of pidfd that you couldn't implement using pdfork. For that matter, neither should really be required to do the task this library accomplishes. In fact, it's only being used for a single plugin which allows running an external command for customizing the output further. Personally I would have stuck that behind a feature flag so that you could compile the program without it, which in theory would have made it portable to anything POSIX-ish.

Now this is exactly the kind of rot that got me pissed off about Linux in the first place. When you use a Linux only interface your code is by definition non-portable. But then there's the issue of dependencies to begin with. The i3status-rust crate pulls in 297 crates. I have no words for this.

This sort of thing has me rethinking my adoption of Rust in the first place. It's more of an ecosystem issue than a language design issue, but it's still extremely problematic. In my own projects I try to keep the dependency tree as small as possible, but even so often end up with up to 100 dependencies for a smallish project that only has a half dozen or listings in it's Cargo.toml. Often I'll go back in after I get things working and see if I can re-implement the bits that I used external crates for so that I can get the dependency graph down, because sometimes all I really need is a function or two which will save me 20-30 entries in the graph.

Now, I still love Rust, but this isn't the only thing that bugs me. Mostly it's small papercuts and things I can work around, but what's funny is that mentioning them is Rust circles tends to bring on a torrent of fiercely defensive spam rather than any kind of constructive dialog. Couple that with some of the Rust leadership's very public faux pax over the past couple of years and it starts to leave a bad taste.