Essential pkgsrc - The Missing Mini Handbook
pkgsrc is a cross operating system package manager. It supports -- among many others -- NetBSD, Minix, SmartOS, Linux, and macOS. I like it because of this portability. It also has the additional, and I would say the best, benefit of being installed in the home directory and run completely without needing root access. I also like that I don't have to depend on binary packages built by someone else, say Joyent, although there's absolutely nothing wrong with it. Finally, it provides a large number of different packages. I have never encountered a package that I needed but was not available. In short pkgsrc is a portable, featureful, and flexible package manager. What's not to like?
pkgsrc can sometimes be a little behind native package managers, such as MacPorts on macOS, but it catches up quickly. For my use case -- getting access to multiple versions of Python -- it works well enough if I closely follow its trunk branch.
There's generally good and detailed documentation available for pkgsrc but an introductory guide that pulled in some essential starter information was lacking. This guide fills that void by making it easy to get started with pkgsrc and learn about some of its core concepts. Thus, I dubbed it the mini handbook or the missing starter handbook.
I wrote this guide by running pkgsrc on macOS. The instructions should work on Linux as well but may need minor modifications.
I wrote a similar guide for Linux (pkgsrc on Linux - Quickstart Guide) a while ago. This guide is an updated and more general version of it.
Install
You need git
to clone the pkgsrc repo from GitHub. I prefer a shallow clone
to save disk space. Newer git
versions can pull and push shallow clones
just fine. I also prefer trunk branch since it has the latest goodies
available. If you prefer stable branches then substitue trunk below with a
quarterly release such as pkgsrc_2016Q4.
$ git clone https://github.com/NetBSD/pkgsrc.git -b trunk --single-branch ~/pkgsrc
I believe you may need build tools such as a compiler. Make sure you have these tools installed just in case.
Unlike the official install I prefer an unprivileged install because it gets installed in my home directory without affecting the rest of the system. As The pkgsrc guide puts it,
In unprivileged mode in contrast to privileged one all programs are installed under one particular user and cannot utilise privileged operations (packages don't create special users and all special file permissions like setuid are ignored).
If you're running on Linux (e.g. Ubuntu), run these steps first. Reason is an error message described on pkgsrc wiki (Shell's echo command is not BSD-compatible).
$ export SH=/bin/bash
Then run the bootstrap.
$ cd ~/pkgsrc/bootstrap $ ./bootstrap --unprivileged
Environment Variables
Insert these lines in ~/.profile
(macOS or Ubuntu) if they're not already
present.
Set the PATH variable. I prefer to give priority to pkgsrc-installed packages.
export PATH PATH="${HOME}/pkg/sbin:${HOME}/pkg/bin:${PATH}"
Set the MANPATH variable to access man pages that come with packages installed with pkgsrc.
export MANPATH MANPATH="${HOME}/pkg/man:${MANPATH}"
NetBSD wiki recommends these settings for Unicode to work.
export LC_CTYPE LC_CTYPE="en_US.UTF-8" export LC_COLLATE LC_COLLATE="C" export LC_TIME LC_TIME="C" export LC_NUMERIC LC_NUMERIC="C" export LC_MONETARY LC_MONETARY="C" export LC_MESSAGES LC_MESSAGES="en_US.UTF-8" export LC_ALL LC_ALL=""
Don't forget to source the file.
$ source ~/.profile # macOS or Ubuntu
mk.conf
The bootstrap process creates ~/pkg/etc/mk.conf. mk.conf is a configuration file used when building packages. Familiarize yourself with it as many problems you will face as a novice will be solved by editing it.
You are encouraged to keep the pkgsrc directory clean by moving all work directories and distfiles in separate paths, such as ~/pkg/usr/work and ~/pkg/usr/distfiles respectively.
Create these directories.
$ mkdir -p ~/pkg/usr/{work,distfiles}
Add these two lines to mk.conf before the last line which most likely will be .endif # end pkgsrc settings.
WRKOBJDIR=${HOME}/pkg/usr/work DISTDIR=${HOME}/pkg/usr/distfiles
Python
Since the pkgsrc install in this guide is custom and unprivileged you have to
build packages from source. If you prefer not to do that then you must follow
the official instructions to install pkgsrc. You can then use pkgin
to
manage packages.
Python 3.6 is the latest version available at the time of writing. It's available in trunk branch of pkgsrc.
Edit ~/pkg/etc/mk.conf and insert the following line before the last line which most likely will be .endif # end pkgsrc settings. This configures the build system to always use Python 3.6 as the default version.
PYTHON_VERSION_DEFAULT=36
To install packages from source navigate to the directory of the package and
run bmake
. Here we install Python 3.6.
$ cd ~/pkgsrc/lang/python36 $ bmake install
Confirm python3.6
was installed and your PATH is setup correctly.
$ which python3.6 ~/pkg/bin/python3.6
Installing pip
is a little trickier. There's ~/pkgsrc/devel/py-pip but
it installs only one version of pip
depending on how you set up
PYTHON_VERSION_DEFAULT above. In our case it will install pip3.6
. We'll
discuss how to install multiple pip
versions in the next section titled
pkg_alternatives.
$ cd ~/pkgsrc/devel/py-pip $ bmake install
Confirm pip3.6
was installed and your PATH is setup correctly.
$ which pip3.6 ~/pkg/bin/pip3.6
Last thing we need is virtualenv-3.6
.
$ cd ~/pkgsrc/devel/py-virtualenv $ bmake install
Confirm virtualenv-3.6
was installed and your PATH is setup correctly.
$ which virtualenv-3.6 ~/pkg/bin/virtualenv-3.6
We have all the things we need to create a virtualenv and start working with Python 3.6.
$ python3.6 Python 3.6.0 (default, Mar 25 2017, 00:15:25) [GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.42.1)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> import pip >>> import virtualenv >>> ^D
pkg_alternatives
Just like Debian pkgsrc has an alternatives system.
But what is an "alternatives system"? It is a framework that allows multiple packages providing similar functionality to be installed concurrently (by removing files with common names), and then using a utility to set up those common names with symlinks to the preferred program.
In other words, we can install, for example, pip2.7
and pip3.6
and set
pip
to point (symlink) to pip3.6
. We can still use pip2.7
and
pip3.6
independent of each other but when we use pip
it will run
pip3.6
.
So let's try this and install Python 2.7 and pip2.7
.
$ cd ~/pkgsrc/lang/python27 $ bmake install
When I initially tried to install an alternative pip
I ran into the same
problem as reported here:
Can't have "pip" packages for python 2.7 and 3.4 installed at the same time.
The solution provided by Mark Davies was
Don't install pip and use the ALTERNATES mechanism to create it. See the similar changes I did to the py-docutils and py-sphinx packages last night.
I asked the pkgsrc-users mailing list on how to do it. Thanks to Adam for pointing me to the solution.
The trick is to temporarily override PYTHON_VERSION_DEFAULT when you run
bmake install
like so.
$ cd ~/pkgsrc/devel/py-pip $ bmake install PYTHON_VERSION_DEFAULT=27 => Bootstrap dependency digest>=20010302: found digest-20160304 ===> Checking for vulnerabilities in py27-pip-9.0.1 ===> Installing binary package of py27-pip-9.0.1 pkg_add: no pkg found for '~/pkg/usr/work/devel/py-pip/work/work/.packages/py27-pip-9.0.1.tgz', sorry. pkg_add: 1 package addition failed *** Error code 1 Stop. bmake[2]: stopped in ~/pkgsrc/devel/py-pip *** Error code 1 Stop. bmake[1]: stopped in ~/pkgsrc/devel/py-pip *** Error code 1 Stop. bmake: stopped in ~/pkgsrc/devel/py-pip
Uh oh. Where did this error come from?
From what I understand we need to clean the work directories from when we built
pip3.6
. Otherwise the install process looks for a pre-built package to
install. In this case it finds py36-pip but not py27-pip and fails.
Fair warning, this is just my very flawed understanding. I can fix the symptom
but can't explain the cause.
The fix is to clean all work directories and run bmake install
again.
$ rm -rf ~/pkg/usr/work/* $ cd ~/pkgsrc/devel/py-pip $ bmake install PYTHON_VERSION_DEFAULT=27
A better way to avoid the problem in the first place is to always run
bmake install clean clean-depends
which will build and install the package
and clean the work directories for the package and its dependencies. The down
side is that the next time you build the package it will re-do all the work.
Similar to how we override PYTHON_VERSION_DEFAULT with py27 we can use other Python versions like py35 and py34.
Let's look at what versions of pip
we have installed.
$ ls -l ~/pkg/bin/pip?.? ~/pkg/bin/pip2.7* ~/pkg/bin/pip3.6*
To run say pip3.6
when you run ~/pkg/bin/pip
you need to use the
pkg_alternatives
tool. Install it first.
$ cd ~/pkgsrc/pkgtools/pkg_alternatives $ bmake install
Let's list all packages that provide alternatives.
$ pkg_alternatives list pkg_alternatives: looking for alternatives in `~/pkg/pkgdb' py27-pip-9.0.1 py36-pip-9.0.1 py36-virtualenv-15.1.0 python27-2.7.13nb1 python36-3.6.0nb2
Let's ensure pip
points to pip3.6
.
$ pkg_alternatives manual py36-pip pkg_alternatives: modifying configuration from `~/pkg/etc/pkg_alternatives~/pkg/bin/pip'
Similarly we can use python3.6
as default Python for other binaries.
$ pkg_alternatives manual python36 pkg_alternatives: modifying configuration from `~/pkg/etc/pkg_alternatives~/pkg/bin/2to3' pkg_alternatives: modifying configuration from `~/pkg/etc/pkg_alternatives~/pkg/bin/pydoc3' pkg_alternatives: modifying configuration from `~/pkg/etc/pkg_alternatives~/pkg/bin/python' pkg_alternatives: modifying configuration from `~/pkg/etc/pkg_alternatives~/pkg/bin/python3'
As always read the man page of pkg_alternatives
for more information.
$ man -S 8 pkg_alternatives
Vulnerabilities
pkgsrc makes it simple to check for vulnerabilities in installed packages.
Fetch the latest list of known vulnerabilities.
$ pkg_admin -K ~/pkg/pkgdb fetch-pkg-vulnerabilities
Audit all installed packages for known vulnerabilities.
$ pkg_admin -v audit No vulnerabilities found
Sometimes packages will not build because there are known vulnerabilities in them or their dependencies. For example,
$ cd ~/pkgsrc/devel/ncurses $ bmake install => Bootstrap dependency digest>=20010302: found digest-20160304 ===> Checking for vulnerabilities in ncurses-6.1 Package ncurses-6.1 has a null-pointer-dereference vulnerability, see https://nvd.nist.gov/vuln/detail/CVE-2018-10754 ERROR: Define ALLOW_VULNERABLE_PACKAGES in mk.conf or IGNORE_URL in pkg_install.conf(5) if this package is absolutely essential. *** Error code 1 Stop. bmake: stopped in /home/codeghar/pkgsrc/devel/ncurses
Sometimes you have no choice but to install the package despite the vulnerability. I prefer to ignore specific CVEs in ~/pkg/etc/pkg_install.conf file rather than ignore all CVEs in ~/pkg/etc/mk.conf file. In this example, the file would look like,
$ more ~/pkg/etc/pkg_install.conf IGNORE_URL=https://nvd.nist.gov/vuln/detail/CVE-2018-10754
Now you can build the package successfully.
Upgrade Single Package
Follow these steps when you want to update a single package, say Python 3.6.
$ cd ~/pkgsrc/lang/python36 $ bmake update clean clean-depends
Upgrade All Packages
pkg_rolling-replace
removes packages before installing them in a safer way.
Read more on
how to upgrade packages.
$ cd ~/pkgsrc/pkgtools/pkg_rolling-replace $ bmake install clean clean-depends
Add PKGSRCDIR variable to ~/pkg/etc/mk.conf just before the last line which most likely will be .endif # end pkgsrc settings.
PKGSRCDIR=${HOME}/pkgsrc
Pull any upstream changes.
$ cd ~/pkgsrc $ git pull
List available updates. Note the use of -n
flag.
$ pkg_rolling-replace -u -n -v
Apply available updates. Note the absence of -n
flag.
$ pkg_rolling-replace -u -v
List Installed Packages
Get a list of all installed packages with pkg_info
. The -u
flag does
not list any dependencies. I prefer this format over using -a
flag.
$ pkg_info -u
Read pkg_info
man page for more information.
$ man -S 1 pkg_info
Delete Package
Use pkg_delete
to remove a package.
$ pkg_delete py27-pip
You can uninstall a package and all its dependencies that are not needed by
any other package with the -R
flag.
$ pkg_delete -R py27-pip