



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)/*~

TARGET_EXEC ?= a.out





BUILD_DIR ?= ./build

SRC_DIRS ?= ./src





SRCS := $(shell find $(SRC_DIRS) -name *.cpp -or -name *.c -or -name *.s)

OBJS := $(SRCS:%=$(BUILD_DIR)/%.o)

DEPS := $(OBJS:.o=.d)





INC_DIRS := $(shell find $(SRC_DIRS) -type d)

INC_FLAGS := $(addprefix -I,$(INC_DIRS))





CPPFLAGS ?= $(INC_FLAGS) -MMD -MP





$(BUILD_DIR)/$(TARGET_EXEC): $(OBJS)

$(CC) $(OBJS) -o $@ $(LDFLAGS)





# assembly

$(BUILD_DIR)/%.s.o: %.s

$(MKDIR_P) $(dir $@)

$(AS) $(ASFLAGS) -c $< -o $@





# c source

$(BUILD_DIR)/%.c.o: %.c

$(MKDIR_P) $(dir $@)

$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@





# c++ source

$(BUILD_DIR)/%.cpp.o: %.cpp

$(MKDIR_P) $(dir $@)

$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@









.PHONY: clean





clean:

$(RM) -r $(BUILD_DIR)





-include $(DEPS)





MKDIR_P ?= mkdir -p





No support for multiple build types (debug, optimized), changing build settings requires editing the Makefile

Output directory is hardcoded, you can't have many build directories with different setups

No install support

Does not work with Visual Studio

No unit testing support

No support for sanitizers apart from manually adding compiler arguments

No support for building shared libraries, apart from manually adding compiler arguments (remember to add -shared in your object file compile args ... or was it on link args ... or was it -fPIC)

in your object file compile args ... or was it on link args ... or was it -fPIC) No support for building static libraries at all

And so on and so on

Conclusions

Simple makefiles are a unicorn. A myth. They are figments of imagination that have not existed, do not exist and will never exist. Every single case of a supposedly simple Makefile has turned out to be a mule with a carrot glued to its forehead. The time has come to let this myth finally die.

Whenever there is a discussion online about the tools to build software, there is always That One Person that shows up and claims that all build tools are useless bloated junk and that you should "just write a simple Makefile" because that is lean, efficient, portable and does everything anyone could ever want.Like every sentence that has the word "just", this is at best horribly simplistic but mostly plain wrong. Let's dive in more detail into this. If you look up simple Makefiles on the Internet, you might find something like this page . It starts with a very simple (but useless) Makefile and eventually improves it to this:Calling this "simple" is a bit of a stretch. This snippet contains four different kinds of magic expansion variables, calls three external commands (two of which are gcc, just with different ways) and one Make's internal command (bonus question: is patsubst a GNU extension or is it available in BSD Make? what about NMake?) and requires the understanding of shell syntax. It is arguable whether this could be called "simple", especially for newcomers. But even so, this is completely broken and unreliable.As an example, if you change any header files used by the sources, the system will not rebuild the targets. To fix these issues you need to write more Make. Maybe something like this example , described asIt's unclear what the appropriate word to describe this thing is, butwould not be at the top of the list for many people.Even this improved version is broken and unreliable. The biggest issue is that changing compiler flags does not cause a recompile, only timestamps do. This is a common reason for silent build failures. It also does not provide for any way to configure the build depending on the OS in use. Other missing pieces that should be considered entry level features for build systems include:As an example of a slightly more advanced feature, cross compilation is not supported at all.These are all things you can add to this supposedly super simple Makefile, but the result will be a multi-hundred (thousand?) line monster of non-simplicityness.