Jade Dungeon

makefile

基本语法

void print_hello();
int factorial(int n);
#include "functions.h"

int factorial(int n){
	if (n!=1) return n * factorial(n-1);
	else return 1;
}
#include <iostream>

void print_hello() {
	std::cout << "hello world" << std::endl;
}
# include <iostream>
# include "functions.h"

int main() {
	print_hello();
	std::cout << "this is main" << std::endl;
	std::cout << "The factorial of 5 is " << factorial(5) << std::endl;
	return 0;
}

定义目标

  • 目标文件:依赖文件定义了文件之间的依赖关系。
  • 依赖关系下一行用TAB缩进,是任务是执行命令。
CC = g++
SRC_DIR = src
OUT_DIR = out
CFLAGS = -c -Wall
LFLAGS = -Wall
CYGLIB = -static-libgcc -static-libstdc++

all: $(OUT_DIR)/hello
$(OUT_DIR)/hello: $(OUT_DIR)/main.o $(OUT_DIR)/function1.o $(OUT_DIR)/function2.o
	$(CC) $(LFLAGS) $(CYGLIB) $(OUT_DIR)/main.o $(OUT_DIR)/function1.o $(OUT_DIR)/function2.o -o $(OUT_DIR)/hello
$(OUT_DIR)/main.o: $(SRC_DIR)/main.cpp
	$(CC) $(CFLAGS) $(CYGLIB) -c $(SRC_DIR)/main.cpp -o $(OUT_DIR)/main.o
$(OUT_DIR)/function1.o: $(SRC_DIR)/function1.cpp
	$(CC) $(CFLAGS) $(CYGLIB) -c $(SRC_DIR)/function1.cpp -o $(OUT_DIR)/function1.o
$(OUT_DIR)/function2.o: $(SRC_DIR)/function2.cpp
	$(CC) $(CFLAGS) $(CYGLIB) -c $(SRC_DIR)/function2.cpp -o $(OUT_DIR)/function2.o 

clean:
	rm -rf $(OUT_DIR)/*.o $(OUT_DIR)/hello

变量代替目标与依赖文件

内置变量可以作为目标与依赖的缩写:

  1. $@: 指代 all ,即 target
  2. $<: 指代 第一个 dependency
  3. $^: 指代 所有的 dependencies
CC = g++
SRC_DIR = src
OUT_DIR = out
CFLAGS = -c -Wall
LFLAGS = -Wall
CYGLIB = -static-libgcc -static-libstdc++

all: $(OUT_DIR)/hello
$(OUT_DIR)/hello: $(OUT_DIR)/main.o $(OUT_DIR)/function1.o $(OUT_DIR)/function2.o
	$(CC) $(LFLAGS) $(CYGLIB) $^ -o $@
$(OUT_DIR)/main.o: $(SRC_DIR)/main.cpp
	$(CC) $(CFLAGS) $(CYGLIB) $^ -o $@
$(OUT_DIR)/function1.o: $(SRC_DIR)/function1.cpp
	$(CC) $(CFLAGS) $(CYGLIB) $^ -o $@
$(OUT_DIR)/function2.o: $(SRC_DIR)/function2.cpp
	$(CC) $(CFLAGS) $(CYGLIB) $^ -o $@

clean:
	rm -rf $(OUT_DIR)/*.o $(OUT_DIR)/hello

文件名查找与替换

wildcard用来查找指定目录下指定类型的文件:

SOURCE_DIR = . # 如果是当前目录,也可以不指定
SOURCE_FILE = $(wildcard $(SOURCE_DIR)/*.cpp)
target:
        @echo $(SOURCE_FILE)

其中@echo前加@是为了避免命令回显。

patsubst 应该是 pattern substitution 的缩写。用它可以方便地将.cpp文件的后缀换成.o。 它的基本语法是:$(patsubst 原模式,目标模式,文件列表)

SOURCES = main.cpp function1.cpp function2.cpp
OBJS = $(patsubst %.cpp, %.o, $(SOURCES))
target:
        @echo $(SOURCES)
        @echo $(OBJS)

新版本:

CC = g++

SRC_DIR = src
OUT_DIR = out
OBJS = $(patsubst $(SRC_DIR)/%.cpp, $(OUT_DIR)/%.o, $(wildcard $(SRC_DIR)/*.cpp))

CFLAGS = -c -Wall
LFLAGS = -Wall
CYGLIB = -static-libgcc -static-libstdc++

all: $(OUT_DIR)/hello
$(OUT_DIR)/hello: $(OBJS)
	$(CC) $(LFLAGS) $(CYGLIB) $^ -o $@
$(OUT_DIR)/main.o: $(SRC_DIR)/main.cpp
	$(CC) $(CFLAGS) $(CYGLIB) $^ -o $@
$(OUT_DIR)/function1.o: $(SRC_DIR)/function1.cpp
	$(CC) $(CFLAGS) $(CYGLIB) $^ -o $@
$(OUT_DIR)/function2.o: $(SRC_DIR)/function2.cpp
	$(CC) $(CFLAGS) $(CYGLIB) $^ -o $@

clean:
	rm -rf $(OUT_DIR)/*.o $(OUT_DIR)/hello

静态替换

Static Pattern Rule,其语法为:targets: target-pattern: prereq-patterns

  • 其中targets不再是一个目标文件了,而是一组目标文件。
  • target-pattern则表示目标文件的特征。例如目标文件都是.o结尾的,那么就将其表示为%.o
  • prereq-patterns表示依赖文件的特征,例如依赖文件都是.cpp结尾的,那么就将其表示为%.cpp

通过上面的方式,可以对 targets 列表中任何一个元素,找到它对应的依赖文件, 例如通过targets中的main.o,可以锁定到main.cpp

CC = g++

SRC_DIR = src
OUT_DIR = out
OBJS = $(patsubst $(SRC_DIR)/%.cpp, $(OUT_DIR)/%.o, $(wildcard $(SRC_DIR)/*.cpp))

CFLAGS = -c -Wall
LFLAGS = -Wall
CYGLIB = -static-libgcc -static-libstdc++

all: $(OUT_DIR)/hello
$(OUT_DIR)/hello: $(OBJS)
	$(CC) $(LFLAGS) $(CYGLIB) $^ -o $@
$(OBJS): $(OUT_DIR)/%.o : $(SRC_DIR)/%.cpp
	$(CC) $(CFLAGS) $(CYGLIB) $< -o $@

clean:
	rm -rf $(OUT_DIR)/*.o $(OUT_DIR)/hello

增加功能

递归编译子目录下的源代码

wildcard方法不能递归遍历出子目录下的文件,所以要自己写一个rwildcard方法, 通过递归地调用自身来一级一级列出所有子目录下的文件:

rwildcard=$(wildcard $1$2) $(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2))

找c++源代码就:

$(call rwildcard,src/,*.cpp)

gcc不能自动创建目录,调用gcc前要事先把目录创建好:

$(OBJS): $(OUT_DIR)/%.o : $(SRC_DIR)/%.cpp
	@mkdir -p $(@D)
	$(CC) $(CFLAGS) $(CYGLIB) $< -o $@

这样有多个层级源代码的makefile:

CC = g++

SRC_DIR = src
OUT_DIR = out

rwildcard=$(wildcard $1$2) $(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2))
OBJS = $(patsubst $(SRC_DIR)/%.cpp, $(OUT_DIR)/%.o, $(call rwildcard,src/,*.cpp))

CFLAGS = -c -Wall
LFLAGS = -Wall
CYGLIB = -static-libgcc -static-libstdc++

all: $(OUT_DIR)/hello
$(OUT_DIR)/hello: $(OBJS)
	$(CC) $(LFLAGS) $(CYGLIB) $^ -o $@
$(OBJS): $(OUT_DIR)/%.o : $(SRC_DIR)/%.cpp
	@mkdir -p $(@D)
	$(CC) $(CFLAGS) $(CYGLIB) $< -o $@

clean:
	rm -rf $(OUT_DIR)/* $(OUT_DIR)/hello