Skip Navigation Links | |
Exit Print View | |
Packaging and Delivering Software With the Image Packaging System in Oracle Solaris 11.1 Oracle Solaris 11.1 Information Library |
1. IPS Design Goals, Concepts, and Terminology
2. Packaging Software With IPS
Creating and Publishing a Package
Add Necessary Metadata to the Generated Manifest
Add Any Facets or Actuators That Are Needed
Publish to a Local File Repository
Converting SVR4 Packages To IPS Packages
Generate an IPS Package Manifest from a SVR4 Package
Other Package Conversion Considerations
3. Installing, Removing, and Updating Software Packages
4. Specifying Package Dependencies
6. Modifying Package Manifests Programmatically
7. Automating System Change as Part of Package Installation
8. Advanced Topics For Package Updating
Packaging software with IPS is usually straightforward due to the amount of automation that is provided. Automation avoids repetitive tedium, which seems to be the principal cause of most packaging bugs.
Publication in IPS consists of the following steps:
Generate a package manifest.
Add necessary metadata to the generated manifest.
Evaluate dependencies.
Add any facets or actuators that are needed.
Verify the package.
Publish the package.
Test the package.
The easiest way to get started is to organize the component files into the same directory structure that you want on the installed system.
Two ways to do this are:
If the software you want to package is already in a tarball, unpack the tarball into a subdirectory. For many open source software packages that use the autoconf utility, setting the DESTDIR environment variable to point to the desired prototype area accomplishes this. The autoconf utility is available in the pkg:/developer/build/autoconf package.
Use the install target in a Makefile.
Suppose your software consists of a binary, a library, and a man page, and you want to install this software in a directory under /opt named mysoftware. Create a directory in your build area under which your software appears in this layout. In the following example, this directory is named proto:
proto/opt/mysoftware/lib/mylib.so.1 proto/opt/mysoftware/bin/mycmd proto/opt/mysoftware/man/man1/mycmd.1
Use the pkgsend generate command to generate a manifest for this proto area. Pipe the output package manifest through pkgfmt to make the manifest more readable. See the pkgsend(1) and pkgfmt(1) man pages for more information.
In the following example, the proto directory is in the current working directory:
$ pkgsend generate proto | pkgfmt > mypkg.p5m.1
The output mypkg.p5m.1 file contains the following lines:
dir path=opt owner=root group=bin mode=0755 dir path=opt/mysoftware owner=root group=bin mode=0755 dir path=opt/mysoftware/bin owner=root group=bin mode=0755 file opt/mysoftware/bin/mycmd path=opt/mysoftware/bin/mycmd owner=root \ group=bin mode=0644 dir path=opt/mysoftware/lib owner=root group=bin mode=0755 file opt/mysoftware/lib/mylib.so.1 path=opt/mysoftware/lib/mylib.so.1 \ owner=root group=bin mode=0644 dir path=opt/mysoftware/man owner=root group=bin mode=0755 dir path=opt/mysoftware/man/man1 owner=root group=bin mode=0755 file opt/mysoftware/man/man1/mycmd.1 path=opt/mysoftware/man/man1/mycmd.1 \ owner=root group=bin mode=0644
The path of the files to be packaged appears twice in the file action:
The first word after the word file describes the location of the file in the proto area.
The path in the path= attribute specifies the location where the file is to be installed.
This double entry enables you to modify the installation location without modifying the proto area. This capability can save significant time, for example if you repackage software that was designed for installation on a different operating system.
Notice that pkgsend generate has applied default values for directory owners and groups. In the case of /opt, the defaults are not correct. Delete that directory because it is delivered by other packages already on the system, and pkg(1) will not install the package if the attributes of /opt conflict with those already on the system. Add Necessary Metadata to the Generated Manifest below shows a programmatic way to delete the unwanted directory.
If a file name contains an equal symbol (=), double quotation mark ("), or space character, pkgsend generates a hash attribute in the manifest, as shown in the following example:
$ mkdir -p proto/opt $ touch proto/opt/my\ file1 $ touch proto/opt/"my file2" $ touch proto/opt/my=file3 $ touch proto/opt/'my"file4' $ pkgsend generate proto dir group=bin mode=0755 owner=root path=opt file group=bin hash=opt/my=file3 mode=0644 owner=root path=opt/my=file3 file group=bin hash="opt/my file2" mode=0644 owner=root path="opt/my file2" file group=bin hash='opt/my"file4' mode=0644 owner=root path='opt/my"file4' file group=bin hash="opt/my file1" mode=0644 owner=root path="opt/my file1"
When the package is published (see Publish the Package), the value of the hash attribute becomes the SHA-1 hash of the file contents, as noted in File Actions.
A package should define the following metadata. See Set Actions for more information about these values and how to set these values.
The name and version of the package as described in Package Identifier: FMRI. A description of Oracle Solaris versioning can be found in Oracle Solaris Package Versioning.
A description of the contents of the package
A one-line synopsis of the description.
Each architectures for which this package is suitable. If the entire package can be installed on any architecture, variant.arch can be omitted. Producing packages that have different components for different architectures is discussed in Chapter 5, Allowing Variations.
A grouping scheme used by the packagemanager(1) GUI. The supported values are shown in Appendix A, Classifying Packages. The example in this section specifies an arbitrary classification.
This example also adds a link action to /usr/share/man/index.d that points to the man directory under mysoftware. This link is discussed further in Add Any Facets or Actuators That Are Needed.
Rather than modifying the generated manifest directly, use pkgmogrify(1) to edit the generated manifest. See Chapter 6, Modifying Package Manifests Programmatically for a full description of using pkgmogrify to modify package manifests.
Create the following pkgmogrify input file to specify the changes to be made to the manifest. Name this file mypkg.mog. In this example, a macro is used to define the architecture, and regular expression matching is used to delete the /opt directory from the manifest.
set name=pkg.fmri value=mypkg@1.0,5.11-0 set name=pkg.summary value="This is an example package" set name=pkg.description value="This is a full description of \ all the interesting attributes of this example package." set name=variant.arch value=$(ARCH) set name=info.classification \ value=org.opensolaris.category.2008:Applications/Accessories link path=usr/share/man/index.d/mysoftware target=opt/mysoftware/man <transform dir path=opt$->drop>
Run pkgmogrify on the mypkg.p5m.1 manifest with the mypkg.mog changes:
$ pkgmogrify -DARCH=`uname -p` mypkg.p5m.1 mypkg.mog | pkgfmt > mypkg.p5m.2
The output mypkg.p5m.2 file has the following content. The dir action for path=opt has been removed, and the metadata and link contents from mypkg.mog have been added to the original mypkg.p5m.1 contents.
set name=pkg.fmri value=mypkg@1.0,5.11-0 set name=pkg.summary value="This is an example package" set name=pkg.description \ value="This is a full description of all the interesting attributes of this example package." set name=info.classification \ value=org.opensolaris.category.2008:Applications/Accessories set name=variant.arch value=i386 dir path=opt/mysoftware owner=root group=bin mode=0755 dir path=opt/mysoftware/bin owner=root group=bin mode=0755 file opt/mysoftware/bin/mycmd path=opt/mysoftware/bin/mycmd owner=root \ group=bin mode=0644 dir path=opt/mysoftware/lib owner=root group=bin mode=0755 file opt/mysoftware/lib/mylib.so.1 path=opt/mysoftware/lib/mylib.so.1 \ owner=root group=bin mode=0644 dir path=opt/mysoftware/man owner=root group=bin mode=0755 dir path=opt/mysoftware/man/man1 owner=root group=bin mode=0755 file opt/mysoftware/man/man1/mycmd.1 path=opt/mysoftware/man/man1/mycmd.1 \ owner=root group=bin mode=0644 link path=usr/share/man/index.d/mysoftware target=opt/mysoftware/man
Use the pkgdepend(1) command to automatically generate dependencies for the package. The generated depend actions are defined in Depend Actions and discussed further in Chapter 4, Specifying Package Dependencies.
Dependency generation is composed of two separate steps:
Dependency generation. Determine the files on which the software depends. Use the pkgdepend generate command.
Dependency resolution. Determine the packages that contain those files on which the software depends. Use the pkgdepend resolve command.
In the following command, the -m option causes pkgdepend to include the entire manifest in its output. The -d option passes the proto directory to the command.
$ pkgdepend generate -md proto mypkg.p5m.2 | pkgfmt > mypkg.p5m.3
The output mypkg.p5m.3 file has the following content. The pkgdepend utility added notations about a dependency on libc.so.1 by both mylib.so.1 and mycmd. The internal dependency between mycmd and mylib.so.1 is silently omitted.
set name=pkg.fmri value=mypkg@1.0,5.11-0 set name=pkg.summary value="This is an example package" set name=pkg.description \ value="This is a full description of all the interesting attributes of this example package." set name=info.classification \ value=org.opensolaris.category.2008:Applications/Accessories set name=variant.arch value=i386 dir path=opt/mysoftware owner=root group=bin mode=0755 dir path=opt/mysoftware/bin owner=root group=bin mode=0755 file opt/mysoftware/bin/mycmd path=opt/mysoftware/bin/mycmd owner=root \ group=bin mode=0644 dir path=opt/mysoftware/lib owner=root group=bin mode=0755 file opt/mysoftware/lib/mylib.so.1 path=opt/mysoftware/lib/mylib.so.1 \ owner=root group=bin mode=0644 dir path=opt/mysoftware/man owner=root group=bin mode=0755 dir path=opt/mysoftware/man/man1 owner=root group=bin mode=0755 file opt/mysoftware/man/man1/mycmd.1 path=opt/mysoftware/man/man1/mycmd.1 \ owner=root group=bin mode=0644 link path=usr/share/man/index.d/mysoftware target=opt/mysoftware/man depend fmri=__TBD pkg.debug.depend.file=libc.so.1 \ pkg.debug.depend.reason=opt/mysoftware/bin/mycmd \ pkg.debug.depend.type=elf type=require pkg.debug.depend.path=lib \ pkg.debug.depend.path=opt/mysoftware/lib pkg.debug.depend.path=usr/lib depend fmri=__TBD pkg.debug.depend.file=libc.so.1 \ pkg.debug.depend.reason=opt/mysoftware/lib/mylib.so.1 \ pkg.debug.depend.type=elf type=require pkg.debug.depend.path=lib \ pkg.debug.depend.path=usr/lib
To resolve dependencies, pkgdepend examines the packages currently installed in the image used for building the software. By default, pkgdepend puts its output in mypkg.p5m.3.res. This step takes a while to run since it loads a large amount of information about the system on which it is running. The pkgdepend utility can resolve many packages at once if you want to amortize this time over all packages. Running pkgdepend on one package at a time is not time efficient.
$ pkgdepend resolve -m mypkg.p5m.3
When this completes, the output mypkg.p5m.3.res file contains the following content. The pkgdepend utility has converted the notation about the file dependency on libc.so.1 to a package dependency on pkg:/system/library, which delivers that file.
set name=pkg.fmri value=mypkg@1.0,5.11-0 set name=pkg.summary value="This is an example package" set name=pkg.description \ value="This is a full description of all the interesting attributes of this example package." set name=info.classification \ value=org.opensolaris.category.2008:Applications/Accessories set name=variant.arch value=i386 dir path=opt/mysoftware owner=root group=bin mode=0755 dir path=opt/mysoftware/bin owner=root group=bin mode=0755 file opt/mysoftware/bin/mycmd path=opt/mysoftware/bin/mycmd owner=root \ group=bin mode=0644 dir path=opt/mysoftware/lib owner=root group=bin mode=0755 file opt/mysoftware/lib/mylib.so.1 path=opt/mysoftware/lib/mylib.so.1 \ owner=root group=bin mode=0644 dir path=opt/mysoftware/man owner=root group=bin mode=0755 dir path=opt/mysoftware/man/man1 owner=root group=bin mode=0755 file opt/mysoftware/man/man1/mycmd.1 path=opt/mysoftware/man/man1/mycmd.1 \ owner=root group=bin mode=0644 link path=usr/share/man/index.d/mysoftware target=opt/mysoftware/man depend fmri=pkg:/system/library@0.5.11-0.175.1.0.0.21.0 type=require
You should use pkgdepend to generate dependencies, rather than declaring depend actions manually. Manual dependencies can become incorrect or unnecessary as the package contents change over time. For example, when a file that an application depends on gets moved to a different package, any manually declared dependencies on the previous package would then be incorrect for that dependency.
Some manually declared dependencies might be necessary if pkgdepend is unable to determine dependencies completely. In such a case, you should add explanatory comments to the manifest.
Facets and actuators are discussed in more detail in Chapter 5, Allowing Variations and Chapter 7, Automating System Change as Part of Package Installation. A facet denotes an action that is not required but can be optionally installed. An actuator specifies system changes that must occur when the associated action is installed, updated, or removed
This example package delivers a man page in opt/mysoftware/man/man1. This section shows how to add a facet to indicate that man pages are optional. The user could choose to install all of the package except the man page. (If the user sets the facet to false, no man pages are installed from any package if their file actions are tagged with that facet.)
To include the man page in the index, the svc:/application/man-index:default SMF service must be restarted when the package is installed. This section shows how to add the restart_fmri actuator to perform that task. The man-index service looks in /usr/share/man/index.d for symbolic links to directories that contain man pages, adding the target of each link to the list of directories it scans. To include the man page in the index, this example package includes a link from /usr/share/man/index.d/mysoftware to /opt/mysoftware/man. Including this link and this actuator is a good example of the self-assembly discussed in Software Self-Assembly and used throughout the packaging of the Oracle Solaris OS.
A set of pkgmogrify transforms that you can use are available in /usr/share/pkg/transforms. These transforms are used to package the Oracle Solaris OS, and are discussed in more detail in Chapter 6, Modifying Package Manifests Programmatically.
The file /usr/share/pkg/transforms/documentation contains transforms similar to the transforms needed in this example to set the man page facet and restart the man-index service. Since this example delivers the man page to /opt, the documentation transforms must be modified as shown below. These modified transforms include the regular expression opt/.+/man(/.+)? which matches all paths beneath opt that contain a man subdirectory. Save the following modified transforms to /tmp/doc-transform:
<transform dir file link hardlink path=opt/.+/man(/.+)? -> \ default facet.doc.man true> <transform file path=opt/.+/man(/.+)? -> \ add restart_fmri svc:/application/man-index:default>
Use the following command to apply these transforms to the manifest:
$ pkgmogrify mypkg.p5m.3.res /tmp/doc-transform | pkgfmt > mypkg.p5m.4.res
The input mypkg.p5m.3.res manifest contains the following three man-page-related actions:
dir path=opt/mysoftware/man owner=root group=bin mode=0755 dir path=opt/mysoftware/man/man1 owner=root group=bin mode=0755 file opt/mysoftware/man/man1/mycmd.1 path=opt/mysoftware/man/man1/mycmd.1 \ owner=root group=bin mode=0644
After the transforms are applied, the output mypkg.p5m.4.res manifest contains the following modified actions:
dir path=opt/mysoftware/man owner=root group=bin mode=0755 facet.doc.man=true dir path=opt/mysoftware/man/man1 owner=root group=bin mode=0755 \ facet.doc.man=true file opt/mysoftware/man/man1/mycmd.1 path=opt/mysoftware/man/man1/mycmd.1 \ owner=root group=bin mode=0644 \ restart_fmri=svc:/application/man-index:default facet.doc.man=true
For efficiency, these transforms could have been added when metadata was originally added, before evaluating dependencies.
The last step before publication is to run pkglint(1) on the manifest to find errors that can be identified before publication and testing. Some of the errors that pkglint can find would also be found either at publication time or when a user attempts to install the package, but of course you want to identify errors as early as possible in the package authoring process.
Examples of errors that pkglint reports include:
Delivering files already owned by another package.
Difference in metadata for shared, reference-counted actions such as directories. An example of this error is discussed at the end of Generate a Package Manifest.
You can run pkglint in one of the following modes:
Directly on the package manifest. This mode is usually sufficient to quickly check the validity of your manifests.
On the package manifest, also referencing a package repository. Use this mode at least once before publication to a repository.
By referencing a repository, pkglint can perform additional checks to ensure that the package interacts well with other packages in that repository.
Use the pkglint -L command to show the full list of checks that pkglint performs. Detailed information about how to enable, disable, and bypass particular checks is given in the pkglint(1) man page. The man page also details how to extend pkglint to run additional checks.
The following output shows problems with the example manifest:
$ pkglint mypkg.p5m.4.res Lint engine setup... Starting lint run... WARNING pkglint.action005.1 obsolete dependency check skipped: unable to find dependency pkg:/system/library@0.5.11-0.175.1.0.0.21.0 for pkg:/mypkg@1.0,5.11-0
This warning is acceptable for this example. The pkglint.action005.1 warning says that pkglint could not find a package called pkg:/system/library@0.5.11-0.175.1.0.0.21.0, on which this example package depends. The dependency package is in a package repository and could not be found since pkglint was called with only the manifest file as an argument.
In the following command, the -r option references a repository that contains the dependency package. The -c option specifies a local directory used for caching package metadata from the lint and reference repositories:
$ pkglint -c ./solaris-reference -r http://pkg.oracle.com/solaris/release mypkg.p5m.4.res
IPS provides three different ways to deliver a package:
Publish to a local file-based repository.
Publish to a remote HTTP-based repository.
Convert to a .p5p package archive.
Generally, publishing to a file-based repository is sufficient to test a package.
If the package must be transferred to other machines that cannot access the package repositories, converting one or more packages to a package archive can be convenient.
The package can also be published directly to an HTTP repository, hosted on a machine with a read/write instance of the svc:/application/pkg/server service, which in turn runs pkg.depotd(1M).
Publishing to an HTTP repository is not generally recommended since there are no authorization or authentication checks on the incoming package when publishing over HTTP. Publishing to HTTP repositories can be convenient on secure networks or when testing the same package across several machines if NFS or SMB access to the file repository is not possible.
Installing packages over HTTP or HTTPS is fine.
Use the pkgrepo(1) command to create and manage repositories. Choose a location on your system, create a repository, then set the default publisher for that repository:
$ pkgrepo create my-repository $ pkgrepo -s my-repository set publisher/prefix=mypublisher $ ls my-repository pkg5.repository
Use the pkgsend(1) command to publish the example package, and then use pkgrepo to examine the repository:
$ pkgsend -s my-repository publish -d proto mypkg.p5m.4.res pkg://mypublisher/mypkg@1.0,5.11-0:20120331T034425Z PUBLISHED $ pkgrepo -s my-repository info PUBLISHER PACKAGES STATUS UPDATED mypublisher 1 online 2012-03-31T03:44:25.235964Z
The file repository can then be served over HTTP or HTTPS using pkg.depotd if required.
Package archives enable you to distribute groups of packages in a single file. Use the pkgrecv(1) command to create package archives from package repositories, or to create package repositories from package archives.
Package archives can be easily downloaded from an existing web site, copied to a USB key, or burned to a DVD for installation in cases where a package repository is not available.
The following command creates a package archive from the simple repository created in the previous section:
$ pkgrecv -s my-repository -a -d myarchive.p5p mypkg Retrieving packages for publisher mypublisher ... Retrieving and evaluating 1 package(s)... DOWNLOAD PKGS FILES XFER (MB) SPEED Completed 1/1 3/3 0.7/0.7 17.9k/s ARCHIVE FILES STORE (MB) myarchive.p5p 14/14 0.7/0.7
Use the pkgrepo command to list the newest available packages from a repository or archive:
$ pkgrepo -s my-repository list '*@latest' PUBLISHER NAME O VERSION mypublisher mypkg 1.0,5.11-0:20120331T034425Z $ pkgrepo -s myarchive.p5p list '*@latest' PUBLISHER NAME O VERSION mypublisher mypkg 1.0,5.11-0:20120331T034425Z
This output can be useful for constructing scripts to create archives with the latest versions of all packages from a given repository.
Temporary repositories or package archives can be provided to pkg install and other pkg operations by using the -g option. Such temporary repositories and archives cannot be used on systems with child or parent images (for example, systems with non-global zones) since the system repository does not get temporarily configured with that publisher information. Non-global zones have a child/parent relationship with the global zone. Package archives can be set as sources of local publishers in non-global zones, however.
The final step in package development is to test whether the published package has been packaged properly.
To test installation without requiring root privilege, assign the test user the Software Installation profile. Use the -P option of the usermod command to assign the test user the Software Installation profile.
Note - If this image has child images (non-global zones) installed, you cannot use the -g option with the pkg install command to test installation of this package. You must configure the my-repository repository in the image.
Add the publisher in the my-repository repository to the configured publishers in this image:
$ pfexec pkg set-publisher -p my-repository pkg set-publisher: Added publisher(s): mypublisher
You can use the pkg install -nv command to see what the install command will do without making any changes. The following command actually installs the package:
$ pfexec pkg install mypkg Packages to install: 1 Create boot environment: No Create backup boot environment: No Services to change: 1 DOWNLOAD PKGS FILES XFER (MB) SPEED Completed 1/1 3/3 0.0/0.0 0B/s PHASE ITEMS Installing new actions 15/15 Updating package state database Done Updating image state Done Creating fast lookup database Done Reading search index Done Updating search index 1/1
Examine the software that was delivered on the system:
$ find /opt/mysoftware/ /opt/mysoftware/ /opt/mysoftware/bin /opt/mysoftware/bin/mycmd /opt/mysoftware/lib /opt/mysoftware/lib/mylib.so.1 /opt/mysoftware/man /opt/mysoftware/man/man-index /opt/mysoftware/man/man-index/term.doc /opt/mysoftware/man/man-index/.index-cache /opt/mysoftware/man/man-index/term.dic /opt/mysoftware/man/man-index/term.req /opt/mysoftware/man/man-index/term.pos /opt/mysoftware/man/man1 /opt/mysoftware/man/man1/mycmd.1
In addition to the binaries and man page, the system has also generated the man page indexes as a result of the actuator restarting the man-index service.
The pkg info command shows the metadata that was added to the package:
$ pkg info mypkg Name: mypkg Summary: This is an example package Description: This is a full description of all the interesting attributes of this example package. Category: Applications/Accessories State: Installed Publisher: mypublisher Version: 1.0 Build Release: 5.11 Branch: 0 Packaging Date: March 31, 2012 03:44:25 AM Size: 0.00 B FMRI: pkg://mypublisher/mypkg@1.0,5.11-0:20120331T034425Z
The pkg search command returns hits when querying for files that are delivered by mypkg:
$ pkg search -l mycmd.1 INDEX ACTION VALUE PACKAGE basename file opt/mysoftware/man/man1/mycmd.1 pkg:/mypkg@1.0-0