# 第一个例子 myfile: echo 'Hello World!' > myfile # end of file # 第二个例子 foo.o: foo.c foo.h gcc -c foo.c bar.o: bar.c bar.h gcc -c bar.c main: main.c foo.h bar.h foo.o bar.o gcc main.c -o main foo.o bar.o # 重要语法 ## 伪目标 .PHONY: clean ## 变量 ### 变量的定义与使用 objects = program.o foo.o utils.o program : $(objects) gcc -o program $(objects) $(objects) : defs.h ### 目标指定变量 CXXFLAGS := -std=c99 -O2 CXXDFLAGS := -std=c99 -g -Wall all debug: main main: main.c gcc -o main main.c $(CXXFLAGS) debug: CXXFLAGS := $(CXXDFLAGS) # 目标指定变量 .PHONY: debug ### 替换引用 foo := a.o b.o c.o bar := $(foo:.o=.c) foobar := obj/test.o test := $(foobar:obj/%.o=%.c) ## 模式规则与自动化变量 CFLAGS := -Wall %.o %.x : %.c defs.h $(CC) $(CFLAGS) $< -o $@ %.x : CFLAGS += -g %.o : CFLAGS += -O2 ## order-only依赖 DIRS := obj bin $(DIRS): mkdir $@ obj/%.o : %.c | obj gcc -o $@ -c $< bin/% : obj/%.o | bin gcc -o $@ $< ## 条件语句 info := 0 ifeq ($(MAKECMDGOALS),test) test: clean ifneq ($(info),0) echo info=1 else echo info=0 endif echo This is target [test]. clean: rm -f bin/* rm -f obj/* endif ## 忽略错误 clean: -rmdir obj -rmdir bin ## 引用外部文件 Objects := obj/foo.o obj/bar.o all: $(Objects) ifneq ($(MAKECMDGOALS),clean) -include $(Objects:.o=.d) endif clean: rm -f bin/* rm -f obj/* %.d: echo "$(@:.d=.o) : $(@:obj/%.d=%.c)\n\tgcc -c \$$< -o \$$@" > $@ obj/bar.o : bar.c gcc -c $< -o $@ # 第三个例子 . ├── debug │ └── debug.cpp ├── defs.h ├── main.cpp ├── Makefile └── src ├── command.cpp ├── file.cpp └── utils └── utils.cpp CXX := g++ RM := rm -f MKDIR := mkdir -p RMDIR := rmdir CXXFLAGS := -std=c++11 -O2 LDFLAGS := CXXINCLUDE := -I. OBJDIR := obj BINDIR := bin Main := $(BINDIR)/main Header := defs.h Objects := $(patsubst ./%.cpp, $(OBJDIR)/%.o, $(shell find . -name '*.cpp')) Dirs := $(BINDIR) $(shell echo $(patsubst %/,%,$(dir $(Objects))) | tr ' ' '\n' | sort -u | tr '\n' ' ') all: $(Main) $(OBJDIR)/%.o: %.cpp $(Header) | $(Dirs) $(CXX) $(CXXFLAGS) $(CXXINCLUDE) -c $< -o $@ $(Main): $(Objects) $(Header) | $(Dirs) $(CXX) $(CXXFLAGS) $(CXXINCLUDE) -o $@ $^ $(LDFLAGS) $(Dirs): $(MKDIR) $@ run: $(Main) $(Main) clean: $(RM) -r $(OBJDIR) $(RM) -r $(BINDIR) .PHONY: all run clean # 易错特性 ## 变量中的空格 nullstring := space := $(nullstring) # end of the line # 高级用法 ## 目录搜寻 VPATH := src:tools:test test/test.o : test.c gcc -c $< -o $@ ## 自动生成头文件依赖 Objects := $(wildcard *.cpp) Objects := $(Objects:.cpp=.o) all: main main: $(Objects) $(CXX) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) %.o: %.cpp $(CXX) $(CXXFLAGS) $(CXXINCLUDE) -c $< -o $@ %.d: %.cpp set -e; rm -f $@; \ $(CXX) $(CXXFLAGS) $(CXXINCLUDE) -MM $< > $@.$$$$; \ sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' $@.$$$$ > $@; \ rm -f $@.$$$$ ifneq ($(MAKECMDGOALS),clean) -include $(Objects:.o=.d) endif clean: rm -f *.o *.d main .PHONY: all clean # 第四个例子 . ├── main.cpp ├── Makefile ├── src │ ├── command.cpp │ ├── command.h │ ├── console.cpp │ └── console.h ├── unit_test.cpp └── utils ├── database.cpp ├── database.h ├── file.cpp └── file.h CXX := g++ RM := rm -f MKDIR := mkdir -p RMDIR := rmdir SED := sed CXXFLAGS := -std=c++11 -O2 CXXDFLAGS := -std=c++11 -g -Wall LDFLAGS := VPATH := src:utils CXXINCLUDE := $(patsubst %,-I%,$(subst :, ,$(VPATH))) OBJDIR := obj BINDIR := bin Dirs := $(OBJDIR) $(BINDIR) Main := $(BINDIR)/main Test := $(BINDIR)/unit_test Objects := $(patsubst %.cpp, $(OBJDIR)/%.o, $(notdir $(wildcard *.cpp */*.cpp))) all: $(Main) $(Test) $(OBJDIR)/%.o: %.cpp | $(Dirs) $(CXX) $(CXXFLAGS) $(CXXINCLUDE) -c $< -o $@ $(OBJDIR)/%.d: %.cpp | $(Dirs) @set -e; $(RM) $@; \ $(CXX) $(CXXFLAGS) $(CXXINCLUDE) -MM $< > $@.$$$$; \ $(SED) -i 's,\($*\)\.o[ :]*,\1.o $@ : ,g' $@.$$$$; \ $(SED) '1s/^/$(OBJDIR)\//' < $@.$$$$ > $@; \ $(RM) $@.$$$$ ifneq ($(MAKECMDGOALS),clean) -include $(Objects:.o=.d) endif $(Main): $(filter-out $(OBJDIR)/$(subst $(BINDIR)/,,$(Test).o), $(Objects)) $(CXX) $(CXXFLAGS) $(CXXINCLUDE) -o $@ $^ $(LDFLAGS) $(Test): $(filter-out $(OBJDIR)/$(subst $(BINDIR)/,,$(Main).o), $(Objects)) $(CXX) $(CXXFLAGS) $(CXXINCLUDE) -o $@ $^ $(LDFLAGS) $(Dirs): $(MKDIR) $@ debug: CXXFLAGS := $(CXXDFLAGS) debug: $(Main) $(Test) run: $(Main) $(Main) test: $(Test) $(Test) clean: $(RM) $(OBJDIR)/*.o $(RM) $(OBJDIR)/*.d $(RM) $(Main) $(RM) $(Test) -$(RMDIR) $(OBJDIR) -$(RMDIR) $(BINDIR) .PHONY: all debug run test clean # 其他语法 ## 其他函数 dirs := a b c d files := $(foreach dir,$(dirs),$(wildcard $(dir)/*)) files := $(wildcard a/* b/* c/* d/*) reverse = $(2) $(1) foo = $(call reverse,a,b) ## 宏 define mycmd echo 'compling .cpp to .o using mycmd' $(CXX) $(CXXFLAGS) $(CXXINCLUDE) -c $< -o $@ endef %.o: %.cpp $(mycmd)