GNU make
GNU Make 就是用来控制生成可执行文件的这么一个工具,它的目的就是把一个已经写好的源文件生成为可执行的程序。想要完成这个任务,Make需要一个makefile的文件来记录如何生成我们想要的程序,这里面包含了要生成的目标对象信息,已经生成这个目标对象所需要依赖的其他文件,生成目标对象的过程指令等。如果,你已经写好了一个程序,你可以为其写一个 makefile,然后使用Make去编译和安装,总之,make并非编译语言的特权,作为一个工具,make实现了一套半自动化的工作流程,你可以用这个流程来做任何东西。
make 的功能
- Make 让终端用户在不需要知道细节情况下,编译和安装我们写好的程序——具体的细节都保存在 makefile 文件中;
- Make 根据那些源文件被修改过,可以自动完成对相应源文件的更新,同时,也可以自动确定更新源文件恰当顺序,除非一个非源文件依赖于另一个非源文件;
- Make 不限制于任何特定的语言。对于程序任何的非源文件,makefile 通过 shell 命令去生成对应的可执行程序。这些_shell_命令可以执行编译命令生成可执行文件,执行连接命令生成可执行文件,ar 命令去更新库,或者 Tex、Makeinfo 命令完成文档的格式;
- Make不限于用于完成安装包的编译。你还可以使用Make去控制安装/卸载安装包、为其生成标记表,或者其他你认为用Make去完成是又价值的事情。
make 规则
Makefile由一组规则(Rule)组成,每条规则的格式
目标 : 条件
目标和条件之间的关系是:欲更新目标,必须先更新它的所有条件;所有条件中只要有一个条件被更新了,目标也必须随之被更新。所谓“更新”就是执行一遍规则中的命令列表,命令列表中的每条命令必须以一个Tab开头,注意不能用空格代替这个Tab,Makefile的格式不像C语言的缩进那么随意。对于Makefile中的每个以Tab开头的命令,make会启动一个Shell进程去执行它。
make 判断是否需要更新是根据文件的修改时间来判断的,如果目标文件的修改时间相较于源文件是更新的,那就不会对目标文件进行更新,如果源代码文件比目标文件新,那么就会对目标文件进行更新,而且是仅仅更新那一部分,而不是全部都重新编译一遍。
注意事项:
make是一个半自动化工具,不涉及编译等任何功能实现,通过调用编译器等底层工具来完成工作。
规则语句前面是制表符TAB,写成空格会出错!
赋值符号:=基本赋值,:=覆盖之前的指,?=如果没有值则赋值,+=继续添加后面的值。
$@ 表示目标文件。 $^ 表示所有依赖项。 $< 表示第一个依赖文件
tutorial
A Makefile consists of a set of rules. A rule generally looks like this:
targets: prerequisites
command
command
command
实践
以下实践例子来自于仕琪副教授发布于B站教学视频,链接在此
以下编写了一个Makefile文件,对包含四个源文件文件的demo项目进行半自动化编译,从Version1到Version4,从简陋到专业。
## Version 1
# disadvantage: too many file, if there is one file that updated, make will rebuild all file, this would waste much time.
hello: main.cpp printhello.cpp factorial.cpp
g++ -o hello main.cpp printhello.cpp factorial.cpp
## Version 2
# more pros
# disadvantage: this version have finish the basic function as a tool,but if we want change the compile flag, it cannot do it.
CXX = g++
TARGET = hello
OBJ = main.o printhello.o factorial.o
$(TARGET): $(OBJ)
$(CXX) -o $(TARGET) $(OBJ)
main.o: main.cpp
$(CXX) -c main.cpp
printhello.o: printhello.cpp
$(CXX) -c printhello.cpp
## Version 3
CXX = g++
TARGET = hello
OBJ = main.o printhello.o factorial.o
CXXFLAGS = -c -Wall
# it means show all of the warning
$(TARGET): $(OBJ)
$(CXX) -o $@ $^
# "$@" means the target file,"$^" means all the dependent file
$(info all: $^)
$(info target: $@)
%.o:%.cpp
$(CXX) $(CXXFLAGS) $< -o $@
# "$<" means the first dependent file
$(info first: $<)
.PHONY: clean
# this line mean that .PHONY dependent on clean ,so the clean will be executed,the purpose of this line is to prevent clean would not be executed when there is a file which named clean in the dir
clean:
rm -f *.o $(TARGET)
## Version 4
CXX = g++
TARGET = hello
SRC = $(wildcard *.cpp)
OBJ = $(patsubst %.cpp, %.o, $(SRC))
CXXFLAGS = -c -Wall
$(TARGET):$(OBJ)
$(CXX) -o $@ $^
%.o:%.cpp
$(CXX) $(CXXFLAGS) $< -o $@