user256017
user256017

Reputation: 13

What is happening in this make clean?

I am learning more about makefiles and came across an example I don't quite understand. What is happening in make clean? It is forcibly removing all object files in /obj, but I am lost after that. I know it also removes the exe file, but how is that translated in the code?

IDIR =../include
CC=gcc
CFLAGS=-I$(IDIR)

ODIR=obj
LDIR =../lib

LIBS=-lm

_DEPS = hellomake.h
DEPS = $(patsubst %,$(IDIR)/%,$(_DEPS))

_OBJ = hellomake.o hellofunc.o
OBJ = $(patsubst %,$(ODIR)/%,$(_OBJ))


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

../hellomake: $(OBJ)
        gcc -o $@ $^ $(CFLAGS) $(LIBS)

.PHONY: clean

clean:
        rm -f $(ODIR)/*.o *~ core $(INCDIR)/*~

Upvotes: 1

Views: 1902

Answers (1)

melpomene
melpomene

Reputation: 85827

Let's look at the line

rm -f $(ODIR)/*.o *~ core $(INCDIR)/*~

The first thing make does is expanding the $(...) variables, leading to:

rm -f obj/*.o *~ core /*~

The last part is likely a bug: INCDIR is not defined, so it expands to nothing. Did you mean $(IDIR) instead?

The second thing make does is running the resulting line via /bin/sh -c .... The shell expands things like wildcards.

Let's assume the object files in obj are hellomake.o and hellofunc.o. Then obj/*.o becomes obj/hellofunc.o obj/hellomake.o.

*~ matches all files in the current directory that end with ~. Why would you have such a file? Because when you use emacs to edit a file foo, it will leave a backup copy under foo~ by default. So this is intended to remove emacs backup files.

core is what you normally get when you enable core dumps and a program you run crashes (and you're not using systemd). It can be used to debug the crash.

The last pattern looks like it was meant to match emacs backup files in INCDIR, but because that's unset, it tries to find backup files in the root directory, /. Normally there are no files at all in /, let alone emacs backups.

If a pattern matches no files, the default behavior of the shell is to leave it unexpanded. So the last part (/*~) is likely left as-is. Let's assume you have no backup files in the current directory either, so *~ is also left alone:

rm -f obj/hellofunc.o obj/hellomake.o *~ core /*~

This line runs rm, passing it an array of strings as arguments:

{ "rm", "-f", "obj/hellofunc.o", "obj/hellomake.o", "*~", "core", "/*~" }

rm (following convention) scans its arguments for strings beginning with -, which it processes as options. In this case there's one option, f. f ("force") tells rm to not ask about files you don't have write permissions for; just delete them anyway. It also suppresses errors such as those you'd normally get for trying to delete non-existent files. (In particular, you probably don't have a file literally called *~.)

So the whole thing deletes

  • object files in obj/
  • core in the current directory
  • emacs backup files in the current directory
  • emacs backup files in / (which probably don't exist, and if they exist, you probably don't have permissions to delete)

while ignoring any errors.

Upvotes: 6

Related Questions