Bryan Hunt
Bryan Hunt

Reputation: 3783

Make unnecessarily recompiles a target

Firstly, here's my Makefile, it's really, really cool. It generates an executable for every 'c' file in the current directory. Trivial with shell, PITA with Make... Did I mention, I haven't 'c'd in a decade so please don't laugh...

CC = gcc
#List all 'c' files by wildcard.
SOURCES=$(wildcard *.c)
#Get the 'exe' equivalent of said file
EXECUTABLES=$(SOURCES:%.c=%)

#For each of the '*.exe' files
#append a 'c' suffix to the target
build: $(EXECUTABLES)
    $(CC) -c $(<:%=%.c) -o $<

clean:
    find ./ ! -name "*.c" -a ! -name "Makefile" -type f -delete

rebuild: clean build

Here are contents of my directory:

% ls -la
total 32
drwxr-xr-x  2 yomom yomom 4096 2012-01-27 13:38 ./
drwx------ 94 yomom yomom 4096 2012-01-27 13:38 ../
-rw-r--r--  1 yomom yomom  990 2012-01-27 12:47 array_example.c
-rw-r--r--  1 yomom yomom  341 2012-01-27 13:38 Makefile
-rw-r--r--  1 yomom yomom  430 2011-12-05 13:08 pointers.c
-rw-r--r--  1 yomom yomom  319 2012-01-27 12:45 should_create_warning.c
-rw-r--r--  1 yomom yomom 1472 2011-12-19 16:16 socket-client.c
-rw-r--r--  1 yomom yomom 1150 2011-12-19 16:15 socket-server.c

Now I run make

% make
gcc     array_example.c   -o array_example
gcc     pointers.c   -o pointers
gcc     should_create_warning.c   -o should_create_warning
gcc     socket-client.c   -o socket-client
gcc     socket-server.c   -o socket-server
gcc -c array_example.c -o array_example

Great, it re-compiled everything, the last line of output looked a bit inconsistent though. How come it's different to the others?

% ls -la
total 68
drwxr-xr-x  2 yomom yomom 4096 2012-01-27 13:38 ./
drwx------ 94 yomom yomom 4096 2012-01-27 13:38 ../
-rw-r--r--  1 yomom yomom 1428 2012-01-27 13:38 array_example
-rw-r--r--  1 yomom yomom  990 2012-01-27 12:47 array_example.c
-rw-r--r--  1 yomom yomom  341 2012-01-27 13:38 Makefile
-rwxr-xr-x  1 yomom yomom 7164 2012-01-27 13:38 pointers*
-rw-r--r--  1 yomom yomom  430 2011-12-05 13:08 pointers.c
-rwxr-xr-x  1 yomom yomom 7139 2012-01-27 13:38 should_create_warning*
-rw-r--r--  1 yomom yomom  319 2012-01-27 12:45 should_create_warning.c
-rwxr-xr-x  1 yomom yomom 7515 2012-01-27 13:38 socket-client*
-rw-r--r--  1 yomom yomom 1472 2011-12-19 16:16 socket-client.c
-rwxr-xr-x  1 yomom yomom 7579 2012-01-27 13:38 socket-server*
-rw-r--r--  1 yomom yomom 1150 2011-12-19 16:15 socket-server.c

I run make again, expecting it to NOP, after all, I haven't changed anything

% make
gcc -c array_example.c -o array_example

But it producted output, what gives ?

% ls -la
total 68
drwxr-xr-x  2 yomom yomom 4096 2012-01-27 13:51 ./
drwx------ 94 yomom yomom 4096 2012-01-27 13:51 ../
-rw-r--r--  1 yomom yomom 1428 2012-01-27 13:51 array_example
-rw-r--r--  1 yomom yomom  990 2012-01-27 12:47 array_example.c
-rw-r--r--  1 yomom yomom  341 2012-01-27 13:38 Makefile
-rwxr-xr-x  1 yomom yomom 7164 2012-01-27 13:38 pointers*
-rw-r--r--  1 yomom yomom  430 2011-12-05 13:08 pointers.c
-rwxr-xr-x  1 yomom yomom 7139 2012-01-27 13:38 should_create_warning*
-rw-r--r--  1 yomom yomom  319 2012-01-27 12:45 should_create_warning.c
-rwxr-xr-x  1 yomom yomom 7515 2012-01-27 13:38 socket-client*
-rw-r--r--  1 yomom yomom 1472 2011-12-19 16:16 socket-client.c
-rwxr-xr-x  1 yomom yomom 7579 2012-01-27 13:38 socket-server*
-rw-r--r--  1 yomom yomom 1150 2011-12-19 16:15 socket-server.c

And it's re-compiled that array_example file, why always that one?

THE SOLUTION (Thanks to Eldar Abusalimov)

CC = gcc
#List all 'c' files by wildcard.
SOURCES=$(wildcard *.c)
#Get the 'exe' equivalent of said file
EXECUTABLES=$(SOURCES:%.c=%)

all: $(EXECUTABLES)
.PHONY: all
$(EXECUTABLES): % : %.c
    $(CC) -c $< -o $@

.PHONY clean:
clean:
    find ./ ! -name "*.c" -a ! -name "Makefile" -type f -delete

rebuild: clean all

FINAL SESSION

Remove all generated files

% make clean 
find ./ ! -name "*.c" -a ! -name "Makefile" -type f -delete

% ls -la
total 48
drwxr-xr-x  2 yomom yomom 4096 2012-01-27 18:07 ./
drwx------ 94 yomom yomom 4096 2012-01-27 18:07 ../
-rw-r--r--  1 yomom yomom  990 2012-01-27 17:38 array_example.c
-rw-r--r--  1 yomom yomom  428 2012-01-27 17:38 array_of_pointers.c
-rw-r--r--  1 yomom yomom  274 2012-01-27 17:38 const_ptr.c
-rw-r--r--  1 yomom yomom  293 2012-01-27 17:38 function_pointers.c
-rw-r--r--  1 yomom yomom  313 2012-01-27 18:06 Makefile
-rw-r--r--  1 yomom yomom  430 2012-01-27 17:38 pointers.c
-rw-r--r--  1 yomom yomom  228 2012-01-27 17:38 pointer_to_constant.c
-rw-r--r--  1 yomom yomom  253 2012-01-27 17:38 pointer_to_pointer.c
-rw-r--r--  1 yomom yomom 1472 2012-01-27 17:38 socket-client.c
-rw-r--r--  1 yomom yomom 1150 2012-01-27 17:38 socket-server.c

Run make again

% make
gcc -c array_example.c -o array_example
gcc -c array_of_pointers.c -o array_of_pointers
gcc -c const_ptr.c -o const_ptr
gcc -c function_pointers.c -o function_pointers
gcc -c pointers.c -o pointers
gcc -c pointer_to_constant.c -o pointer_to_constant
gcc -c pointer_to_pointer.c -o pointer_to_pointer
gcc -c socket-client.c -o socket-client
gcc -c socket-server.c -o socket-server

Now it doesn't do silly things anymore...

% make
make: Nothing to be done for `all'.

Upvotes: 0

Views: 108

Answers (1)

Eldar Abusalimov
Eldar Abusalimov

Reputation: 25483

Just remove the recipe of build: $(EXECUTABLES). First, such rule is not supposed to create a build file. Second, and answering to your question about why only array_example is being always recompiled, $(<:%=%.c) results in the first listed prerequisite, which is likely to be the first one returned by wildcard (which sorts the result in the lexicographical order, not guaranteed, but usually it does). That is,

SOURCES=$(wildcard *.c)# returns 'array_example.c pointers.c ...'.
EXECUTABLES=$(SOURCES:%.c=%)# is 'array_example pointers ...'.

build: $(EXECUTABLES)
# Here '$<' is the first item of '$(EXECUTABLES)', which is 'array_example'.
# And '$(<:%=%.c)' returns 'array_example.c'
    $(CC) -c $(<:%=%.c) -o $<

Finally, don't forget to add .PHONY target.

.PHONY: build
build: $(EXECUTABLES)

Make builds all programs listed in $(EXECUTABLES) using an implicit rule to make executables from C sources. There is no need to specify anything else.

In case when the implicit rule is not OK, you can, however, override it, e.g. as follows:

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

Upvotes: 3

Related Questions