Package Management

To create package management tools, many distributive developers choose powerful interpreters, such as Python, or even high-level programming languages requiring compiler utilization. Graphical environments for package management are implemented in the form of applications using such libraries as GTK+ and Qt.

Distributives prepared with the help of these kinds of tools are primarily intended for usage on personal computer and servers.

On the other hand, if we want to provide the possibility to prepare distributives also for the simplest microcontroller-based devices, we should support only the minimal required toolset. Redundancy here is unacceptable. Moreover, all tools must be equally applicable on the target device and in the cross-development environment, which means that all package management tools must be available to the build system which is used to create distributives with the help of simple Makefiles.

Keeping in mind the minimal toolset available in every Linux-based system, namely, coreutils, ncurses, bash, sed, tar, gzip, etc., we choose bash and dialog as the tools to create a package management system.

Such choice allows us to form target filesystems and manage application package sets both during boot images creation on the developer's machine and directly on a target device that can be some embedded management device, a tablet, a smartphone or an Intel personal computer.

Creating Packages

The purpose of the make-package utility is to create application packages. Before using this utility, the user shall create a temporary directory, store there all required files reproducing the file structure of the target filesystem, create system files .PKGINFO, .DESCRIPTION, .INSTALL and, if the current package is dependent on other packages in the target system, create the .REQUIRES file.

Then the user should make the following call from this temporary directory:

$ make-package [options] DESTDIR

Here DESTDIR is a required parameter specifying the target directory where the prepared package will be stored.

This way, if we stored all needed files in the directory /tmp/pkgtool-package and want to write the finished package to the /tmp directory, the following commands shall be executed:

$ cd /tmp/pkgtool-package
$ make-package ..

or

$ cd /tmp/pkgtool-package
$ make-package /tmp

Note that this illustrates the fact that DESTDIR can be either an absolute or a relative path to the directory for storing the prepared package. The only limitation is that it must not be the same as the directory where the source files are kept.

Next, let's discuss the make-package utility's directives.

Options

Of course, the make-package utility takes standard directives common for all management utilities, such as '-h | --help' and '-v | --version' that are used to display quick help info and version number, respectively.

But the key directives for package creation are the directives responsible for archiving symbolic links to files, if there are any, and for storing the prepared packages in the target directory.

Restore Links

Application packages can contain symbolic links. Since different filesystems can treat symbolic file links differently, it is recommended to delete such links before building an archive and restore them after installation is finished.

Of course, not all users want to follow this rule. Because of this, the make-package utility allows specifying various ways of symbolic links treatment using the --linkadd directive. The following listing illustrates possible combinations of the --linkadd directive usage:

$ make-package -l yes ..
$ make-package -l y ..
$ make-package -l no ..
$ make-package -l n ..

$ make-package --linkadd yes ..
$ make-package --linkadd y ..
$ make-package --linkadd no ..
$ make-package --linkadd n ..

$ make-package --linkadd=yes ..
$ make-package --linkadd=y ..
$ make-package --linkadd=no ..
$ make-package --linkadd=n ..

If the source directory of a package contains symbolic links, but the --linkadd directive is not specified, the make-package utility will prompt the user with a question like the following:

$ make-package ..

Package maker, version 1.0.

Search for symbolic links:

sbin/pkgtool/install -> install-package

Restore symbolic links script:
( cd sbin/pkgtool ; rm -rf install )
( cd sbin/pkgtool ; ln -sf install-package install )

It is recommended to remove symbolic links and create the '.RESTORELINKS'
script.

Do you want create the '.RESTORELINKS' script for this package
and remove symbolic links from the package archive ([y]es, [n]o)?

The answer 'y' means that symbolic links in the package will be deleted, and the set of package's hidden system files will be expanded by the .RESTORELINKS file containing the symbolic links restoration scenario. Symbolic links restoration after the package has been installed to the target system is the responsibility of the install-package utility that we will discuss in more details later.

To prevent the make-package utility from prompting the user with such questions, it is recommended to always use the --linkadd={yes|no} directive.

FLAVOUR

When a distributive is being prepared, packages are usually sorted by groups. In addition, in the ready packages repository it is convenient to store packages in the respective directories, which simplifies searching through them.

The make-package utility supports such group directories management. If the group variable is defined in the .PKGINFO file, then the resulting package is stored in the ${group} directory that is created in the target directory DESTDIR.

Since the build system provides possibility to create various package modifications using the FLAVOURS mechanism, a related directive is supported:

$ make-package -fl FLAVOUR ..

$ make-package --flavour FLAVOUR ..

$ make-package --flavour=FLAVOUR ..

where FLAVOUR defines a subdirectory name in the ${group} directory.

This way, if we are using the group variable by setting group=libs and additionally utilize the '--flavour=512M' directive, for example, to create some library modification for a device with 512 MB of RAM, then after running this command:

$ make-package --linkadd=yes --flavour=512M ..

the resulting package will be stored in the ../libs/512M directory relative to the source package directory.

.PKGINFO

Creating a package should start with a package description file preparation. This is a simple text file that has the name .PKGINFO. This file shall be viewed as a part of a shell scenario. Since Bourne Again Shell is the most popular command interpreter for Linux, we will use it.

There is the minimal variable set that must be defined to ensure proper operation:

pkgname
-
package name. For example, pkgtool ;
pkgver
-
package version. For example, 0.0.1 ;
arch
-
architecture. For example, rk3288-glibc ;
distroname
-
distributive name. For example, radix ;
distrover
-
distributive version. For example, 1.0 ;

Since the .PKGINFO file is, basically, a part of a bash scenario, spaces before or after the equal mark are not allowed.

These limitations simplify working with packages. If the following .PKGINFO variables:

pkgname=pkgtool
pkgver=0.0.1
arch=rk328x-glibc
distroname=radix
distrover=1.0

are loaded into the current environment using the command

$ . ./.PKGINFO

we can unambiguously define the name of the file containing this application package:

$ echo "PACKAGE_TARBALL=$pkgname-$pkgver-$arch-$distroname-$distrover.txz"
PACKAGE_TARBALL=pkgtool-0.0.1-rk328x-glibc-radix-1.0.txz

$

The architecture for which this package is built is defined conditionally. The user can utilize his/her own naming conventions to define the architectures the distributive is intended for. We are using a simplified scheme that allows identifying the target platform. For example, here the name of a processor line RK328X is used and the name of a C language library. The first name almost unambiguously defines the toolchain in use, and the second – the operating system based on Linux kernel.

The name and version of a distributive are usually defined by the $(DISTRO_NAME) и $(DISTRO_VERSION) variables' values that are specified in the build system's constants.mk file.

Recommended Variables

We strongly recommend defining four more variables in .PKGINFO files:

group
-
the name of the group to which the current package belongs. For example, base ;
short_description
-
a short package description. For example, "Package Tools" ;
url
-
the package creator's web-site. For example, 'http://www.radix.pro' ;
license
-
license type.

Let's look at these variables in more details.

Group Name

The value of the group variable is specified without quotation marks and, basically, represents the directory name (relatively to the target directory ${DESTDIR}) where the created package is written.

We are keeping to the following package group definition rules:

app
-
console application without graphical interface or applications using ncurses;
base
-
basic system packages, like pkgtool or system start/stop scenarios;
dev
-
applications required for software development. These are various programming languages, system header files, autotools, cmake, etc.;
dict
-
dictionaries;
doc
-
documentation automation tools;
libs
-
libraries;
net
-
network utilities and libraries;
xorg
-
packages included in X.org and utilizing the X11 protocol;
waylang
-
packages utilizing the Wayland protocol;

Regarding the xorg and wayland groups, we assume that the source directory tree has the following hierarchy: protocol/version/implementation. Thus, for example, the X.org project is an X Window implementation, and, ideally, the source tree hierarchy for package creation could be represented by the formula X/11R7.7/xorg/group/package. But in practice, it's too difficult to pinpoint certain package versions belonging to one or another version of X, so we reduce the formula to X11/xorg/group/package. By doing it, we leave packages not included in the classic delivery of X Windows, for example, created within the Freedesktop project, in the X11/group directories (formula: X11/group/package). Note that the name of the group directory doesn't have to always match the name of the package group group. Thus, in the X11/X.org directory all packages have the group value group=xorg independent of the subdirectory containing the Makefiles for building this or that package. In turn, for packages outside X.org we usually use a group directory name matching the group name, but without the 'x' prefix. For example, in the X11/libs directory packages with group name group=xlibs are located.

xapp
-
applications utilizing the X11 protocol but not included directly into X Window delivery from X.org (X11R7.7);
xfont
-
third-party fonts for X Window;
xlibs
-
libraries utilizing the X11 protocol.

The Wayland protocol also supports different implementations, so, when Wayland gains popularity, we will adhere to the same application set structuring principle. And, probably, we should expect the sets wapp, wlibs, etc.

Short Description

A package's short description short_description is defined with one string enclosed in double quotes. The maximal string length is 45 characters.

For example, in a Makefile the short package description can be specified like this:

###                         |---handy-ruler-------------------------------|
BIN_PKG_SHORT_DESCRIPTION  = Package Tools

to be used during automatic .PKGINFO file building. Since Makefile vocabulary applies certain restrictions to the character sets allowed in variable values, short descriptions in Makefiles must utilize a special character to escape illegal characters, namely, backslash ('\').

The following characters must be escaped: '&', '*', '(', ')'. In Makefiles such escaping looks like this:

###                          |---handy-ruler-------------------------------|
KSBA_PKG_SHORT_DESCRIPTION  = X.509 \& CMS library for S/MIME and TLS

If the .PKGINFO file is prepared manually, escaping is not obligatory but allowed. Anyway, the escape character '\' will be deleted from the description by the make-package utility.

Despite a package's short description is optional, we strongly recommend defining the short_description variable in .PKGINFO files. Packages' short descriptions are used during inter-package dependency tree building to display the package information.

WEB Address

Developer's address is provided to facilitate feedback. Knowing the developer's address, users can always ask for help or point the developer to some bugs. In the build system's file constants.mk this address is represented by the BUG_URL variable that is always accessible in user Makefiles, for example, in the following manner:

SQLITE_PKG_URL = $(BUG_URL)

        .   .   .

        echo "url=$(SQLITE_PKG_URL)" >> $(SQLITE_PKG)/.PKGINFO ; \

        .   .   .

We suggest that you never forget about users and always define the url variable when preparing application packages.

License Type

License type license is specified without quotation marks with one word, for example, GPLv3, LGPLv2, BSD, MIT.

The following is an example of a full package description in a .PKGINFO file:

pkgname=pkgtool
pkgver=0.0.1
arch=rk328x-glibc
distroname=radix
distrover=1.0
group=base
short_description="Package Tools"
url=http://www.radix.pro
license=GPLv2

Here all required information about a package can be found. But there is more than this. Another required file is the full package description .DESCRIPTION.

.DESCRIPTION

The make-package utility only considers lines starting with the package name followed by colon ':'. Since all other lines are ignored, it is possible to use the following template containing the rules regarding the full package description creation:

# HOW TO EDIT THIS FILE:
# The "handy ruler" below makes it easier to edit a package description.  Line
# up the first '|' above the ':' following the base package name, and the '|'
# on the right side marks the last column you can put a character in. You must
# make exactly 11 lines for the formatting to be correct. It's also
# customary to leave one space after the ':'.

       |-----handy-ruler------------------------------------------------------|
pkgtool: pkgtool 0.0.1 (Package Tools)
pkgtool:
pkgtool: This is a set of scripts used for package creation, installing, etc.
pkgtool:
pkgtool:
pkgtool:
pkgtool:
pkgtool:
pkgtool:
pkgtool:
pkgtool:

We should stress again that the description must contain exactly 11 lines, where it is recommended to leave the last line empty since the make-package utility will add two more lines to this description, and it is better to have some kind of divider between them.

The line length must not exceed 70 characters including leading space.

Now, let's discuss shortly the additional information that is provided by the make-package utility.

During package creation the following two variables will be added to the .PKGINFO file:

uncompressed_size=144K
total_files=11

The first defines the total space taken by the package in the target filesystem, and the second – the total number of files this package is comprised.

During package installation the full amount of space taken by the package is reported after the full package description:

Uncompressed Size: 144K
  Compressed Sise: 20K

And during package deletion instead of compressed package size the total number of files constituting the package is reported:

Uncompressed Size: 144K
      Total Files: 11

If a package is installed in text mode (without the help of the dialog library), then the following information is displayed on the user's terminal screen:

bash-4.2$ install-package --root /opt/rootfs base/pkgtool-0.0.1-rk328x-glibc-radix-1.0.txz

 Installing package pkgtool...
|======================================================================|

 pkgtool 0.0.1 (Package Tools)

 This is a set of scripts used for package creation, installing, etc.








 Uncompressed Size: 144K
   Compressed Sise: 20K
|======================================================================|

bash-4.2$

.INSTALL

This file is a command line interpreter scenario consisting of six functions. The functions describe operations that shall be executed in the system during one or another package installation stage. All stages are relative to the moment of package decompression and copying files to the root filesystem. If no operations need to be executed, all functions shall return true.

The file presented in the following listing shall be used as a template for user scenario preparation.

#!/bin/sh 

# arg 1: the new package version
pre_install() {
  /bin/true
}

# arg 1: the new package version
post_install() {
  /bin/true
}

# arg 1: the new package version
# arg 2: the old package version
pre_update() {
  /bin/true
}

# arg 1: the new package version
# arg 2: the old package version
post_update() {
  post_install
}

# arg 1: the old package version
pre_remove() {
  /bin/true
}

# arg 1: the old package version
post_remove() {
  /bin/true
}


operation=$1
shift

$operation $*

Preparing the installation scenario for one or another package requires clear understanding of the utilities available in the system or on the developer's machine.

Let's discuss the three main installation modes.

  1. Managing packages on a working system.
  2. Installing the system from an external media.
  3. Preparing the target filesystem on the developer's machine.

In the first mode, any tools presented in the system can be utilized in the installation scenario. The only thing the developer needs to know is whether this or that tool has already been installed in the system, or if it is still unavailable.

In the second mode, the target machine was booted from an external media containing a certain minimal set of utilities and the operating system installation program. Here the set of available tools is fixed, and thefu ll list of such tools is well known to the distributive creator. In this mode the main disk space partition of the target machine is mounted by the system installer into a temporary directory. After that, all required files are copied there, and the system is set up for subsequent launch and autonomous operation.

The third mode is used when a boot disk for the target machine is created on the developer's machine. In this case, a temporary directory or a virtual filesystem is created, and packages are installed there as soon as they are created. In this mode, the installation scenario can utilize only those tools that are available on the developer's machine.

To identify the current installation mode from the package installation scenario, it is enough to implement a special file creation in the second mode. For example, the file /etc/system-installer can be created by the system installation program at start and deleted after the system installation has been finished.

This way, the current installation mode can be easily checked. For example, to make sure that the current mode is the third mode from the list above, and all operations are being executed in a working system, we need to check whether the following condition is true:

if [ -r proc/sys/kernel/osrelease -a ! -r /etc/system-installer ] ; then

  # we are on the working system

fi

Let's examine a couple of examples.

Suppose our package contains documentation in the texinfo format, and we want this documentation to be correctly indexed and available to users after package installation is finished. At the same time, we know that the install-info utility can be used in any installation mode, because the authors provided the required directives to use this utility in any situation, independent of the root filesystem mount point.

In addition, we should make sure that the documentation index is cleaned up directly before package files are deleted from the system. So, the installation scenario can be simple and independent of the usage mode:

#!/bin/sh 

# arg 1: the new package version
pre_install() {
  /bin/true
}

# arg 1: the new package version
post_install() {
  #
  # NOTE:
  # 'install-info' can work using relative paths and we can make use build machine
  # utility during installation to the some partition and use target 'install-info'
  # during installation directly on the running target machine.
  #
  if [ -x /usr/bin/install-info ] ; then
    install-info --info-dir=usr/share/info usr/share/info/grep.info.gz 2> /dev/null
  elif ! grep "(grep)" usr/share/info/dir 1> /dev/null 2> /dev/null ; then
    cat << EOF >> usr/share/info/dir

Text creation and manipulation
* grep: (grep).                 Print lines matching a pattern.
EOF
  fi
}

# arg 1: the new package version
# arg 2: the old package version
pre_update() {
  /bin/true
}

# arg 1: the new package version
# arg 2: the old package version
post_update() {
  post_install
}

# arg 1: the old package version
pre_remove() {
  if [ -x /usr/bin/install-info ] ; then
    install-info --delete --info-file=usr/share/info/grep.info.gz --dir-file=usr/share/info/dir 2> /dev/null
  fi
}

# arg 1: the old package version
post_remove() {
  /bin/true
}


operation=$1
shift

$operation $*

It is easy to see that in this scenario relative paths are used, so, working in the root of the target filesystem, the scenario will execute all required operations correctly. In this, it will use the install-info utility which is available both on the developer's machine (third installation mode) and on the working target machine (first mode). The distributive's creator only needs to ensure that the install-info utility is also available in the installation program's filesystem (second mode), that the texinfo package is installed to the target machine before the current package (first mode), and, of course, that texinfo is available on the developer's machine.

The following example illustrates the operations that need to be executed only in the first installation mode.

For example, refreshing font cache only makes sense on the working system. Therefore, we can implement these operations in two cases only: first, for package installation in the first mode, and second, for autonomous system start. In all other cases these operations are not required.

#!/bin/sh 

# arg 1: the new package version
pre_install() {
  /bin/true
}

# arg 1: the new package version
post_install() {
  # We use an relative path to 'proc/sys/kernel/osrelease' because we have
  # to be sure that we are running on the target platform. Only in this case
  # we will use absolute path to mkfonts{dir,scale}.
  if [ -r      proc/sys/kernel/osrelease \
       -a ! -r /etc/system-installer     \
       -a -x   /usr/bin/mkfontdir        \
       -a -x   /usr/bin/mkfontscale ] ; then
    ( cd /usr/share/fonts/TTF
      mkfontscale .
      mkfontdir .
    )
  fi
  # We use an relative path to 'proc/sys/kernel/osrelease' because we have
  # to be sure that we are running on the target platform. Only in this case
  # we will use absolute path to fc-cache.
  if [ -r      proc/sys/kernel/osrelease \
       -a ! -r /etc/system-installer     \
       -a -x   /usr/bin/fc-cache ] ; then
    /usr/bin/fc-cache -f
  fi
}

# arg 1: the new package version
# arg 2: the old package version
pre_update() {
  /bin/true
}

# arg 1: the new package version
# arg 2: the old package version
post_update() {
  post_install
}

# arg 1: the old package version
pre_remove() {
  /bin/true
}

# arg 1: the old package version
post_remove() {
  /bin/true
}


operation=$1
shift

$operation $*

In conclusion, it is worth mentioning that a lot of installation scenario examples can be found in the distributive's source code repository at http://svn.radix.pro/svn/platform/trunk.

.REQUIRES

Can be absent or empty. If there are other packages the current package is dependent on, then the .REQUIRES file must contain records like in the following listing:

libtiff=4.0.3
libxshmfence=1.1
libXinerama=1.1.3
giflib=5.1.0
libXft=2.3.2
libXrandr=1.4.2
libXaw=1.0.12
gettext=0.18.3.2

Here, only the equality sign '=' is allowed. The greater than sign ('>'), less than sign ('<'), greater-than-or-equal-to sign ('>=') and less-than-or-equal-to sign ('<=') are not allowed.

Basically, the equality sign '=' means that the required package version must be equal to or greater than specified.

The build system provides functions to build the .REQUIRES files automatically using the REQUIRES list the Makefile starts with.

Install Package

The install-package utility installs one or several packages specified in the command line.

$ install-package [options] package_tarball[s]

The main task during a package installation is unpacking its contents to the target filesystem. At this, a Setup Database is filled and a log is written.

The package's creator is provided the ability to execute additional operations directly before the package contents is unpacked to the target filesystem as well as after unpacking is finished. For this, the .INSTALL file can include the pre_install() and post_install() functions. In the section where we discuss the make-package utility you will find an example .INSTALL file template that shows that both functions take the package version as an argument.

Usually, if the install-package utility receives several packages as input and it's impossible to install a certain package, it simply continues with the next package in the list. But if during package installation an error occurs that can lead to inconsistency of the Setup Database, the install-package utility halts.

The output of the install-package utility is discussed in details in the Setup Database section.

Options

The key directive of the install-package utility is the --root option that allows specifying the full path to the mount point of the target filesystem. If this path is not specified, the install-package utility will try to install the package to the root / directory.

If the Setup Database has not yet been deployed to the target filesystem when installation is triggered, the install-package utility creates all required directories, effectively, creating the setup database.

Since the install-package utility is intended for usage in different installation modes, it also provides several display modes to view the information about the installed packages.

Info Modes

By default, the so-called console mode is used. In this mode information about package is printed to the console screen in text form, for example, like in the following listing:

bash-4.2$ install-package --root /opt/rootfs base/pkgtool-0.0.1-rk328x-glibc-radix-1.0.txz

 Installing package pkgtool...
|======================================================================|

 pkgtool 0.0.1 (Package Tools)

 This is a set of scripts used for package creation, installing, etc.








 Uncompressed Size: 144K
   Compressed Sise: 20K
|======================================================================|

bash-4.2$

The purpose of this mode is to print the information on the screen during distributive building the way it is done during source code compilation. In short, this display mode fits well the general policy for information output. This is especially useful when a large number of packages for different target platforms are built in parallel, and dialog installation mode is simply unacceptable.

Infodialog

When a system installation utility is used, it is important to offer two ways to display process information. First, it must be information messages about the currently installed packages, and second, similar output, but with an additional user control possibility (so-called dialog mode).

In the first case the --infodialog directive can be used. In this, information about packages is displayed in the form of alternating messages. This uses the dialog application which, in turn, uses the ncurses library.

In the second case, if we want to allow the user to select whether this or that package shall be installed, the --menudialog directive should be used.

But the dialog mode is controlled not only by the --infodialog and --menudialog directives. The install-package utility also takes the package priority into account. The priority value can be specified directly with the controlling argument --priority and, in addition, the install-package utility can read package priority values from the packages list. We will talk about installation package lists in more details later.

It is important to remember that in the console and infodialog modes installation is carried out automatically, and the user isn't able to cancel it. At the same time, if a package has the priority value of SKIP, then this package is ignored, and the install-package utility continues with the next package installation, according to the order in the input list. Packages having priorities of REQUIRED, RECOMMENDED and OPTIONAL are installed automatically, and the priority value is displayed in the respective information message.

Menudialog

To provide the user the possibility to choose which package to install in the system and which to skip, the menudialog mode is implemented. If the --menudialog directive is specified and the package priority is REQUIRED, then the package is installed automatically in the infodialog mode. Packages with the priority value equal to SKIP are ignored in this mode.

In all other cases the user can choose to install a certain package or to cancel this package installation and continue with the next from the set given to the install-package utility as input. In addition, it is possible to completely halt the installation process for all packages given to the install-package utility.

To enable dialog with user in all cases, even when a package's priority equals REQUIRED or SKIP, it is required to specify the --always-ask directive.

Priority

As previously noted, the priority of a package or group of packages given to the install-package utility as input can be specified using the --priority directive. If this is done, then priority values specified in the packages list are not taken into account.

It is important to remember that the --priority directive sets the same priority value for all packages that are currently given to the install-package utility.

Packages List

If a package priority hasn't been specified explicitly, then the install-package utility tries to read the priority value from the .pkglist file. The install-package utility expects to find the .pkglist file in the same directory where the package that is currently being installed is located. This is convenient in situations when we have a set of packages with the installation order defined in the .pkglist file.

Such way to define priorities is convenient in situations where all packages are located in the same directory and we want to use the install-package utility, but the package names are specified directly in the command line. The install-package utility doesn't follow the order defined in the .pkglist list, it only reads the package priority values from this file.

It is possible to specify the location of the packages list explicitly using the --pkglist directive. The full name of the file containing the list must be passed as an argument.

It's important to note that if the packages list contains more than one entry with the same package name, then the install-package utility will only be able to read the priority from the first such entry. This limit the possibilities to use complex packages list with the install-package utility.

We will talk about the capabilities of package management utility set in more details in the section dedicated to the install-pkglist utility. In addition, there we will examine the format and purpose of the packages lists.

Update Package

The update-package utility is used to update one or several packages specified in the command line. Note that the update-package utility doesn't have any assumptions regarding the packages' version numbers, which means that the user can either update a package or install a previous version. At the same time, the packages' version numbers are important for inter-package dependencies check: even if replacing a certain package with a previous version meets all the requirements posed by this package, it doesn't immediately mean that other packages in the system that are dependent on this package will be able to operate properly.

$ update-package [options] package_tarball[s]

The update-package utility takes the same arguments as the install-package utility. But, as opposed to usual package installation, the update-package utility first checks if the current package is present in the system, then deletes the files belonging to the existing package version and only after that installs the files from the new package version in the target filesystem.

To ensure successful package update, the installation scenario .INSTALL should provide the functions pre_update() and post_update() that, unlike the pre_install() and post_install() functions, take two arguments. The first is the version number of the new package to be installed and the second – the version number of the package that is already installed in the system before the update procedure is started.

In the section where we talk about the make-package utility, you will find a template .INSTALL file giving insight into the number and purpose of the functions' arguments.

It is worth noting that if any errors are encountered during the pre_update() function operation, before the package version existing in the system is destroyed, the update-package utility halts, so the user can be sure that the existing package is left unchanged.

At the same time, if errors are encountered during the post_update() function operation, the update-package utility deletes the files belonging to the new package version that were copied to the target filesystem before post_update() was called. This means that the package is removed.

In case of any errors during package update, independent of the process stage when they occurred, the user shall check the integrity of the Setup Database.

Install Packages List

The purpose of the install-pkglist utility is best presented using an example.

Suppose we created a temporary directory called products and saved the following packages inside:

  products
  ├── base
  │   ├── pkgtool-0.0.1-rk328x-glibc-radix-1.0.sha256
  │   ├── pkgtool-0.0.1-rk328x-glibc-radix-1.0.txz
  │   └── pkgtool-0.0.1-rk328x-glibc-radix-1.0.txt
  ├── dict
  │   ├── words-ru-0.99f7-1-rk328x-glibc-radix-1.0.sha256
  │   ├── words-ru-0.99f7-1-rk328x-glibc-radix-1.0.txz
  │   ├── words-ru-0.99f7-1-rk328x-glibc-radix-1.0.txt
  │   ├── words-ru-0.99f8-1-rk328x-glibc-radix-1.0.sha256
  │   ├── words-ru-0.99f8-1-rk328x-glibc-radix-1.0.txz
  │   └── words-ru-0.99f8-1-rk328x-glibc-radix-1.0.txt
  │
  └── .pkglist

In addition, we created the following list in the .pkglist file:

pkgtool:0.0.1:Package Tools:base/pkgtool-0.0.1-rk328x-glibc-radix-1.0.txz:install:REQUIRED
words-ru:0.99f7-1:Russian wordlist:dict/words-ru-0.99f7-1-rk328x-glibc-radix-1.0.txz:install:REQUIRED
words-ru:0.99f8-1:Russian wordlist:dict/words-ru-0.99f7-1-rk328x-glibc-radix-1.0.txz:update:RECOMMENDED

This way, by changing the current directory to products and running install-pkglist:

$ cd products
$ install-pkglist --root /destination

we can install our packages to the /destination directory.

In this, first a dialog will be displayed on the screen allowing the user to cancel installation of some packages in the list or activate installation of other packages marked in the list as OPTIONAL.

Choose Packages Dialog
Fig.1. Choose Packages Dialog

In this dialog, the word UPDATE in uppercase will be displayed next to the packages which have the procedure "update" (field 5), as shown on the following figure.

Choose Packages Dialog
Fig.2. Confirm UPDATE words-ru package

To cancel installation, the user can press either the Escape key or the <Cancel> button.

After the <Ok> button is pressed, the only thing that is possible is to watch information messages about the installed packages appear one after another.

Here it should be noted that the build system supports automatic installation package lists creation. Such lists should be managed with great care, because cancelling installation of one package that is required for other packages in the system to work, we can disrupt the distributive's integrity. In addition, if such situation arises, the install-pkglist utility will halt. If there really is a pressing need to ignore inter-package dependencies, then dependencies tracking can be cancelled using the --skip-requires directive. In this case, packages will be installed independently, and the Setup Database will not contain the correct information about package interdependencies.

Setup Database

A setup database is a set of text files located in special directories and containing all information about package. The following listing contains an example setup database directory structure.

   var
   └── log
       └── radix
           ├── packages
           │   ├── pkgtool-0.0.2-rk328x-glibc-radix-1.0
           │   ├── words-en_CA-7.1-rk328x-glibc-radix-1.0
           │   ├── words-en_CA-huge-7.1-rk328x-glibc-radix-1.0
           │   ├── words-en_CA-small-7.1-rk328x-glibc-radix-1.0
           │   ├── words-en_GB-insane-7.1-rk328x-glibc-radix-1.0
           │   └── words-ru-0.99f7-1-rk328x-glibc-radix-1.0
           ├── removed_packages
           │   └── pkgtool-0.0.1-rk328x-glibc-radix-1.0
           └── setup
               └── setup.log

The database is located in the /var/log/radix directory. The directory name, radix, corresponds to the distributive name. The build system supports changing the distributive name before build is started. For this, the DISTRO_NAME variable shall be defined in the constants.mk file. The value of this variable is used in creating all utilities composing the package management application set.

The /var/log/radix/packages directory contains so-called package log-files for the packages installed in the system. The /var/log/radix/removed_packages directory, in turn, contains log-files for the packages removed from the system using the remove-package utility. In the /var/log/radix/setup directory the file setup.log can be found which reflects all operations involving packages that are executed in the system. In addition, the directory /var/log/radix/setup can contain system setup scenarios that can be used either when the system installation utility is running or to set up the system when it is already in operation.

Successful package installation is followed by a package log-file creation containing the detailed information about the package itself as well as the inter-package dependencies in the system. It is better to examine the structure of such file with an example.

PACKAGE NAME: pkgtool
PACKAGE VERSION: 0.0.1
ARCH: rk328x-glibc
DISTRO: radix
DISTRO VERSION: 1.0
GROUP: base
URL: http://www.radix.pro
LICENSE: GPLv2
UNCOMPRESSED SIZE: 144K
TOTAL FILES: 11
REFERENCE COUNTER: 5
words-ru=0.99f7-1
words-en_CA-small=7.1
words-en_CA-huge=7.1
words-en_GB-insane=7.1
words-en_CA=7.1
REQUIRES:
PACKAGE DESCRIPTION:
pkgtool: pkgtool 0.0.1 (Package Tools)
pkgtool:
pkgtool: This is a set of scripts used for package creation, install, etc.
pkgtool:
pkgtool:
pkgtool:
pkgtool:
pkgtool:
pkgtool:
pkgtool:
pkgtool:
RESTORE LINKS:
INSTALL SCRIPT:
#!/bin/sh 

# arg 1: the new package version
pre_install() {
  /bin/true
}

# arg 1: the new package version
post_install() {
  /bin/true
}

# arg 1: the new package version
# arg 2: the old package version
pre_update() {
  /bin/true
}

# arg 1: the new package version
# arg 2: the old package version
post_update() {
  post_install
}

# arg 1: the old package version
pre_remove() {
  /bin/true
}

# arg 1: the old package version
post_remove() {
  /bin/true
}


operation=$1
shift

$operation $*
FILE LIST:
sbin/pkgtool/change-refs
sbin/pkgtool/check-db-integrity
sbin/pkgtool/check-package
sbin/pkgtool/check-requires
sbin/pkgtool/install-package
sbin/pkgtool/install-pkglist
sbin/pkgtool/make-package
sbin/pkgtool/make-pkglist
sbin/pkgtool/pkginfo
sbin/pkgtool/pkglog
sbin/pkgtool/remove-package
sbin/pkgtool/update-package

Every log-file contains 7 sections. The first section mostly contains information received from the .PKGINFO file:

  1. General information about the package.
PACKAGE NAME: pkgtool
PACKAGE VERSION: 0.0.1
ARCH: rk328x-glibc
DISTRO: radix
DISTRO VERSION: 1.0
GROUP: base
URL: http://www.radix.pro
LICENSE: GPLv2
UNCOMPRESSED SIZE: 144K
TOTAL FILES: 11

Here, the short package description is missing, because there is no need to have one. Then the sections follow containing:

  1. The list and number of packages that depend on the current package.
  2. The list of packages the current package depends on.
  3. The full description of the package.
  4. Symbolic links recovery scenario that was prepared during package creation.
  5. Package installation scenario.
  6. The full list of files installed in the system. Filenames are presented relative to the root directory.

The names of the sections are written in uppercase with mandatory trailing colon (':'):

REFERENCE COUNTER:
REQUIRES:
PACKAGE DESCRIPTION:
RESTORE LINKS:
INSTALL SCRIPT:
FILE LIST:

If a package is removed from the system using the remove-package utility or during the update procedure using the update-package utility, then its log-file is moved to the /var/log/radix/removed_packages directory.

Such database structure is convenient for the user because it makes it easy to search for information about packages or even to check if this or that package is installed – the user simply needs to look for the file with the respective name in the /var/log/radix/packages directory.

Remove Package

Removing a package from the system is possible using the remove-package utility. This utility takes an argument containing either the filename of the archive containing the package or the name of a log-file from the Setup Database.

To remove a package without respect to possible inter-package dependencies disruption, the --skip-refs directive shall be used.

In conclusion, we need to emphasize again that directives allowing to disregard inter-package dependencies shall be used with great care since it can lead to errors in the system functioning.

Of course, while the distributive is being debugged such directives are allowed. But if we talk about a working target system, using the --skip-refs and --skip-requires directives is not recommended; instead follow the guidance of the package management utilities.