patternMinor
A basic, modern, idiomatic GNU makefile
Viewed 0 times
makefileidiomaticgnumodernbasic
Problem
I have prepared a small makefile for a small project, but I am trying to find what would be the best practices; up to now I have gathered these:
Also a couple of make-quirk-addressing details like immediate assignment :=, order-only prerequisites…
So here's the makefile:
```
WARNINGS := -Wall -Wextra
CFLAGS ?= -std=gnu99 -g $(WARNINGS)
OBJDIR := obj
SRCDIR := src
# silent by default
ifeq ($(VERBOSE),1)
SILENCER :=
else
SILENCER := @
endif
# pass this environment variable to the C source
ifeq ($(DEBUG_BUILD),1)
CFLAGS +=-DDEBUG_BUILD
endif
# typical way to list files and build full paths
# list the sources, not the object files (nor includes)
_SRCS := uthreads.c main.c
SRCS := $(patsubst %,$(SRCDIR)/%,$(_SRCS))
OBJS := $(patsubst %,$(OBJDIR)/%,$(_SRCS:c=o))
# generate phony deps during compilation
CFLAGS += -MMD -MP
DEPS := $(patsubst %,$(OBJDIR)/%,$(_SRCS:c=d))
# can't include the deps before the first, default target has appeared!
# "all" is the classical default target
all: main
createdir:
$(SILENCER)mkdir -p $(OBJDIR)
main: $(OBJS)
#@echo " LINK $^"
$(SILENCER)$(CC) $(CFLAGS) -o $@ $^
# put object and dependency files away from the sources
# create the dir before building into it
$(OBJDIR)/%.o: $(SRCDIR)/%.c | createdir
#@echo " CC $<"
$(SILENCER)$(CC) $(CFLAGS) -c -o $@ $<
clean:
$(SILENCER)$(RM) -f *~ core main
$(SILENCER)$(RM) -r $(OBJDIR)
.PHON
- relatively idiomatic (typical structures, flows, var names for Least Astonishment)
- define the products in terms of the inputs (if I have .c files and want an exec, why should I think about intermediate files?)
- up-to-date with GNU make's and gcc's capabilities (auto-dependency tracking)
- silent by default
- but can be verbose (for Eclipse integration for example)
- keeps object files in directories separate from sources
- avoids implicit rules (explicit is clearer and anyway I wanted to mute the output)
Also a couple of make-quirk-addressing details like immediate assignment :=, order-only prerequisites…
So here's the makefile:
```
WARNINGS := -Wall -Wextra
CFLAGS ?= -std=gnu99 -g $(WARNINGS)
OBJDIR := obj
SRCDIR := src
# silent by default
ifeq ($(VERBOSE),1)
SILENCER :=
else
SILENCER := @
endif
# pass this environment variable to the C source
ifeq ($(DEBUG_BUILD),1)
CFLAGS +=-DDEBUG_BUILD
endif
# typical way to list files and build full paths
# list the sources, not the object files (nor includes)
_SRCS := uthreads.c main.c
SRCS := $(patsubst %,$(SRCDIR)/%,$(_SRCS))
OBJS := $(patsubst %,$(OBJDIR)/%,$(_SRCS:c=o))
# generate phony deps during compilation
CFLAGS += -MMD -MP
DEPS := $(patsubst %,$(OBJDIR)/%,$(_SRCS:c=d))
# can't include the deps before the first, default target has appeared!
# "all" is the classical default target
all: main
createdir:
$(SILENCER)mkdir -p $(OBJDIR)
main: $(OBJS)
#@echo " LINK $^"
$(SILENCER)$(CC) $(CFLAGS) -o $@ $^
# put object and dependency files away from the sources
# create the dir before building into it
$(OBJDIR)/%.o: $(SRCDIR)/%.c | createdir
#@echo " CC $<"
$(SILENCER)$(CC) $(CFLAGS) -c -o $@ $<
clean:
$(SILENCER)$(RM) -f *~ core main
$(SILENCER)$(RM) -r $(OBJDIR)
.PHON
Solution
You should be able to simplify the definition of
For small projects that don't require complicated configuration schemes, it's often times easiest to get the list of source files by wildcard instead of manually maintaining an explicit list:
That way, you won't have to change the makefile when you add, delete, or rename source files.
Since all of your object files are going into the same output folder, you only need to create the output folder when it does not exist. You are currently calling
The second line of your recipe for "clean" should add the "-f" flag to avoid error messages when
DEPS toDEPS := $(OBJS:.o=.d)For small projects that don't require complicated configuration schemes, it's often times easiest to get the list of source files by wildcard instead of manually maintaining an explicit list:
SRCS := $(wildcard $(SRCDIR)/*.c)
OBJS := $(patsubst $(SRCDIR)/%.c,$(OBJDIR)/%.o,$(SRCS))
DEPS := $(OBJS:.o=.d)That way, you won't have to change the makefile when you add, delete, or rename source files.
Since all of your object files are going into the same output folder, you only need to create the output folder when it does not exist. You are currently calling
mkdir for every C file that gets compiled. This won't break your build, but it's unnecessary overhead and doesn't scale well. You should be able to simplify to something like this:$(OBJDIR)/%.o: $(SRCDIR)/%.c
#@echo " CC %%CODEBLOCK_2%%lt;"
$(SILENCER)$(CC) $(CFLAGS) -c -o $@ %%CODEBLOCK_2%%lt;
$(OBJS): | $(OBJDIR)
$(OBJDIR):
$(SILENCER)mkdir -p $(OBJDIR)The second line of your recipe for "clean" should add the "-f" flag to avoid error messages when
$(OBJDIR) doesn't exist.Code Snippets
DEPS := $(OBJS:.o=.d)SRCS := $(wildcard $(SRCDIR)/*.c)
OBJS := $(patsubst $(SRCDIR)/%.c,$(OBJDIR)/%.o,$(SRCS))
DEPS := $(OBJS:.o=.d)$(OBJDIR)/%.o: $(SRCDIR)/%.c
#@echo " CC $<"
$(SILENCER)$(CC) $(CFLAGS) -c -o $@ $<
$(OBJS): | $(OBJDIR)
$(OBJDIR):
$(SILENCER)mkdir -p $(OBJDIR)Context
StackExchange Code Review Q#104807, answer score: 6
Revisions (0)
No revisions yet.