Compile to many files with Make

2017-07-29

Categories: software Tags: c Make guide

Make is a brilliant little tool you can use to automate eg. compiling your C programs or run scripts from the command line.
You can set up your commands/rules in a file called Makefile and then run them from the command line.

The problem

What I wanted to do is to automate compiling multiple C programs into separate executables. My project folder looks something like this:

.
	Makefile
	src/
		prog1.c
		prog2.c
		prog3.c
	build/
		prog1
		prog2
		prog3

What I wanted to do is run a simple command like make all and have all the .c files with the program’s source in them compiled into the corresponding files in build/.

The solution

Here is the Makefile I came up with:

srcfiles = $(notdir $(wildcard src/*.c))
outputs = $(basename $(srcfiles))

all: $(outputs)

$(outputs): %: src/%.c
    gcc -o build/$@ $<

I’ll break down what it does.

The breakdown

In the first section we declare some variables. srcfiles is the list of C source files, outputs is the list of executable files we want to build.

The first line creates a list of all the .c files in the src folder ($(wildcard src/*.c)), then removes the part of the path that’s the directory with $(notdir). What we end up there is a list of files: srcfiles = prog1.c prog2.c prog3.c.
The next line uses the list above to create the list of compiled executable files by removing the file extension with $(basename).

The next part sets up a rule called “all” and tells it to run this rule on all names in outputs. At this point Make doesn’t yet know what we want it to do, but we’re about to solve that.

The last section sets up a rule that is valid for all files in the outputs list. This means whenever any other rule in this Makefile would effect a name from the outputs list, Make will look at this rule and use it. This is what we’re declaring by starting the line with $(outputs):.
After the first colon we declare that every file in the outputs list (% basicall means “every element”) depends on the file with the same name and a .c extension in the src folder. All that’s left to do here is run the C compiler “gcc” and give it all the needed input. The -o flag tells the compiler what the output file it compiles into should be. Here we use the special variable $@ which points to the filename of the target of the rule, in our case the file from the outputs list. At the end $< points to the first dependency, in out case src/%.c which Make then interprets as src/NAME FROM OUTPUTS.c.

With this Makefile if we run make all in the project directory, all the files in src/ will be compiled into build/ under the correct name.

If you’d like to read a bit more on how to manipulate filenames and text with Make, I recommend the documentation for GNU Make, especially the sections on text functions, filename functions and automatic variables.