make的使用和Makefile的编写

时间:2022-06-04 12:49:55

标签: Linux C/C++开发


简介

  • make:维护程序模块关系和生成可执行程序的工具
  • makefile:定义了整个项目的编译规则,定义了模块间的依赖关系,指定文件的编译顺序和编译使用的命令
  • makemakefile文件中获取模块间的依赖关系,判断哪些文件过时了,根据这些信息make确定哪些文件需要重新编译,然后使用makefile中的编译命令进行编译
  • 过时:一个文件生成后,用来生成该文件的源文件或者头文件被修改了,导致生成该文件所需要的源文件或头文件的修改时间比生成该文件的时间晚

make使用

基本构成

目标文件列表 分隔符 依赖文件列表 [; 命令]
[命令]
[命令]
  • 如果某一行是命令,必须以Tab键开始
  • #为注释行
#示例1
main:main.o module1.o module2.o ; gcc main.o module1.o module2.o -o main
main.o:main.c head1.h head2.h common_head.h
gcc -c main.c

#示例2
main:main.o module1.o module2.o
gcc main.o module1.o module2.o -o main
main.o: head1.h head2.h common_head.h
module1.o:head1.h
module2.o:head2.h

流程

  1. 在当前目录下找名为Makefile或makefile的文件
  2. 在当前目录下寻找第一行中的目标文件main,如果没有找到就去寻找生成main文件所依赖的文件,如果没有找到就跳过第二行的编译命令定位到第三行,第三行的目标文件也没有,但所依赖的源文件和头文件在当前目录下都被找到了,于是执行第四行的命令
  3. 定位到第5行,发现目标文件module1.o没有,但所依赖的文件都被找到了,就执行第六行的编译命令。依次向下执行,直到最后一行
  4. 定位到第一行,然后执行第二行的命令,生成目标文件

  5. 在当前目录下找名为Makefile或makefile的文件

  6. 在当前目录下寻找第一行中的目标文件main,如果找到就去寻找生成main文件所依赖的文件,如果找到。然后比较main和依赖文件的修改时间,如果main的修改时间晚。就定位到第三行,执行相同的操作,直至最后一行,否则就执行下面的命令
  7. 定位到第一行,比较看是否要执行第二行的命令

说明

  • 命令行之间可以插入任意多个空行,空行也要按Tab键开头
  • 执行:make, make -f othername, make --file=othername
  • 通常把makefile文件中第一行的目标文件作为最终目标文件
  • $表示使用变量或者调用函数,如果要是该字符需要书写两个连续的$$

文件的构成

说明

  • 一个完整的makefile文件由:显式规则,隐含规则,使用变量,文件指示,注释,5部分构成
  • 显式规则:显式指定依赖文件,命令
  • 隐含规则:需要自动推到处的规则
foo:foo.c | somelib
gcc -o foo foo.c somelib
  • |前面的文件是普通依赖文件,|后面的文件过时,foo不会被重新生成,也就是第二行的命令不会被执行
  • 可以在Tab键后面先写上一个+,-,@然后再协商命令
    • - : 执行本行命令时如果遇到错误,继续执行而不退出make
    • + : 本行命令始终被执行
    • @ : 执行本行命令时不在屏幕上打印命令内容
  • 一个文件可以有多个目标
a.o b.o c.o: e.h
#相当于
a.o: a.c e.h
gcc -c a.c -o a.o
b.o: b.c e.h
c.o: c.c e.h

伪目标

  • 伪目标不要求生成实际文件而是执行一些辅助命令
  • 当一个目标被声明为伪目标后,make在执行此规则时不会试图去查找该目标的依赖文件
clean:
-rm -f *.o
  • 以上用来删除所有已.o结尾的文件
  • 使用make clean
  • 由于clean没有依赖文件,所以始终认为它是最新的,而不去执行下面的命令,除非使用make clean或把它申请为伪目标.PHONY : clean

特殊目标

  • .PHONY 所有的依赖被作为伪目标
  • .IGNORE 后面的依赖文件,生成这些文件的命令在执行时如果出现错误,将被忽略继续执行
  • .SUFFIXES 该目标的依赖文件被认为是一个后缀列表
  • .SILENT 执行生成依赖文件的命令时不会打印所执行的命令
  • .PRECLOUS 该目标的依赖文件会受到特殊对待,如果make被终止,或者终止,这些依赖并不会被删除,如果是中间文件,不需要时也不会删除
  • .INTERMEDIATE 目标依赖文件在make执行时被当做中间文件对待

搜索目录

  • 通过VPATH指定依赖文件的搜索目录,如:VPATH = /usr/src:/headers
  • 定义VPATH时,使用空格或者冒号将多个搜索目录分开
  • 通过使用make的关键字vpath
    • vpath 为符合模式的文件指定目录
    • vpath 清除符合模式的文件的搜索目录
    • vpath 清除设置的文件搜索目录
  • vpath中的需要包含%,意思是匹配零个或若干个字符。例如%.h表示所有以.h结尾的文件。指定了要搜索的文件集
  • 这仅仅是对于makefile来说搜索目标和依赖文件的路径,但是对于命令行来说是无效的,也就是说在执行g++或者gcc时不会自动从VPATH或者vpath中自动搜索要包含的头文件等信息文件
vpath %.c path1
vpath %.c path2
vpath %.h path3
vpath %.c src1
vpath % src2
vpath %.c src3
表示以.c结尾的文件现在src1中找,然后是src2,最后是src3

使用变量

  • 定义变量的形式:变量名 赋值符 变量值
  • 引用变量:$(变量名), 如果要用字符$要使用$$来表示
foo = $(bar)
bar = $(ugh)
ugh = Huh
all:
echo $(foo)
  • 赋值符::=,赋值的变量是立即展开变量
CFLAGS := $(a)-o
a := later

CFLAGS的值是-o而不是later-o
  • 条件赋值符`?
  • FOO ?= bar 如果变量FOO在之前没有定义,就给它赋值bar,否则不改变它的值
  • 可以使用=, +=

  • 预定义变量

宏名 初始值 说明
CC cc 默认使用的编译器
CFLAGS -o 编译器使用的选项
MAKE make make命令
MAKEFLAGS make命令的选项
SHELL 默认使用的Shell类型
PWD 运行make命令时的当前目录
AR ar 库管理命令
ARFLADS -ruv 库管理命令选项
LIBSUFFIXE .a 库的后缀
A a 库的扩展名

* 自动变量
1. $@ 一个规则中的目标文件名。如果目标是一个文档文件(.a文件为文档文件/静态库文件)
2. $%当规则的目标文件是一个静态库文件时,$%代表静态库的一个成员名
3. $<规则中的第一个依赖文件名,如果规则中使用了隐含规则,则是由隐含规则引入的第一个依赖文件名
4. $> 只适用于库文件,它的值是库名
5. $?
6. $^规则的所有依赖文件列表,使用空格分隔,如果目标是静态库文件,所代表的只是所有库成员,会去掉重复的依赖文件
7. $* 目标文件去掉后缀后的名称
* 自动变量只能出现在命令中,要是想出现在文件列表中,要在前加上一个$

file1.o file2.o: header.h
cp $@ /backup
当目标文件过时时,将原来的文件备份到/backup目录下,然后重新生成新的目标文件

使用条件语句

  • ifeq表示条件语句的开始,并指定一个比较条件(相等), else之后是当条件不满足时要执行的部分,endif表示一个条件语句结束
  • 还有关键字ifdef, ifndef, ifneq
libs_for_gcc = -lgnu
normal_libs =
ifeq ($(CC),gcc)
libs = $(libs_for_gcc)
else
libs = $(normal_libs)
endif

使用库

  • 大型软件开发项目中,通常把编译好的模块按照功能的不同放在不同的库中
  • 库中的文件一般称为库的成员,表示为:库名(成员名)
  • mylib.a(file.o) 表示静态库mylib.a中一个名为file.o的文件,静态库也称为文档文件
  • 建立和维护一个库时,将库名作为目标文件,把希望放到库中的文件名作为依赖文件。格式:库名: 库名(成员1) 库名(成员2)...或者库名: 库名(成员1 成员2 ...)
  • 命令:ar -ruv 库名 目标文件名

make参数

  • -C dir 线切换到dir目录下,然后把dir当做当前目录
  • -d 打印出所有的调试信息
  • -e 不允许在makefile中对系统环境变量进行重新赋值
  • -f filename --file=FILE --makefile=FILE使用指定文件作为makefile文件
  • -i 忽略执行过程中产生的错误
  • -h 打印出帮助信息
  • -k 遇到错误时不终止执行,知道出现致命的错误才终止
  • -n 只打印出要执行的命令
  • -o filename指定文件filename不需要重建,同时也不重建依赖才文件的任何目标文件
  • -q 不执行任何命令,只返回一个查询状态,0:没有目标需要重建 1:存在需要重建的目标 2:错误发生
  • -r 忽略隐含规则
  • -R 取消make内嵌的预定义变量
  • -s执行但不西安市所执行的命令
  • -t 把所有目标文件的最后修改时间设置为当前系统时间
  • -v打印出make的版本信息

命令行

  • 可以通过-I制定文件目录
  • 如果使用$<,会直接引用make的文件路径
a.out: main.cpp comm.h
g++ main.cpp
如果main.cpp不在当前目录,会出现错误,改正如下:
a.out: main.cpp comm.h
g++ -Ipath main.cpp
或者直接使用`$<`
a.out: main.cpp comm.h
g++ $<