Reputation: 31
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
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
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