How to Write a DarwinPorts Portfile


Kevin Van Vechten (kevin@opendarwin.org), Felix Kronlage (fkr@opendarwin.org)
15-Mar-2003

Abstract

DarwinPorts automates the common tasks needed to port software to Darwin. Portfiles provide the information necessary for building and installing a particular software title correctly on Darwin. The goal of DarwinPorts is to keep Portfile syntax as simple as possible, while still supporting all the special cases that many software titles require to build and install successfully.

This article describes the construction of a simple Portfile, and explores a few of DarwinPorts' most common features.

Getting Started

In order to work with DarwinPorts, you'll need to download and install it on your system. The DarwinPorts project homepage describes how to get and install it.

Since you're interested in writing a Portfile, you should invoke the port command with the -v (verbose output) and the -d (debugging output) switches. This will display useful debugging messages that are usually suppressed while running DarwinPorts.

DarwinPorts performs several basic predefined tasks, these are:

Basic Topics

Advanced Topics

Appendix

Fetching the Sources

The first step is to choose a piece of software to bring to port. For this example, we'll be porting ircII, a popular internet relay chat client. We can start with a simple Portfile describing the basic attributes of ircII such as its name, version, and the site where we can download the sources. Create a working directory named ircii and inside it create a file named Portfile with the following contents:


# $Id: $
PortSystem 1.0
name            ircii
version         20020912
categories      irc
maintainers     kevin@opendarwin.org
description     an IRC and ICB client
long_description        The ircII program is a full screen, termcap based interface to Internet Relay \
                        Chat. It gives full access to all of the normal IRC functions, plus a variety \
                        of additional options.
homepage        http://www.eterna.com.au/ircii/
master_sites    ftp://ircftp.au.eterna.com.au/pub/ircII/

A Portfile consists of key/value pairs. Every Portfile starts with # $Id: $ which is a commented out RCS Id tag. Following the RCS Id tag comes the PortSystem declaration. Currently the only valid declaration is PortSystem 1.0. The name and version key describe the name and version of the software. The categories key is a list of the logical categories to which the software belongs; this is used for organizational purposes. The first entry in categories should match the directory in which the port's directory resides in the port tree. The maintainers key should contain your email address. description provides a short description of the port, while long_description holds a more detailed description of the Software. The master_sites key should contain a list of sites where the distribution sources may be downloaded. To refer to the main website of the software, the homepage key is used. DarwinPorts uses the terms 'keys' and 'options' interchangeably since most keys are used as options of a particular task in the porting process.

At this point, the Portfile is complete enough to download ircII. By default, DarwinPorts will append the version to the name and assume sources are in .tar.gz format. From your working directory, execute the following command:


% port -d -v checksum

The port command operates on the Portfile in the current working directory. You should see the following output:


DEBUG: Executing com.apple.main (ircii)
DEBUG: Executing com.apple.fetch (ircii)
--->  ircii-20020912.tar.gz doesn't seem to exist in /opt/local/var/db/dports/
distfiles
--->  Attempting to fetch ircii-20020912.tar.gz from ftp://
ircftp.au.eterna.com.au/pub/ircII/
DEBUG: Executing com.apple.checksum (ircii)
Error: No checksums statement in Portfile.  File checksums are:
ircii-20020912.tar.gz md5 2ae68c015698f58763a113e9bc6852cc
Error: Target error: com.apple.checksum returned: No checksums statement in 
Portfile.

Verifying the Downloaded File

Notice that DarwinPorts first checks for a local copy of ircii-20020912.tar.gz and doesn't find it, so it then downloads from the remote site. The port doesn't finish because of an error: "No checksums statement in Portfile." Portfiles must contain an md5 checksum of all distribution files--this allows DarwinPorts to verify the accuracy and authenticity of the sources. For convenience, an md5 checksum of the downloaded files is printed when the checksums argument is not specified. Go back and add the following to your Portfile:


checksums       md5 2ae68c015698f58763a113e9bc6852cc

Extracting the Sources into a Working Directory

Now that we have a checksum and can verify our sources, we can proceed to extracting the sources into our working directory. Execute the following:


% port -d -v extract

Which should display the following output:


DEBUG: Skipping completed com.apple.main (ircii)
DEBUG: Skipping completed com.apple.fetch (ircii)
DEBUG: Executing com.apple.checksum (ircii)
--->  Checksum OK for ircii-20020912.tar.gz
DEBUG: Executing com.apple.extract (ircii)
--->  Extracting for ircii-20020912
--->  Extracting ircii-20020912.tar.gz ... DEBUG: Assembled command: 'cd /Users/
kevin/opendarwin/proj/darwinports/dports/irc/ircii/work && gzip -dc /opt/local/
var/db/dports/distfiles/ircii-20020912.tar.gz | tar -xf -'
Done

Running a Configure Script

Now that the sources have been extracted into a work directory in the current working directory, we can configure the sources to compile with the desired options. By default DarwinPorts assumes the software you're porting uses an autoconf configure script, also by default, DarwinPorts will pass the --prefix=${prefix} argument to configure, specifying that the software should be installed in the directory tree used by DarwinPorts.

ircII's standard set of options looks fine for a base install on Darwin, so won't do the configure phase manually and instead just move on to the build phase.

Building the Sources

To build, type the following:


% port build

By default, the build phase executes the system's make(1) utility. (This can be changed with the build.type option which accepts an argument of bsd, gnu, or pbx. Alternatively, the build.cmd option may be used to specify an arbitrary build command.) The above step has starting compiling the sources, when it finishes we'll be ready to install the software.

Installing the Finished Product on the System

The former method of including a contents list has been made obsolete by the destroot mechanism. With destroot the software is installed into a directory-tree below in the work directory. While some software (like ircII) does not require any special tweaks to be installed into the destroot, others (like ncftp) need the install.destroot option in order to correctly install into the destroot.


install.destroot        mandir=${destroot}${prefix}/man prefix=${destroot}${prefix}

Take a look at some of our ports to see more examples on how to use the install.destroot option.

Now we have a complete portfile. Re-run the installation step to add your port to your own registry:


% sudo port -d -v install
Which will output the following:

DEBUG: Skipping completed com.apple.main (ircii)
DEBUG: Skipping completed com.apple.fetch (ircii)
DEBUG: Skipping completed com.apple.checksum (ircii)
DEBUG: Skipping completed com.apple.extract (ircii)
DEBUG: Skipping completed com.apple.patch (ircii)
DEBUG: Skipping completed com.apple.configure (ircii)
DEBUG: Skipping completed com.apple.build (ircii)
DEBUG: Skipping completed com.apple.install (ircii)
DEBUG: Executing com.apple.registry (ircii)
--->  Adding ircii to registry, this may take a moment...

Advanced Topics

Overriding Targets

It's possible to override the functionality of any build target with Tcl code. A common example is the following, which might be useful for a script without an autoconf configure script:


configure {}

In the Portfile, this will replace the functionality of the configure target, thus skipping that step. It is also possible to execute Tcl code immediately before or after any of the standard targets. This can be accomplished in the following manner:


post-configure {
    reinplace "s|change.this.to.a.server|irc.openprojects.net|g" \
        "${workdir}/${worksrcdir}/config.h"
}

This example replaces the occurrence of change.this.to.a.server with irc.openprojects.net in the config.h file that was generated during the preceding configure phase. Note this is a somewhat contrived example, since the same could have been accomplished by specifying --with-default-server=irc.openprojects.net in configure.args, but the approach is generally useful when such configure arguments aren't present.

Portfile Variants

Since Darwin 6.0 has ipv6, it would be possible to configure with the --with-ipv6 option. This can be done by adding the following option to the Portfile:


configure.args      --disable-ipv6

variant ipv6 {
    configure.args-append  --enable-ipv6
}

Now the default build will not include ipv6 support, but if the ipv6 variant is requested, ircII will have it. Options by themselves should be thought of as an assignment operator. Since variants may be used in combination with one another, it's good practice to only append to options instead of overwrite them. All options may be suffixed with -append or -delete to append or delete one term from the list. You can specify building with the ipv6 variant in the following way:


% port build +ipv6

Appendix

Portfile Listing

The following is a complete listing of the ircII Portfile:


# $Id: $
PortSystem 1.0
name            ircii
version         20020912
categories      irc
maintainers     kevin@opendarwin.org
description     an IRC and ICB client
long_description        The ircII program is a full screen, termcap based interface to Internet Relay \
                        Chat. It gives full access to all of the normal IRC functions, plus a variety \
                        of additional options.
homepage        http://www.eterna.com.au/ircii/
master_sites    ftp://ircftp.au.eterna.com.au/pub/ircII/
checksums       md5 2ae68c015698f58763a113e9bc6852cc
configure.args  --disable-ipv6

post-configure {
        reinplace "s|change.this.to.a.server|irc.openprojects.net|g" \
                  "${workdir}/${worksrcdir}/config.h"
}

variant ipv6 {
        configure.args-append --enable-ipv6
}