Packages are used to keep all aspects of a product in one manageable file. Similar to a .tgz, this one file is a compressed version of all of the files that make up your product.
Packages, however, have additional elements beyond the standard "tar ball" you may have seen before. Packages also include a manifest, and any support files used within the package, such as licenses, scripts, or HTML web pages. Since all of this information is packaged together, the package installer can extract the required information to install your product into the package file system (which manages all software on your system).
What follows is a description of the packager utility (or packager), and how you can use it to package your own product into a set of packages that can either be distributed or made available on a repository (a set of packages at a particular URL location).
The QNX package manifest (QPM) file describes all aspects of a package - including a list of the files within the package, the locations where each of the files will be installed, a description of the package, any requirements that the package has, other packages on which the package depends (or those that conflict), and any scripts to be executed when the package is installed or uninstalled. This information (and more) is described via the QPM file.
The QPM file, which follows XML formatting guidelines, can be edited using any standard ASCII text editor. However, we recommend against editing the file by hand. Instead, any changes to the package should be done as part of a re-packaging procedure, using packager. This not only ensures that the data is entered correctly, it also updates other information automatically, such as the date that the package was created, the release number, and the size of your product. Packager also gathers and compresses your files into the required structure.
The packager utility was created to generate fully package installer-compliant packages, with all the necessary tags to comply with the current version, and future versions, of the package installer. It can also be used to re-package existing packages to reflect updates in the program or to create patches from a previous package to a new one.
When you start using packager, keep in mind that there are a large number of tags that make up a QPM file, so you should expect a correspondingly large number of questions in order to make a package that is accurate and detailed. Packager was designed as a command-line utility so that once you have answered these questions, it's easy to pass your previous package as a command-line argument and bypass the questions next time!
If you do a use packager command, you will see that there are a large number of options that can be passed. Don't let this daunt you - when you're finished creating a fully automated packaging process (by the end of Part 2 of this article), you will only have to type packager to build a brand-new package at any time.
First, defaults are your best friend. There are some useful defaults set up in the packager utility that you should expect to deal with. When you're asked a question, packager will appear with a set of parentheses surrounding the default value, like this: (default). By hitting "enter" without entering any text, this default value will be used. (Use Ctrl B to go to the previous question if you make a mistake).
Packager gives you detailed comments about each question (by default). Once you're used to the questions it asks, you can pass the argument -v1 (advanced mode) or -v2 (expert mode), to reduce the amount of documentation shown onscreen for each question.
The default packaging method used by packager expects that you pass it a directory as the only argument. This directory holds all of the files that you would like to have packaged, laid out in a directory structure that reflects the root (/) file system, when the files are installed. Public software should all be installed under the /opt directory. For example:
$ packager basedir/
With basedir holding the following files:
basedir/
opt/
photon/
bin/
my_executable1
my_executable2
share/
myapp/
config_file1
config_file2
Packager will take all files under basedir/ and put them into your package by creating a QPM file. The file lists all answers to the questions, along with a list of each of the files under the basedir/ directory. It will also create a QNX package file (QPK) which is a tarred/gzipped archive of the manifest file (renamed MANIFEST), and each of the files in the basedir/ directory. By default, packager will also create a QNX repository file (QPR) which is itself a tarred/gzipped archive of the QPM and QPK files. You can view the contents of a QPK or a QPR file by typing: tar -ztf filename.qpk.
When packager is sorting your files, it will automatically detect any executable files made to execute on a particular processor (host) and create a separate package into which these files will be placed; links are also made between these processor-specific and processor-independent packages automatically. So, when packager is working, you may see two QPM files and two QPK files created. The QPR file will be an archive of all four files. In fact, if you have files to be hosted on other processors, a separate QPR file will be created for each processor.
In addition, if you have executable files that generate files targeting different processors or header (.h) files, packager will start to create development packages with a -dev- qualifier in the file name. There is no limit to how many packages packager may create as it works, although you can expect to see two for a standard package, and perhaps six for an advanced package.
The first step is to get your files into a directory structure reflective of the file system itself. This can be done in a number of different ways:
1. Create the directory structure and copy your files into the correct locations.
Create an empty directory to hold your files, something like "basedir" or "myappdir." This directory then becomes reflective of your root file system (/). So within that directory, create directories like /opt or /usr. Note that if you create an /opt directory with a standard directory inside it (like /opt/bin), then packager will later ask if you wish to automatically union /opt/bin to /usr/bin. You should answer "yes." Similarly, if you make a directory structure that is uncommon (like /mydir), you will be notified by packager that you have an "Uncommon Directory Path (/mydir)," indicating that you should move your program to a standard location. In response, tell packager to exit, and make changes to your paths. Once the directory structure is in place, copy each of your files (for this product) into their respective locations within this basedir directory.
2. Take an existing .tgz, .tar.gz, or .tar.F file and decompress it into a new directory.
Again, create an empty directory to hold your files, something like "basedir" or "myappdir," which then becomes reflective of your root filesystem (/). Copy your compressed file into this directory. Decompress the file or files, as required, to make the directory structure required for your program. Once the files are in place, remove the compressed file.
(3) Take an existing package and have packager unzip it into a given directory.
As above, create an empty directory to hold your files. Run packager with a -z option, followed by the name of the package (either a QPK or a QPR) and the basedir directory, as shown here:
packager -z mypackage-1.0-x86-me.qpk basedir/
If you're using a QPR file, all files within it will be decompressed into the basedir. If you're using a QPK file, you may wish to decompress a number of the files into the same basedir. To do this, just enter the first part of the QPK files that are common to all desired QPK files in your current directory, as shown here:
packager -z mypackage-1.0- basedir/
Packager will extract the files from your package(s) and place them into the basedir as it would if they were installed on your root file system (/).
You will find that packager asks many questions. Here are some helpful hints for answering what you'll be asked.
Click here for a list of the questions that packager may ask you. Of course, some questions, such as the following, are more important than others.
product identifier This is the name used by other packages to reference your package. It should be a short, simple identifier (no more than 13 characters), and be made up only of alphanumeric characters, dashes, or underscores. Ideally it should be a single lower-case word that is a short-form of your product name.
vendor identifier Identifies your company or yourself so that your packages don't conflict with anyone else's (that may have identical product identifiers). Together, the vendor identifier and product identifier describe your product uniquely within the QNX community.
release version Indicates the version number of your product, e.g. 1.0, 2.3.4, 14.2A, and so on. If you're porting software from another source, you should use the software's original version number and simply change the release build number each time you remake the software. By doing this, the version number accurately reflects the source program's version.
release build An integer that indicates the build number of your package. This number should be incremented by one each time your software is changed, unless the version number changes. The release build number should be reset to "1" whenever the version changes.
package release number An integer indicating the release number of your package. This number should be incremented by one each time your software is re-packaged, even if the software hasn't changed. This number should be reset to "1" whenever the version or build number changes.
content topic The topic you select for your package from the list supported by the package installer. The packager shows all available topics and allows you to select more and more specific topics until you have fully described the location at which you would like your package to appear in your repository. Packager allows you to search by keyword to quickly find an appropriate topic. The more specific you are in choosing the topic, the more organized your repository will be later, especially if the repository includes many packages.
product name The product's name is obviously very important in telling the user exactly what they are about to download/install. This is the name that will appear in the package installer in the list of packages in the repository, and should be a descriptive, capitalized name such as: "Packager" or "Package Installer." The package installer will automatically tack "for x86," or similar qualifiers on the end when the package is displayed, so you don't have to enter processor or version references in this field.
product description long This field defines the complete textual description that will appear in the package installer when a user clicks on your package (in the list of available packages). It should consist of one or two paragraphs describing your product and its main features. Proper sentence structure and grammar go a long way toward making your product look professional. And, a good description can also help users find your software more easily, as this field is searched by the package installer when the find function is used.
license URL To have a license show up when your software is installed, you can specify the URL to the license file as your response to this question. By entering a file name that is on your local hard drive, you include a copy of the license file in your package (QPR) without having to put the file into your basedir directory structure. When you enter a local file in response to this question, the file is copied into the package.repdata directory which corresponds to your package. Packager will display a message stating that the file has been copied into the package. After you answer this question, you should confirm that this message is shown or your package may not be able to be installed.
After the packager has asked you the basic questions, it will sort your software into the packages needed to properly install your product. Once this sorting is complete, you will be asked another set of questions - no, you're not off the hook yet!
Different questions apply to different packages. So for each package, packager will first display the name of the package, and then ask these questions. They are divided into three main sections: scripts, dependencies, and the QNX Photon® launch menu.
Scripts can be executed at eight different times during the life of a package:
Pre-install Post-install (before/after the installation takes place)
Pre-use Post-use (before/after the activation of a package)
Pre-unuse Post-unuse (before/after the deactivation of a package)
Pre-uninstall Post-uninstall (before/after the removal of a package)
As well as choosing when the script will execute, you may decide to execute either your own script, or to simply run a program that is available on the file system. Remember that before you activate a package and after you deactivate the package, its files will not be available to execute.
Your package may be dependent upon another package having been installed in order to operate correctly. Such requirements are specified by entering dependencies.
Before entering any dependencies, take note of the following: Packager will automatically insert tags into your package that detail any shared object libraries which are either contained in your package, or are required by your package (it does an objdump of all executable files in your package to determine any libraries that are required). Package installer automatically turns any required shared objects into dependencies that must be satisfied in order for your package to be installed. So, you need not add dependencies upon packages that provide shared objects that your product inherently requires (i.e. if you're packaging a QNX Photon microGUI application, an automatic dependency upon libph.so.2 will be placed within your package).
Also, packager automatically puts in dependencies between any development packages that it generates, so you need not add dependencies between these packages either.
For any other situation, where you have a requirement upon another package's presence, you can manually enter a dependency into packager at this time. To enter a dependency, simply enter the name of any file on your local system that is required for proper operation of your package. The packager will automatically determine the package from which the file originated and add the dependency upon that package into your new package.
You may specify any number of launch menu items to appear in QNX Photon when this package is active. For each, answer the questions as follows:
Launch menu position The name that will appear on the launch menu (should not exceed 25 characters).
vendor identifier The hierarchy under which the entry will appear. You can enter any values here, but they must all be under the Applications/ heading. You should stick to the standard ones shown in the launch menu (i.e. Applications/Editors, Applications/Utilities, Applications/Internet, Applications/MultiMedia, Applications/Development, or Applications/Games).
Execution command The command that will be executed when the operator clicks on the menu item. This must be a fully qualified command (i.e. /usr/photon/bin/ped -r readme.txt).
As packager works to create your packages, a number of messages will be shown onscreen, starting with a welcome message indicating the process that is about to be undertaken. If any problems are detected with the command-line options you have entered, an error message may be displayed, followed by the standard usage message for packager. If you see this usage message, check above it for possible indications of what went wrong. If no error is shown, confirm that the options entered are valid. Here are some of the warnings and errors you may encounter:
Warning: /usr/local should not be packaged using this utility.
The /usr/local directory is considered to be a special directory that can contain files from a number of different software packages. The packager utility is intended for a single product which should install its files into /opt/bin, /usr/share, or similar directories. You should copy the required files into a different directory before packaging commences.
Warning: There are files at the root, where only directories should reside.
All of your files should be installed into the correct directory. Packager will detect if you have files destined to be installed at the root directory (/), where only sub- directories should ever appear (with the exception of system files). You should relocate your files to appear within the correct sub-directories.
Warning: Uncommon directory paths have been detected.
There is a set of standard directories that are recommended by the QNX realtime platform. Your files should all appear within this directory structure. If this message appears, you should relocate your files into one of the following standard root directories: /boot, /dev, /etc, /opt, /usr, or /var.
Error: This is a required field.
Some questions that packager asks must be answered in full; you may not simply accept the default value (or no value) for the question asked. The question will be asked again until it is answered properly.
Error: This value cannot exceed n characters.
There is a maximum length to this field which must not be exceeded.
Error: QNX software can only be released by QNX Software Systems Ltd.
If you try to answer questions as if you were packaging QNX software, this message may appear. Answer truthfully, with your own company or personal information.
Once your package is created, try to install it using the package installer. Just type the following command: pkg-installer -u mypackage.qpr. The package installer will show your software as a new package that is ready to be installed. When you install it, no errors or warnings should appear. You can then verify on the command line that your files are actually installed to the locations you've specified.
If your package does not show up in package installer, try adding a -v (verbose) option to your command-line options. Errors detected in the package will be shown as a stderr message. You might also select "All Packages" from the SHOW combo-box. If your package is shown as being installed already, you may have chosen the same product identifier or vendor identifier as another package that is already installed.
If there appear to be missing files, or something simply doesn't seem to be correctly installed, you can check the manifest for the packages you've installed. As long as you are using the default user repository to install your package, the files are located in /pkgs/repository. Under that directory, find your vendor directory, then the package identifier directory, and finally the version directory. For example, if packager had its own package, it would be found at: /pkgs/repository/qnx/packager/core-1.1/.
Inside this directory, you will find a file called manifest. This is a copy of the QPM file for this package and includes all processor-independent information about the package. If you have processor-specific components to your package, look for a sub- directory with the same name as the processor (i.e. x86/), which will contain another manifest for the processor-specific portion of your package.
To understand the manifest, there is a detailed document showing the purpose of each tag, which can be found here. Take care if you choose to edit the file, as you must follow proper XML formatting rules. Be aware that changing some values may make your package unusable to the point where the package installer will not understand the package well enough even to remove it! This information is for reference purpose only, and you should remember that using the packager utility is the only correct way to make your packages.
In the next installment of this article, we'll discuss the use of QNX package generation (QPG) files and, specifically, how to use them to automate your packaging process. See you next time!