Working with FreeBSD Ports

In some situations it may be necessary to make changes to existing software that comes in FreeBSD packages.

This section describes how to make such changes using an example.

Example

Suppose you would like to make a small change to the source code of software that comes as a FreeBSD package. Since packages are built from FreeBSD Ports, you need to modify the corresponding FreeBSD port.

Here is a real-life example on how to do that:

https://github.com/lxqt/qterminal/issues/474 can be fixed by changing a few lines in

https://github.com/lxqt/qtermwidget/blob/059d832086facb19035a07d975ea4fd05d36b1ec/lib/TerminalDisplay.cpp#L3206

Here is how to do it using FreeBSD Ports:

Pulling the ports tree

$ sudo git clone https://git.freebsd.org/ports.git /usr/ports --depth=1

If you are running an end-of-life FreeBSD version such as 12.1, you also need to

$ export ALLOW_UNSUPPORTED_SYSTEM=1
// Or in csh
% setenv ALLOW_UNSUPPORTED_SYSTEM YES

Building the port without changes

Build the port without changes first to ensure it builds and works as expected.

$ cd /usr/ports/x11-toolkits/qtermwidget/

// Install all build-time dependencies from packages
$ make build-depends-list | cut -c 12- | xargs pkg install -y

// Build the library
// NOTE: Applications that have a library that is used by nothing but that application are just
// annoying because now you have to deal with two entities rather than one
$ MAKE_JOBS_UNSAFE=yes sudo -E make -j4

// Now, the application itself
$ cd /usr/ports/x11/qterminal

// Install all build-time dependencies from packages
$ make build-depends-list | cut -c 12- | xargs pkg install -y

// Build the application
$ QTermWidget5_DIR=/usr/ports/x11-toolkits/qtermwidget/work/stage/usr/local/lib/cmake/qtermwidget5/  MAKE_JOBS_UNSAFE=yes sudo -E make -j4

// Run the application with the changed library
$ LD_LIBRARY_PATH=/usr/ports/x11-toolkits/qtermwidget/work/stage/usr/local/lib/ /usr/ports/x11/qterminal/work/stage/usr/local/bin/qterminal

Making changes to the port

Now make changes to the port.

Note

In the ports system you cannot just change the source code because it would be overwritten during the build process.

You first need to make a copy of the source code, then make the changes in the copy, then create a patch that will get applied automatically at build time.

// Copy the file to be changed
$ cd /usr/ports/x11-toolkits/qtermwidget
$ sudo cp work/qtermwidget-*/lib/TerminalDisplay.cpp work/qtermwidget-*/lib/TerminalDisplay.cpp.orig

// Make the change in the library
$ sudo nano work/qtermwidget-*/lib/TerminalDisplay.cpp
        QChar q(QLatin1Char('\''));
        dropText += q + QString(urlText).replace(q, QLatin1String("'\\''")) + q;
        dropText += QLatin1Char(' ');

// Make a patch
$ sudo make makepatch

// Build the library again
$ sudo make clean # Run this only if the next line does nothing
$ MAKE_JOBS_UNSAFE=yes sudo -E make -j4

// Run the application with the changed library
$ LD_LIBRARY_PATH=/usr/ports/x11-toolkits/qtermwidget/work/stage/usr/local/lib/ /usr/ports/x11/qterminal/work/stage/usr/local/bin/qterminal

As a result we have a patch:

$ cat files/patch-lib_TerminalDisplay.cpp
--- lib/TerminalDisplay.cpp.orig        2020-11-03 08:19:26 UTC
+++ lib/TerminalDisplay.cpp
@@ -3099,7 +3099,9 @@ void TerminalDisplay::dropEvent(QDropEvent* event)
         // without quoting them (this only affects paths with spaces in)
         //urlText = KShell::quoteArg(urlText);
 
-        dropText += urlText;
+        QChar q(QLatin1Char('\''));
+        dropText += q + QString(urlText).replace(q, QLatin1String("'\\''")) + q;
+        dropText += QLatin1Char(' ');
 
         if ( i != urls.count()-1 )
             dropText += QLatin1Char(' ');

Making packages

Optionally, make packages for the ports. Note that make build needs to have run already before this step.

// Library
$ cd /usr/ports/x11-toolkits/qtermwidget/
$ sudo make package
$ ls work/pkg

// Application
$ cd /usr/ports/x11/qterminal
$ sudo make package
$ ls work/pkg

Creating a port from scratch

The FreeBSD Porter’s Handbook is the authoritative source on how to write new ports from scratch. This section shows a hands-on example on how to package a set of tools from a GitHub repository.

Note

This section is a work in progress. Corrections are welcome.

First, prepare the Ports environment:

$ sudo su
# pkg install portlint
# echo DEVELOPER=yes >> /etc/make.conf

Next, create a directory for the new port:

# mkdir /usr/ports/sysutils/fluxengine
# cd /usr/ports/sysutils/fluxengine

Create Makefile with the following content:

# $FreeBSD$

PORTNAME=       fluxengine
DISTVERSION=    572
CATEGORIES=     sysutils

MAINTAINER=     probono@puredarwin.org
COMMENT=        USB floppy disk interface for reading and writing non-PC disk formats

LICENSE=        MIT

LIB_DEPENDS=    libsqlite3.so:databases/sqlite3 \
                libstdc++.so:lang/gcc9

BUILD_DEPENDS=  ninja:devel/ninja

USES=           gmake

USE_GITHUB=     yes
GH_ACCOUNT=     davidgiven
GH_TAGNAME=     61ff48c

do-install:
        ${INSTALL_PROGRAM} ${WRKSRC}/brother120tool ${STAGEDIR}${PREFIX}/bin/
        ${INSTALL_PROGRAM} ${WRKSRC}/brother240tool ${STAGEDIR}${PREFIX}/bin/
        ${INSTALL_PROGRAM} ${WRKSRC}/fluxengine ${STAGEDIR}${PREFIX}/bin/

.include <bsd.port.mk>

Run

# make makeplist > pkg-plist

to create that file. Check and edit it by hand, especially the first line.

Notes

  • See ls /usr/ports/ for possible categories, such as sysutils

  • See 5.2. Naming for naming and versioning conventions

  • The lines must be in a defined order. Run portlint to get information on this. When portlint complains about appears out-of-order, the blocks and lines in the Makefile need to be reshuffled to match the order described at https://docs.freebsd.org/en/books/porters-handbook/order/.

  • Run make stage-qa to find out dependencies. Using something like make stage-qa 2>&1 | grep "you need" | sort | uniq | cut -d " " -f 4 can speed this up

  • The do-install section is needed in this example because there is no make install in the original software’s Makefile

Create pkg-descr based on the description on the GitHub README.md:

The FluxEngine is a very cheap USB floppy disk interface capable of
reading and writing exotic non-PC floppy disk formats.
It allows you to use a conventional PC drive to accept Amiga disks,
CLV Macintosh disks, bizarre 128-sector CP/M disks,
and other weird and bizarre formats.
 :
The hardware consists of a single, commodity part with a floppy drive
connector soldered onto it. No ordering custom boards,
no fiddly surface mount assembly, and no fuss:
nineteen simpler solder joints and you're done.

WWW: http://cowlark.com/fluxengine/

Notes

Create the checksum file by running

# make makesum

Check the Makefile with

# portlint

and correct any mistakes it reports, then repeat.

Once portlint says looks fine, try to build by running

# make

Note that the compilation will fail. This is because in this case the application needs to be built with gmake rather than make.

Run tests

# make stage
# make stage-qa
# make package
# make install
# make deinstall

None of these must produce errors. See 3.4. Testing the Port for details.

At this point it may be a good idea to have an experienced FreeBSD Ports developer have a look at your new port.

Once everything looks good, prepare a .shar file for submitting it to https://bugs.freebsd.org/submit/:

# rm -rf work/
# cd ..
# tar cf  fluxengine.shar --format shar fluxengine

Updating existing ports

This real-life example shows how to update a port, e.g., x11/qterminal.

$ sudo su
# cd /usr/ports
# git pull
# cd x11/qterminal

Change PORTVERSION=    1.1.0 to PORTVERSION=    1.2.0. Turns out that one has to update x11-toolkits/qtermwidget and devel/lxqt-build-tools too for it to compile.

# portlint # Fix errors, if any
# rm pkg-plist
# make clean
# make package reinstall
# make makeplist > pkg-plist
# nano pkg-plist # Remove first line and check the others
# make clean
# make package reinstall

Finally, once everything is tested, create separate patches for each port with

# cd /usr/ports
# git diff -U999999 x11/qterminal > /home/user/Desktop/x11_qterminal.diff
# git diff -U999999 devel/lxqt-build-tools > /home/user/Desktop/devel_lxqt-build-tools.diff
# git diff -U999999 x11-toolkits/qtermwidget > /home/user/Desktop/x11-toolkits_qtermwidget.diff

Go to https://reviews.freebsd.org/differential/, click “Create Diff”, can log in using GitHub credentials, subject devel/lxqt-build-tools: Update to 0.12.0, under “Reviewers” add the maintainer.

Result:

  • https://reviews.freebsd.org/D37378

  • https://reviews.freebsd.org/D37379

  • https://reviews.freebsd.org/D37380

If a reviewer requires changes to be made, make the changes and upload (paste) a new diff (like the original diff; not a “diff of the diff”!) by clicking on “Update Diff” on the respective Phabricator page, e.g., https://reviews.freebsd.org/D37378.