(3)Makefie:C语言编译标志、通配符、CFLAGS、LDFLAGS、LDLIBS

目录

一、C编译器标志

二、基本知识

1、常用的变量

2、通配符/预处理

3、添加宏和链接库进行编译和链接

a、CFLAGS(宏定义、包含路径、编译过程信息)

 b、LDFLAGS(指定库路径、链接的库)

c、LDLIBS(指定连接的库)

d、-Wl,--start-group 和 -Wl,--end-group

e、$(LDLIBS_$@)

f、$^和$@的使用

g、句子前面加@:不输出到终端

h、目标文件依赖于头文件

i、%_output的使用

j、%_only

k、以"-"开头用于忽略错误或警告

一、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.alibstring.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.alib2.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 $@ [email protected]

        表示将所有的所有的目标文件复制,并在原来的名字后面加上.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.hb.h 发生变化时,需要重新编译 main.ofunc.

(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过程中断。

猜你喜欢

转载自blog.csdn.net/weixin_45981798/article/details/132001724