Why Test Your Code With Multiple Compilers
2024-12-08
I don’t know whether this is surprising or not: Getting Vara to compile and run on
BSD was way easier than getting the OS set up with a GUI. Eager to get Vara
ported to BSD, I ignored the classic BSD variants and directly went for the ones
I thought were friendlier. Guess what, couldn’t even finish downloading the
MidnightBSD ISO due to the slow mirror. What about DragonflyBSD? It was a breeze
to try live and install with QEMU, but adding a desktop (or any package)
wouldn’t work even after setting up DHCP and pkg
repos. I’m sure I
made some mistake. Anyway, I moved on to GhostBSD.
I’d call GhostBSD the Ubuntu or Mint of the BSD world. It comes with a
desktop, and everything works–except for the mouse pointer and the compilation
of a basic Hello World program in C. Strange creatures we developers are, the
latter was more urgent to me. The exact issue was, even with clang
being installed by default, basic headers like stdio.h were missing. I
tried installing gcc
, which also failed to find the headers.
find
command confirmed that the files were indeed missing and this
was not a matter of include path.
Turns out one
has to execute pkg install -g GhostBSD*-dev
for that (don’t
forget the -g
flag). It’s a set of development-related packages
(similar to the -dev
or -devel
ones you’d encounter in
a GNU/Linux distro), one of which gets the libc headers installed. Better to get
all of it installed unless there is a specific reason.
Once it is installed, manual compilation of C files works fine. The next
challenge is getting make
to work. There are certain
incompatibilities between GNU Make and BSD Make. For example, GNU Make uses
ifdef
while it is .ifdef
in BSD. Fortunately, there is
an easy solution: just install devel/gmake
and call it a day (of
course, the command to use then is gmake
instead of
make
).
If I remember correctly, the development packages for GTK were already
installed, probably as part of GhostBSD*-dev
. The only missing
piece was pkg-config
, which is availed by installing the package
devel/pkgconf
.
Now that the whole build system is ready, the only thing that can go wrong is the code. And go wrong it did.
Why Try Different Compilers
Fixing Vara’s code for Clang on BSD didn’t require much work. However, it reminded me of the importance of trying out different compilers on the same code.
Getting your code to work with GCC is a must if you are targetting GNU/Linux, and that’s all you need even if you target BSD, macOS, or even Windows, as long as you provide pre-built binaries for all these platforms. But if you want others to be able to freely compile your code with the default compiler for their platform, you need to make sure your code works with at least Clang (and maybe MSVC as well, for many reasons including MinGW64 and MinGW64-generated binaries being incorrectly marked malicious).
One added advantage of trying out different compilers is getting warnings
that your favorite compiler missed. For example, if it weren’t for Clang, I
wouldn’t have noticed that the #pragma pack(1)
that I used in one
header for a particular struct was affecting other structs from other headers as
well. This helped me switch to enclosing the particular struct with
#pragma pack(push, 1)
and #pragma pack(pop)
instead.
Standards Aren’t Enough
Isn’t enforcing a standard with flags like -std=c99
enough for
portability? Not actually. For example, initializing a global variable to
sqrt(2)
would work fine on GCC, while Clang would complain
error: initializer element is not a compile-time constant
. What
Clang says is valid, and it wasn’t a problem on GCC only because
sqrt(2)
was being replaced by a precomputed value
(M_SQRT2
from glibc’s math.h).
Platforms Matter Too
Trying out different compilers on your development platform is a really good
start, but you cannot be sure until you do so on the target platforms. For
example, substituting pthread_t
with unsigned long
(don’t ask me why) would work fine with both GCC and Clang on GNU/Linux (amd64),
but not on GhostBSD (amd64). The definition of pthread_t
is
incompatible with unsigned long
there.
Nandakumar Edamana
Tags: bsd, clang, gcc, vara, c, portability, programming
Read more from Nandakumar at nandakumar.org/blog/