CMake is a (meta-)buildsystem that handles finding-dependencies and building-things. It’s been around for many years, and has been in use by the KDE community for 14 years. In that time, CMake itself has changed quite a bit: there’s “legacy CMake”, version 2.8, and “modern CMake” which is roughly everything after version 3.0. But even within the 3.0 series there is a slow shift in language and tooling. This means that for released software, the CMake buildsystem “bitrots”, to some extent. I’ll give some examples revealed by the CMake 3.20 release.

It is in some sense “unfair” to complain that some software released in 2013, which claims to need CMake 2.8.0 or newer, that that software needs work in 2021 with CMake 3.20. It is! Still, packagers need to deal with this somehow.

When a CMake update arrives, FreeBSD packagers (like me) will make a branch in the ports tree, land the CMake update in the branch, and then start test-building ports that use CMake. This is called an exp-run in FreeBSD-words. A real exp-run happens on the build cluster, across all supported architectures and supported releases, but poudriere is a tool that can help do it locally, too.

If builds fail during the exp-run, then we fix the relevant ports. That means diving into released software – often released years ago – and figuring out what’s wrong. Then we land those fixes, and try it all again, until all the fallout (failures) has been resolved.

CMake 3.20

There are two major changes in CMake 3.20 that affect older software:

  • Command-line processing is more strict. There’s a handful of build instructions in FreeBSD ports that use the --build and --target arguments to CMake. Older CMake ignores them when they don’t make sense. CMake 3.20 errors out.

    In FreeBSD ports, it never makes sense to use these arguments: --build is for avoiding the generation of a build-system (that is, generating the Makefiles or ninja configuration that we normally use to build). --target specifies what target to build.

    I have removed the instances where this was used. it’s not a failure of the released software, or of CMake; more of a “derp” in packaging.

  • The CMake language is more strict. The syntax of if() and elseif() and else() in CMake has traditionally (er.. “legacy”) been weird, with an additional optional condition. In older CMake, this is valid:
      # things
      # things for b
      # things for c

    I couldn’t tell you what will happen if none of those three conditions is true, and I won’t bother writing out a test. Regardless, multiple else() blocks are now a language error,

  • (related) The CMake language is more strict. It looks like endif() and endfor() and similar end-something constructs were once treated as interchangeable. There is code that does for-if-endfor-endif and apparently it worked; only now has this special form of destructured programming turned into an error.

Keeping Old Stuff Building

Up-to-date software with current releases tends to have fewer issues – even when a change unexpectedly introduces build failures, we can count on the Open Source community to contribute fixes. So GNOME evolution-data-server had fixes in upstream git before I even noticed (and I spotted them only after I’d independently writted roughly-the-same-patch).

Ancient MySQL releases, on the other hand, need new patches. This can be a somewhat frustrating rabbit hole of building old stuff for no other purpose than fixing the old stuff you don’t use anyway.

Patches all end up in the FreeBSD ports collection git tree. Search for “CMake 3.20” for the kind of things that needed updating.

After the Update

Even with best-effort building and patching before packaging is updated, things slip through the cracks. Not every single CMake-using port is detected by my (simplistic) checks, so I forgot to build GNOME evolution even if I did fix the -data-server before. Thanks fluffy@ for chasing that one.

There’s some interesting fallout with pthreads (there are flags getting lost somewhere, PR 255123) and Python (with multiple versions installed, the old- and new-style find modules from CMake find different versions; in CMake 3.19 they found the same versions).

So, on to the next round of updates (I think I also need to land some fixes and features upstream eventually, but that keeps getting pushed to the periphery by other jobs).