Chapter 22. Porting an Application

Porting an application means starting with the original sources and changing them a little bit to make the application compile and install on a specific system. An Aap port recipe offers a simple way to create a port, because all the standard work does not need to be specified.

NOTE: not all features mentioned here fully work. Make sure you test your port recipe before publishing it.

The Port Recipe

The basic idea is that you assign values to a number of predefined variables. Aap will then generate the steps for building and installing the package, using the values you have specified. The presence of the "PORTNAME" variable triggers Aap to handle the recipe as a port recipe. The list of variables is in the next section.

This is thee list of steps performed when executing Aap without arguments, in this order:

dependcheckearly check to see if dependencies can be met; abort without doing anything if something is known to fail
fetchdependhandle dependencies for fetch and checksum
fetchget the required files
checksumcheck if the files have correct checksums
extractdependhandle dependencies for extract and patch
extractunpack archives
patchapply patches
builddependhandle dependencies for configuring and building
configdo pre-building configuration
testdependhandle dependencies for testing
testcheck if building was done properly
packagecreate a binary package
installinstall the binary package
rundependhandle runtime dependencies
installtesttest if the package fully works and was installed properly

For each step a dependency with build commands is added. The purpose of each step is further explained below.

[Note]Meaning of "dependency"

The term "dependency" is ambiguous here. You should be able to guess from the context whether it is used for a dependency of one software package on another package, or a dependency of a target file on a source file.

You can do part of the work by starting Aap with one of the step names. The steps before it will also be executed if necessary. For example, "aap package" will do all the steps necessary to generate the binary package but not install it.

If your port requires non-standard stuff, you can specify your own build commands. You can replace the normal step, prepend something to it and append something to it:

do-XXXreplace the body of a step
pre-XXXdo something before a step
post-XXXdo something after a step


          # delete the directory used for testing
          :del {r}{f} $WRKDIR/testdir


Various variables need to be set to specify properties of the port.

variableused forexample value
PORTNAMEname of the port"foobar"
PORTVERSIONapp version number"3.8alpha"
PORTREVISIONport patchlevel (optional)"32"
CVSMODULESnames of CVS modules to checkout (optional)Exec
MAINTAINER_NAMEmaintainer name (optional)John Doe
MAINTAINERmaintainer e-mail (optional)
PORTCOMMENTshort description get foo into the bar
PORTDESCR long descriptionblah blah blah
IS_INTERACTIVErequires user input (optional)yes or no

Variables that can be used by the port recipe:

variableused for  default value
WRKDIRdirectory all files are extracted in and the building is done in  "work"
DISTDIRdirectory where downloaded distfiles are stored  "distfiles"
PATCHDISTDIRdirectory where downloaded patches are stored  "patches"
PKGDIRdirectory where files are stored before creating a package  "pack"

Variables that may be set by the port recipe, defaults are set only after reading the recipe:

variableused fordefault value
WRKSRCDirectory inside $WRKDIR where the unpacked sources end up. This should be the common top directory in the unpacked archives. When using CVS it is always set to $CVSWRKSRC (also when $WRKSRC was already set).$PORTNAME-$PORTVERSION
CVSWRKSRCDirectory inside $WRKDIR where files obtained with CVS end up.the first entry in $CVSMODULES
PATCHDIRDirectory inside $WRKDIR where patches are to be applied. Can be overruled for a specific patch with a "patchdir" attribute.$WRKSRC
BUILDDIRDirectory inside $WRKDIR where building is to be done.$WRKSRC
TESTDIRDirectory inside $WRKDIR where testing is to be done.$WRKSRC
INSTALLDIRDirectory inside $WRKDIR where $INSTALLCMD is to be done.$WRKSRC
CONFIGURECMDSet to the command used to configure the application. Usually "./configure".nothing
BUILDCMDSet to the command used to build the application. Usually just "make"."aap"
TESTCMDSet to the command used to test the application. Usually "make test"."aap test"
INSTALLCMDSet to the command used to do a fake install of the application."aap install DESTDIR=$PKGDIR"

Dependency Format

Dependencies on other modules are specified with the various DEPEND_ variables. The format of these variables is a list of items.

NOTE: Although you can currently specify dependencies, the code for checking them has not been implemented yet! Thus the user will have to figure it out by himself...

Items are normally white space separated, which means there is an "and" relation between them. Alternatively "|" can be used to indicate an "or" relation.

        DEPENDS = perl python           # require both perl and python
        DEPENDS = perl | python         # require perl or python

Parenthesis can be used to group items. Parenthesis must be used when combining "or" and "and" relations. Example:

        DEPENDS = (foo bar) | foobar    # Either both "foo" and "bar" or "foobar"
        DEPENDS = foo bar | foobar      # Illegal
        DEPENDS = foo (bar | foobar)    # "foo" and either "bar" or "foobar"

When a dependency is not met the first alternative will be installed, thus the order of "or" alternatives is significant.

Each item is in one of these formats:

name-version_revisiona specific version and revision
nameany version
name-regexpa version specified with a regular expression (shell style)
name>=version_revisionany version at or above a specific version and revision
name>version_revisionany version above a specific version and revision
name<=version_revisionany version at or below a specific version and revision
name<version_revisionany version below a specific version and revision name!version_revision any version but a specific version and revision

In the above "_revision" can be left out to ignore the revision number. It actually works as if there is a "*" wildcard at the end of each item.

"name" can contain wildcards. When a part is following it is appended at the position of the wildcard (or at -9 if it comes first).

foo-*matches foo-big, foo-big-1.2 and foo-1.2
foo-*!1.2matches foo-big, foo-big-1.2 and skips foo-1.2

The version specifications can be concatenated, this creates an "and" relation. Example:

foo>=1.2<=1.4versions 1.2 to 1.4 (inclusive)
foo>=1.2!1.8versions 1.2 and above, excluding 1.8
xv>3.10versions above 3.10, accepts xv-3.10a

The "!" can be followed by parenthesis containing a list of specifications. This excludes the versions matching the specifications in the parenthesis. Example:

foo>=1.1!(>=1.3<=1.5)versions 1.1 and higher, but not versions 1.3 to 1.5
foo>=6.1!6.1[a-z]*version 6.1 and later but not 6.1a, 6.1rc3, etc.

When a dependency is not met the newest version that meets the description is used.

For systems that do not allow specifying dependencies like this in a binary pacakge, the specific package version that exists when generating the package is used.

Dependencies For Various Steps

The various variables used to specify dependencies:

DEPEND_FETCHRequired for fetching files. Also for computing checksums.
DEPEND_EXTRACTRequired for unpacking archives.
DEPEND_BUILDRequired for building but not necessarily for running; these are not included in the binary package; items may also appear in DEPEND_RUN.
DEPEND_TESTRequired for testing only; don't include items that are already in DEPEND_RUN.
DEPEND_RUNRequired for running; these will also be included in the generated binary package.

Only the dependencies specified with DEPEND_RUN will end up in the generated binary package. When using a shared library, it is recommended to put a dependency on the developer version (includes header files) in DEPEND_BUILD and a dependency on the library itself in DEPEND_RUN. The result is that when installing binary packages the header files for the library don't need to be installed.

The "CONFLICTS" variable should be set to specify modules with which this one conflits. That means only one of the two packages can be installed in the same location. It should still be possible to install the packages in different locations. The format of CONFLICTS is identical to that of the DEPENDS_ variables.

NOTE: Although you can currently specify dependencies and conflicts, the code for checking them has not been implemented yet! Thus the user will have to figure it out by himself...

Dependencies are automatically installed, unless "AUTODEPEND" is "no". The dependencies are normally satisfied by installing a port. When a satisfying port can not be found a binary package is installed. The ports and packages are first searched for on the local system. When not found the internet is searched.

The order of searching can be changed with "AUTODEPEND":

binaryonly search for binary packages, default locations
sourceonly search for ports, default locations
source {path = /usr/ports}only search for ports in /usr/ports and on the web site.


These are the individual steps for installing a ported application. Each step up to "install" depends on the previous one. Thus "aap install" will do all the preceding steps. But the steps that have already been successfully done in a previous invocation of Aap will be skipped. The "rundepend", "installtest", "clean", etc. targets do not depend on previous steps, they can be used separately.


Check if required dependencies can be fulfilled.

This doesn't install anything yet, it does an early check if building and/or installing the port will probably work before starting to download files.

This uses all the DEPEND_ variables that will actually be used. Fails if something is not available.


Check dependencies for fetch and checksum.

Uses DEPEND_FETCH, unless disabled with AUTODEPEND=no


Get required files.

If $CVSMODULES is set and $CVS is not "no", obtain files from CVS:

Uses $CVSROOT or cvsroot attribute in $CVSMODULES.
$CVSWRKSRC is where the files will end up (default is first member in $CVSMODULES).
Also obtain $CVSDISTFILES if defined.
Also obtain $CVSPATCHFILES if defined.
Use a post-fetch target if directories need to be renamed.


if $DISTFILES is set obtain them
if $PATCHFILES is set obtain them


The directory can be overruled with a {distdir = dir} attribute on individual patch files.

Files that already exist are skipped (if there is a checksum error, delete the file(s) manually).


Check if checksums are correct.

The port recipe writer must add the "do-checksum" target with :checksum commands to verify that downloaded files are not corrupted. Example:

        # >>> automatically inserted by "aap makesum" <<<
            :checksum $DISTDIR/foo-1.1.tgz {md5 = 2341423423423423434}
            :checksum $PATCHDISTDIR/foo-patch3.gz {md5 = 3923858739234}
        # >>> end <<<

The "aap makesum" command can be used to generate the lines.


Check dependencies for extract and patch.

Uses DEPEND_EXTRACT, unless disabled with AUTODEPEND=no


Unpack archives in the right place. Use $EXTRACT_ONLY if defined, otherwise $DISTFILES or $CVSDISTFILES when CVS was used.

Uses the "extract" action. To extract a new type of archive:

add filetype detection for this type of archive
define an "extract" action for this filetype

Extraction is done in $WRKDIR. A subdirectory may be specified with the "extractdir" attribute on each archive.

        DISTFILES = foo-1.1.tgz   foo_doc-1.1.tgz {extractdir = doc}

Apply patches not applied already.

$PATCHCMD defines the patch command, default: "patch -p < ". The patch file name is appended, unless "%s" appears in the string, then it's replaced by the file name.

A "patchcmd" attribute on each patch file may specify a patch command that overrules $PATCHCMD.

The patches are applied in $WRKDIR/$PATCHDIR (default: $WRKSRC). A "patchdir" attribute on each patch file may overrule the value of $PATCHDIR.


Check dependencies for configure and build.

Uses DEPEND_BUILD, unless disabled with "AUTODEPEND=no".


Perform configuration.

Executes the command specified with CONFIGURECMD. Usually autoconf, imake, etc. May be empty.


Run the command specified with BUILDCMD. When empty "aap" is used. Useful values are "gmake", "make", etc. An argument may be included. Example: "BUILDCMD=make foo"



Check test dependencies.


check if all required items are present try to install them automatically, unless disabled AUTODEPEND=no This is skipped when "SKIPTEST=yes"


Check if building was done properly.

Executes TESTCMD. When it is empty "aap test" is used. Example: "TESTCMD=make testall"

This is skipped when "SKIPTEST=yes" Done in $WRKDIR/$TESTDIR, default: $WRKDIR/$WRKSRC


Create a package with selected files, usually including the compiled program.

There are two methods to select files to be included in the package:

  1. Specify the list of files below $WRKDIR, with a "dest" attribute where they should end up. Assign the list to $PACKFILES. Example:

            PACKFILES = $WRKSRC/bin/prog {dest = /usr/local/bin/prog}
                        $WRKSRC/man/prog.1 {dest = usr/local/man/man1/prog.1}

  2. Specify a command to fake-install into $PKGDIR and use all files that end up there. Set $INSTALLCMD to the command to be used. Example:

            INSTALLCMD = "aap install DESTDIR=$PKGDIR"

    Set INSTALLDIR to the directory inside $WRKDIR where the files are put. Default is $WRKSRC. $PKGDIR/$PREFIX is where files end up.

A packing list is generated with the files that exist in the package. Then "pkg_create" is executed to actually create the package. Arguments are given such that $PORTDESCR is used as the description of the package and $PORTCOMMENT for a short explanation of what the package is for.


Install the binary package.

This executes the "pkg_add" command in a separate shell. You are asked to type the root password. This is reasonably safe, since the shell is only connected to Aap and it can only install a package.

Exception: This updates the "rundepend" and "installtest" targets after updating the post-install target. This allows doing "aap install", which is a lot more obvious than "aap installtest".


Check runtime dependencies.

Check if all required items specified with $DEPEND_RUN are present and tries to install them automatically, unless $AUTODEPEND is "no".

This is skipped if $SKIPRUNTIME is "yes". The pre-rundepend and post-rundepend are still done, they should check $SKIPRUNTIME themselves.

"aap rundepend" will _not_ cause previous steps to be updated.


Test if the installed package works.

This is empty by default, specify a "do-installtest" target to actually do something.

Note that when $SKIPRUNTIME is "yes" the dependencies have not been verified and running the application might not work.


Uninstall the binary package. Not implemented yet!

Execute pkg_delete or equivalent. Does not depend on other steps.


Delete all generated, unpacked, patched and CVS files.

Does not delete the downloaded files. Does not depend on other steps. Does not clean packages this one depends on.


Delete everything except the toplevel recipe. After this all steps must be redone.

Does not depend on other steps. Does not clean packages this one depends on.


Generates a "do-checksum" dependency that checks if the fetched files were not corrupted.

If the recipe already contained a "do-checksum" dependency that was generated it is replaced. Otherwise a new one is appended. Do not change the markers before and after the "do-checksum" dependency, otherwise you end up with two of these when doing "aap makesum" again.

Does not depend on other steps. The files must already be present. You can use "aap fetch --nofetch-recipe" to obtain the files, if needed (it obtains the files but not the recipes).


Generate a package with recipe and source files. Not implemented yet!

Puts the main recipe and all downloaded files into an archive. The resulting archive can be installed without downloading.

Depends on the "fetch" target.

Port Description

The text to describe the port is usually a page full of plain text. Here is an example:

        PORTDESCR << EOF
        This is the description of the port.
        A very important application indeed.

        See our website

In the rare situation that "EOF" actually appears in the text you can use anything else, such as "THEEND".