Makefile

Make 官方文档

https://www.gnu.org/software/make/manual/make.html

Makefile语法

语法简单介绍

注释

#注释代码

显式规则

目标:依赖

[TAB]指令

目标:一般是指要编译的目标,也可以是一个动作。

依赖:指执行当前慕白所要依赖的先项,包括其他目标,某个文件或者库等。一个目标可以有多个依赖,依次执行

命令:该目标下要执行的指令,可以多条,也可以没有

伪目标

.PHONY:

clean:

  1. 变量 A

    1. A= 替换
    2. A+= 追加
    3. A:= 恒等于(常量,不能追加)
    4. $(A) 取值
  2. 隐含规则

    1. %.c %.o 任意的.c文件,任意的.o文件

      # 需要.o文件则都通过.c 产生
      %.o:%.c
          gcc -c %.c -o %.o
    2. *.c *.o 所有的.c文件,所有的.o文件

    3. 区别是%是当需要寻找构造规则时,找到依赖文件进行构造。*不知道目标的名字,系统目录下所有后缀为.c的文件,然后遍历目录中的文件,是不是匹配,然后进行构造

Make命令

make [-f file] [options] [target]

make 默认在当前目录中寻找GUNmakefile、makefile、Makefile的文件作为make的输入文件

  • -f 指定输入文件

  • -v 显示版本号

  • -n 只输出命令,不执行

  • -s 只执行命令,不输出具体命令,或者使用@进行抑制输出

  • -w 显示执行前执行后的路径

  • -C dir 指定构造目录

没有指定目标时,默认使用第一个目标

如果指定,则执行对应的命令。

变量

系统变量

  • $@:目标的名字

  • $^:构造所需文件列表所有文件的名字

  • $<:构造所需文件列表的第一个文件的名字

  • $?:构造所需文件列表中更新过的文件

  • $*:不包括扩展名的目标文件名称

  • $+:所有的依赖文件,以空格分隔

  • $%:如果目标是归档成员,则该变量表示目标的归档成员名称

系统常量(make -p 查看)

  1. AS 汇编程序的名称

  2. CC C编译器的名称

  3. CPP C与编译器名称

  4. RM 文件按删除程序别名

自定义变量

  • 定义:变量名=变量值

  • 使用:$(变量民)/${变量名}

伪目标和模式匹配

.PHONY:clean

声明目标为伪目标之后,make将不会判断目标是否存在或该目标是否需要更新。

当touch clean时,不会出现问题

%.o:%.cpp     .o依赖于对应的.cpp

wildcard    $(wildcard ./*.cpp) #获取当前目录下所有的.cpp文件
patsubst    $(patsubst %.cpp, %.o, ./*.cpp) #将对应的cpp文件名替换为.o文件名
OBJ=$(patsubst %.cpp, %.o, $(wildcard ./*.cpp)) #将目录下的所有cpp文件转为.o的目标文件

模式匹配

%目标:%依赖

条件判断

ifeq #判断是否相等,相等返回true,不相等返回false

ifneq

ifdef #判断变量是否存在,如果存在则返回true,不存在返回false

ifndef

else

endif

#没有elseif的写法,只能写嵌套

ABC=y

ifeq ($(ABC),y)  #ifeq后面必须要有一个空格,ifneq也一样
	DEF=y
else
	ifeq ($(ABC),123)
		DEF=123
	else
		DEF=y
	endif
endif

传参

可通过传参写入变量

make Moliam=12_years_old all
all:
	@echo $(Moliam)

输出结果为

12_years_old

循环

只有一个循环 foreach

可以在循环中逐个修改值

只有GUN make支持,其他版本中的make,可以用shell中的循环来实现

TAGGET=a b c d
FILE=$(foreach v,$(TARGET),$v.txt)

all:
	echo $(foreach v,$(TARGET),$v)#v为被展开的变量
	echo $(FILE)

自定义函数

自定义函数不是真的函数,本质上是多行命令,可以有传参,没有返回值

define FUN1
	echo $(0) #函数名
	echo $(1) $(2) #传参第一个和第二个
	echo fun1
endif

all:
	$(call FUNC1,abc,$(Moliam))#传参在后面以逗号隔开

Make运行流程

流程图

当依赖的文件比目标的文件更新时,会再次执行。保证目标是用最新的依赖生成的。

第一次完全编译,后面只编译最新的代码(部分编译)。

Makefile 中编译动态链接库

动态链接库:不会把代码编译到二进制文件中,而是在运行时才去加载,所以只需要维护一个地址。

  • -fPIC 产生位置无关的代码
  • -shared 共享
  • -l(小L)指定动态库
  • -I(大i)指定头文件目录,默认当前目录
  • -L 手动指定库文件搜索目录,默认只链接共享目录

动态:运行时才去加载,动态加载

链接:库文件和二进制程序分离,用某种特殊手段维护两者的关系。

库: 库文件 .dll .so

好处是程序可以和库文件分离,可以分别发版,然后库文件可以被多处共享。

gcc -shared -fPIC a.c -o liba.so  # 生成动态库
gcc  -la -L./ b.c -o b# 依赖动态库

linux默认动态库路径配置文件

/etc/ld.so.conf
/etc/ld.so.conf.d/*.conf

Makefile 中编译静态链接库

静态链接库:会把库中的代码编译到二进制文件中,当程序编译完成后,该库文件可以删除。动态链接库不行,动态链接库必须和程序同时部署,还要保证程序能加载得到库文件。

与动态库相比,静态库可以不用部署(已经加载到程序内部),运行速度更快(因为不用去加载),但是会导致程序体积更大,并且库中内容更新,需要重新编 译生成程序。

Makefile 通用部分做公共头文件

include 目录

调用其他位置的Makefile相关文件,进行相关操作

在一个目录中做通用部分如clean,对象和目标等。然后在其他部分的makefile中,可以直接进行包含,而减少代码重复的工作量。

=和:=

Makefile中,是先展开所有变量,再调用指令

A=123
B=$(A)
A=456

输出B的值,也是456

Y=123
Y=$(Y)  #此处给变量Y赋值,会报错,错误原因为不能引用自己
Y:=$(Y)111 #此处会输出123111
  • = 取用最终值,不管变量调用写在赋值前还是赋值后。写在前还是写在后

  • := 只受当前行以及之前的代码影响,而不会受后面的赋值影响。

调用shell命令

A:=$(shell ls)
B:=$(shell pwd)
C:=$(shell if [! -f abc];then touch abc;fi)  #如果文件abc不存在则进行创建
$(shell cmd) #cmd为具体shell脚本/命令,可用变量直接等于命令返回结果

嵌套调用和多项目同时编译

all:
	make -C dir1 -f Makefile all
	make -C dir2 all
clean:
	make -C dir1 clean
	make -C dir2 clean

调用dir1,dir2中的make all和clean

简略写如下:

.PHONY:dir1 dir2 clean

DIR=dir1 dir2
all:$(DIR)

$(DIR):
	make -C $@
	
clean:
	# $$dir表示展开shell中的变量
	$(shell for dir in $(DIR);do make -C $$dir clean;done)

Make install

  1. 将源文件编译成二进制可执行文件
  2. 创建目录,将可执行文件拷贝到指定目录
  3. 加全局可执行的路径
  4. 加全局启停脚本
  5. 重置编译环境,删除无关文件

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!