Here is the standard way I usually define clean
target in hand-written Makefiles:
clean:
rm -fr $(BUILD_DIR)/*
If there is a typo in the variable or I forgot to define it, it will run rm -rf /*
instead, which is not a risk I want to accept. But after having an accident when exactly this happened, I've been trying to find a better way to reference paths in Makefile in a safe way and I couldn't find it.
Is there a better way?
Let's start with there are many, many ways to structure a Makefile
to make things easier to clean. Most are by personal preference, and therefore not a right or wrong issue.
In your case, the thing that stands out is the attempt to cleanup with a single rm
command under the clean:
phony target. It is far easier to use multiple specific rm
commands rather than one "encompasses all" rm -r
. As you have found, a misnamed or absent variable setting can have dire consequences when you rm -r $(SOMEVAR)/*
.
What I generally do is put sources in the src
subdirectory, includes in the include
subdirectory and then have all object files output to an obj
subdirectory. (you can also have binaries created in a bin
directory if you like). In that case clean:
becomes
clean:
rm -rvf $(OBJ)
rm -rvf $(BIN)
An easy setup that allows you to do just that is:
TARGET = myexe
CC = gcc
BIN = bin
SRC = src
OBJ = obj
SRCS = $(wildcard $(SRC)/*.c)
OBJS = $(patsubst $(SRC)%.c, $(OBJ)/%.o, $(SRCS))
CFLAGS += -Wall -Wextra -pedantic -Wshadow -Werror -std=c11
CFLAGS += -Iinclude
LDFLAGS +=
# combines clean and create directories before build
all: clean setup $(TARGET)
# create directories
setup:
@mkdir -p $(BIN)
@mkdir -p $(OBJ)
# build executable
$(TARGET): $(OBJS)
$(CC) $(CFLAGS) $(LDFLAGS) -o $(BIN)/$(TARGET) $^
# compile objects
$(OBJ)/%.o: $(SRC)/%.c
$(CC) -c $(CFLAGS) $< -o $@
# clean
clean:
@rm -rvf $(OBJ)
@rm -rvf $(BIN)
(note: shown for building C sources, adjust extensions as needed)
This way you never have the potential to rm -rf $(SOMEVAR)/*
when SOMEVAR
is undefined or empty. Good luck with your coding.