Some previous posts for reference:

So I’ve previously talked about why I think pkgsrc is cool as well as how to use pkgsrc with Clang instead of GCC. Now for this post, I’m going to go over how I am actually using pkgsrc. This may have been a little bit backwards, but it’s just the way I ended up writing stuff.

I had a few specific goals in mind that were ultimately solved by pkgsrc:

I was honestly only looking for a way to build software from source and have the code mirrored on my local network. I was partially doing this manually myself until I found pkgsrc. Then pkgsrc (and I suppose BSD in general) convinced me that having a separate installation location for system packages versus user packages would be nice. And on top of that, installing stuff into my local user’s home directory would prevent me from needing root privileges to install stuff (at the expense of only installing the software for my one user).

First of all, I’m using pkgsrc on Debian. Yes, I can install Debian packages on my local network (I run my own mirror of that too). I think there’s a way to install Debian packages from source, but I’ve never done it. Also Debian packages install stuff into locations that kinda mix base system stuff and user stuff which makes it annoying to know what I can safely remove without breaking my system. So I decided to try pkgsrc on top of Debian to install packages separate from the base system, and it works very nicely.

I read through both the pkgsrc guide and this pkgsrc mini handbook blogpost to figure out what commands and options I needed. I re-ran the bootstrap process several times and finally landed on what I wanted as my setup.

There’s two main sections to this: how I bootstrap pkgsrc and how I use pkgsrc.

First we need to go over how to setup and bootstrap pkgsrc in the first place.

Feel free to skip this section and move on to Actual bootstrapping.

I previously wrote about using pkgsrc with Clang, and in my experiments with doing so, I may have gone a little overboard on the bootstrapping side of this. I wanted to bootstrap pkgsrc with Clang and then use Clang built with pkgsrc as the default compiler. I even went a step further and wanted to be able to do this even if I only have GCC available as the system compiler.

The details on doing this are probably not relevant to most people, so just as a brief summary, I have several scripts that go through this process:

This is totally crazy, and I actually don’t recommend it myself.

So ignoring the above, lets assume I have Clang installed somewhere (it could be from my separate pkgsrc bootstrap or just installed as a normal Debian package).

First we need to get the pkgsrc code. Again, one my goals to be able

to use this on my local network, so I am actually using my own Git

mirror of pkgsrc (syncing from their Github mirror). So we

first need to clone the repo. Then I personally prefer to work on a

quarterly release branch rather than trunk.

git clone https://github.com/NetBSD/pkgsrc.git

cd pkgsrc

git checkout pkgsrc-2022Q1

Remember where you cloned the code because we’ll need to reference it

later in our mk.conf.

The standard bootstrap process as described in the docs doesn’t do everything I would like. So let’s look at some of the options we can change.

cd bootstrap

./bootstrap --help

Before bootstrapping, as mentioned in my Clang post, you may need to help pkgsrc know where your compiler is.

export CLANGBASE=/usr/lib/llvm-11

export PKGSRC_COMPILER=clang

export CC=$CLANGBASE/bin/clang

export CXX=$CLANGBASE/bin/clang++

Then I define some other variables and do some initial setup:

export PREFIXDIR="${HOME}/pkg"

export MAKE_JOBS=4

export WORKDIR="${PREFIXDIR}/usr/work"

export DISTDIR="${PREFIXDIR}/usr/distfiles"

mkdir -p $DISTDIR

Then this is the actual bootstrap command that I use:

./bootstrap \

--unprivileged \

--compiler clang \

--prefix $PREFIXDIR \

--make-jobs $MAKE_JOBS \

--workdir $WORKDIR \

--mk-fragment "${d}/mk.conf"

Let’s go through these options a bit just for completeness. I want to

install pkgsrc for my local user only with --unprivileged. I want

the bootstrap process to use Clang as the compiler with --compiler clang. I want to install everything into my home directory under

~/pkg with the --prefix flag. I can make better use of my

processor with --make-jobs 4 (feel free to adjust or use something

like $(nproc)). I would like the temporary work dir to be

centralized outside of the pkgsrc directory, so I set --workdir ~/pkg/usr/work. Otherwise you may end up with a bunch of work

directories inside your pkgsrc git mirror directory. Finally I have

--mk-fragment set which will initialize my mk.conf with some

values.

Before I explain more about my mk.conf, there is one last thing my

bootstrap script does. I need to create a symlink to the pkgsrc Git

mirror. This isn’t strictly necessary, but it helped keep my mk.conf

file uniform even if I cloned the pkgsrc code into a different

directory.

My script assumes you’re in the pkgsrc boostrap dir, but otherwise the

PKGSRC_DIR is simply the pkgsrc root directory.

PKGSRC_DIR=$(dirname $(pwd))

PKGSRC_LINK="${PREFIXDIR}/usr/pkgsrc"

rm -f $PKGSRC_LINK

ln -s "${PKGSRC_DIR}" $PKGSRC_LINK

mk.confNow that pkgsrc is bootstrapped, we need to configure it.

In the above bootstrap command, I had a mk.conf fragment

specified. After bootstrapping, the default pkgsrc mk.conf file is

placed in ~/pkg/etc/mk.conf and then this fragment file that I

specified is appended to the end of it.

Here is my mk.conf fragment:

# Separate work and distfiles outside of pkgsrc

WRKOBJDIR= ${LOCALBASE}/usr/work

DISTDIR= ${LOCALBASE}/usr/distfiles

# Set the source dir for pkgsrc itself

PKGSRCDIR= ${LOCALBASE}/usr/pkgsrc

# Use the processor more

MAKE_JOBS= 4

# Use my distfile mirror by default

MASTER_SITE_OVERRIDE= https://mirrors.wisellama.rocks/netbsd/pkgsrc/distfiles/

# Keep partial downloads

PKG_RESUME_TRANSFERS= YES

# Extend acceptable licenses

ACCEPTABLE_LICENSES+= gnu-agpl-v3

ACCEPTABLE_LICENSES+= unrar-license

# Set Python version

PYTHON_VERSION_DEFAULT= 310

# Package-specific options

PKG_OPTIONS.cmus= pulseaudio

PKG_OPTIONS.strawberry= pulseaudio

PKG_OPTIONS.vlc= -lirc

PKG_OPTIONS.retroarch= opengl pulseaudio

PKG_OPTIONS.dolphin-emu= llvm pulseaudio

You’ll notice that the WRKOBJDIR and the DISTDIR line up with the

values we set up during bootstrapping. This way my actual pkgsrc

installation uses them just like the bootstrap script did. Similarly

the PKGSCRDIR is set to the symlink for the pkgsrc Git clone, and

the MAKE_JOBS I have set to 4 again.

Then we start getting into some extra config options.

I wanted my system to be able to download distfiles using my own

mirror. To do that, I specified a MASTER_SITE_OVERRIDE pointing to

my distfile mirror. Without this, pkgsrc will simply download the

distfiles from the project website (e.g. Github releases, sourceforge,

etc.).

The PKG_RESUME_TRANSFERS option has actually screwed me up a few

times, so I would only recommend setting it to YES when using a bad

internet connection.

The ACCEPTABLE_LICENSES stuff is used to allow me to install some

software that isn’t in the default allowed license list. This variable

is probably intended to be used by companies to be able to limit their

licenses for legal reasons.

PYTHON_VERSION_DEFAULT is kinda self-explanatory. You can have

multiple versions of Python installed, so pkgsrc needs to know which

one to use by default when other packages say they depend on Python,

particularly for the build process.

Finally, the PKG_OPTIONS.* settings are specific setting overrides

for individual packages. For example, I’m telling cmus to build with

pulseaudio support.

So, pkgsrc is bootstrapped and the mk.conf is configured. We

can actually use the pkgsrc installation.

I have my .zshrc file configured to add pkgsrc stuff to my PATH

and MANPATH. Alternatively you could always run the binaries

directly like ~/pkg/bin/bmake, but I like the convenience of just

running bmake.

# Add pkgsrc

export PATH="${HOME}/pkg/bin:${HOME}/pkg/sbin:${HOME}/pkg/gnu/bin:${PATH}"

export MANPATH="${HOME}/pkg/man:/usr/share/man:${MANPATH}"

export XDG_DATA_DIRS="${HOME}/pkg/share":"${XDG_DATA_DIRS}"

Finally we can use pkgsrc to install something. Let’s install pfetch

as a simple example. Go back into you pkgsrc Git clone directory and

then run the following:

cd sysutils/pfetch

bmake install

Then on zsh, I have to run rehash to get it to recognize that a

new binary has been added to my path. Then I can run pfetch and see

my system overview.

_____

/ __ \ os Debian GNU/Linux 11 (bullseye)

| / | host 20NL0007US ThinkPad X395

| \___- kernel 5.10.0-13-amd64

-_ uptime 8d 20h 57m

--_ pkgs 4514

memory 3826M / 5915M

Now that we’ve confirmed a simple package is working, we can move on to installing some more complicated things.

One of the main reasons I love pkgsrc is that it allows me to build Firefox and Thunderbird from source very easily. So let’s try installing Firefox (I’m going to do the ESR version, but the normal version is there too).

cd www/firefox-esr

bmake install

Now…Firefox is a beast of a project. To build it from source, pkgsrc

needs to build an install a lot of other projects. When using Clang,

I’ve run into several packages that require a little extra coercion to

build correctly, so you may run into errors. When that happens, pkgsrc

should tell you which package it was trying to build. I then go to

that package’s directory, fix whatever was wrong, finish installing it

with bmake, and then move back to building Firefox again.

Notably for Firefox, I think audio/pulseaudio requires GCC to

build. Also lang/rust requires libgcc. I have some more details

over on my pkgsrc with Clang article

I’ve also run into a problem with Thunderbird where I have to actually change my default Python install back to 3.9 or 3.8 to be able to build it. Then I can switch back.

Building Firefox can take several hours, potentially even all

night. But once it completes, you can launch firefox (in this case

firefox91) and it will launch…and look like crap.

Some programs like Firefox depend on fonts being installed. Even though I’m on Debian with fonts already installed somewhere, pkgsrc likes to act as its own separate system and won’t find my current system fonts. This is both good and bad. Ultimately, I viewed it as a cool good thing that pkgsrc was so separate from the base system that it didn’t even find my system fonts.

Anyway, this was simple to fix after some web searches. I had to install a font and tell Firefox how to use it. Let’s install Noto fonts as an example:

cd fonts/noto-ttf

bmake install

Even then, I couldn’t get some programs to recognize my fonts unless I added a symlink to my home directory.

ln -s ~/pkg/share/fonts/X11 ~/fonts

And still, Firefox had some issues depending on how I launched it. I

finally found a setting in Firefox’s about:config that let me use

these fonts correctly, although I suppose this technically degrades

the security of Firefox slightly:

security.sandbox.content.level = 2

Then I finally had a working version of Firefox that I had built from source with pkgsrc. I don’t think I can properly describe just how happy this made me.

pkg_alternatives)Just like how Debian has an alternatives system, so does pkgsrc. This is a simple script that lets you specify which version of a package should be used as the default in case you have multiple versions installed. First we need to install it because it is itself a package:

cd pkgtools/pkg_alternatives

bmake install

Now we can set the default version for something like Python for example:

pkg_alternatives manual python310

Note that this only works for packages that specifically have an alternative listed. You can see those options with

pkg_alternatives list

pkg_rolling-replace)So you’ve been using pkgsrc for a while and maybe a new quarterly

release has come out. How do you upgrade all of your pkgsrc packages?

Well, one of the pkgtools is called pkg_rolling-replace. This

package will determine which packages are out-of-date, build a

dependency tree to know which packages to upgrade first, and then go

through each package running bmake replace for them.

Remember way back in our mk.conf where we defined the PKGSRCDIR

variable? This is where it’s finally used. pkg_rolling-replace will

check the pkgsrc code to determine if your installed packages are out

of date. So first of all, make sure you update the pkgsrc code

repo. This could just be doing a git pull for trunk, but for a

quarterly release you need to switch to the new branch.

cd pkgsrc

git fetch -p

git checkout pkgsrc-2022Q2

Then pkg_rolling-replace needs to be installed:

cd pkgtools/pkg_rolling-replace

bmake install

I tend to run pkg_rolling-replace with some extra flags. I forget

where I found these as recommended, but they work well for me.

pkg_rolling-replace -rusv

Similar to the warning I gave for Firefox, sometimes packages will

have errors while upgrading too. When that happens, I go into that

package directory, fix whatever was wrong, manually run bmake replace, and then continue running the pkg_rolling-replace replace.

Very rarely, I may need to completely uninstall and reinstall a

package to upgrade it. To remove a package, I used pkg_delete <package_name>. Then I can run bmake install to install it again.

Some package tools are installed by default including the pkg_delete

I mentioned above to uninstall packages. There’s also pkg_info which

I very useful. It’s very similar to apt show where it will give you

the details of an installed package.

And that’s it. That’s how I’m using pkgsrc. I bootstrap it crazily to make sure it was built with Clang. I set it up to use its own version of Clang as the default. I tend to use the quarterly release branches and update each quarter.

I’ve been trying to run as much software as I can using pkgsrc. Some stuff like my desktop environment doesn’t make sense because pkgsrc really only as that stuff set up to support NetBSD. However, everything else that’s like userland-level software I’ve tried to build and install with pkgsrc.

I’m writing this blog post in emacs running in tmux and zsh, all built with pkgsrc. I’m testing it with hugo and firefox, built with pkgsrc. I’m listening to music in cmus and checking my email with thunderbird, built with pkgsrc. You get the idea. I’m using pkgsrc for everything that I can, a bit inspired by this blog post from Rubenerd.

Pkgsrc scratched an itch I’ve had for a while. I’ve always been a free and open source software advocate (since I was like 12), but pkgsrc seemed like a culmination of that philosophy into a fantastic project. I remember an old friend teasing me about open source software, asking if I’ve actually inspected the source code and built it myself. And now with pkgsrc, I can finally answer yes, I have.