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.patch
ports_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 master
Using 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.