Makefile介绍及语法

  1. Makefile的介绍

当我们遇到复杂的工程时,每次用gcc/g++等编译工具去操作就会显得很低效。因此make工具就出现了, make的出现是为了解决手动编译和链接大型工程的问题,它可以避免重复的工作,提高效率,保证正确性。make工具就根据makefile中的命令进行编译和链接的。但是当工程非常大的时候,手写makefile也是非常麻烦的,如果换了个平台makefile又要重新修改,因此更高级的一些构建系统或者工具工具像cmake、qmake、ninja和auto make就出现了,它们可以根据一些配置文件来自动化编译和链接软件项目。

2.Makefile的规则

2.1 基本规则

target ... : prerequisites ...

<tab缩进>command

<tab缩进>...

<tab缩进>...

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

Makefile一个示例:

debug:

@echo "hello world"

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

#include <iostream>

int main(int argc, char *argv[ ])

{

        std::cout << “hello world!” <<std::endl;

        return 0

}

Makefile修改如下:

debug:

         @echo “hello world!”

test:

         g++ -o test test.cpp

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

test@test:~/makefiletest$ rm test

test@test:~/makefiletest$ ls

Makefile  test.cpp

test@test:~/makefiletest$ make debug

hello world

test@test:~/makefiletest$ ls

Makefile  test.cpp

test@test:~/makefiletest$ make test

g++ -o test test.cpp

test@test:~/makefiletest$ ls

Makefile test test.cpp

2.2 伪目标

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

debug:

@echo "hello world"

test:

g++ -o test test.cpp

.PHONY:  debug

2.3 变量赋值和预定义变量

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

$符的其他用法:

$^ 表示所有的依赖文件

$@ 表示生成的目标文件

$< 代表第一个依赖文件

2.4注释和换行符

采用#进行一行注释

采用\作为续行符

2.5 变量的替换引用

语法格式是:

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

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

src := a.cpp b.cpp c.cpp

obj := $(src:cpp=o)

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

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

demo:

# 这是一个Makefile的注释

TARGET = hello     #TARGET延迟赋值hello

CC := g++          #CC立即赋值g++

CC += -g           #CC追加赋值-g, g++ -g表示添加调试信息,可用于gdb的调试

SRC = hello.cpp

OBJ = $(SRC:.cpp=.o)    #变量的替换引用,把hello.cpp的.cpp替换成.o

debug :

@echo "hello world"

echo $(SRC)

echo $(OBJ)

$(TARGET): $(SRC)

$(CC) -o $@ $<

# $(CC) -o ${TARGET} hello.cpp

compile: $(TARGET)

clean:

@rm hello hello.o -r

.PHONY: clean compile

2.6 常见函数

Makefile函数的基本格式是:$( )或者是${ },调用函数的时候要使用字符“$”,后面可以跟小括号或者大括号。

1) wildcard 通配符:

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

$(wildcard argments)

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

例如:

SRC = $(wildcard src/*.cpp) 表示查找并返回src目录下所有的.cpp文件, *表示通配符, 匹配一个或者多个任意字符

2)shell:

$(shell  <cmd> <args>)

cmd: 执行命令名称

args:参数列表

返回值:返回命令执行结果

SRC = $(shell find . -name *.cpp) 表示查找当前目录及子目录下的所有.cpp文件结尾的代码源文件

3) patsubst替换函数:

$(patsubst pattern, replacement, text)

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

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

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

返回值:patsubst 函数会在 text 中找到所有符合 pattern 的单词,并用 replacement 替换它们,然后 返回替换后的文本。 例如,如果有一个变量 src,它的值是:

src = a.cpp b.cpp c.cpp

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

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

这样,obj 的值就是:

obj = a.o b.o c.o

4) subst替换函数

$(subst from, to, text)

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

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

text: 是要处理的字符串。

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

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

返回:

fEEt on the strEEt

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

test@test:~/makefiletest$ tree

.

├── Makefile

└── src

        └─test.cpp

Makefile内容:

CC = g++

CC += -g

SRC := $(shell find . -name *.cpp)

TARGET := $(patsubst %.cpp, %,$(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

test.cpp内容:

#include <iostream>

int main()

{

std::cout << “hello world!” << std::endl;;

return 0;

}

执行:

make compile

生成obj/test:

pg@pg-Default-string:~/makefiletest$ tree -a

.

├── Makefile

├── obj

           └──test

└── src

          └── test.cpp

5)dir函数:

$(dir NAMES...)

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

优化Makefile内容:

CC = g++

CC += -g

SRC := $(shell find . -name *.cpp)

TARGET := $(patsubst %.cpp, %,$(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

6)suffix函数

$(suffix <names...>)

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

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

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

返回:

.cpp .cpp

7)basename函数

格式:

$(basename <names...>)

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

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

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

返回:

src/foo src-1.0/bar hacks

8) addsuffix函数

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

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

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

例如:

$(addsuffix .cpp, foo bar)

返回值:

foo.cpp  bar.cpp

9)addprefix函数

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

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

例如:

$(addprefix src/, foo bar)

返回值:

src/foo  src/bar

10)foreach函数

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

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

例如:

files := foo bar baz

files-with-c := $(foreach file,$(files),$(file).cpp)

11)条件判断语言

Makefile条件判断有下面几种:

ifeq/ifneq语句:

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

ifeq (arg1,  arg2)

#arg1 arg2 相等执行这里的语句

else

#arg1 arg2 不相等执行这里的语句

endif

ifdef/ifndef语句

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

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

ifdef:

ifdef  var

#如果定义了var,执行这里的内容

else

#如果没定义var,执行这里的内容

endif

ifndef:

infdef var

#如果没定义var,执行这里的内容

else

#如果定义var,执行这里的内容

endif

Makefile demo:

CC := aarch64-linux-gnu-gcc

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

INC := ./inc \

         ./3rd/usr/local/include  \

         ./3rd/usr/include \

         ./3rd/usr/include/python3.10 \

         ./3rd/usr/include/aarch64-linux-gnu/python3.10 \

         ./3rd/usr/include/aarch64-linux-gnu

OBJ := $(subst src/,obj/,$(SRC:.c=.o))

TARGET=obj/SmartHouse

CFLAGS := $(foreach item, $(INC),-I$(item))  # -I./inc -I./3rd/usr/local/include

LIBS_PATH  :=  ./3rd/usr/local/lib \

                                                                 ./3rd/lib/aarch64-linux-gnu \

                                                                 ./3rd/usr/lib/aarch64-linux-gnu \

                                                                 ./3rd/usr/lib/python3.10

LDFLAGS := $(foreach item, $(LIBS_PATH),-L$(item)) # -L./3rd/usr/local/libs

LIBS := -lwiringPi -lpython3.10 -pthread -lexpat -lz -lcrypt

obj/%.o:src/%.c

         mkdir -p obj

         $(CC) -o $@ -c $< $(CFLAGS)

$(TARGET) :$(OBJ)

         $(CC) -o $@ $^ $(CFLAGS) $(LDFLAGS) $(LIBS)

compile : $(TARGET)

clean:

         rm $(TARGET) obj $(OBJ) -rf

debug:

         echo $(CC)

         echo $(SRC)

         echo $(INC)

         echo $(OBJ)

         echo $(TARGET)

         echo $(CFLAGS)

         echo $(LDFLAGS)

         echo $(LIBS)

.PHONY: clean compile debug

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Gidetimothy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值