The package system ([[http://crux.nu/Wiki/FaqPkgUtils | 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 install'''.
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.
When installing a package the package manager will ensure that no existing 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:
->No such error message is issued for already-existing directories, since the package database allows a directory to be associated with more than one package. However, the owner/group and permissions of an existing directory will not be changed to match those of the requested package.
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.
->''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.
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 “upgrade” version 5.0.18-1 with version 5.0.16-1 (or even with version 5.0.18-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%%.
%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. \\
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.
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 ports#1.6-3.pkg.tar.gz
pkgadd: rejecting etc/ports/compat-32.pub, keeping existing version
pkgadd: rejecting etc/ports/compat-32.rsync.inactive, keeping existing version
pkgadd: rejecting etc/ports/contrib.pub, keeping existing version
pkgadd: rejecting etc/ports/contrib.rsync.inactive, keeping existing version
pkgadd: rejecting etc/ports/core.pub, keeping existing version
pkgadd: rejecting etc/ports/core.rsync, keeping existing version
pkgadd: rejecting etc/ports/opt.pub, keeping existing version
pkgadd: rejecting etc/ports/opt.rsync, keeping existing version
pkgadd: rejecting etc/ports/xorg.pub, keeping existing version
pkgadd: rejecting etc/ports/xorg.rsync, keeping existing version
$ tree -L 3 /var/lib/pkg/rejected/
/var/lib/pkg/rejected
etc
ports
compat-32.pub
compat-32.rsync.inactive
contrib.pub
contrib.rsync.inactive
core.pub
core.rsync
drivers
opt.pub
opt.rsync
xorg.pub
xorg.rsync
@]
If this %fn%pkgadd.conf%% had been in place during an upgrade from CRUX 3.6 to 3.7, and you neglected to check the contents of %fn%/var/lib/pkg/rejected/%% (either manually or by running @@rejmerge@@), then '''ports -u''' would synchronize your ports tree with the older branch! Hence you should always pay attention to warnings and errors reported by @@pkgadd@@.
->''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%%).''
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.
->''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''''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' that prt-get should search for ports. 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%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.
'''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:
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%%:
->''prt-get's alias function does NOT automatically replace ports during an install or depinst operation. If a port depends on '%fn%duktape%%' and neither '%fn%duktape%%' nor '%fn%mozjs91%%' are installed, prt-get will install '%fn%duktape%%' as listed in the port's dependencies. If '%fn%mozjs91%%' was 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 [[https://crux.nu/doc/prt-get%20-%20User%20Manual.html | manual]] and
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.
->''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 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 %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.''
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 package
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.
* 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.
--> Apart from ensuring a consistent mechanism to verify the integrity of sources (@@signify@@), the policy of separating 'download' from 'build' allows a user with intermittent internet access 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 team to sustain the historic method of software deployment, in the face of software development trends all heading away from this model.
* 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.
* Packages should not contain “junk files”. 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). Ports built using GNU autoconf can be told not to install these files by passing the option '''--disable-nls'''. Other compiler toolchains often provide no such option, forcing you to clean up the package footprint by hand.
* Useless or obsolete binaries (e.g. %fn%/usr/games/banner%% and %fn%/sbin/mkfs.minix%%).
* 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=(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.
* 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 details.
The commented lines at the beginning of a Pkgfile do not affect the operation of '''pkgmk''', but '''prt-get''' and other CRUX tools will make use of the information provided there. Four headers of utmost importance are:
@@Depends on@@ can be omitted if there are no dependencies. To keep this line short, runtime dependencies in the 'core' collection are omitted, unless they provide a library that is dynamically linked to the built program. See the Pkgfile man page for more details.
Two other headers you will often encounter when inspecting Pkgfiles are @@Optional@@ and @@Nice to have@@. None of the official CRUX tools will be affected by the contents of these lines, but maintainers find it helpful to store in @@Optional@@ the list of “soft” dependencies (libraries that will be linked to the built program, if present during compilation). This header then serves as a reminder of tests that might need to be included in the @@build()@@ function in order to set the appropriate @@configure@@ flags. As an end user of official ports, you might find in these headers some clues about:
* the additional functionality that can be unlocked, when dependencies beyond the bare minimum are installed before the desired port.
* the reason that '''pkgmk''' reports a footprint mismatch.
* the breakage that might happen if you try to install on your CRUX system a package built on someone else's CRUX system.