typedef int (*funcptr)();

An engineers technical notebook

Building custom ports with Poudriere and Portshaker

Guest post by Scott Sturdivant.

Maintaining custom ports and integrating them into your build process doesn't need to be difficult. The documentation surrounding this process however is either non-existent, or lacking in its clarity. At the end of the day, it really is as simple as maintaining a repository whose structure matches the ports tree layout, then managing that repository and the standard ports tree with portshaker, and finally handing the end result off to poudriere.

Your Custom Repository

For this example, we'll assume a git repo is used and that you're already familiar with how to build FreeBSD ports. We'll also assume that we have but a single port that we're maintaining and that it is called myport. The hierarchy of your repo should simply be category/myport. We'll refer to this repo simply as myrepo.

Portshaker

Portshaker is the tool responsible for taking multiple ports sources and then merging them down into a single target. In our case, we have two sources: our git repo (myrepo) containing myport, and the standard FreeBSD ports tree. We aim to merge this down into a single ports tree that poudriere will then use for its builds.

To configure portshaker, add the following to the /usr/local/etc/portshaker.conf file:

# vim:set syntax=sh:
# $Id: portshaker.conf.sample 116 2008-09-30 16:15:02Z romain.tartiere $

#---[ Base directory for mirrored Ports Trees ]---
mirror_base_dir="/var/cache/portshaker"

#---[ Directories where to merge ports ]---
ports_trees="default"

use_zfs="no"
poudriere_ports_mountpoint="/usr/local/poudriere/ports"
default_poudriere_tree="default"
default_merge_from="freebsd myrepo"

Some key points here are that the two items listed in for the default_merge_from argument need to have scripts present in the /usr/local/etc/portshaker.d directory. Further more, the combination of the poudriere_ports_mountpoint and default_poudriere_tree needs to be a ports tree that is then registered with poudriere.

Next, we need to tell portshaker how to go off and fetch our two types of ports trees, freebsd and myrepo. For the freebsd ports tree, create /usr/local/etc/portshaker.d/freebsd with the following contents and make it executable:

#!/bin/sh
. /usr/local/share/portshaker/portshaker.subr
method="portsnap"
run_portshaker_command $*

Next, create a similar script to handle our repository containing our custom port. /usr/local/etc/portshaker.d/myrepo should contain the following and similarly be executable:

#!/bin/sh
. /usr/local/share/portshaker/portshaker.subr
method="git"
git_clone_uri="http://github.com/scott.sturdivant/packaging.git"
git_branch="master"
run_portshaker_command $*

Obviously replace the git_clone_uri and git_branch variables to reflect your actual configuration. For more information about the values and what they can contain, consult man portshaker.d

Now, portshaker should be all set. Execute portshaker -U to update your merge_from ports trees (freebsd and myrepo). You'll see the standard portsnap fetch and extract process as well as a git clone. After a good bit of time, these will both be present in the /var/cache/portshaker directory. Go ahead and merge them together by executing portshaker -M.

Hooray! You now have /usr/local/poudriere/ports/default/ports that is a combination of the normal ports tree and your custom one.

We're effectively complete with configuring portshaker. Whenever your port is updated, just re-run portshaker -U and portshaker -M to grab the latest changes and perform the merge.

Poudriere

Poudriere is a good tool for building ports. We will use it to handle our merged directory. Begin by configuring poudriere (/usr/local/etc/poudriere.conf):

NO_ZFS=yes
FREEBSD_HOST=ftp://ftp.freebsd.org
RESOLV_CONF=/etc/resolv.conf
BASEFS=/usr/local/poudriere
USE_PORTLINT=no
USE_TMPFS=yes
DISTFILES_CACHE=/usr/ports/distfiles
CHECK_CHANGED_OPTIONS=yes

Really there's nothing here that is specific to the problem at hand, so feel free to consult the provided configuration file to tune it to your needs.

Now, the step that is specific is to set poudriere up with a ports tree that it does not manage, specifically our resultant merged directory. If you consult man poudriere, it specifies that for the ports subcommand, there is a -m method switch which controls the methodology used to create the ports tree. By default, it is portsnap. This is confusing as in our case, we do not want poudriere to actually do anything. We want it to just use an existing path. Fortunately, there is a way!

The poudriere wiki has an entry for using the system ports tree, so we adopt it for our needs by executing:

poudriere ports -c -F -f none -M /usr/local/poudriere/ports/default \
-p default

If you've consulted the poudriere manpage, you'll see that the -F and -f switches both reference ZFS in their help. As we're not using ZFS, it's not clear how they will behave. However, in conjunction with the custom mountpoint (-M /usr/local/poudriere/ports/default), we ultimately wind up with what we want, a ports tree that poudriere can use, but does not manage:

# poudriere ports -l
PORTSTREE            METHOD     PATH
default              -          /usr/local/poudriere/ports/default

Note that this resulting PATH is the combination of the poudriere_ports_mountpoint and default_poudriere_tree variables present in our /usr/local/etc/portshaker.conf configuration file.

Building software from your custom ports tree

Go ahead and create your jail(s) like you normally would (i.e. poudriere -c -j 92amd64 -V 9.2-RELEASE -a amd64) and any other configuration you would like, and then go ahead and build myport with poudriere bulk -j 92amd64 -p default category/myport. Success!