Understanding deeply how makefiles are interpreted

Hi guys!

I'm trying to understand deeply how makefiles work.

For example, I've the following one:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
CC = gcc
CFLAGS = -I.
DEPS = int_array.h
OBJS = int_array.o test_int_array.o

%.o: %.c $(DEPS)
	$(CC) -c -o $@ $< $(CFLAGS)


test_int_array: $(OBJS)
	$(CC) -o $@ $^ $(CFLAGS)


clean:
	rm -rf *.o test_int_array *.dSYM


The part that I really don't understand fully is :

1
2
3
4
5
6
7
8
9
10
...

%.o: %.c $(DEPS)
	$(CC) -c -o $@ $< $(CFLAGS)


test_int_array: $(OBJS)
	$(CC) -o $@ $^ $(CFLAGS)

...


I know that the option -c basically indicates just to run the preprocessor, compiling and assembling steps (i.e. without producing executables, I guess).

-o means to write the output to the specified file. Which file in this case?

I understood that $@ (and $^ for right) is apparently referring to a "left" side, but which one? Is it referring, in the first case, to the left side of :, that is %.o ?

What does $< mean?

Could you please explain step by step how the make tool would interpret those two statements?


I think I understood this part more or less:

1
2
3
4
5
6

...
test_int_array: $(OBJS)
	$(CC) -o $@ $^ $(CFLAGS)

...


which should mean produce an executable called "test_int_array" (which basically is indicated by these options -o $@ from the $(OBJS) files on the right (stated using the option $^).

Is $(CFLAGS) needed in both cases? Does the order matter?
Last edited on
https://www.gnu.org/software/make/manual/html_node/Automatic-Variables.html

The variables expand, so
1
2
test_int_array: $(OBJS)
	$(CC) -o $@ $^ $(CFLAGS)

expands to
gcc -o test_int_array int_array.o test_int_array.o -I.

That, however is essentially a call to linker, and linker does not need to include (-I.). There could be linking-specific options instead.

Wait, I said "expands to", but I did show only the command to run.
There is the other line too:
test_int_array: int_array.o test_int_array.o
which says that the target test_int_array cannot be assembled before the two files
int_array.o and test_int_array.o are up to date.

Each of them invokes te previous rule:
1
2
%.o: %.c $(DEPS)
	$(CC) -c -o $@ $< $(CFLAGS)


Which says: If %.o does not exist or is older than the %.c $(DEPS), then run this command ...

In other words, make creates these rules on the fly:
1
2
3
4
5
int_array.o: int_array.c int_array.h
	gcc -c -o int_array.o int_array.c -I.

test_int_array.o: test_int_array.c int_array.h
	gcc -c -o test_int_array.o test_int_array.c -I.


Lets recap. When you run
make

it attemtpts to create the first target in the file: test_int_array (unless such file already exists and is no older than its requirements).
In order to do that, it forms some concrete rules and (perhaps) ends up running the following commands:
gcc -c -o int_array.o int_array.c -I.
gcc -c -o test_int_array.o test_int_array.c -I.
gcc -o test_int_array int_array.o test_int_array.o -I.
Topic archived. No new replies allowed.