linux-Makefile

本文详细介绍了Linux Makefile的使用,包括基本规则、工作原理、变量、函数及清理操作。通过实例展示了如何利用Makefile提升编译效率,如使用make-j参数并行编译,以及如何定义和使用自动变量、模式规则。还讨论了Makefile中的.PHONY伪目标和清理目标,以实现高效自动化构建和清理。
摘要由CSDN通过智能技术生成

参考学习视频:[linux从零到精通] gcc和Makefile,多文件编译神器

通配符
$@ 所有的目标文件 
$^ 所有的依赖文件 
$< 所有的依赖文件的第一个文件

在这里插入图片描述
make -j 参数加快编译效率

对于大型项目,在使用cmake控制编译时,仅仅执行make指令效率较低,使用make -j后面跟一个数字,比如make -j4 make -j6 make -j14等。

含义是 让make最多允许n个编译命令同时执行,这样可以更有效的利用CPU资源。

假设我们的系统是cpu是12核,在不影响其他工作的情况下,我们可以make -j12
将cpu资源充分利用起来,一般来说,最大并行任务数为cpu_num * 2

1. 查看物理CPU的个数
#cat /proc/cpuinfo |grep "physical id"|sort |uniq|wc -l
 
2. 查看逻辑CPU的个数
#cat /proc/cpuinfo |grep "processor"|wc -l
 
3. 查看CPU是几核
#cat /proc/cpuinfo |grep "cores"|uniq
 
4. 查看CPU的主频
#cat /proc/cpuinfo |grep MHz|uniq
#以'#'开头的行表示注释
#定义变量VAR,强制赋值为app
VAR=test
#在VAR之前定义的值后面再追加app这个值,这时该变量值扩展为testapp
VAR+=app
#如果之前VAR没有被定义,则定义并使用testapp;否则使用之前的值。
VAR?=testapp

# 第一条目标为总的目标,
# 依赖可以是文件(目录)或为其他目标,依赖不是必须
# 动作可以是Linux命令,动作的那一行必须以TAB键开头

target: depend1 depend2 depend3 ...
[TAB] action1
[TAB ] action2

target1:
[TAB] action1
[TAB] action2

默认的情况下,make命令会在当前目录下按顺序找寻文件名为“GNUmakefile”、“makefile”、“Makefile”的文件,找到后就解释并执行该文件,如果找不到就提示错误并退出。一般Makeifle文件名我们会用Makefile或makefile,而不会使用GNUmakefile。接下来我们以之前的静态库和动态库为例讲解makefile的编写和使用。

之前我们在src路径下需要敲好几条命令才能编译生成动态库文件,而如果要拷贝安装文件或删除不用的文件时也要添加额外的命令。这样如果每次都要编译、安装、删除就比较麻烦。而如果我们写了一个makefile之后,接下来的工作只需要敲一条命令即可。下面是lib路径下用来同时生产静态库和动态库的makefile文件:

#定义变量指定生成的静态库和动态库的名字
LIBNAME=mycrypto

#定义一个变量指定库文件和头文件的安装路径
INSTPATH=`pwd`/../lib/

#定义编译器,如果今后交叉编译的话,只需要在这里改成交叉编译器即可。
CC=gcc
AR=ar

# 这里all是整个makefile文件的第一个目标,也就是总的目标,当我们输入make命令时就是要完成这个目标;
#该目标有两个依赖dynamic_lib和 static_lib 和 两个动作 make clear和 make install

# 其中两个依赖 dynamic_lib 和 static_lib 也是makefile文件的目标,
#所以整个makefile要先执行完dynamic_lib和static_lib这两个目标后才能执行后面的动作;

# 在动作make clear和make install前面有个 @ 符,这个符号会让执行make名时不打印这两天命令本身,而只是输出命令执行的结果;
#另外make命令在执行过程中,也会多次载入makefile文件;

all: dynamic_lib static_lib
@make clear
@make install

# dynamic_lib 目标用来编译生成动态库,它是all目标的一个依赖;
dynamic_lib:
${CC} -shared -fPIC *.c -o lib${LIBNAME}.so

# static_lib 目标用来编译生成静态库,它是all目标的一个依赖;
static_lib:
${CC} -c *.c
${AR} -rcs lib${LIBNAME}.a *.o

# install是一个单独的目标,他用来将编译生成的库文件和头文件拷贝到相应的安装路径下。
# 在总目标all下有个动作@make install 会执行该目标;
install:
cp -rf lib${LIBNAME}.* ${INSTPATH}
cp -rf *.h ${INSTPATH}

# uninstall是一个单独的目标,他用来在安装路径下删除之前安装的库文件和头文件
# 该目标没有被总的目标all依赖或执行,所以默认该目标不会被执行,
#如果想执行该目标,则可以在Linux命令行下输入make uninstall来执行

uninstall:
rm -f ${INSTPATH}/lib${LIBNAME}.*
rm -f ${INSTPATH}/*.h

# clear是一个单独的目标,他用来将编译生成的object临时文件删除。
# 在总目标all下有个动作@make clear会执行该目标;
clear:
rm -f *.o

# clean是一个单独的目标,它依赖clear目标,所以先通过clear目标删除所有的object临时文件,之后再删除编译产生的库文件;
# 该目标没有被总的目标all依赖或执行,所以默认该目标不会被执行,如果想执行该目标,则可以在Linux命令行下输入make clean 来执行
clean: clear
rm -f lib${LIBNAME}.*

Makefile中.PHONY的作用
单词phony (即phoney)的意思是:伪造的,假的。

来自collins的解释是:
If you describe something as phoney, you disapprove of it because it is false rather than genuine.

举个例子:

$ cat -n Makefile1
     1    clean:
     2        rm -f foo
$ cat -n Makefile2
     1    .PHONY: clean
     2    clean:
     3        rm -f foo

Makefile1和Makefile2的差别就是在Makefile2中定义了:
.PHONY: clean

直接Make看看

$ make -f Makefile1 clean
rm -f foo
$ make -f Makefile2 clean
rm -f foo

从上述来看,Makefile1和Makefile2的行为没有啥子区别。

$ touch clean
$ ls -l
total 8
-rw-r--r-- 1 lzy lzy  0 Jul 13 18:06 clean
-rw-r--r-- 1 lzy lzy18 Jul 13 17:51 Makefile1
-rw-r--r-- 1 lzy lzy 32 Jul 13 17:51 Makefile2
$ make -f Makefile1 clean
make: 'clean' is up to date.
$ make -f Makefile2 clean
rm -f foo

区别来了,Makefile1拒绝了执行clean, 因为文件clean存在。而Makefile2却不理会文件clean的存在,总是执行clean后面的规则。由此可见,.PHONY clean发挥了作用。

小结:

.PHONY: clean
    o means the word "clean" doesn't represent a file name in this Makefile;
    o means the Makefile has nothing to do with a file called "clean" 
      in the same directory.

1 makefile的基本规则

makefile由一组规则组成,规则如下:

目标: 依赖
(tab)命令

makefile基本规则三要素:

  • 目标: 要生成的目标文件
  • 依赖: 目标文件由哪些文件生成
  • 命令: 通过执行该命令由依赖文件生成目标

下面以具体的例子来讲解:
当前目录下有main.c fun1.c fun2.c sum.c, 根据这个基本规则编写一个简单的makefile文件, 生成可执行文件main.

第一个版本的makefile:


缺点: 效率低, 修改一个文件, 所有的文件会全部重新编译.

2 makefile工作原理

基本原则:

  • 若想生成目标, 检查规则中的所有的依赖文件是否都存在:

    • 如果有的依赖文件不存在, 则向下搜索规则, 看是否有生成该依赖文件的规则:
      如果有规则用来生成该依赖文件, 则执行规则中的命令生成依赖文件;
      如果没有规则用来生成该依赖文件, 则报错.
      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2XKPbzSd-1626428799399)(media/fc37c4e09b30fbb42a82d66e4f9fa8a9.png)]
  • 如果所有依赖都存在, 检查规则中的目标是否需要更新, 必须先检查它的所有依赖,依赖中有任何一个被更新, 则目标必须更新.(检查的规则是哪个时间大哪个最新)

    • 若目标的时间 > 依赖的时间, 不更新
    • 若目标的时间 < 依赖的时间, 则更新

总结:

  • 分析各个目标和依赖之间的关系

  • 根据依赖关系自底向上执行命令

  • 根据依赖文件的时间和目标文件的时间确定是否需要更新

  • 如果目标不依赖任何条件, 则执行对应命令, 以示更新(如:伪目标)

第二个版本:


缺点: 冗余, 若.c文件数量很多, 编写起来比较麻烦.

3 makefile中的变量

在makefile中使用变量有点类似于C语言中的宏定义, 使用该变量相当于内容替换, 使用变量可以使makefile易于维护, 修改起来变得简单。
makefile有三种类型的变量:

  • 普通变量

  • 自带变量

  • 自动变量

3.1 普通变量

  • 变量定义直接用 =

  • 使用变量值用 $(变量名)

如:下面是变量的定义和使用

foo = abc // 定义变量并赋值

bar = $(foo) // 使用变量, $(变量名)

定义了两个变量: foo、bar, 其中bar的值是foo变量值的引用。
除了使用用户自定义变量, makefile中也提供了一些变量(变量名大写)供用户直接使用, 我们可以直接对其进行赋值:

CC = gcc #arm-linux-gcc

CPPFLAGS : C预处理的选项 -I

CFLAGS: C编译器的选项 -Wall -g -c

LDFLAGS : 链接器选项 -L -l

3.2 自动变量

  • $@: 表示规则中的目标

  • $<: 表示规则中的第一个条件

  • $^: 表示规则中的所有条件, 组成一个列表, 以空格隔开, 如果这个列表中有重复的项则消除重复项。

特别注意:自动变量只能在规则的命令中使用.

模式规则

至少在规则的目标定义中要包含’%’, ‘%’表示一个或多个, 在依赖条件中同样可以使用’%’, 依赖条件中的’%’的取值取决于其目标:
比如: main.o:main.c fun1.o: fun1.c fun2.o:fun2.c, 说的简单点就是: xxx.o:xxx.c

makefile的第三个版本:

4 makefile函数

makefile中的函数有很多, 在这里给大家介绍两个最常用的。

  1. wildcard – 查找指定目录下的指定类型的文件
    src=$(wildcard *.c) //找到当前目录下所有后缀为.c的文件,赋值给src
  2. patsubst – 匹配替换
    obj=$(patsubst %.c,%.o, $(src)) //把src变量里所有后缀为.c的文件替换成.o

在makefile中所有的函数都是有返回值的。

当前目录下有main.c fun1.c fun2.c sum.c

src=$(wildcard *.c) 等价于src=main.c fun1.c fun2.c sum.c

obj=$(patsubst %.c,%.o, $(src))等价于obj=main.o fun1.o fun2.o sum.o

makefile的第四个版本:

缺点: 每次重新编译都需要手工清理中间.o文件和最终目标文件

5 makefile的清理操作

用途: 清除编译生成的中间.o文件和最终目标文件

make clean 如果当前目录下有同名clean文件,则不执行clean对应的命令, 解决方案:

  • 伪目标声明:
    .PHONY:clean

    • 声明目标为伪目标之后,
      makefile将不会检查该目标是否存在或者该目标是否需要更新
  • clean命令中的特殊符号:

  • “-”此条命令出错,make也会继续执行后续的命令。如:“-rm main.o”
    rm -f: 强制执行, 比如若要删除的文件不存在使用-f不会报错

  • “@”不显示命令本身, 只显示结果。如:“@echo clean done”

  • 其它

– make 默认执行第一个出现的目标, 可通过make dest指定要执行的目标

make -f : -f执行一个makefile文件名称, 使用make执行指定的makefile: make -f mainmak

makefile的第5个版本:

在这里插入图片描述
在makefile的第5个版本中, 综合使用了变量, 函数, 模式规则和清理命令, 是一个比较完善的版本.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值