Makefile入门

## VERSION 1 
#hello: main.cpp printhello.cpp factorial.cpp
#	g++ -o hello main.cpp printhello.cpp factorial.cpp 


## VERSION 2
#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
#
#factorial.o: factorial.cpp
#	$(CXX) -c factorial.cpp


## VERSION 3
#CXX = g++
#TARGET = hello
#OBJ = main.o printhello.o factorial.o
#
#CXXFLAGS = -c -Wall
#
#$(TARGET): $(OBJ)
#	$(CXX) -o $@ $^
#
#%.o: %.cpp
#	$(CXX) $(CXXFLAGS) $< -o $@
#
#.PHONY: clean
#clean:
#	rm -rf *.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 $@

.PHONY: clean
clean:
	rm -rf *.o $(TARGET)

VERSION 1 的问题在于,当项目中的源文件数量很大时,每次make都需要编译所有的源文件后生成最终目标文件,这会造成编译时间过长;

因此,出现了 VERSION 2 的版本,每次make只会编译被修改的源文件,而不会发生所有的源文件都要被重新编译一次(原理是,最终目标hello依赖于各个.o文件,而各个.o文件依赖于各自对应的.c源文件,只有.c源文件被修改,make检查才需要重新编译.o);

VERSION 3 是在此基础上进一步精简,使用 $@ 表示目标文件,$^ 表示所有的依赖文件,$< 表示第一个依赖文件;

VERSION 4 使用通配符 $(wildcard *.cpp) 表示匹配所有的.cpp文件,$(patsubst %.cpp, %.o, $(SRC)) 表示取出$(SRC)中的值,然后将.c替换为.o,这种方式在目录下有很多个.c源文件时,可以做到避免写很多条规则语句。

语法规则:

目标 ... : 依赖 ...
	命令1
	命令2
	...
  1. “目标”: 即要生成的文件。如果 目标文件 的更新时间晚于 依赖文件 的更新时间,则说明 依赖文件 没有改动,目标文件不需要重新编译;否则会进行重新编译并更新 目标文件;
  2. 默认情况下,Makefile的第一个目标是编译的 终极目标
  3. “依赖”: 表明目标文件由哪些文件编译生成;
  4. “命令”: 通过执行命令 由依赖文件生成目标文件。注意每条命令之前必须有一个 tab 保持缩进(不能是空格),这是语法要求(会有一些编译工具默认tab为4个空格,这会造成Makefile语法错误);
  5. all: Makefile文件默认只要生成第一个目标文件即完成编译,但可以通过 all 指定所需要生成的目标文件。

变量:

$ 符号表示 “取变量的值”,当变量名多于一个字符时,使用 “()”。

$ 符号的其他用法:

`$^` 表示所有的依赖文件
`$@` 表示生成的目标文件
`$<` 表示第一个依赖文件

变量赋值:

注意Makefile中使用 = 进行赋值时,变量的值是整个Makefile中最后被指定的值,在make时会把整个Makefile展开,来决定变量的值;

:= 表示直接赋值,赋予当前位置的值;

?= 表示如果该变量没有被赋值,则赋予等号后面的值;

+= 与平时写代码的理解是一样的,表示将符号后面的值添加到前面的变量上。

伪目标:

假如当make目录下有一个文件的文件名刚好是“clean”,此时如果执行 make clean,make会将Makefile中的clean当做一个“目标文件”,而判断当前目录下的clean文件已经是最新(up to date),导致无法执行(因为clean其实并没有依赖文件,而make判断是否需要重新编译的准则是依赖文件的更新时间要比目标文件的更新时间更“新”,说明依赖文件有新的改动了才会重新编译,由于没有依赖文件,所以“目标文件”已经是最新的了)。

解决这个问题的方法是使用 伪目标 .PHONY,它是一个标签,用于声明clean是一个伪目标,没有依赖文件,当调用make时直接执行就好了,不要判断是否需要重新编译。

.PHONY: clean 这句话相当于 .PHONY 是目标文件,clean是依赖文件,而.PHONY永远不可能存在,所以每次make clean都会重新执行。(这么理解对吗?)

main: main.c
	gcc main.c -o main
	
clean:
	rm -rf main

.PHONY: clean

你可能感兴趣的:(C/C++,c++)