Maintaining port modifications in FreeBSD
Introduction
Every so often, customization options provided by FreeBSD's ports
system do not suffice. In such an occasion, the need to maintain a
fork of the desired port arises. Unfortunately, considering the pace
at which ports update necessitates a constant and time-consuming watch
on the stream of updates so the child port can be rebased on the
parent port to keep the system up-to-date. In this post we'll explore
three ways that a small number of ports can be maintained with
out-of-tree modifications. The inspiration for this post was the
missing mmrm1stspace module in sysutils/rsyslog8 which is, by default,
disabled with no option to enable it.
Separate port using custom categories
By creating
a new category in the ports tree, it is possible to copy existing
ports, and modify and install them separately or in place of the
original port. It may also be possible to have a modified port with
virtual categories by copying the port to the same directory with a
new name and adding a virtual category to the Makefile of the new port. Make sure to still
update VALID_CATEGORIES as stated. See
this patch, this
patch and this
note to learn how virtual categories are built and used.
To create
a physical ports category, create a directory under the ports tree
(in my case, it is /usr/ports/custom)
and copy the desired port under it (/usr/ports/custom/rsyslog8). Modify the CATEGORIES field in Makefile under the new port to the new
category you created (custom).
Then, modify /etc/make.conf (make.conf(5)) and add VALID_CATEGORIES+=<category name> as a
new line, replacing <category name> accordingly.
To distinguish between the new port and the original port when
installing with pkg, just specify the
category name like so: $ pkg install custom/rsyslog8.
If you're using poudriere, edit
/usr/local/etc/poudriere.d/*-make.conf
instead of /etc/make.conf.
Patch using poudriere's postupdate hook
Poudriere can run scripts at various points called hooks,
which are located under /usr/local/etc/poudriere.d/hooks/HOOKNAME.sh.
The hook we're most interested in is called ports_update which is run after ports are
updated with ports -u. This hook can
then do anything to the ports tree including patch arbitrary
ports.
In my case, I maintain a set of patches in a separate directory
following the ports tree directory structure. This allows diff(1) and patch(1) to work without extra hand-holding.
To prepare a patch, copy files that need modification to another
directory with the same directory structure as the original port, make
changes, run diff, and save the patch in
the same directory. I have my port tree under /poudriere/ports/HEAD, so the following
commands work. Make sure to modify your paths accordingly.
cp /poudriere/ports/HEAD/sysutils/rsyslog8/Makefile{,.orig}
# make changes to Makefile
diff --text -U 3 /poudriere/ports/HEAD/sysutils/rsyslog8/Makefile{.orig,} > /my/patches/sysutils--rsyslog8--Makefile.patchports_update.sh then should be able
patch easily. To understand the following line, see patch(1) manpage, focusing on the Filename Determination section. The -p flag strips slashes from file path, so make
sure to set the appropriate number for your paths. For example, in my
case, -p4 is appropriate since 4 slashes
need to be removed to get from /poudriere/ports/HEAD/sysutils/rsyslog8/ to
sysutils/rsyslog8. The -d flag then specifies the directory which
contains sysutils. The contents of ports_update.sh can then be reduced to the
following.
#!/bin/sh
find /my/patches -type f -name '*.patch' -exec patch -d /poudriere/ports/HEAD -p4 -i {} \;Make sure ports_update.sh has
appropriate permissions (e.g. execute). Now every time poudriere
updates its ports with ports -u, your
personal patches are applied.
Manual rebasing with git
If you have your ports managed with git, you can create a branch, modify any file, commit, and rebase on origin whenever you need to update the ports tree. This way you'll be able to resolve any conflicts as they occur and have git manage your modifications.
git checkout master
git pull origin master
git checkout my-modifications
git rebase masterUsing ports overlays
Recently, Portshaker's overlay
functionality has been ported to ports. I have
not tested this, but it seems adding OVERLAYS=path1 path2 to make.conf (be it poudriere's <jailname>-make.conf or /etc/make.conf) makes poudriere or ports use
path1 or path2 (in that order) instead of the original
ports tree if there is a port or dependency that is needed that exists
in the overlay. The essence of the patches appears to be for overlay in ${dp_OVERLAYS} ${PORTSDIR}; do
which treats overlays similarly to original ports tree but does not
require all ports to be present under overlay.
Final thoughts
I ended up creating a separate physical category in the ports tree
named custom. This has worked well so
far, and allows me to have both the original port and the modified one
where I have the modified one installed in one jail and the original
installed elsewhere. If you use any other workflow or have any
additions to the above, let me know. You can find me as fengshaun on libera.chat irc.