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.