Using BSD make for your (small) project

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:

 1: #include <stdio.h>
 2: 
 3: /* Not gonna create an header file for a simple makefile
 4:  * example.... */
 5: 
 6: void
 7: get_url(const char *s);
 8: 
 9: int
10: main(void)
11: {
12:      puts("getting suragu.net...");
13:      get_url("suragu.net");
14: }

geturl.c:

15: #include <curl/curl.h>
16: 
17: void
18: get_url(const char *s)
19: {
20:      CURL *curl = curl_easy_init();
21:      curl_easy_setopt(curl,CURLOPT_URL,s);
22:      curl_easy_setopt(curl,CURLOPT_WRITEDATA,stdout);
23: 
24:      curl_easy_perform(curl);
25: 
26:      curl_easy_cleanup(curl);
27: 
28: }

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

Makefile:

 1: CC ?= cc
 2: LDFLAGS = `pkg-config --cflags --libs libcurl`
 3: OBJS = main.o geturl.o
 4: TARGET = geturl
 5: 
 6: # Link the thing
 7: all: $(OBJS)
 8: $(CC) $(CFLAGS) $(LDFLAGS) $(OBJS) -o $(TARGET)
 9: 
10: # Compile all source code to object files
11: %.o : %.c
12: $(CC) -c $(CFLAGS) $< -o $@
13: 
14: .PHONY clean
15: clean:
16: 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:

1: PROG   = geturl
2: SRCS   = main.c geturl.c
3: LDADD != ${PREFIX}/bin/pkg-config --cflags --libs libcurl
4: MAN    =
5: 
6: .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:

1: sukamu@wakaran ~/docs/xdd $ bmake CFLAGS="-O2 -pipe -Wall -pedantic"
2: cc -pipe -O2 -pipe -Wall -pedantic           -c main.c
3: cc -pipe -O2 -pipe -Wall -pedantic           -c geturl.c
4: 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.

Compilation options using BSD make

configure scripts have their weird defined optins, such as --enable-xxx or --disable-xxx, which enables or disables features in the software you’re compiling. This can be also be done with BSD make and CFLAGS To do this you only have to use the simple Make conditionals. Consider the following C source code:

 1: #include <stdio.h>
 2: 
 3: int main(void) {
 4: 
 5: #ifdef USE_OPTION
 6:      puts("This is a string that will only be printed if use-option is enabled at compile time.");
 7: #endif
 8:      puts("Hello world!");
 9: 
10: 
11: 
12:      return 0;
13: }
 1: PROG   = option
 2: SRCS   = main.c
 3: LDADD != ${PREFIX}/bin/pkg-config --cflags --libs libcurl
 4: MAN    =
 5: 
 6: # Compilation options
 7: use-option = "no"
 8: .if "${use-option}" == "yes"
 9: CFLAGS +="-DUSE_OPTION"
10: .endif
11: 
12: .include <bsd.prog.mk>
13: 

If you compile normally, nothing weird would happen:

1: diego@sukamu ~/xdxd $ make      
2: cc -pipe -g                   -MD            -c main.c
3: cc -pipe           -o option  main.o  -lcurl
4: diego@sukamu ~/xdxd $ make
5: Hello world!

Now, let’s recompile with use-option=yes.

1: diego@sukamu ~/xdxd $ bmake use-option=yes
2: cc -pipe -g "-DUSE_OPTION"                   -MD            -c main.c
3: cc -pipe           -o option  main.o  -lcurl 
4: diego@sukamu ~/xdxd $ ./option
5: This is a string that will only be printed if use-option is enabled at compile time.
6: Hello world!

So, if you add "use-option=yes" to the make flags, the Makefile will add the required CFLAGS to enable the compile time option.

Conclusion

BSD make is great for both small and big projects. And maybe more sane than other alternatives, as doesn’t require you to write a lot of stuff just to build your project. BSD Make is a build system made for lazy people. And lazy people always come with the simplest solutions.