# Customizing FreeBSD Ports and Packages Without a doubt, one of the most convenient tools of modern open-source operating systems is the package manager. The ability to search, download, and install a wide range of pre-compiled software with only a single command is unparalleled. FreeBSD is no exception here, providing access to binary packages via the `pkg(8)` utility. However, these packages must all be compiled somewhere, and package consumers are at the mercy of the package producer when it comes to the contents and compile-time options used. This article is for anyone who is looking to step into the world of building their own FreeBSD package sets. There are many reasons to do so; perhaps you want finer grained control over the contents or your packages, or optimize them for a certain device, or maybe you are managing a specific cluster or fleet of FreeBSD devices. Existing experience with using FreeBSD's ports framework would be beneficial to the reader, although it is not strictly required. ## Port Options ## The simplest customization that can be made to individual ports is through each port's options menu. These options are defined to include/exclude certain functionality from the program(s), or otherwise influence the final package contents. Simple ports may have no options, while larger or more heavily used software may have a dozen or more. All of this is at the discretion of the port maintainer. Executing `make config` in a port's directory will bring up an options menu similar to the following: ``` $ cd sysutils/tmux $ make config ┌──────────────────────────────── tmux-3.1c ───────────────────────────────────┐ │ ┌──────────────────────────────────────────────────────────────────────────┐ │ │ │+[ ] BACKSPACE Build with tty/keys patch │ │ │ │+[x] DOCS Build and/or install documentation │ │ │ │+[x] EXAMPLES Build and/or install examples │ │ │ │+[ ] LIBEVENT_STATIC Build with static libevent │ │ │ │+[ ] UTF8PROC Build with utf8proc support │ │ │ └──────────────────────────────────────────────────────────────────────────┘ │ ├──────────────────────────────────────────────────────────────────────────────┤ │ < OK > <Cancel> │ └──────────────────────────────────────────────────────────────────────────────┘ ``` Although the exact set of options provided is unique to the port in question, there are several options that are common to many ports. The `DOCS` option shown above is one such example. As you might imagine, unsetting this globally could be useful for saving disk and/or network space on installations where it's know that the port documentation won't be required. This can be unset globally by adding `OPTIONS_UNSET=DOCS` to your `make.conf(5)`. Conversely, `OPTIONS_SET` can be used to globally enable certain options. Both variables accept a space-separated list of options, and can be overwritten by an indivdual port's configuration. For more information on using these variables, see `ports(7)`. A few other common options that may be useful are `EXAMPLES`, `NLS`, `X11`, `CUPS`. Readers are encouraged to explore these options and their effects, and to search out others. ## Building Custom Package Sets with Poudriere ## The FreeBSD ports tree is designed so that it can be driven by a single `make` invocation at any level. There is power in such simplicity, particularly in its ability to be wrapped or automated by other tools. The most popular of any such tools is `poudriere(8)`, a tool for building FreeBSD packages in bulk. Anyone who expects to build packages from source regularly should learn to use `poudriere`. There are many guides out there for how to set up and configure the tool, so we won't focus on that here. Instead, we will focus on basic usage required for building and configuring packages. ### Building in Bulk Building packages with `poudriere(8)` is done with the `bulk` command. This process requires three things: 1. A jail 2. A checkout of the FreeBSD ports tree 3. A list of ports to be built Jails provide a clean environment in which packages can be built, free of any existing programs or libraries on the host. They also define the version of FreeBSD which the resulting packages will be built for; packages built in a 12.2-RELEASE jail will be built to run on a 12.2-RELEASE system, even if the host machine is running 13-STABLE. Jails can be constructed by `poudriere` itself with the `jail` subcommand. The name must be specified with `-j` and the FreeBSD version with `-v`. ``` $ poudriere jail -c -j 13_0_release -m ftp -v 13.0-RELEASE $ poudriere jail -c -j 14_current -m git -v main ``` The above example creates two new and distinct jails. `13_0_release`, will be fetched from the official release distribution sets, and runs a 13.0-RELEASE userland. `14_current` will be built from source, using a checkout of the latest git revision. Next, a ports tree checkout is required. `poudriere` allows you to fetch a new one, or provide the path to an existing checkout. The following example creates two such ports trees: ``` $ poudriere ports -c -p 2021q1 -m git -B 2020q1 $ poudriere ports -c -p dev -m null -M $HOME/freebsd-ports ``` `2020q1` consists of a freshly checked-out tree from version control, based on the `2021q1` branch in git. `dev` was created as a special unmanaged ports tree using an existing checkout at `$HOME/freebsd-ports` -- useful for testing customizations. Finally, a list of ports is required. This list can be provided on the commandline, or as the contents of a text file. The following command will generate a properly formatted list of port origins for the packages installed on the host: ``` $ pkg info -ao | awk '{ print $2 }' | sort > portlist.txt ``` With this set up, we can start building. ``` $ poudriere bulk -j 13release -p dev -f portlist.txt ``` This command is enough to build all of the ports list in `portlist.txt` in the `13release` jail, based on the ports tree `dev`. Hopefully it is clear that swapping out the jail or ports tree in this command would result in a unique set of packages. This ability to build distinct packages sets from differing versions of FreeBSD or port sources is one of the key features that makes `poudriere` such a powerful tool. ### Configuring Options We've seen how to set port options in the previous section. Now we will look at how this is managed when using `poudriere(8)`. Setting options for an individual port is done with the `options` command. Like the `bulk` command, `options` accepts the `-j jail` and `-p ports-tree` arguments, optionally. This offers a lot of flexibility because it enables you to configure an individual port's options on a per-tree or per-tree-and-jail tuple basis. To make this clearer, consider the following invocations: ``` $ poudriere options -p dev sysutils/tmux $ poudriere options -j 13_0_release -p dev sysutils/tmux ``` The first line sets the selected options for `sysutils/tmux` on all builds using `dev`, while the second only takes effect when building with the `13_0_release` jail as well. To configure options globally across all ports, `poudriere` allows for `make.conf(5)` files to be specified with similar granularity as above. These files are stored in the `/usr/local/etc/poudriere.d/` directory. From the `poudriere(8)` man page, any of the following are possible: ``` Any of the following are allowed and will all be used in the order shown: /usr/local/etc/poudriere.d/make.conf /usr/local/etc/poudriere.d/<setname>-make.conf /usr/local/etc/poudriere.d/<tree>-make.conf /usr/local/etc/poudriere.d/<jailname>-make.conf /usr/local/etc/poudriere.d/<tree>-<setname>-make.conf /usr/local/etc/poudriere.d/<jailname>-<tree>-make.conf /usr/local/etc/poudriere.d/<jailname>-<setname>-make.conf /usr/local/etc/poudriere.d/<jailname>-<tree>-<setname>-make.conf ``` Note that `<setname>` is not considered here. So, to apply custom options only ports built in the `14_current` jail on the `2021q1` branch, you would do the following: ``` echo 'OPTIONS_UNSET=DOCS EXAMPLES X11' > /usr/local/etc/poudriere.d/14_current-2021q1-make.conf ``` ## Further Reading The purpose of this article was to provide a brief introduction to the tools and options required for producing a customized set of FreeBSD packages. However, we have only scratched the surface of what is possible with `poudriere(8)` and the FreeBSD ports infrastructure. The FreeBSD Handbook [1] and Porter's Handbook [2] both provide chapters on `poudriere`, discussing other details of its setup and usage. The previously mentioned `ports(7)` man page describes some of the `make(1)` variables that influence the ports tree, along with other details of its usage. Finally, there are many other blog posts and tutorials related to `poudriere`, in which users describe their particular setup. When planning how to build and distribute packages, you could search and see if someone has already documented a similar use-case. [1] https://docs.freebsd.org/en/books/handbook/ports-poudriere.html [2] https://docs.freebsd.org/en/books/porters-handbook/testing-poudriere.html