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.