• Makefile泛谈


    Makefile基础概念

    相当于一个编译器,在没有IDE的情况下,可以用cmd命令执行Makefile文件来编译工程。

    工作原理

    在默认的方式下,也就是我们只输入 make 命令。那么

    1. make 会在当前目录下找名字叫“Makefile”或者“makefile”的文件。
    2. 如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“all”这个目标,并把这个文件作为最终的目标文件。(Makefile 中的第一个目标会被作为其默认目标)
    3. 如果all 文件不存在,或是 all 所依赖的后面的 .o 文件的文件修改时间要比 all这个文件新,那么他就会执行后面所定义的命令来生成all 这个文件。
    4. 如果 all所依赖的.o 文件也存在,那么 make 会在当前文件中找目标为.o 文件的依赖性,如果找到则再根据那一个规则生成.o 文件。
    5. 你的 C 文件和 H 文件是存在的,于是 make 会生成 .o 文件,然后再用 .o 文件生成执行文件。

    这就是整个 make 的依赖性,make 会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。

    显式规则

    1. 目标文件: 依赖文件 ... 可以有很多个
    2. 执行命令 (可以有一些选项)

    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最初的内容

    1. main:mian.o hello.o
    2. gcc main.o he11o.o -o main
    3. mian.o:main.c hello.h
    4. gcc -c main.c
    5. he11o.o: hello.c hello.h
    6. gcc -c he11o.c
    7. clean:
    8. rm -f *.o main

    可以通过变量指定目标文件(后面会讲到,咱们先在这里接触下,看不懂不要紧)

    1. OBJ=mian.o he11o.o
    2. main:$(OBJ)
    3. gcc $(OBJ) -o main
    4. mian.o:main.c hello.h
    5. gcc -c main.c
    6. he11o.o: hello.c hello.h
    7. gcc -c he11o.c
    8. clean:
    9. rm -f *.o main

    利用隐式规则

    1. OBJ=mian.o he11o.o
    2. main:$(OBJ)
    3. gcc $(OBJ) -o main
    4. mian.o:hello.h
    5. he11o.o:hello.h
    6. clean:
    7. rm -f *.o main

    利用$符号(后面会讲到,咱们先在这里接触下,看不懂不要紧)

    1. OBJ=mian.o he11o.o
    2. CC=gcc
    3. TARGETS=main
    4. $(TARGETS):$(OBJ)
    5. $(CC) $^ -o $@
    6. %.o:%.c
    7. $(CC) -c $<
    8. clean:
    9. rm -f *.o $@

    符号

    All目标

    Makefile文件默认只生成第一个目标文件即完成编译,但是我们可以通过all 指定所需要生成的目标文件,也就是我们编译的终极目标。

    1. all: target1 target2 target3
    2. target1:
    3. # 编译规则1
    4. target2:
    5. # 编译规则2
    6. target3:
    7. # 编译规则3

    all被设置为第一个目标,并且target1、target2和target3被列为all的依赖。当你在命令行中运行make时,make命令会寻找并执行all目标规则,这将依次执行target1、target2和target3的编译。


    $符号

    1. $符号表示取变量的值
    2. $^ 表示所有的依赖文件
    3. $@ 表示生成的目标文件
    4. $< 表示第一个依赖文件
    5. $? 代表依赖文件列表中被改变过的所有文件
    6. $* 代表与通过模式规则匹配的目标文件相对应的部分。例如,若目标是foo.o,则$*将表示foo。

    @符号

    @符号用于禁止显示命令自身,只输出命令执行结果。

    @echo $(@F)

    $(@F)是一个自动化变量,表示当前目标文件的名称(不包括路径)。

    %符号

    %.c匹配所有.c文件

    1. %.o:%.c
    2. gcc -c $<

    用路径下所有.c文件都各自生成一个.o文件

    #符号

    也就是单行注释,Makefile没有多行注释。

    变量赋值

    =

    使用 “=”进行赋值,变量的值是整个Makefile中最后被指定的值。

    1. VIR_A = A
    2. VIR_B = $(VIR_A) B
    3. VIR_A = AA

    最后VIR_B的值是AA B,而不是A B,会把整个变量展开。

    :=

    直接赋值,赋予当前位置的值。

    1. VIR_A := A
    2. VIR_B := $(VIR_A) B
    3. VIR_A := AA

    最后BIR_B的值是A B,即根据当前位置进行赋值。

    ?=

    表示如果该变量没有被赋值,赋值予等号后面的值。

    VIR ?= new_value

    如果VIR在之前没有被赋值,那么VIR的值就为new_value。

    +=

    表示将符号后面的值添加到前面的变量后面

    常用函数

    取目录函数dir

    从字符串中取出目录部分,目录部分是指最后一个反斜杠(“/”)之前的部分。

    dir src/foo.c hacks

    取到的是“src/./

    shell函数

    可以借助这个函数来执行一些shell能够执行的命令

    $(shell pwd)

    这样就可以执行pwd命令了

    ALL_DIRS = $(shell "find" $(SRC_PATH) -type d)

    -type d参数用于限定find命令只返回路径(无文件名)。

    ALL_DIRS将包含通过find命令获取的指定目录下的所有子目录路径列表。

    过滤函数filter

    过滤掉一个指定的字符串

    1. FILE = a.c b.h c.s d.cpp
    2. SRC = $(filter %.c, $(FILE))

    留下所有.c文件,最后SRC = a.c

    排除函数filter-out

    1. FILE = a.c b.h c.s d.cpp
    2. SRC = $(filter-out %.c, $(FILE))

    去掉所有.c文件,最后SRC = b.h c.s d.cpp

    通配符函数wildcard

    支持“*”,“?”,“[...]” ,“~”通配符

    SRC = $(wildcard ./*.c)

    匹配当前目录下所有.c 文件,并将其赋值给SRC变量。

    wildcard $(dir)/*.c

    匹配所有目录下.c文件

    扩展通配符函数patsubst

    文件名替换

    1. sources := main.c foo.c bar.c
    2. objects := $(patsubst %.c,%.o,$(sources))

    将sources中所有的.c文件名替换为.o,所以objects的值将是 main.o foo.o bar.o。

    wildcard $(dir)/*.c

    在某个目录中获取所有以 .c 扩展名结尾的文件路径。

    $(dir) 是一个变量,表示当前迭代的目录。

    /*.c 表示在该目录中搜索所有以 .c 结尾的文件。

    目录替换

    1. vpath := src/foo
    2. new_vpath := $(patsubst src/%,obj/%,$(vpath))

    将vpath中的src/替换为obj/,所以new_vpath的值将是 obj/foo。

    文件扩展名替换

    1. txt_files := file1.txt file2.txt file3.txt
    2. md_files := $(patsubst %.txt,%.md,$(txt_files))

    将txt_files中所有的.txt文件名替换为.md,所以md_files的值将是 file1.md file2.md file3.md。

    添加前缀

    1. names := apple orange banana
    2. prefixed_names := $(patsubst %,fruit_%,$(names))

    为names中的每个名字添加了前缀"fruit_",所以prefixed_names的值将是 fruit_apple fruit_orange fruit_banana。

    从路径中提取文件名(去除路径)

    1. full_paths := /home/user/main.c /home/user/foo.c
    2. file_names := $(patsubst /home/user/%.c,%,$(full_paths))

    从full_paths中提取了每个文件的文件名,所以file_names的值将是 main foo。

    循环函数foreach

    $(foreach dir, $(DIRS), $(wildcard $(dir)/*.c))

    遍历所有DIRS(纯目录,无文件名)里面的.c文件,以带目录的方式返回。

    去除路径notdir

    OBJ=$(notdir  a/b/c.cpp, aaa.cpp)

    将带路径的文件名去除路径,只保留文件名。

    提取前缀basename

    返回字符串 “.”之前的所有字段

    1. SRC := src/main.c src/hello.c
    2. OBJ := $(basename $(SRC))

    返回文件名序列 的前缀序列,如果文件没有前缀,则返回空字串。

    最后返回src/main src/hello

    添加前缀addprefix

    给字符串中的每一个子串前加上一个前缀

    1. SOURCE = main.c foo.c bar.c
    2. OBJ = $(PWD)
    3. add_source = $(addprefix $(OBJ)/, $(SOURCE))

    这样就给每个文件前面添加了路径

    字符串替换函数subst

    $(subst from,to,text),对 text 文本执行文本替换:每次出现的 from 都替换为 to。

    $(subst ee,EE,feet on the street)

    生成值 fEEt on the strEEt

    foreach遍历集合所有元素

    ALL_SRCS = $(foreach dir, $(DIRS), $(wildcard $(dir)/*.c))

    VPATH

    注意区分大小写,是两个变量来的。

    执行时,首先会搜索当前目录下是否存在所需的文件,如果没有,则去VPATH这个变量的值所对应的路径寻找文件。VPATH变量可以给多个路径赋值,各路径之间可以用空格或者冒号隔开,执行make命令时,搜索的顺序是按照书写时的顺序进行的。

    伪目标 .PHONY

    伪目标只是一个标签,clean是个伪目标没有依赖文件。当目标文件已存在时,忽略,继续执行规则。

    1. clean:
    2. rm -rf $(OBJ) all.out
    3. .PHONY: clean ALL

    清理

    清理make命令所产生的所有文件

    1. clean:
    2. rm -rf $(OBJ) hello.out

    clear

    删除中间生成.o文件

    1. clear:
    2. rm *.o

    指定路径

    头文件路径

    一般都是通过"-I"来指定头文件路径

    1. CFLAGS=-I/home/develop/include
    2. app:*.c
    3. gcc $(CFLAGS) -o app

    指定库文件路径

    一般都是通过"-L"来指定头文件路径

    1. CFLAGS=-L/home/develop/lib1 -L/home/develop/lib2
    2. app:*.c
    3. gcc $(CFLAGS) -o app

    宏定义

    1. -D macro=xxx
    2. 相当于在头文件中定义:#define macro xxx

    参数选择

    编译的时候有很多参数可以选择,在这里举点例子。

    1. -cpu=rh850g3kh:指定编译器的目标CPU架构为rh850g3kh。
    2. -bsp generic:使用通用的BSP(板级支持包)。
    3. -dwarf2:使用DWARF2调试信息格式。
    4. -nothreshold:禁用阈值优化。
    5. -preprocess_assembly_files:对汇编文件进行预处理。
    6. -fsoft:启用软浮点支持。
    7. -list:生成一个列表文件。
    8. -object_dir=$(OBJ_OUTPUT_DIR):指定目标文件的输出目录为$(OBJ_OUTPUT_DIR)。
    9. --no_additional_output:不生成额外的输出文件。
    10. -callgraph:生成函数调用图。
    11. -Ogeneral:启用一般优化级别。
    12. -Mx:启用所有的警告信息。
    13. -e _RESET:指定程序入口点为_RESET。
    14. -D__GHS__:定义一个名为__GHS__的宏。
    15. -elf:生成ELF可执行文件格式的输出。
    16. -no_v850_simd:禁用V850 SIMD(单指令多数据)指令集。
    17. -g:生成调试信息。
    18. -passsource:将源代码传递给后续处理阶段。
    19. -v:详细显示编译过程中的操作信息。
    20. -w:禁用警告信息的显示。
    21. -nofpu:禁用FPU(浮点运算单元)支持。
    22. -nomacro:禁用宏展开。
    23. -b0:设置链接器的起始地址为0
    24. -DSAIOS_GLOBAL_A1SAMPLE_ENABLE=$(SAIOS_GLOBAL_A1SAMPLE_ENABLE):定义一个名为SAIOS_GLOBAL_A1SAMPLE_ENABLE的宏,并将其值设置为$(SAIOS_GLOBAL_A1SAMPLE_ENABLE)。
    25. -c:表示生成目标文件而不进行链接,即只进行编译而不进行链接。
    26. -c99:表示使用C99标准进行编译,启用C99的新特性和语法。
    27. -map="$(TARGET_OUTPUT_DIR)/$(CURRENT_APPL_NAME).map":表示生成一个链接地图文件,将其指定为TARGET_OUTPUT_DIR目录下的CURRENT_APPL_NAME.map。
    28. -o "$(TARGET_OUTPUT_DIR)/$(CURRENT_APPL_NAME).out":表示指定输出文件的名称和路径,将其设置为TARGET_OUTPUT_DIR目录下的CURRENT_APPL_NAME.out
    29. -M:自动找寻源文件中包含的头文件,并生成一个依赖关系。
    30. -c,执行汇编阶段,生成 object 目标文件。
    31. -E,执行预处理阶段,生成预处理文件。
    32. -S,执行编译阶段,生成汇编代码文件。
    33. -o [FILE],生成指定的输出文件,用在生成可执行文件时。
    34. -O0,不进行优化处理。
    35. -O,等同于 -O1 优化生成代码。
    36. -O2,比 -O1 进一步优化。
    37. -O3,比 -O2 更进一步优化,包括 inline 函数。
    38. -w,不生成任何警告信息。
    39. W,只生成编译器认为会出现错误的警告。
    40. -Wall,生成所有警告信息。
    41. -g,生成调试信息,GNU 调试器调试时可利用该信息。
    42. -ansi,只支持 ANSI 标准的 C 语法,这一选项将禁止 GNU C 的一些些特色,例如 asm 或 typeof 关键词。
    43. -DMACRO,以字符串 “1” 定义 MACRO 宏。
    44. -DMACRO=DEFN,以字符串 “DEFN” 定义 MACRO 宏。
    45. -IDIRECTORY,指定额外的头文件搜索路径 DIRECTORY。
    46. -LDIRECTORY,指定额外的函数库搜索路径 DIRECTORY。
    47. -shared,生成共享目标文件。通常用在建立共享库时。
    48. -static,静态连接,即禁止使用共享连接。
    49. -UMACRO,取消对 MACRO 宏的定义。
    50. -w,不生成任何警告信息。
    51. -W,只生成编译器认为会出现错误的警告。
    52. -Wall,生成所有警告信息。
    53. -Waddress,使用可疑的内存地址时给出警告。
    54. -Waggregate-return,当返回结构,联合或数组时给出警告。
    55. -Waliasing,为可能的虚参重叠给出警告。
    56. -Walign-commons,对 COMMON 块对齐的警告。
    57. -Wampersand,若延续字符常量中缺少 & 则给出警告,不支持 C 语言。
    58. -Warray-bounds,当数组访问越界时给出警告。
    59. -Warray-temporaries,创建临时数组时给出警告,不支持 C 语言。
    60. -Wattributes,当对属性的使用不合适时给出警告。
    61. -Wbad-function-cast,当把函数转换为不兼容类型时给出警告。
    62. -Wbuiltin-macro-redefined,当内建预处理宏未定义或重定义时给出警告。
    63. -Wc++-compat,当在 C 语言中使用了 C 与 C++ 交集以外的构造时给出警告。
    64. -Wcast-align,当转换指针类型导致对齐边界增长时给出警告。
    65. -Wcast-qual,当类型转换丢失限定信息时给出警告。
    66. -Wchar-subscripts,当下标类型为 char 时给出警告。
    67. -Wenum-compare,对不同枚举类型之间的比较给出警告。
    68. -Wformat,对 printf/scanf/strftime/strfmon 中的格式字符串异常给出警告
    69. -Wfloat-equal,当比较浮点数是否相等时给出警告。
    70. -Wignored-qualifiers,当类型限定符被忽略时给出警告。
    71. -Wimplicit,对隐式函数声明给出警告。
    72. -Wimplicit-function-declaration,对隐式函数声明给出警告。
    73. -Wimplicit-int,当声明未指定类型时给出警告。
    74. -Winline,当内联函数无法被内联时给出警告。
    75. -Wmain,对可疑的 main 声明给出警告。
    76. -Wswitch-enum,当使用枚举类型作为 switch 变量但又缺少某个 case 时给出警告。
    77. -Wuninitialized,自动变量未初始化时警告。
    78. -Wunused-value,当一个表达式的值未被使用时给出警告。
    79. -Wunused-variable,有未使用的变量时警告。
    80. -Wvla,使用变长数组时警告。
    81. -Wreturn-type,当 C 函数的返回值默认为 int,或者 C++ 函数的返回类型不一致时给出警告。
    82. -Wredundant-decls,对同一个对象多次声明时给出警告。
    83. -Wold-style-cast,程序使用 C 风格的类型转换时给出警告,不支持 C 语言。
    84. -Wempty-body,当 ifelse 语句体为空时给出警告。
    85. -Winit-self,对初始化为自身的变量给出警告。
    86. -Wlong-long,当使用 -pedantic 时不对 long long 给出警告。
    87. -Wmissing-prototypes,全局函数没有原型时给出警告。
    88. -Wnonnull,当将 NULL 传递给需要非 NULL 类型的参数的函数时给出警告。
    89. -Wparentheses,可能缺少括号的情况下给出警告。
    90. -Wunused,启用所有关于 xx未使用 的警告。
    91. -Wvolatile-register-var,当一个寄存器变量被声明为 volatile 时给出警告。
    92. -Wundef,当 #if 指令中用到未定义的宏时给出警告。
    93. -Wenum-compare,对不同枚举类型之间的比较给出警告。
    94. -Wdouble-promotion,对从 floatdouble 的隐式转换给出警告。
    95. -Wnested-externs,当 extern 声明不在文件作用域时给出警告。
    96. -Wformat-zero-length,对长度为 0 的格式字符串给出警告。
    97. -Wendif-labels,当 #elif 和 #endif 后面跟有其他标识符时给出警告。
    98. -Wdiv-by-zero,对编译时发现的零除给出警告。
    99. -Wunused-macros,当定义在主文件中的宏未被使用时给出警告。
    100. -Wunused-function,有未使用的函数时警告。
    101. -Wswitch-default,当使用枚举类型作为开关变量,但没有提供 default 语句时给出警告。
    102. -Wunused-label,有未使用的标号时警告。
    103. -Wprotocol,当继承来的方法未被实现时给出警告,不支持 C 语言。
    104. -O0,不进行优化处理。
    105. -O,即等同于 -O1,在不影响编译速度的前提下,尽量采用一些优化算法降低代码大小,和提高目标代码运行速度。
    106. -O2,该优化选项会牺牲部分编译速度,采用几乎所有的目标配置支持的优化算法以提高目标代码运行速度。
    107. -O3,最大化速度(maximize speed),会增加代码大小,即提高目标代码的并行执行能力。
    108. -Os,优选代码空间(favor code space)即优化生成目标文件的大小。
    109. -Og,启用全局优化(enable global optimization),为了能够生成更好的调试信息,精心挑选部分与调试不冲突的优化选项。
    110. -Ofast,使用最高级别的优化选项来编译代码,可以无视严格的语言标准以提高程序的执行速度。
    111. -Fa [file],命名程序集列表文件(name assembly listing file。
    112. -Fo,命名对象文件(name object file。
    113. -FA [sc],配置程序集列表(configure assembly listing。
    114. -Fp,命名预编译头文件(name precompiled header file。
    115. -Fd [file],命名 .PDB 文件(name .PDB file)。
    116. -Fr [file],命名源浏览器文件(name source browser file。
    117. -Fe,命名可执行文件(name executable file)。
    118. -FR [file],命名扩展 .SBR 文件(name extended .SBR file)。
    119. -Fm [file],命名映射文件(name map file)。
    120. -FI,命名强制包含文件(name forced include file)。
    121. -C,不吸取注释(don’t strip comments)。
    122. -U,移除预定义宏(remove predefined macro)。
    123. -D {=|#},定义宏(define macro)。
    124. -u,移除所有预定义宏(remove all predefined macros)。
    125. -E,将预处理定向到标准输出(preprocess to stdout)。
    126. -I,添加到包含文件的搜索路径(add to include search path)。
    127. -EP,将预处理定向到标准输出,不要带行号(preprocess to stdout, no #line)。
    128. -X,忽略 “标准位置”(ignore “standard places”)。
    129. -P,预处理到文件(preprocess to file)。
    130. --version,显示 GCC 详细版本信息。
    131. -o [FILE],生成指定的输出文件,用在生成可执行文件时。
    132. -c,执行汇编阶段,生成 object 目标文件。
    133. -E,执行预处理阶段,生成预处理文件。
    134. -S,执行编译阶段,生成汇编代码文件。
    135. -x [language] [file name],根据约定 C 语言的后 缀名称是 .c ,如果不是 .c,则需要使用该选项指定文件类型,例如 -x c++ hello.a。
    136. -w,不生成任何警告信息。
    137. -W,只生成编译器认为会出现错误的警告。
    138. -Wall,生成所有警告信息。
    139. -g,生成调试信息到目标文件中,将编译时的调试信息保存到本地文件中(stabs,COFF,XCOFF,DWARF)。
    140. -ggdb,为 GDB 产生调试信息,包含 GDB 的扩展。
    141. -ggdb[level],设定产生何种等级的调试信息, level 为 1-31 最少,3 最多,例如 -ggdb3。
    142. -pipe,在多个编译过程之间使用管道。
    143. -static,静态连接,即禁止使用共享连接。
    144. -ansi,C 模式下支持所有 ISO C90 标准的 C 程序, C++ 模式下去除对 GNU C++ 扩展的支持(GNU 扩展会与 ISO C++ 冲突)。
    145. -std=,确定编译语言的标准,例如 -std=c11,目前只在编译 C 和 C++ 时有效 -fno-asm 不将 asminline,typeof 作为关键字,可以用他们做变量名等。
    146. -funsigned-char,将 char 的数据类型设为 unsigned,即无符号。
    147. -fsigned-char,正好相反,将 char 设为 signed,即有符号。
    148. -fsyntax-only,只检查语法错误,不做其他任何事。
    149. -pedantic,显示所有的 ISO C 和 ISO C++ 的警告,并且拒绝所有使用禁止扩展的程序。
    150. -ftime-report,统计编译消耗的时间并显示报告。
    151. -fmem-report,显示所有的静态内存分配。
    152. -ftest-coverage,为 gcov 工具产生数据文件。
    153. -DMACRO,以字符串 “1” 定义 MACRO 宏。
    154. -DMACRO=DEFN,以字符串 “DEFN” 定义 MACRO 宏。
    155. -IDIRECTORY,指定额外的头文件搜索路径 DIRECTORY。
    156. -LDIRECTORY,指定额外的函数库搜索路径 DIRECTORY。
    157. -shared,生成共享目标文件。通常用在建立共享库时。
    158. -UMACRO,取消对 MACRO 宏的定义。
    159. -M,生成文件关联的信息。包含目标文件所依赖的所有源代码
    160. -MM,和 -M 一样,但是它将忽略由 #include 造成的依赖关系。
    161. -MD,和 -M 相同,但是输出将导入到 .d 文件。
    162. -MMD,和 -MM 相同,但是输出将导入到 .d 文件。
    163. -I [dir],表示将 dir 目录添加到头文件搜索路径中,这样就可以直接使用 #include 的形式进行包含。
    164. -I-[dir],表示将指定的目录从头文件搜索路径中移除,移除后将不再该路径搜索头文件。
    165. -idirafter [dir],在指定的头文件搜索路径里面查找失败,则到这里指定的目录中查找。
    166. -L [dir],制定编译的时候,指定库的搜索路径,告诉编译器在 dir 目录中查找库文件,例如指定搜索我们自定义库。
    167. -l[library name],指定编译时候使用的库,例如指定数学库 gcc hello.c -lm。

    正斜杠/和反斜杠\


    /:正斜杠多用于选项

    del /F /Q    #强制删除只读文件 安静模式
    

    \:反斜杠在 Makefile 中通常用于转义特殊字符或者路径。这是因为反斜杠在许多操作系统中被用作路径分隔符,因此为了在 Makefile 中正确使用它,需要使用双反斜杠(\\)来表示一个反斜杠字符。例如,C:\\Users\\TS\\file.txt 表示在 Windows 系统中的路径 C:\Users\TS\file.txt。

    在表示路径当中,有时候正反斜杠可以混着用,这个要看工具。

    表示路径的时候最好还是要加一句,把正斜杠替换成反斜杠。

    $(subst /,\,$(OBJ_OUTPUT_DIR))

    重定向

    1>nul(即>nul)

    > 是标准输出重定向符号,用于将命令的标准输出(通常是在终端或命令提示符中显示的内容)重定向到空设备(nul)。这样,命令的输出将被丢弃,不会在终端显示。

    2>nul

    2> 是标准错误输出重定向符号,用于将命令的错误输出重定向到空设备(nul)。这样,命令的错误消息将被丢弃,不会在终端显示。

    实例

    经典实例

    1. CC := g++
    2. CFLAGS := -g
    3. TARGET := test
    4. SRCS := $(wildcard *.cpp)
    5. OBJS := $(patsubst %cpp,%o,$(SRCS))
    6. all:$(TARGET)
    7. %.o:%.cpp
    8. $(CC) $(CFLAGS) -c $< -o $@
    9. $(TARGET):$(OBJS)
    10. $(CC) $(CFLAGS) -o $@
    11. clean:
    12. rm -rf $(TARGET) *.o

    嵌套实例

    主Makefile(Makefile)

    1. SUBDIRS = subdir1 subdir2
    2. .PHONY: all $(SUBDIRS)
    3. all: $(SUBDIRS)
    4. $(SUBDIRS):
    5. $(MAKE) -C $@
    6. clean:
    7. for dir in $(SUBDIRS); do \
    8. $(MAKE) -C $$dir clean; \
    9. done

    在主Makefile中,我们定义了一个变量SUBDIRS,它包含要构建的子目录名称。.PHONY指令用于指明all$(SUBDIRS)是伪目标,而不是实际存在的文件。

    all目标依赖于$(SUBDIRS),并通过运行$(MAKE) -C $@命令进入每个子目录,执行子目录中的Makefile。

    clean目标会遍历$(SUBDIRS),并通过运行$(MAKE) -C $$dir clean命令在每个子目录中执行clean目标。

    子目录Makefile(subdir1/Makefile 和 subdir2/Makefile)

    1. all:
    2. $(MAKE) -C subdir1
    3. $(MAKE) -C subdir2
    4. clean:
    5. $(MAKE) -C subdir1 clean
    6. $(MAKE) -C subdir2 clean

    在子目录的Makefile中,我们使用相同的方式来调用$(MAKE)命令进入其他子目录并执行相应的目标。

    通过这种嵌套Makefile的结构,我们可以在主Makefile中定义整个项目的构建流程,然后在子目录的Makefile中定义各自的构建规则。这样可以实现清晰的模块化,并确保每个子目录可以独立构建。

    运行主Makefile中的make命令将会进入每个子目录,依次执行各自的Makefile。运行主Makefile中的make clean命令将会在每个子目录中执行clean目标。

    亲身经历问题

    注释不能在命令里面

    提示“文件名、目录名或卷标语法不正确。”但是查过文件名目录名都是对的。

    发现问题在注释那一行,连续几行的命令里面是不允许有空行或者注释的!!!

    注释不能在目标之间

    下面的也是,哪怕用空行隔开了,也不能加注释!!!

    1. all: $(CURRENT_APPL_NAME).a
    2. # @echo $(ALL_SRCS_NOWITH_PATH)//这行有问题
    3. # @echo $(CINCLUDE)//这行有问题
    4. AR_INSTALL_DIR = C:\MinGW\bin
    5. AR=$(AR_INSTALL_DIR)\ar.exe

    make: Nothing to be done for  all

    有时候会看到这个错误,它只是告诉你,生成文件已经存在,不用重新编译,没啥要做的。但是有个可能,你改了Makefile或者源文件,但是由于make的时候先从最后的生成文件来检查,有最后的生成文件就不会重新编译所有的中间文件或者查找源文件,这时候要先clean再重新编译。

  • 相关阅读:
    【开源】基于JAVA的音乐偏好度推荐系统
    【SpringBoot+Vue实现书籍管理系统--下篇】整合MyBatisPlus完成分页功能的开发并部署到docker容器中
    tftp服务配置的详细过程,简单快速
    Spring 核心
    PLC SSD来了,固态硬盘SSD马上要成白菜价了吗?
    每日优鲜深陷“破产风波”,生鲜电商路在何方?
    PC网站支付宝扫码登录
    【大数据ETL工具,Kettle的学习和使用】
    【MySQL】 B+ 树存储的原理
    three.js学习笔记(十九)——后期处理
  • 原文地址:https://blog.csdn.net/qiu421/article/details/134056719