Harry Austen
Harry Austen

Reputation: 31

Compiling both C and CPP source in a single makefile target

I have a makefile where I am working with both C source and CPP source. Below is a snippet of code from the makefile. Is there a way to combine the following two targets to compile both filetypes?

#definitions
OBJ_DIR := obj
DEP_DIR := dep

CXX := g++
DEBUG := -g -O0
OPT := -std=c++11 -Wextra -Wall -pthread
LFLAGS = $(DEBUG) $(OPT) $(INC)
CFLAGS = $(LFLAGS) -c

#auto-dependency generation (part 1)
DEP_FLAGS = -MT $@ -MMD -MP -MF $(DEP_DIR)/$*.Td
POSTCOMPILE = @mv -f $(DEP_DIR)/$*.Td $(DEP_DIR)/$*.d && touch $@

#compile object files from CPP source
$(OBJ_DIR)/%.o: %.cpp $(DEP_DIR)/%.d
    $(CXX) $(CFLAGS) $(DEP_FLAGS) $< -o $@
    $(POSTCOMPILE)

#compile object files from C source
$(OBJ_DIR)/%.o: %.c $(DEP_DIR)/%.d
    $(CXX) $(CFLAGS) $(DEP_FLAGS) $< -o $@
    $(POSTCOMPILE)

#auto-dependency generation (part 2)
$(DEP_DIR)/%.d: ;
.PRECIOUS: $(DEP_DIR)/%.d
include $(wildcard $(DEP_DIR)/*.d)

I have tried to use the wildcard function with a variety of different formatting using second expansion but to no avail.

I am using Make 4.2. The auto-dependency generation code was taken from http://make.mad-scientist.net/papers/advanced-auto-dependency-generation/.

Thanks in advance

Upvotes: 3

Views: 1722

Answers (2)

Martin F Schulz
Martin F Schulz

Reputation: 11

beware of setting your OBJ_DIR or DEP_DIR to '.' or $(RM) will either succeed or reluctantly fail to delete your working directory. Also make tends to strip off a leading ./ in rules (I guess that's mostly desirable), but it will get in the way if you use CPPFLAGS in a rule and OBJ_DIR as '.' Also you cannot leave those *_DIR simply empty (or you are writing to root (/). And if OBJ_DIR == DEP_DIR, your rule to make the directories will complain. Much of this can be salvaged with a couple of conditionals (or never changing the working Makefile).

Upvotes: 0

Chnossos
Chnossos

Reputation: 10486

Using the right variables as well as the define, call and eval features, the following is possible:

EXT := c cpp

define rule =
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.$(1)
    $$(COMPILE.$(1)) $$< -o $$@
endef

$(foreach ext, $(EXT), $(eval $(call rule,$(ext)))) # NO SPACE before $(ext)!!!

make has implicit variables and rules, especially many COMPILE.* rules (you can see them all by issuing the shell command make -p | grep 'COMPILE.* ='):

COMPILE.c   = $(CC)  $(CFLAGS)   $(CPPFLAGS) $(TARGET_ARCH) -c
COMPILE.cpp = $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c

CPPFLAGS is used for preprocessor flags (cpp is the preprocessor executable in the GNU GCC toolchain). TARGET_ARCH is empty on most platforms by default.


Here is a full yet minimalist working Makefile with better auto-dependency generation (note that putting the .d files in a folder separate from the .o pointlessly complicates the makefile):

TARGET  := executable
EXT     := c cpp

SRC_DIR := .
OBJ_DIR := obj
DEP_DIR := dep

CPPFLAGS  = -MMD -MP -MF $(@:$(OBJ_DIR)/%.o=$(DEP_DIR)/%.d)
CFLAGS   := -Wall -Wextra -pthread
CXXFLAGS := -std=c++11 $(CFLAGS)
LDFLAGS  := -pthread

SOURCE := $(foreach ext, $(EXT), $(wildcard $(SRC_DIR)/*.$(ext)))
OBJECT := $(SOURCE:$(SRC_DIR)/%=$(OBJ_DIR)/%.o)
DEPEND := $(OBJECT:$(OBJ_DIR)/%.o=$(DEP_DIR)/%.d)

define rule =
$(OBJ_DIR)/%.$(1).o: $(SRC_DIR)/%.$(1) | $(OBJ_DIR) $(DEP_DIR)
    $$(COMPILE.$(1)) $$< -o $$@
endef

.PHONY: all clean

all: $(TARGET)

$(TARGET): $(OBJECT)
    $(CXX) $(LDFLAGS) $^ -o $@

$(foreach ext, $(EXT), $(eval $(call rule,$(ext))))

$(OBJ_DIR) $(DEP_DIR):
    mkdir -p $@

-include $(DEPEND)

clean:
    $(RM) -r $(TARGET) $(OBJ_DIR) $(DEP_DIR)

Also note that I chose to add the original source file extension (c or cpp) to the object file name (.c.o or .cpp.o) to tackle the case where we could encounter source files with different extension but with the same name.

Upvotes: 4

Related Questions