Introduction
Last time I showed you how to compile your first C program. Today we’re gonna dive more into the topic with makefiles!
What is a makefile?
Makefile is a file containing instruction for the make command. The make command automates the compiling process with a single command! You invoke it by simply calling make in your shell. You might need to install the program depending on your system distribution.
Make us of make
Let’s take our Hello World program from last time.
#include <stdio.h>
int main() {
printf("Hello world!\n");
return 0;
}Let’s compile it now using make. The command needs a target to work properly. You can also specify several options. Refer to man make for further info.
$ make hello
cc hello.c -o helloBy typing this command into the shell you invoke the make’s default rules: Run the default compiler program (cc) using hello.c as the source file and compile it into hello binary. It’s similar to how we invoked gcc last time.
How to write one
The default rules are surely nice, but to make proper use of the make command we need to write our own Makefiles.
So bust out your favourite editor of choice and start typing. Do not mix tabs with spaces when indenting, makefile structure allows tab characters only and respects indentation. After you’re done, save the file as Makefile. The make command will find it automatically now.
CC=gcc
CFLAGS=-Wall -Wextra -g
hello:
$(CC) $(CFLAGS) hello.c -o hello
clean:
rm -f helloThere are several variables that the make command generally looks for. CC is the name of the compiler you’re using. CFLAGS is a set of option that is passed to the compiler.
The main part are the targets. In the example above, these are hello and clean. These are the final arguments you call with make. Entering make now will run the first target specified in the makefile. This is hello for us.
$ make
make: 'hello' is up to date.As you can see, the make command recognizes the Makefile instantly, running the first target hello. You can also see that the makefile recognizes that the binary is up to date and does not need additional recompile.
$ make clean
rm -f helloNow we are running make with target clean. It does what is written in the makefile. Removing the hello binary.
$ make # or make hello
gcc -Wall -Wextra -g hello.c -o helloRunning the make now will compile the hello.c file with the extra CFLAGS options now.
–Wall flag enables *most* warnings.
–Wextra flag enables extra warnings not enabled by -Wall
-g flag enables debugging info when run through debugger (e.x. GDB)
I will explore these and more flags later on 🙂 These are just for your information right now.
More things to come…
You can already see the need of using makefiles I bet. But this is just a beginning. The real fun begins when you need to compile multiple programs, or multiple source files with header files into one. I’ll skip through a lesson and introduce you to C’s math library.
#include <stdio.h>
#include <math.h>
int main() {
float num;
printf("Enter a number: ");
scanf("%g", &num);
printf("The square root of given number is: %g\n", sqrt(num));
return 0;
}This program asks the user for a number and prints out its squre root. The included math library is called <math.h>. scanf function prompts the user and saves the value to num variable. But that is not important for this lesson.
Try to compile the program for yourself! It won’t go smoothly though.
$ make sqrt
cc sqrt.c -o sqrt
/usr/bin/ld: /tmp/ccu0cNOk.o: in function `main':
sqrt.c:(.text+0x45): undefined reference to `sqrt'
collect2: error: ld returned 1 exit status
make: *** [<builtin>: sqrt] Error 1The math library is linked differently than most other C libraries (due to historical reasons). That’s why when trying to compile this program the usual way, it fails! We need to link the library using another argument.
Back to makefile
Let’s solve the linking issue and make use of the convenient targets to smoothe our compiling experience.
CC=gcc
CFLAGS=-Wall -Wextra -g
LDFLAGS=-lm
BIN_FILES=hello sqrt
all: $(BIN_FILES) # compile all programs
hello:
$(CC) $(CFLAGS) hello.c -o hello
sqrt:
$(CC) $(CFLAGS) sqrt.c -o sqrt $(LDFLAGS)
clean: # clean the binaries
rm -f $(BIN_FILES)Let’s save the names of the binary programs into a variable called BIN_FILES, we can change the the clean target accordingly.
Then, let’s make all target, which compiles all of our programs and creates executables for them. This is done by declaring dependencies after the target’s name. Dependencies are targets that need to be run before the original target can be run. We can make an empty target here (all) but create dependencies for it to compile both of our programs.
Finally, we will define the LDFLAGS variable that specifies the linking options of our compiler. -lm option will link the mathematical library accordingly now and is placed at the end of the compiler command. Otherwise, the compilation is the same, as you can see.
Comments are created the same way as in Python. Type # to begin your comment. I added these for more clarity.
Let’s clear our structure using make clean and compile everything again.
$ make
gcc -Wall -Wextra -g hello.c -o hello
gcc -Wall -Wextra -g sqrt.c -o sqrt -lmNotice that the chosen target is all without specifying it. That is because it is defined as the first target in our makefile. The commands specified in the makefile are always written to the command line (if not prepended with @).
You can play around with the new program now. They are both available and ready to execute. You can clean them afterward. There is no need to have you executables sitting around if you can compile them with a single command 🙂
What’s next?
Automatic variables, implicit rules, header files, static and dynamic libraries… There are tons of things that you can do with your Makefile. Those are just some of those that came to my mind. I’m preparing an advanced lesson for Makefiles, so stay tuned if you want to learn more 😉 Makefiles aren’t just used for compiling C programs (though they are good at that). Makefile is a great automation tool that can be run anywhere and has a great support!
These basics of the make command and of makefiles in general will suit your needs for most parts of your C life. I bet you’re tired of compiling your programs now and would love to learn more about C only. That is coming in the next lesson. Formatted output, data types, and much more! 😁
See you in the next one!