GNU make vs. BSD make - a practical problem experience

3 minute read

I’m currently evaluating Olivier Sessinks jailkit to maintain a small cvsd CHROOT with an included SSH server. The cvsd-buildroot command e.g. doesn’t handle the libpam-modules dependency in the CHROOT. So I tried to update the Debian packaging files included in the jailkit source. During this work I found some issues with the build environment - e.g. no DESTDIR support and some other stuff (another user already complained that there might be problems with the SUID installation part for jk_chrootsh, jk_uchroot, and jk_procmailwrapper of the Makefiles).

So I began to check and “fix” the Makefiles and the configure script and then sent everything to Olivier. I did not only add the DESTDIR variables. I also made use of built-in rules, common variables (also for implicit rules), etc. to make the Makefiles shorter and easier to maintain. My fault: I tested everything with GNU make. But now Olivier complained, that BSD’s make doesn’t work anymore. So we began a short hack session to check for the problems.

My first issue: I had to find some BSD make to test it myself. I found it in the freebsd5-buildutils which contains the freebsd-make command. Then we began to check for incompatibilities and that’s the result:

ifdef statements differ in syntax

  • GNU make uses ifdef VAR

  • BSD make uses .ifdef VAR

We replaced the statement with a construct similar to AM_CONDITIONAL. But we do not use this macro, because it would need to be shipped in aclocal.m4, which is currently not necessary. The code simply sets a variable foo_TRUE to ‘# ‘ based on a condition check.

if test "x$my_condition" != "xno" ; then
    AC_SUBST([foo_TRUE], [])
    AC_SUBST([foo_TRUE], [# ])

Processing the variable then gets replaced with the assigned value. If the value # was assigned, the second line in the following example would be commented.

FILES = bar
@foo_TRUE@FILES += foo


FILES = bar
# FILES += foo

in the final Makefile.

prerequisite variables differ in syntax

  • GNU make uses $<, $^, $@, …

  • BSD make uses ${.IMPSRC}, ${.ALLSRC}, ${.TARGET}, … (note, that both *SRC variables hold a list of source files)

Unfortunately both make implementations don’t seem to share a variable for the list of prerequisites. So the only solution is to list all prerequisites in the rule again. A variable would be much more comfortable. Maybe one could write a function which first tests ${.IMPSRC} and falls back to $^. Maybe that’s an alternative, but it’s IMHO not a very good solution. We’ve chosen to write down all prerequisites in a rule instead of using a variable. This is a little bit harder to maintain, but it works.

built-in rules are not shared and do not use the same variables

  • GNU make ships built-in rules to build objects from sources and link them; rules consider CPPFLAGS

  • BSD make ships built-in rules to build objects from sources, but there are no rules to link them; rules do not consider CPPFLAGS

In the case of GNU make one can simply write:

myprog: foo.o bar.o

and GNU make will automatically create foo.o from foo.c (.c.o:, ditto for bar.o) and at least link myprog (.o:). But BSD make will only create the object files. There is no final link-step. So we had to include the rule to link the program in the Makefile.


Finally we got it and it looks good now. I/we learned a lot of new things (I especially learned a lot about BSD make), but it took us 1-2 hours to understand and fix the problems.

Here is a list of URLs that might give some more information about the programs, their syntax and limitations: