Loading

MakeFile学习

Makefile学习

Makefile的规则

基本规则

target ... : prerequisites ...
<tab缩进>command
<tab缩进>...
<tab缩进>...

target是一个目标文件,可以使Object File,也可以是执行文件。还可以是一个标签(Label),对于标签这种特性,在后续‘“伪目标”中会有叙述。prerequisites就是要生成target所需要的文件或目标。command是make需要执行的任意shell命令。

Makefile的一个示例:

debug:
	@echo "hello world"

如果,我们要编译下面这个简单的例子:

#include<stdio.h>
int main(int argc, char *argv[])
 {
 	printf("hello world!\n");
 	return 0
 }

Makefile修改如下:

debug:
 	@echo "hello world"
 test:
 	gcc -o test test.c

执行命令make test 可以生成 test文件, 执行make debug可以输出“hello world”:

test@test:~/makefiletest$ rm test
 test@test:~/makefiletest$ ls
 Makefile  test.c
 test@test:~/makefiletest$ make debug
 hello world
 test@test:~/makefiletest$ ls
 Makefile  test.c
 test@test:~/makefiletest$ make test
 gcc -o test test.c
 test@test:~/makefiletest$ ls
 Makefile  test  test.c

伪目标

如果一个目标和一个实际文件同名,那么make会认为该目标已经是最新的,不需要重新生成,也不会执 行其命令。通过将目标声明为伪目标,可以避免这种情况,强制执行其命令。

debug:
 	@echo "hello world"
 test:
 	gcc -o test test.c
 .PHONY: debug

变量赋值和预定义变量

Makefile中的变量赋值运算符有四种,分别是=、:=、?=和+=,$ 符号表示取变量的值,当变量名多于一 个字符时,使用"( )":

= 表示延迟展开赋值,即变量的值是在使用时才确定,可能会受到后面的赋值影响。例如,VAR_A = A, VAR_B = (VAR_A) B,VAR_A = AA,那么最后VAR_B的值是AA B,而不是A B。

:= 表示直接赋值,即变量的值是在定义时就确定,不会受到后面的赋值影响。例如,VAR_A := A, VAR_B := $(VAR_A) B,VAR_A := AA,那么最后VAR_B的值是A B,而不是AA B。

?=表示条件赋值,即只有当变量没有被赋值时,才使用等号后面的值作为变量的值。例如,VAR ?= new_value,如果VAR在之前没有被赋值,那么VAR的值就为new_value,否则保持原来的值不变。

+= 表示追加赋值,即将等号后面的值追加到变量原来的值之后,形成一个新的值。例如,VAR += new_value,如果VAR在之前没有被赋值,那么VAR的值就为new_value,如果VAR在之前被赋值为 old_value,那么VAR的值就为old_value new_value

$符的其他用法:

  1. $^ 表示所有的依赖文件
  2. $@ 表示生成的目标文件
  3. $< 代表第一个依赖文件

注释和换行符

  1. 采用#进行一行注释
  2. 采用\作为续行符

变量的替换引用

语法格式:

$(var:a=b)或${var:a=b}

表示把变量var的值中的a后缀替换成b后缀。例如:

 src := a.c b.c c.c
 obj := $(src:c=o)

把变量src的值中的.c后缀替换成.o后缀,赋值给变量obj,结果是:

 obj := a.o b.o c.o

举例:

# 这是一个Makefile的注释
TARGET = hello #TARGET延迟赋值hello
 CC := gcc    
#CC立即赋值gcc
 CC += -g    
#CC追加赋值-g, gcc -g表示添加调试信息,可用于gdb的调试
SRC = hello.c
OBJ = $(SRC:.c=.o) #变量的替换引用,把hello.c的.c替换成.o
debug:
	@echo "hello world"
 	echo $(SRC)
	echo $(OBJ)
	
 $(TARGET): $(SRC)
	 $(CC)  -o $@  $<
 #   $(CC) -o ${TARGET} hello.c
 
 compile: $(TARGET)
 clean:
 	@rm hello hello.o -r
 	
 .PHONY:  clean compile

常见函数

Makefile函数的基本格式是:$( )或者是${ },其中,第一个是函数名,后面是函数的参数,参数之间要用逗号分隔开,参数和函数名之间使用空格分开。调用函数的时候要使用字符$,后面可以跟小括号或者大括号。

  1. wildcard 通配符:

Makefile中的wildcard 是一个函数,用于扩展通配符,返回与通配符匹配的文件列表。通配符是一种特殊的字符,可以表示多个文件名或目录名,常见的通配符有 * 和 ?,分别表示任意长度的任意字符和单个 任意字符。格式如下:

$(wildcard argments)

比如*.c 表示所有以 .c 结尾的文件名,a?.txt 表示所有以 a 开头,中间有一个任意字符,以 .txt 结尾的文 件名。例如:

SRC = $(wildcard src/*.c)

表示查找并返回src目录下所有的.c文件, *表示通配符, 匹配一个或者多个任意字符

  1. shell:

    $(shell <cmd> <args>)
    

    cmd: 执行命令名称

    args:参数列表

    返回值: 返回命令执行结

    例如:

    SRC = $(shell find . -name *.c)
    

    表示查找当前目录及子目录下的所有.c文件结尾的代码源文件

  2. patsubst替换函数:

     $(patsubst pattern,replacement,text)
    

    pattern: 是一个包含通配符 % 的模式,表示匹配任意长度的任意字符 、

    replacement: 是一个替换字符串,也可以包含 %,表示用 pattern 中匹配的字符替换。

    text: 是一个要处理的文本,可以包含多个以空格分隔的单词。

    返回值:patsubst 函数会在 text 中找到所有符合 pattern 的单词,并用 replacement 替换它们,然后 返回替换后的文本。

    例如,如果有一个变量 src,它的值是:

    src = a.c b.c c.c
    

    想把它的值中的所有 .c 后缀替换成 .o 后缀,可以这样写:

    obj = $(patsubst %.c,%.o,$(src))
    

    这样,obj 的值就是:

    obj = a.o b.o c.o
    
  3. subst替换函数

    $(subst from,to,text)
    

    from: 是要被替换的字符或单词

    to: 是替换后的字符或单词

    text: 是要处理的字符串。

    返回值:subst 函数会在 text 中找到所有的 from,并用 to 替换它们,然后返回替换后的字符串。

    $(subst ee,EE,feet on the street) 
    

    返回:

    fEEt on the strEEt
    

    综合举例,测试工程代码目录:

    test@test:~/makefiletest$ tree
     .
     ├── Makefile
     └── src
     └── test.c
    

    Makefile内容:

    CC = gcc
    CC += -g
    SRC := $(shell find . -name  *.c)
    TARGET := $(patsubst %.c, %,$(subst src,obj, $(SRC)))
    debug: 
    	@echo "hello world"
    	echo $(SRC)
    	echo $(TARGET)
    
    $(TARGET): $(SRC)
    	mkdir  -p obj
    	$(CC) -o $@ $<
    	
    compile: $(TARGET)
    clean:
    	@rm obj -r
    .PHONY:  clean  compile
    

    执行:

    make compile
    

    生成obj/test:

    pg@pg-Default-string:~/makefiletest$ tree -a
     .
     ├── Makefile
     ├── obj
     │   
    └── test
     └── src
     └── test.c 
    
  4. dir函数:

    $(dir NAMES...)
    

    dir 函数是一个用于从文件名序列中提取目录部分的函数

    优化Makefile内容:

    CC = gcc
    CC += -g
    SRC := $(shell find . -name  *.c)
    TARGET := $(patsubst %.c, %,$(subst src,obj, $(SRC)))
    debug: 
    	@echo "hello world"
    	echo $(SRC)
    	echo $(TARGET)
    
    $(TARGET): $(SRC)
        mkdir  -p $(dir $(TARGET))
        $(CC) -o $@ $<
    compile: $(TARGET)
    clean:
    	@rm $(dir $(TARGET)) -r
    .PHONY:  clean  compile
    
  5. suffix函数

    $(suffix <names...>)
    

    功能:从文件名序列中取出各个文件名的后缀。

    返回值:返回文件名序列的后缀序列,如果文件没有后缀,则返回空字串。

    例如:

    $(suffix src/foo.c src-1.0/bar.c hacks)
    

    返回:

    .c .c
    
  6. basename函数

格式:

$(basename <names...>)

功能:从文件名序列中取出各个文件名的前缀部分。

返回值:返回文件名序列的前缀序列,如果文件没有前缀,则返回空字串。

例如:

$(basename src/foo.c src-1.0/bar.c hacks)

返回:

src/foo src-1.0/bar hacks
  1. addsuffix函数

    $(addsuffix <suffix>,<names...>)
    

    功能:把后缀加到中的每个单词后面。

    返回:返回加过后缀的文件名序列。

    例如;

    $(addsuffix .c,foo bar)
    

    返回值:

    foo.c bar.c
    
  2. addprefix函数

    功能:把前缀加到中的每个单词后面。

    返回值:返回加过前缀的文件名序列。

    例如:

    $(addprefix src/,foo bar)
    

    返回值:

     src/foo src/bar
    
  3. foreach函数

    $(foreach <var>,<list>,<text>)
    

    把list中使用空格分割的单词依次取出并赋值给变量var, 然后执行text表达式

    例如:

    files := foo bar baz
    files-with-c := $(foreach file,$(files),$(file).c)
    
  4. 条件判断语言

    Makefile条件判断有下面几种:

    ifeq/ifneq语句:

    ifeq语句 : 判断参数 是否相等,相等为 true, 否则是 false.

    ifeq (arg1, arg2)
    	#arg1 arg2 相等执行这里的语句
    else
    	#arg1 arg2 不相等执行这里的语句
    endif
    

    ifneq语句:判断参数 是否不等,不等为 true, 否则为 false.

    ifneq (arg1, arg2)
    	#arg1 arg2 不相等执行这里的语句
    else
    	#arg1 arg2 相等执行这里的语句
    endif
    

    ifdef/ifndef语句

    ifdef 语句: 判断参数是否有值,有值为 true, 否则是 false

    ifdef var
    	#如果定义了var,执行这里的内容
    else
    	#如果没定义var,执行这里的内容
    endif
    

    ifndef : 判断参数是否没有值,没有值为 true, 否则为 false

    infdef var
    	#如果没定义var,执行这里的内容
    else
    	#如果定义var,执行这里的内容
    endif
    
posted @ 2024-03-22 11:01  ⭐⭐-fighting⭐⭐  阅读(11)  评论(0编辑  收藏  举报