目录
d、-Wl,--start-group 和 -Wl,--end-group
一、C编译器标志
-c
:只进行编译而不进行链接,生成目标文件。-o <filename>
:指定输出文件名。-I <directory>
:添加头文件搜索路径。-L <directory>
:添加库文件搜索路径。-l <library>
:链接指定的库。-g
:生成调试信息。-Wall
:开启所有警告信息。-Werror
:将警告视为错误。-O<level>
:优化级别,例如-O0
表示关闭优化,-O2
表示启用中等级别优化。-std=<standard>
:选择C语言的标准版本,如-std=c89
表示使用C89标准,-std=c99
表示使用C99标准。-pedantic
:对于严格遵循标准的代码,给出额外的警告信息。-D<macro>
:定义宏。-U<macro>
:取消已定义的宏。-E
:只进行预处理,输出预处理结果。
二、基本知识
1、常用的变量
$^ 所有依赖文件
$@ 所有目标文件
$< 第一个依赖文件
2、通配符/预处理
SRC = $(wildcard *.c) //当前目录下所有的.c文件
OBJ=$(patsubst %c,%o,$(SRC)) //所有的.c文件替换成.o文件
include $(PARAMETER) //预处理,把该文件的信息都导入进来
3、添加宏和链接库进行编译和链接
a、CFLAGS(宏定义、包含路径、编译过程信息)
CFLAGS=-DDEBUG -DVERBOSE -DENABLE_FEATURE_X
- 定义了名字为DEBUG、VERBOSE、ENABLE_FEATURE_X的宏
- 在代码中可以用#ifdef DEBUG、#ifdef VERBOSE、#ifdef ENABLE_FEATURE_X分别判断是否开启调试模式、是否开启详细输出模式、是否启用特定功能X
CFLAGS += -g -Wall -Wno-unused-parameter
- "-g":生成调试信息,使得程序可以被调试器跟踪和调试。
- "-Wall":显示所有的警告信息,
- "-Wno-unused-parameter":禁止显示关于未使用函数参数的警告信息,因为在某些情况下,函数参数可能未被使用。
CFLAGS += -I/path/to/include # 告诉编译器在编译过程中包含位于该目录下的头文件
b、LDFLAGS(指定库路径、链接的库)
LDFLAGS=-L/path/to/lib -lexample #-L后面跟着路径,-l后面跟着库文件的名字
gcc -o myprogram myprogram.o $(LDFLAGS)
c、LDLIBS(指定连接的库)
LDLIBS= -lmath -lio -lstring #链接三个库文件
libmath.a
,libio.a
和libstring.a
gcc -o myprogram myprogram.o $(LDLIBS)
d、-Wl,--start-group
和 -Wl,--end-group
target: main.o lib1.a lib2.a
gcc -o $@ main.o -Wl,--start-group -l1 -l2 -Wl,--end-group
lib1.a: lib1.o
ar rcs $@ lib1.o
lib2.a: lib2.o
ar rcs $@ lib2.o
-Wl,--start-group
和-Wl,--end-group
1、对于解决循环依赖或交叉依赖的库文件非常有用。
2、通过将依赖的库文件放置在
-Wl,--start-group
和-Wl,--end-group
之间,可以确保所有库文件都被正确处理,并且符号解析不会因为依赖关系而失败。3、链接器会正确处理
lib1.a
和lib2.a
中的符号依赖关系,并生成最终的可执行文件。
e、$(LDLIBS_$@)
target1: file1.o file2.o
gcc -o $@ $^ -Wl,--start-group $(LDLIBS) $(LDLIBS_$@) -Wl,--end-group
target2: file3.o
gcc -o $@ $^ -Wl,--start-group $(LDLIBS) $(LDLIBS_$@) -Wl,--end-group
LDLIBS_target1 = lib1.a lib2.a
target1中:$(LDLIBS_$@)展开为lib1.a lib2.a
target2中:由于LDLIBS_target2没有定义,所以$(LDLIBS_$@)展开为空
f、$^和$@的使用
target: file1.c file2.c
gcc -o $@ $^
上面会展开为:gcc -o target file1.c file2.c
g、句子前面加@:不输出到终端
@cp $@ $@.c
表示将所有的所有的目标文件复制,并在原来的名字后面加上.c
cp前面加了@:表示不输出到终端
h、目标文件依赖于头文件
OBJS = main.o func.o
$(OBJS): a.h b.h
target: $(OBJS)
gcc -o $@ $(OBJS)
(1)$(OBJS): a.h b.h
告诉 Make,当a.h
或b.h
发生变化时,需要重新编译main.o
和func.
(2)在
$(OBJS): a.h b.h中:
当main.c
文件发生变化时,Make 工具会自动检测到main.o
需要重新编译。这是因为 Make 在构建过程中会根据文件的时间戳(timestamp)比较源文件和目标文件的最后修改时间,从而确定是否需要重新编译目标文件(3)因此,在基于规则
$(OBJS): a.h b.h
的情况下,.c
文件的变化已经被隐式地考虑在内,并且 Make 会相应地处理源文件到目标文件的重新编译。
i、%_output的使用
%_output.txt: %.txt
cp $< $@
如果存在一个名为
example.txt
的源文件,执行该规则后会生成一个名为example_output.txt
的文件,内容与example.txt
相同。
j、%_only
文件目录结构如下
- Makefile
- src/
- module1/
- file1.c
- file2.c
- module2/
- file3.c
- file4.c
Makefile中
%_only:
echo Building $@ in $(@:_only=) directory.
$(MAKE) -j$(HOST_NCPU) -C $(@:_only=)
当在终端输入make module1_only
命令,将会触发 %_only
规则
Building module1_only in module1 directory.
make -j<host_ncpu> -C module1
其他解释:
$(@:_only=)表示将目标名字去掉_only
$(HOST_NCPU):如果
$(HOST_NCPU)
的值为4,那么-j$(HOST_NCPU)
就相当于-j4
,表示使用4个线程进行构建操作。
k、以"-"开头用于忽略错误或警告
clean:
-rm *.o
"-rm *.o"表示在执行"rm *.o"命令时,即使出现文件不存在或其他错误,也会继续执行后续的命令,而不会停止整个Make过程。这样可以避免由于某个命令执行失败而导致整个Make过程中断。