The Package System

Introduction

The package system (pkgutils) is made with simplicity in mind, where all packages are plain tar.gz files (i.e. without any kind of meta data).

Packages follow the naming convention <name>#<version>-<release>.pkg.tar.gz, where <name> is the name of the program, <version> is the version number of the program, and <release> is the version number of the package. The pkg.tar.gz extension is used (instead of just tar.gz) to indicate that this is not just any tar.gz file, but a tar.gz that is meant to be installed using pkgadd.
This helps distinguish packages from other tar.gz files.
Note that pkgmk nowadays supports additional compression schemes like bzip2 with the tar.bz2 extension or XZ ending with tar.xz.

pkgadd(8), pkgrm(8), pkginfo(8), and pkgmk(8) are the package management utilities. With these utilities you can install, uninstall, inspect, make packages, or query the package database.

When a package is installed using pkgadd a new record is added to the package database (stored in /var/lib/pkg/db). The basic package system does not have any kind of dependency checking, thus it will not warn you if you install a package that requires other packages to be installed. The included prt-get tool, however, does support dependencies.

The following sections will in short describe how to use the package utilities. Additional information about these utilities can be found on their respective man page.

Using the Package System

Installing a Package

Installing a package is done by using pkgadd. This utility requires at least one argument, the package you want to install. Example:

$ pkgadd bash#2.05-1.pkg.tar.gz

When installing a package the package manager will ensure that no previously installed files are overwritten. If conflicts are found, an error message will be printed and pkgadd will abort without installing the package. The error message will contain the names of the conflicting files. Example:

To force the installation and overwrite the conflicting files, you can use the option -f (or --force). Example:

$ pkgadd -f bash#2.05-1.pkg.tar.gz

The package system allows a file to be owned by exactly one package. When forcing an installation the ownership of the conflicting files will be transferred to the package that is currently being installed. Directories can however be owned by more then one package.

Warning

It is often not a good idea to force the installation unless you really know what you are doing. If a package conflicts with already installed files it could be a sign that the package is broken and installs unexpected files. Use this option with extreme care, preferably not at all.

As earlier, the package file itself does not contain any meta data. Instead, the package manager uses the package filename to determine the package name and version.
Thus, when installing a package file named bash#2.05-1.pkg.tar.gz, the package manager will interpret this as a package named bash at version 2.05-1.
If pkgadd is unable to interpret the filename (e.g. # is missing or the filename does not end with .pkg.tar.gz) an error message will be printed and pkgadd will abort without installing the package.

Upgrading a Package

Upgrading a package is done using pkgadd with the -u option. Example:

$ pkgadd -u bash#2.05-1.pkg.tar.gz

This will replace the previously installed bash package with the new one. If you have not previously installed bash, pkgadd will print an error message. The package system does not care about the version number of the package in that you can “upgrade” version 2.05-1 with version 2.04-1 (or even with version 2.05-1 itself). The installed package will be replaced with the specified package.

Upgrading a package is equivalent to executing pkgrm followed by pkgadd with one (big) exception. When upgrading a package (with pkgadd -u) you have the option to prevent some of the already installed files from getting replaced. This is typically useful when you want to preserve configuration and log files.

When executing pkgadd the file /etc/pkgadd.conf will be read. This file can contain rules describing how pkgadd should behave when doing upgrades.
A rule is built out of three fragments; event, pattern and action. The event describes in what kind of situation this rule applies. Currently only one type of event is supported, that is UPGRADE. The pattern is a filename pattern expressed as a regular expression and the action applicable to the UPGRADE event is YES or NO. More than one rule of the same event type is allowed, in which case the first rule will have the lowest priority and the last rule will have the highest priority. Example:

The above example will cause pkgadd to never upgrade anything in /etc/ or /var/log/ (subdirectories included), except files in /etc/X11/ (subdirectories included), unless it is the file /etc/X11/xorg.conf. The default rule is to upgrade everything, rules in this file are exceptions to that rule.

Note

A pattern should never contain an initial “/” since you are referring to the files in the package, not the files on the disk.

If pkgadd finds that a specific file should not be upgraded, it will install it under /var/lib/pkg/rejected/. Files in this directory are never added to the package database. The user is then free to examine, use and/or remove that file manually. Another option is to use rejmerge. For each rejected file found in /var/lib/pkg/rejected/, rejmerge will display the difference between the installed version and the rejected version. The user can then choose to keep the installed version, upgrade to the rejected version or perform a merge of the two. Example (using the above /etc/pkgadd.conf):

Removing a Package

Removing a package is done by using pkgrm. This utility requires one argument, the name of the package you want to remove. Example:

$ pkgrm bash

Warning

This will remove all files owned by the package, no questions asked. Think twice before doing it and make sure that you did not misspell the package name since that could remove something completely different (e.g. think about what could happen if you misspelled glib as glibc).

Querying the Package Database

Querying the package database is done using pkginfo. This utility has a few options to answer different queries.

Package management frontend: prt-get

To address the different requirements towards package management in CRUX, a number of users started discussion about an advanced package management frontend to pkgutils, with dependency handling and support for large install transactions. The result of this community effort is prt-get, a tool which provides a number of features on top of pkgutils while keeping pkgutils' original character and power. Its main features are

Creating Packages

Creating a package is done using pkgmk. This utility uses a file called Pkgfile, which contains information about the package (such as name, version, etc) and the commands that should be executed in order to compile the package in question.
To be more specific, the Pkgfile file is actually a bash(1) script, which defines a number of variables (name, version, release and source) and a function (build). Below is an example of what a Pkgfile file might look like. The example shows how to package the grep(1) utility.
Some comments are inserted for explanation.

# Specify the name of the package.
name=grep
# Specify the version of the package.
version=2.4.2
# Specify the package release.
release=1
# The source(s) used to build this package.
source=(ftp://ftp.ibiblio.org/pub/gnu/$name/$name-$version.tar.gz)
# The build() function below will be called by pkgmk when
# the listed source files have been unpacked.
build() {
# The first thing we do is to cd into the source directory.
cd $name-$version
# Run the configure script with desired arguments.
# In this case we want to put grep under /usr/bin and
# disable national language support.
./configure --prefix=/usr --disable-nls
# Compile.
make
# Install the files, BUT do not install it under /usr, instead
# we redirect all the files to $PKG/usr by setting the DESTDIR
# variable. The $PKG variable points to a temporary directory
# which will later be made into a tar.gz-file. Note that the
# DESTDIR variable is not used by all Makefiles, some use prefix
# and others use ROOT, etc. You have to inspect the Makefile in
# question to find out. Some Makefiles do not support redirection
# at all. In those cases you will have to create a patch for it.
make DESTDIR=$PKG install
# Remove unwanted files, in this case the info-pages.
rm -rf $PKG/usr/info
}

In reality you do not include all those comments, thus the real Pkgfile for grep(1) looks like this:

The build() function in the example above is just an example of how grep is built. The contents of the function can differ significantly if the program is build in some other way, e.g. does not use autoconf.

When the build() function has been executed, the $PKG directory will be made into a package named <name>#<version>-<release>.pkg.tar.gz.
Before the package creation is completed, pkgmk will check the content of the package against the .footprint file. If this file does not exist, it will be created and the test will be skipped.
The .footprint file will contain a list of all files that should be in the package if the build was successful or a list of all the files that were installed in $PKG (if the .footprint did not already exist). If there is a mismatch the test will fail and an error message will be printed. You should not write the .footprint file by hand. Instead, when a package has been upgraded and you need to update the contents of the .footprint file you simply do pkgmk -uf. This test ensures that a rebuild of the package turned out as expected.

If the package built without errors it's time to install it by using pkgadd and try it out. I highly recommend looking at the Pkgfile in another package(s), since looking at examples is a great way to learn.

Adjusting/Configuring the Package Build Process

Many settings pertaining to the package build process can be adjusted by editing the pkgmk(8) configuration file /etc/pkgmk.conf. Some of these configurable settings include:

CFLAGS, CXXFLAGS - these settings control optimization and architecture options for package compiles. It is best NOT to change these unless you absolutely know what you're doing!

PKGMK_SOURCE_MIRRORS - this setting defines locations from which pkgmk will attempt to fetch source archives

PKGMK_SOURCE_DIR - this setting defines where pkgmk will store (if downloading) and use source archives when building

PKGMK_PACKAGE_DIR - this setting defines where pkgmk will create package files once the build process is complete

PKGMK_WORK_DIR - this setting defines a work area pkgmk will use to build the package

Here are some examples:

PKGMK_SOURCE_MIRRORS=(http://fileserver.intranet/crux/sources/)

This setting instructs pkgmk to attempt to fetch all source archives from http://fileserver.intranet/crux/sources/ before falling back to the source URL specified in the Pkgfile. Multiple URLS can be separated by spaces.

PKGMK_SOURCE_DIR="/usr/ports/srcfiles"

This example instructs pkgmk to store and find source archives in /usr/ports/srcfiles. An example benefit of this setup would be the ability to store /usr/ports/srcfiles on an NFS server on your local network for use by multiple crux installations. PKGMK_PACKAGE_DIR can be set and used the same way.

PKGMK_WORK_DIR="/usr/ports/work/$name"

This example instructs pkgmk to use /usr/ports/work/$name as a work area for building the specified package. Building the grep package would result in the work area being /usr/ports/work/grep. An alternative would be to use a tmpfs as your work directory.

There are a few more settings which can be found in the pkgmk.conf man page.

Package Guidelines

General

The name of a package should always be lowercase (e.g. name=eterm and not name=Eterm). In case the package is added to the CRUX ports system the exact same name should be use for the name of the directory in the ports structure, i.e. /usr/ports/???/eterm.

Do not combine several separately distributed programs/libraries into one package. Make several packages instead.

Directories

In general packages should install files in these directories. Exceptions are of course allowed if there is a good reason. But try to follow the following directory structure as close as possible.

Directory

Description

/usr/bin/

User command/application binaries

/usr/sbin/

System binaries (e.g. daemons)

/usr/lib/

Libraries

/usr/include/

Header files

/usr/lib/<prog>/

Plug-ins, addons, etc

/usr/share/man/

Man pages

/usr/share/<prog>/

Data files

/usr/etc/<prog>/

Configuration files

/etc/

Configuration files for system software (daemons, etc)

/opt directory is reserved for manually compiled/installed applications. Packages should never place anything there.

/usr/libexec/ is not used in CRUX, thus packages should never install anything there. Use /usr/lib/<prog>/ instead.

Pkgfile

Do not add new variables to the Pkgfile. Only in very few cases does this actually improve the readability or the quality of the package. Further, the only variables that are guranteed to work with future versions of pkgmk are name, version, release, and source. Other names could be in conflict with internal variables in pkgmk.

Use the $name and $version variables to make the package easier to update/maintain. For example, source=(http://xyz.org/$name-$version.tar.gz) is better than source=(http://xyz.org/myprog-1.0.3.tar.gz) since the URL will automatically updated when you modify the $version variable.

Remember that source is an array, i.e. always do source=(...) and not source=...

Pkgfile header

Provide a header including the following fields:

Name

Meaning

Description

A short description of the package; keep it factual

Maintainer

Your full name and e-mail address, obfuscated if you want

Packager

The original packager's full name and e-mail address

URL

A webpage with more information on this software package

Depends on

A list of dependencies, separated either by spaces or commas

Depends on can be omitted if there are no dependencies; Packager can be omitted if the maintainer and packager are the same person.