Middleware, development tools, realtime operating system
software and services for superior embedded design
 
 
Home
QNX Community Resources
Technical Articles
View Article - Packaging Your QNX®-based Product - Part 2

 

Packaging Your QNX®-based Product - Part 2

by Jerry Chappell, QNX Software Systems Ltd.

Part one of this article dealt with the 'packager' utility and explained how to create an initial package of your product. Now that you've mastered this process, let's hope you never have to do it again! In this part, you'll learn how to keep a record of the answers you entered, and how to automate the packaging process so that when you change your software in the future, you can easily re-package it for the world.

Working with a package-generation file

Learning to automate the packaging process means that future changes to your software are easily reflected in a new package without much effort. Here are the steps you'll follow: (1) generate your package; (2) tell packager to generate a QPG (QNX Package Generation) file from your package; (3) edit the QPG to point to your source files on disk; (4) pass the QPG file to packager to regenerate your package; and (5) test the resultant package.

The QPG file is used to provide the command-line options and answers to packager's questions automatically, without user intervention. It is also an XML-style document that can be edited using any standard ASCII text editor. (As was mentioned in part one of this article, we recommend against editing the file by hand.) You can generate an initial QPG file by typing: packager -m mypackage.qpr -x basedir. This command generates a file called xpackage.qpg which contains the manifest values within your package, and a reference to each of the files inside your package. The 'basedir' you specify should be the directory in which your files reside that was created in your initial packaging session.

If you do a 'use packager' command, you may notice that packager will accept either a 'basedir' or a 'qpg_file' as its final argument. If no basedir/ is specified on the command line when packager is started, you can specify a QPG file name for packager to use. If none is specified, it will look for a package.qpg file by default. This allows you to simply type 'packager' on the command line, and packager will rebuild your package according to the options in the local package.qpg file.

A QPG file has five main sections: (1) command-line options; (2) ownership/editor of the package; (3) other QPG files to merge as if they are part of the same QPG; (4) a list of manifest values that packager uses as answers to its questions; and (5) a list of files to be packaged, along with their installation locations. There are many options in a QPG file, so we recommend you read the documentation found here, which details the possible tags in a QPG file, and includes descriptions for each.

Editing your QPG file

Here is a sample QPG file that shows all the elements that might appear in your final generation file. Edit the QPG file carefully since it must follow the XML format, where each opening tag has a corresponding closing tag, and spelling mistakes are not tolerated.

The list of files <QPG:Files> is the most advanced (and flexible) part of the QPG file. Here, you can list the files that are to be packaged from your hard disk (in any location), the location where they should appear once the package is installed, the types of files they are, any special handling for the files, and any files that should be excluded from your package. Packager will automatically determine each of these values, if they are not specified, as long as you provide the source file name (file="") and the install location (install="").

The <QPG:PackageFilter> section is where you place manifest tags that you would like to appear in the final package manifest(s). The easiest way to fill in this section is to let packager create the QPG for you (-x option). However, when you're trying to add a special feature of some kind, e.g. launch menu items, you can edit this section to include all the tags you need. The document showing the possible tags you can enter into your manifests can be found in Part 1 of this article, or it can be found here. Some manifest tags are packager-determined each time packager is run, so specifying those values will have no effect in the generated packages. An empty tag has the effect of suppressing the question for that tag, when running packager in attended mode.

Symbolic links

To create a symbolic link, you simply add another <QPG:Add> line to your QPG file. But this time, you'll change the entries to look like this:

   <QPG:Add file="sym_link_name" install="/opt/bin/" filetype="symlink"
   linkto="file/to/link/to"/>

When packager detects the filetype="symlink" attribute, it recognizes this as a request to put a symbolic link into one of your packages. It will automatically search the other files being packaged, looking for the file to which the symbolic link points. When such a file is found, the symbolic link is placed into the same package as that file.

For example, add a file from your hard drive (libmine.so), but install it as a different file (libmine.so.1). Then create a symbolic link (libmine.so -> libmine.so.1):

   <QPG:Add file="src/lib/libmine.so" install="/opt/lib/libmine.so.1"/>
   <QPG:Add file="libmine.so" install="/opt/lib/" filetype="symlink"
   linkto="libmine.so.1"/>
   

Working with components

Components are simply optional parts of a master product. Parts of your software can be installed with or without other components. For example, think of the set of QNX Photon® microGUI packages, which consists of a core package, a drivers component, a games component, and other components. With a single session of packager, you can generate a product and all of its components.

Any file that you are packaging can be specified to be part of a component. To specify which component a given file is to be part of, simply add a component="" to the <QPG:Add> line:

    <QPG:Add file="bin/my_game" install="opt/bin/" component="games"/>

When packager finds such a line, the file will be put into a new package that has a <QPM:PackageModel> of "games," with the same product identifier as your core package. This new package is independent of the other portions of your product, but its installation location will be alongside the rest of the product it's derived from. For example:

 /pkgs/base/qnx/ph/core-2.0.2/
 /pkgs/base/qnx/ph/games-2.0.2/
 /pkgs/base/qnx/ph/drivers-2.0.2/

Note that packager uses components automatically when it detects that you are attempting to package development files. Let's say you're packaging some header (*.h) files. When packager detects these files being packaged, it will automatically create a component for your package called "dev." This development package will contain all of the files that are part of the software development process. Only executable files and configuration files actually go into the core product itself.

Forcing files into certain packages

If the idea of a development (dev) package is not what you're looking for when you package your software, or if packager puts a file into a package that you had not intended, it's possible to force files to be handled differently. To do this, make further specifications on the <QPG:Add> line to tell packager exactly how to handle the file. Check the QPG file structure document under <QPG:Add> to see the various options that can be specified. Below are some examples below showing common ways to redirect files...

[1] Packager uses the elf header information, along with the file name extension, to determine what type of file is being packaged. After this determination, the file will automatically be assigned a "file type." You can override this and force a change to the file type, by setting the filetype="" attribute. For instance, set filetype="exe" to force a file to be considered executable, and to appear in a processor-specific package that corresponds to a proc="processor" attribute, like this:

    <QPG:Add file="src/include/readme.txt" filetype="exe" proc="x86"
    install="/opt/share/"/>

[2] Packager automatically strips your executable files so that unnecessary debug information is not left in your packaged files. This behavior is sometimes undesirable, so you can turn it off. To change this for the entire session, look in the <QPG:Options> section, where you can set:

    <QPG:FileSorting strip="no"/>

This option turns off all strip operations. As an alternative, you can define that a particular file not be stripped when packaged, as shown here:

    <QPG:Add file="src/bin/my_program" install="/opt/bin/" strip="no"/>

[3] Packager attempts to keep your file's ownership, group, and permissions identical to the original file's settings. To override this behaviour, or to apply specific settings to a file, use the permissions="", user="", and/or group="" attributes, as required:

  <QPG:Add file="src/bin/my_program" install="/opt/bin/" permissions="x+s"/>


Merging other QPG files

Merging additional QPG files is easy. By adding a <QPG:Merge> entry into your QPG file, you can simplify a complicated product into a number of easy-to-manage QPG files. Note that the <QPG:Options> block is only read from the primary QPG file; a merged QPG file's option block will be ignored by packager.

For example, your package.qpg file might show:

    <QPG:Merge file="author.qpg"/>
    <QPG:Merge file="drivers.qpg"/>
    <QPG:Merge file="/home/common/license.qpg"/>

Here's a sample license.qpg you can use to add a license to your package. If you want to use this file, add the <QPG:Merge> line to your main QPG file and be sure to remove the manifest tags which are referenced in the license.qpg file (i.e. QPM:ReleaseCopyright, QPM:ReleaseCopyrightURL, and QPM:LicenseURL) from your main QPG file.

When merging, you may want to apply a set of file attributes to an entire QPG file. For instance, you might change the destination component for the files referenced within the QPG. To do this, use a <QPG:MergeFilter> block with the desired attributes around any number of <QPG:Merge> commands:

 <QPG:MergeFilter component="games">
  <QPG:Merge file="src/games/photon_games.qpg"/>
  <QPG:Merge file="src/games/xphoton_games.qpg"/>
 </QPG:MergeFilter>

SLIB packages

The packager will automatically detect any shared object libraries that you are packaging (.so files) and will insert a corresponding <QPM:ContainsLibrary> tag into the manifest of a package that contains such a library:

 <QPM:ContainsLibrary>libmine.so.1</QPM:ContainsLibrary>

Similarly, if any files you're packaging require a shared object library in order to operate correctly (and such a library is not being packaged alongside this file), then packager will automatically add a corresponding <QPM:RequiresLibrary> tag into the manifest of the package that contains such a requirement. This becomes an automatic dependency when the package is installed. If the required library is not available, the package installer will search all installed software to try to find another package that contains the required library. If such a package is not installed, the package installer will search all known repositories for a package that contains the corresponding <QPM:ProvidesLibrary> tag. To avoid downloading a large package that happens to contain the library you require, and perhaps a lot of unnecessary files as well, the package installer tries to find a small package that contains only a set of libraries. Such a package is called a SLIB package.

When you're packaging your files and a shared object library is detected, it's placed into the package you are making and the library is placed into a SLIB package. This SLIB package has a similar name to your package; the only difference is that the component name has "slib-" added to it. The SLIB package will be made available alongside your product (in the same repository), but it will not show up in the package installer as a new package. It's only there for dependency resolution, in case someone who doesn't already have your product installed, is installing a package that requires one of your libraries.

If you are packaging a QNX Photon microGUI application, for example, your application will already have requirements on phlib.so.1 internally, and packager puts these requirement into your package. The great advantage of SLIB packages is that you don't have a separate dependency that QNX Photon be installed, since this will happen automatically when your package is installed.

Regenerating a package

Each time a package is generated, you must change either its version number, its build number, or its release number. By doing so, you ensure that the package installer will uninstall any previous versions of the program and will recognize your package as being a newer version than the one already installed.

Version numbers for your software can change at any time, usually indicating that a set of features have changed, or that a new architecture is in place. You can set the version number in the QPG under the <QPM:ReleaseVersion> tag. It must be specified in this format: major[.minor[.sub]][letter]

For example: 1.0, 1.0A, 2.13.0, 2.13.1A, 7.15.433B

If you are keeping the same version, you can change the build number (an integer) to indicate that the software has been rebuilt, perhaps after fixing bugs or making minor changes. You can set the build number in your QPG using the <QPM:ReleaseBuild> tag, however, this fixes the build number and forces you to change the QPG each time packager is executed. A better method is to set an option in the <QPG:Generation> block. Add the <QPG:Build> tag and either set the build number to "+" (auto-increment), or to "date" (use today's date as yyyymmddhh). You can also set the build number from the command line using the -b option.

If you're keeping the same version and build number, you can change the release number to indicate that the software hasn't been modified and is simply being repackaged. The release number, an integer, will be automatically incremented by packager each time it's executed, although it will be reset to "1" whenever the build number changes. You can also set the release number in your QPG using the <QPM:PackageReleaseNumber> tag. However, this also fixes the release number and forces you to change the QPG each time packager is executed. You can disable auto- incrementation by setting the block. Add the tag and set the release number to "-" (do not auto-increment). You can also disable auto- incrementation from the command line using the -i option.

Generating a repository

Packager can also be used to generate a repository (a location holding any number of packages) and make it available to anyone who can access that repository. To do this, place all of your desired QPM and QPK files into the repository directory. (If you are working with QPR files, decompress them using tar -zxf my_package.qpr). Change directory into this repository and type the following: packager -r

Packager will ask you a number of questions related to your repository, which you should answer fully and truthfully. The description will be the first thing a user sees when they click on your repository, so it should be as detailed and accurate as possible. Here are the questions that will be asked of you when you create a new repository.

In generating your repository, packager will create the following files:

index - a list of the files in the repository
content.tgz - an archive of all of the QPM files in the repository, which have been stripped of the file listing section (for faster download), including all support files like licenses, scripts, and web pages
repository.qrm - a repository manifest file with details about the number of packages in the repository, a web site, icons, and a description of the repository

Whenever you change the packages that appear in your repository, or when any packages are changed, you should again run packager -r in this directory. Once you have answered the questions you won't have to answer them again, so you may wish to subsequently set packager to run unattended and in expert mode with the following command: packager -ruv2

More options

There are many options that haven't been mentioned in this article. You need not worry about them for the majority of your packaging needs. However, you may wish to look over packager's options (with the use message or via the links throughout this document), to get an idea of the various ways that packager can be used to package your software.

Feel free to post any questions you have in the qdn.public.qnxrtp.porting newsgroup.