cat

Using BSD make for your (small) project

If you enjoy this article, please consider donating: https://liberapay.com/qorg11. For questions or comments on this article feel free to reach me out at teru-sama [at] riseup [dot] net.

Alright, so you wrote your software! Bad news kid, now you have to compile it! Worse than that, you have to make that the compilation is not a pain in the ass so more people can actually use your software!

Thankfully, developers thought about on the unbearable pain of compiling software, and thus make was born. make, A makefile is a set of instructions that tells the software make how to compile the software. Being honest, if you’re in this website you already know what make is.

BSD Make (also called bmake) comes with interesting features that make writing makefiles easier. As it comes with some kind of templates that will surely help you at the time of writing the makefile, bsd makefiles tend to be readable and easily editable. Consider this source tree. I am adding libcurl to this example to add some “complexity” to the makefile.

main.c:

#include <stdio.h>

/* Not gonna create an header file for a simple makefile
 * example.... */

void
get_url(const char *s);

int
main(void)
{
     puts("getting qorg11.net...");
     get_url("qorg11.net");
}

geturl.c:

#include <curl/curl.h>

void
get_url(const char *s)
{
     CURL *curl = curl_easy_init();
     curl_easy_setopt(curl,CURLOPT_URL,s);
     curl_easy_setopt(curl,CURLOPT_WRITEDATA,stdout);

     curl_easy_perform(curl);

     curl_easy_cleanup(curl);

}

This, the traditional Makefile would look a bit like this:

Makefile:

CC = cc
LDFLAGS = `pkg-config --cflags --libs libcurl`
OBJS = main.o geturl.o
TARGET = geturl

# Link the thing
all: $(OBJS)
     $(CC) $(CFLAGS) $(LDFLAGS) $(OBJS) -o $(TARGET)

# Compile all source code to object files
%.o : %.c
        $(CC) -c $(CFLAGS) $< -o $@

.PHONY clean
clean:
     rm *.o $(TARGET)

Typing make will result on a working makefile, the makefile will compile the software as expected and not much else would happen. The software also works as expected, however, in my opinion make syntax makes 0 sense and it could be improved. Fortunately, this can be solved using the BSD make templates. Consider the following Makefile:

Makefile:

PROG   = geturl
SRCS   = main.c geturl.c
LDADD != pkg-config --cflags --libs libcurl
MAN    =

.include <bsd.prog.mk>

If you’re in Linux, you might have to install bmake, which is a port of NetBSD make, it is more likely in your distro’s repositories. To run that Makefile, just type bmake, and magic will happen. But let’s explain it

PROG is like the target, is what the template uses to get the resulting binary. If SRCS is empty, bmake will just compile progname.c. SRCS are the sources files you want to compile. And LDADD are the flags you want to pass to the linker, notice that in this case I used != instead of \=, this is because when you want to assign the output of a comman in BSD make, you have to do !=, you can’t do SRCS = `pkg-config ...` because it won’t work.

the .include <bsd.prog.mk> line makes all the magic possible. It is the template, and then you pass all the variables you defined before to that template, so the .include directive must be at the very bottom of the Makefile.

Also, this simple makefiles comes with all the rules someone would like. “bmake clean” works, so does “bmake install”.

Notice how there isn’t “CFLAGS” in this makefile, this is because, if you want to add any CFLAG, you can do it this way, and BSD make will understand:

qorg@wakaran ~/docs/xdd $ bmake CFLAGS="-O2 -pipe -Wall -pedantic"
cc -pipe -O2 -pipe -Wall -pedantic           -c main.c
cc -pipe -O2 -pipe -Wall -pedantic           -c geturl.c
cc -pipe           -o geturl  main.o geturl.o  -lcurl 

You can specify default CFLAGS in the Makefile, but when adding CFLAGS in the command line, those will be overwritten.

Conclusion

BSD make is great for small projects which don’t have a lot of files and do not have any compile time option. For larger projects in which you want to enable/disable options at compilation time, you might have to use a more complete build system.