722 lines
26 KiB
Raw Blame History

!The Package System
The package system ([[ | pkgutils]]) is made with
simplicity in mind, where all packages are plain %fn%tar.gz files%% (i.e.
without any kind of meta data).
Packages follow the naming convention
%fn%''<name>#<version>-<release>''.pkg.tar.gz%%, where %fn%<name>%% is the name
of the program, %fn%<version>%% is the version number of the program, and
%fn%<release>%% is the version number of the package. \\ The %fn%pkg.tar.gz%%
extension is used (instead of just %fn%tar.gz%%) to indicate that this is not
just any %fn%tar.gz%% file, but a %fn%tar.gz%% that is meant to be installed
using '''pkgadd'''. This helps distinguish packages from other %fn%tar.gz%%
files. Note that pkgmk nowadays supports additional compression schemes like
bzip2 with the %fn%tar.bz2%% extension or XZ ending with %fn%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 %fn%/var/lib/pkg/db%%). The basic package system
does not have any kind of dependency checking, thus it will not warn you if you
try to build a package that requires libraries or headers from other packages.
Your mistake will only be revealed when the pkgmk build function exits with
errors. The included '''prt-get''' tool, however, can be told to resolve
dependencies, if called with '''prt-get depinst''' rather than simply '''prt-get
The following sections will in describe in short how to use the package
utilities. Additional information about these utilities can be found on their
respective man pages.
!! 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#5.0.18-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:
$ pkgadd bash#5.0.18-1.pkg.tar.gz
pkgadd error: listed file(s) already installed (use -f to ignore and overwrite)
To force the installation and overwrite the conflicting files, you can use the
option '''-f''' (or '''--force'''). Example:
$ pkgadd -f bash#5.0.18-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.
->''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
%fn%bash#5.0.18-1.pkg.tar.gz%%, the package manager will interpret this as a
package named %fn%bash%% at version %fn%5.0.18-1%%. If '''pkgadd''' is unable to
interpret the filename (e.g. # is missing or the filename does not end with
%fn%.pkg.tar.gz%%) an error message will be printed and pkgadd will abort
without installing the package.
!!! [[#UpgradingaPackage]] Upgrading a Package
Upgrading a package is done using '''pkgadd''' with the '''-u option'''. Example:
$ pkgadd -u bash#5.0.18-1.pkg.tar.gz
This will replace the previously installed %fn%bash%% package with the new one.
If you have not previously installed %fn%bash%%, '''pkgadd''' will print an
error message. The package system does not care about the version number of the
package in that you can &#8220;upgrade&#8221; 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 not simply a wrapper for '''pkgrm''' followed by
'''pkgadd''', because you usually want to preserve your customizations of the
config and log files that are owned by the already-installed package. Therefore,
@@pkgadd -u@@ conducts some upgrade-specific checks of the filesystem and the
package database to construct what is called a '''keep list''' (in addition to
the '''non-install list''' which is initialized for every '''pkgadd'''
transaction). The construction of both lists is governed by the file
%fn%/etc/pkgadd.conf%% can contain rules describing how '''pkgadd''' should
behave when installing or upgrading any package. A rule is built out of three
fragments; ''event'', ''pattern'' and ''action''. The event names the kind of
operation (INSTALL or UPGRADE) to which this rule will be applied. The
''pattern'' is a filename pattern expressed as a regular expression and the
action applicable to the INSTALL or 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.
# /etc/pkgadd.conf: pkgadd(8) configuration
UPGRADE ^etc/.*$ NO
UPGRADE ^var/log/.*$ NO
UPGRADE ^etc/X11/.*$ YES
UPGRADE ^etc/X11/xorg.conf$ NO
# End of file
The above example will cause '''pkgadd''' to never upgrade anything in
%fn%/etc/%% or %fn%/var/log/%% (subdirectories included), except files in
%fn%/etc/X11/%% (subdirectories included), unless it is the file
%fn%/etc/X11/xorg.conf%%. The default rule is to upgrade everything, rules in
this file are exceptions to that rule.
->''A pattern should never contain an initial &#8220;/&#8221; 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 %fn%/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 %fn%/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 fn%/etc/pkgadd.conf%%):
$ pkgadd -u bash#5.0.18-1.pkg.tar.gz
pkgadd: rejecting etc/profile, keeping existing version
$ ls /var/lib/pkg/rejected/
$ ls /var/lib/pkg/rejected/etc/
!!! 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
->''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 %fn%glib%% as %fn%glibc%%).''
!!! Querying the Package Database
Querying the package database is done using '''pkginfo'''. This utility has a
few options to answer different queries.
||cellpadding="3" rules="all" frame="box"
||! Option ||! Description
||-i, --installed ||List installed packages and their version.
||-l, --list package|file ||List files owned by the specified package or contained in file
||-o, --owner pattern ||List owner(s) of file(s) matching pattern.
$ pkginfo -i
audiofile 0.2.3-1
autoconf 2.52-1
automake 1.5-1
xmms 1.2.7-1
zip 2.3-1
zlib 1.1.4-1
$ pkginfo -l bash
$ pkginfo -l grep#2.5-1.pkg.tar.gz
$ pkginfo -o bin/ls
e2fsprogs usr/bin/lsattr
fileutils bin/ls
modutils sbin/lsmod
!! Package management frontend: prt-get
In its current form '''pkgutils''' does not have a concept of dependency
handling. To address this a frontend utility called '''prt-get''' was created.
It supports dependency handling (with the caveat mentioned below) as well as
some overlap with '''pkgutils''' features and has been an official part of CRUX
for some time.
!!! Functionality
Some examples of prt-get's functionality and use are as follows:
Listing installed ports:
$ prt-get listinst
$ prt-get listinst -v
acl 2.2.53-3
attr 2.4.48-1
autoconf 2.69-2
Querying information about a port:
$ prt-get info acl
Name: acl
Path: /usr/ports/core
Version: 2.2.53
Release: 3
Description: Access Control Lists library
Maintainer: CRUX System Team, core-ports at crux dot nu
Dependencies: attr
Searching for ports by name:
$ prt-get search glibc
$ prt-get search --regex '(ba|z)sh$'
Searching for ports by installed file:
$ prt-get fsearch gconv
Found in /usr/ports/core/glibc:
Found in /usr/ports/core/glibc-32:
Searching for ports by words in their descriptions:
$ prt-get dsearch shell
Viewing dependency lists:
$ prt-get depends bash
-- dependencies ([i] = installed)
[i] ncurses
[i] readline
[i] bash
$ prt-get quickdep bash
ncurses readline bash
$ prt-get deptree bash
-- dependencies ([i] = installed, '-->' = seen before)
[i] bash
[i] readline
[i] ncurses
Installing ports:
$ prt-get install xterm
->''The 'install' command does NOT process dependencies and it is usually
recommended to use 'depinst' (next) instead!''
$ prt-get depinst xterm
Viewing and updating outdated ports (generally after 'ports -u'):
Listing installed ports which are out of date:
$prt-get diff
Differences between installed packages and ports tree:
Port Installed Available in the ports tree
bind 9.16.7-1 9.16.8-1
Updating an individual port:
$ prt-get update xterm
Updating all installed ports:
$ prt-get sysup
->''Currently 'update' and 'sysup' do not process new dependencies introduced
after the initial installation of a port. To show such additions to the
dependency lists of installed ports, you can chain together several invocations
of %fn%prt-get%% with one invocation of %fn%awk%% as follows.''
$ prt-get isinst $(prt-get quickdep $(prt-get quickdiff)) | awk '/not installed/ {print $2}'
!!! Configuration
!!!! /etc/prt-get.conf
'''prt-get''''s main configuration file, '/etc/prt-get.conf', contains options
that can be used to change prt-get's behavior. Notably in this file the
following options can be configured:
%fn%prtdir%% - This option can occur multiple times and specifies a directory
with a 'collection' of ports prt-get should check in its operation. By default
the 'core', 'opt', and 'xorg' collections are enabled. The 'compat-32' and
'contrib' collections are disabled by default, see sections "Enabling the
'contrib' collection" and "Enabling the 'compat-32' collection".
%fn%logfile%% - This option configures a file for prt-get to log its operation
if desired.
%fn%runscripts%% - This option configures prt-get to run pre-/post-install
scripts if they exist in ports being installed or updated. It is recommended
that this be enabled as in many cases if a pre- or post-install script exists
in a port, it is required to be run for proper operation.
!!!! /etc/prt-get.aliases
'''prt-get''' has a concept of aliases which can be used in a fashion similar to
the concept of 'provides' in some other Linux distributions. This file is
%fn%/etc/prt-get.aliases%% and contains lines in the following format:
postfix: sendmail
exim: sendmail
qmail: sendmail
masqmail: sendmail
The above set of aliases indicates that %fn%postfix%%, %fn%exim%%, %fn%qmail%%,
and %fn%masqmail%% are all considered sufficient to satisfy a dependency on
'%fn%sendmail%%' in a port.
Sometimes the port maintainer will list among the required dependencies a
lightweight library, to save on compilation time for the majority of users. If
you already have the more powerful library installed, you can use
%fn%prt-get.aliases%% to avoid automatic installation of the lightweight
alternative. For example, on a system with %fn%mozjs91%% already built, you
would not want %fn%prt-get depinst polkit%% to build fn%duktape%% as well. This
can be accomplished with the following line in %fn%prt-get.aliases%%:
mozjs91: duktape
Another case where aliases might be useful would be that of replacing a
slow-compiling source port with a precompiled binary port in order to save time.
For example the following would indicate that '%fn%rust-bin%%' is considered
sufficient to satisfy a dependency on '%fn%rust%%' in a port:
rust-bin: rust
->''prt-get's alias function does NOT automatically replace ports during an
install or depinst operation. If a port depends on '%fn%rust%%' and neither
'%fn%rust-bin%%' or '%fn%rust%%' are installed, prt-get will install
'%fn%rust%%' as listed in the port's dependencies. If '%fn%rust-bin%%' is
installed before the depending port's install or depinst operation, on the other
hand, '''prt-get''' will consider the dependency satisfied.''
This is NOT an exhaustive list of all of '''prt-get''''s commands, features, and
configuration options, merely a starting point. More information can be found in
the [[ | manual]] and
[[ | quick start]]
!! Creating Packages
Creating a package is done using '''pkgmk'''. This utility uses a file called
%fn%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 %fn%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 %fn%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.
# Specify the version of the package.
# Specify the package release.
# The source(s) used to build this package.
# 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.
# 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 %fn%Pkgfile%%
for '''grep(1)''' looks like this:
# Description: GNU grep, egrep and fgrep
# URL:
# Maintainer: Per Lid<69>n, per at fukt dot bth dot se
build() {
cd $name-$version
./configure --prefix=/usr --disable-nls
make DESTDIR=$PKG install
rm -rf $PKG/usr/info
->''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 built in some other way, e.g. does not use '''autoconf'''.''
When the build() function has been executed, the %fn%$PKG%% directory will be
made into a package named fn%''<name>#<version>-<release>''.pkg.tar.gz%%. Before
the package creation is completed, '''pkgmk''' will check the contents of the
package against the %fn%.footprint%% file. If this file does not exist, it will
be created and the test will be skipped. The %fn%.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 %fn%$PKG%% (if the
%fn%.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
%fn%.footprint%% file by hand. Instead, when a package has been upgraded and you
need to update the contents of the %fn%.footprint%% file you simply do
'''pkgmk -uf'''. This test ensures that a rebuild of the package turned out as
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 %fn%Pkgfile%% in
another package(s), since looking at examples is a great way to learn.
->''Please see the [[#PackageGuidelines | Package Guidelines]] for
additional recommendations regarding Pkgfile variables (name, version, release,
build) and the footprint.''
!! 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. If this array only contains mirrors
administered by the CRUX team, building a port from your personal collection
might result in ''404 Not Found'' errors until all the mirrors are exhausted,
and then the source defined in the Pkgfile will be tried.
* 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
Here are some examples:
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.
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. But if NFS is too challenging to set up on your local network and you find
it easier to configure an http server instead, %fn%pkg-get%% from the %fn%opt%%
collection gives you an alternative mechanism for sharing built packages.)
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.
[[#PackageGuidelines]] !! 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. fn%/usr/ports/???/eterm%%.
* Do not combine several separately distributed programs/libraries into one
package. Make several packages instead.
* The build function should never touch anything outside the work directory, and
ideally should not rely on having an internet connection (say, to download
sources not listed in the %fn%source%% array). While '''pkgmk''' does not
currently recognize git urls in the %fn%source%% array, efforts are underway
to add this feature, thereby removing the temptation to write a build function
that reaches out to the internet for the latest git source tree.
-> The motivation for a policy of separating 'download' from 'build' is that a
user with intermittent internet access should be able to run @@pkgmk -do@@ in
the directory of every outdated package, and then go offline to finish the sysup
operation. Language-specific toolchains, like those embraced by rust and python
(cargo and pip, respectively), are making this policy more difficult to enforce.
You are free to forego CRUX pkgutils and let the ecosystems of the ''N''
different languages manage their respective software in separate subdirectories
(like python's %fn%~/.local/share/virtualenv%% or rust's %fn%~/.cargo%%), at the
expense of having to learn '''N+1''' administration suites rather than just the
1 suite of CRUX pkgutils. Every language-specific project that appears in the
official repositories represents a hard-won effort by the CRUX development team
to sustain the historic method of software deployment, in the face of software
development trends all heading away from this model.
!!! 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.
||cellpadding="3" rules="all" frame="box"
||! Directory ||! Description
||%fn%/usr/bin/%% ||User command/application binaries
||%fn%/usr/sbin/%% ||System binaries (e.g. daemons)
||%fn%/usr/lib/%% ||Libraries
||%fn%/usr/include/%% ||Header files
||%fn%/usr/lib/<prog>/%% ||Plug-ins, addons, etc
||%fn%/usr/share/man/%% ||Man pages
||%fn%/usr/share/<prog>/%% ||Data files
||%fn%/usr/etc/<prog>/%% ||Configuration files
||%fn%/etc/%% ||Configuration files for system software (daemons, etc)
* %fn%/opt%% directory is reserved for manually compiled/installed applications.
Packages should never place anything there.
* %fn%/usr/libexec/%% is not used in CRUX, thus packages should never install
anything there. Use %fn%/usr/lib/<prog>/%% instead.
!!! Remove Junk Files
* Packages should not contain &#8220;junk files&#8221;. This includes info pages
and other online documentation, man pages excluded (e.g. %fn%usr/doc/*%%,
%fn%README%%, %fn%*.info%%, %fn%*.html%%, etc).
* Files related to NLS (national language support), always use
'''--disable-nls''' when available.
* Useless or obsolete binaries (e.g. %fn%/usr/games/banner%% and
!!! Pkgfile
* Do not add new variables to the %fn%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=($name-$version.tar.gz)=]
is better than [=source=(] 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=...@@
* Another array that '''pkgmk''' will make use of, if defined, is @@renames@@
(introduced in CRUX 3.7). This array lets you save the downloaded source
tarballs with a more descriptive filename, to avoid filename collisions when
using a shared directory for downloads. See the %fn%Pkgfile%% man page for more
!!!! Pkgfile header
Provide a header including the following fields:
||cellpadding="3" rules="all" frame="box"
||!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.
'''Example header'''
# Description: Terminal based IRC client for UNIX systems
# URL:
# Maintainer: Jukka Heino, jukka at karsikkopuu dot net
# Packager: Daniel K. Gebhart, dkg at con-fuse dot org
# Depends on: glib