相当于一个编译器,在没有IDE的情况下,可以用cmd命令执行Makefile文件来编译工程。
在默认的方式下,也就是我们只输入 make 命令。那么
这就是整个 make 的依赖性,make 会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。
目标文件: 依赖文件 ... 可以有很多个
执行命令 (可以有一些选项)
1、目标文件就是要生成的文件。
2、依赖文件就是源头。
3、执行命令:即通过执行命令由依赖文件生成目标文件。注意每条命令之前必须有一个tab
又称为自动推导。
可以自动推导文件以及文件依赖关系后面的命令,于是我们就没必要去在每一个[.o]文件后都写上类似的命令,因为我们的 make 会自动识别并自己推导命令。只要 make 看到一个[.o]文件,它就会自动的把[.c]文件加在依赖关系中,如果 make找到一个 a.o,那么 a.c,就会是 a.o 的依赖文件,并且 cc -c a.c 也会被推导出来。下面举个例子:
文件结构
hello.c hello.h list.c list.h main.c makefile
Makefile最初的内容
main:mian.o hello.o
gcc main.o he11o.o -o main
mian.o:main.c hello.h
gcc -c main.c
he11o.o: hello.c hello.h
gcc -c he11o.c
clean:
rm -f *.o main
可以通过变量指定目标文件(后面会讲到,咱们先在这里接触下,看不懂不要紧)
OBJ=mian.o he11o.o
main:$(OBJ)
gcc $(OBJ) -o main
mian.o:main.c hello.h
gcc -c main.c
he11o.o: hello.c hello.h
gcc -c he11o.c
clean:
rm -f *.o main
利用隐式规则
OBJ=mian.o he11o.o
main:$(OBJ)
gcc $(OBJ) -o main
mian.o:hello.h
he11o.o:hello.h
clean:
rm -f *.o main
利用$符号(后面会讲到,咱们先在这里接触下,看不懂不要紧)
OBJ=mian.o he11o.o
CC=gcc
TARGETS=main
$(TARGETS):$(OBJ)
$(CC) $^ -o $@
%.o:%.c
$(CC) -c $<
clean:
rm -f *.o $@
Makefile文件默认只生成第一个目标文件即完成编译,但是我们可以通过all 指定所需要生成的目标文件,也就是我们编译的终极目标。
all: target1 target2 target3
target1:
# 编译规则1
target2:
# 编译规则2
target3:
# 编译规则3
all被设置为第一个目标,并且target1、target2和target3被列为all的依赖。当你在命令行中运行make时,make命令会寻找并执行all目标规则,这将依次执行target1、target2和target3的编译。
$符号表示取变量的值
$^ 表示所有的依赖文件
$@ 表示生成的目标文件
$< 表示第一个依赖文件
$? 代表依赖文件列表中被改变过的所有文件
$* 代表与通过模式规则匹配的目标文件相对应的部分。例如,若目标是foo.o,则$*将表示foo。
@
符号用于禁止显示命令自身,只输出命令执行结果。
@echo $(@F)
$(@F)是一个自动化变量,表示当前目标文件的名称(不包括路径)。
%.c匹配所有.c文件
%.o:%.c
gcc -c $<
用路径下所有.c文件都各自生成一个.o文件
也就是单行注释,Makefile没有多行注释。
使用 “=”进行赋值,变量的值是整个Makefile中最后被指定的值。
VIR_A = A
VIR_B = $(VIR_A) B
VIR_A = AA
最后VIR_B的值是AA B,而不是A B,会把整个变量展开。
直接赋值,赋予当前位置的值。
VIR_A := A
VIR_B := $(VIR_A) B
VIR_A := AA
最后BIR_B的值是A B,即根据当前位置进行赋值。
表示如果该变量没有被赋值,赋值予等号后面的值。
VIR ?= new_value
如果VIR在之前没有被赋值,那么VIR的值就为new_value。
表示将符号后面的值添加到前面的变量后面
从字符串中取出目录部分,目录部分是指最后一个反斜杠(“/”)之前的部分。
dir src/foo.c hacks
取到的是“src/
, ./
”
可以借助这个函数来执行一些shell能够执行的命令
$(shell pwd)
这样就可以执行pwd命令了
ALL_DIRS = $(shell "find" $(SRC_PATH) -type d)
-type d参数用于限定find命令只返回路径(无文件名)。
ALL_DIRS
将包含通过find
命令获取的指定目录下的所有子目录路径列表。
过滤掉一个指定的字符串
FILE = a.c b.h c.s d.cpp
SRC = $(filter %.c, $(FILE))
留下所有.c文件,最后SRC = a.c
FILE = a.c b.h c.s d.cpp
SRC = $(filter-out %.c, $(FILE))
去掉所有.c文件,最后SRC = b.h c.s d.cpp
支持“*”,“?”,“[...]” ,“~”通配符
SRC = $(wildcard ./*.c)
匹配当前目录下所有.c 文件,并将其赋值给SRC变量。
wildcard $(dir)/*.c
匹配所有目录下.c文件
sources := main.c foo.c bar.c
objects := $(patsubst %.c,%.o,$(sources))
将sources中所有的.c文件名替换为.o,所以objects的值将是 main.o foo.o bar.o。
wildcard $(dir)/*.c
在某个目录中获取所有以 .c
扩展名结尾的文件路径。
$(dir)
是一个变量,表示当前迭代的目录。
/*.c
表示在该目录中搜索所有以 .c
结尾的文件。
vpath := src/foo
new_vpath := $(patsubst src/%,obj/%,$(vpath))
将vpath中的src/替换为obj/,所以new_vpath的值将是 obj/foo。
txt_files := file1.txt file2.txt file3.txt
md_files := $(patsubst %.txt,%.md,$(txt_files))
将txt_files中所有的.txt文件名替换为.md,所以md_files的值将是 file1.md file2.md file3.md。
names := apple orange banana
prefixed_names := $(patsubst %,fruit_%,$(names))
为names中的每个名字添加了前缀"fruit_",所以prefixed_names的值将是 fruit_apple fruit_orange fruit_banana。
full_paths := /home/user/main.c /home/user/foo.c
file_names := $(patsubst /home/user/%.c,%,$(full_paths))
从full_paths中提取了每个文件的文件名,所以file_names的值将是 main foo。
$(foreach dir, $(DIRS), $(wildcard $(dir)/*.c))
遍历所有DIRS(纯目录,无文件名)里面的.c文件,以带目录的方式返回。
OBJ=$(notdir a/b/c.cpp, aaa.cpp)
将带路径的文件名去除路径,只保留文件名。
返回字符串 “.”之前的所有字段
SRC := src/main.c src/hello.c
OBJ := $(basename $(SRC))
返回文件名序列
最后返回src/main src/hello
给字符串中的每一个子串前加上一个前缀
SOURCE = main.c foo.c bar.c
OBJ = $(PWD)
add_source = $(addprefix $(OBJ)/, $(SOURCE))
这样就给每个文件前面添加了路径
$(subst from,to,text),对 text 文本执行文本替换:每次出现的 from 都替换为 to。
$(subst ee,EE,feet on the street)
生成值 fEEt on the strEEt
ALL_SRCS = $(foreach dir, $(DIRS), $(wildcard $(dir)/*.c))
伪目标只是一个标签,clean是个伪目标没有依赖文件。当目标文件已存在时,忽略,继续执行规则。
clean:
rm -rf $(OBJ) all.out
.PHONY: clean ALL
清理make命令所产生的所有文件
clean:
rm -rf $(OBJ) hello.out
删除中间生成.o文件
clear:
rm *.o
一般都是通过"-I"来指定头文件路径
CFLAGS=-I/home/develop/include
app:*.c
gcc $(CFLAGS) -o app
一般都是通过"-L"来指定头文件路径
CFLAGS=-L/home/develop/lib1 -L/home/develop/lib2
app:*.c
gcc $(CFLAGS) -o app
编译的时候有很多参数可以选择,在这里举点例子。
-cpu=rh850g3kh:指定编译器的目标CPU架构为rh850g3kh。
-bsp generic:使用通用的BSP(板级支持包)。
-dwarf2:使用DWARF2调试信息格式。
-nothreshold:禁用阈值优化。
-preprocess_assembly_files:对汇编文件进行预处理。
-fsoft:启用软浮点支持。
-list:生成一个列表文件。
-object_dir=$(OBJ_OUTPUT_DIR):指定目标文件的输出目录为$(OBJ_OUTPUT_DIR)。
--no_additional_output:不生成额外的输出文件。
-callgraph:生成函数调用图。
-Ogeneral:启用一般优化级别。
-Mx:启用所有的警告信息。
-e _RESET:指定程序入口点为_RESET。
-D__GHS__:定义一个名为__GHS__的宏。
-elf:生成ELF可执行文件格式的输出。
-no_v850_simd:禁用V850 SIMD(单指令多数据)指令集。
-g:生成调试信息。
-passsource:将源代码传递给后续处理阶段。
-v:详细显示编译过程中的操作信息。
-w:禁用警告信息的显示。
-nofpu:禁用FPU(浮点运算单元)支持。
-nomacro:禁用宏展开。
-b0:设置链接器的起始地址为0。
-DSAIOS_GLOBAL_A1SAMPLE_ENABLE=$(SAIOS_GLOBAL_A1SAMPLE_ENABLE):定义一个名为SAIOS_GLOBAL_A1SAMPLE_ENABLE的宏,并将其值设置为$(SAIOS_GLOBAL_A1SAMPLE_ENABLE)。
-c:表示生成目标文件而不进行链接,即只进行编译而不进行链接。
-c99:表示使用C99标准进行编译,启用C99的新特性和语法。
-map="$(TARGET_OUTPUT_DIR)/$(CURRENT_APPL_NAME).map":表示生成一个链接地图文件,将其指定为TARGET_OUTPUT_DIR目录下的CURRENT_APPL_NAME.map。
-o "$(TARGET_OUTPUT_DIR)/$(CURRENT_APPL_NAME).out":表示指定输出文件的名称和路径,将其设置为TARGET_OUTPUT_DIR目录下的CURRENT_APPL_NAME.out
-M:自动找寻源文件中包含的头文件,并生成一个依赖关系。
CC := g++
CFLAGS := -g
TARGET := test
SRCS := $(wildcard *.cpp)
OBJS := $(patsubst %cpp,%o,$(SRCS))
all:$(TARGET)
%.o:%.cpp
$(CC) $(CFLAGS) -c $< -o $@
$(TARGET):$(OBJS)
$(CC) $(CFLAGS) -o $@
clean:
rm -rf $(TARGET) *.o
主Makefile(Makefile):
SUBDIRS = subdir1 subdir2
.PHONY: all $(SUBDIRS)
all: $(SUBDIRS)
$(SUBDIRS):
$(MAKE) -C $@
clean:
for dir in $(SUBDIRS); do \
$(MAKE) -C $$dir clean; \
done
在主Makefile中,我们定义了一个变量SUBDIRS
,它包含要构建的子目录名称。.PHONY
指令用于指明all
和$(SUBDIRS)
是伪目标,而不是实际存在的文件。
all
目标依赖于$(SUBDIRS)
,并通过运行$(MAKE) -C $@
命令进入每个子目录,执行子目录中的Makefile。
clean
目标会遍历$(SUBDIRS)
,并通过运行$(MAKE) -C $$dir clean
命令在每个子目录中执行clean
目标。
子目录Makefile(subdir1/Makefile 和 subdir2/Makefile):
all:
$(MAKE) -C subdir1
$(MAKE) -C subdir2
clean:
$(MAKE) -C subdir1 clean
$(MAKE) -C subdir2 clean
在子目录的Makefile中,我们使用相同的方式来调用$(MAKE)
命令进入其他子目录并执行相应的目标。
通过这种嵌套Makefile的结构,我们可以在主Makefile中定义整个项目的构建流程,然后在子目录的Makefile中定义各自的构建规则。这样可以实现清晰的模块化,并确保每个子目录可以独立构建。
运行主Makefile中的make
命令将会进入每个子目录,依次执行各自的Makefile。运行主Makefile中的make clean
命令将会在每个子目录中执行clean
目标。