首页 > 其他分享 >Makefile

Makefile

时间:2024-05-02 21:45:14浏览次数:26  
标签:ten make Makefile echo debug VAR hello

编译工具及构建工具介绍

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

cmake:是一个跨平台的构建系统,它可以根据CMakeLists.txt中的指令来生成不同平台和工具的工程文件,例如Makefile、Visual Studio解决方案、Ninja文件等。cmake可以支持多种语言和多种架构,它还提供了一些高级功能,如测试、打包、安装等。

qmake:是一个用于Qt项目的构建系统,它可以根据.pro或.pri中的指令来生成Makefile或其他形式的工程文件。

ninja:是一个小巧而快速的构建工具,它可以根据ninja.build中的规则来执行编译和链接命令。ninja主要关注于性能和效率,它可以利用多核处理器和并行处理来加速构建过程。ninja通常不需要用户直接编写
配置文件,而是由其他构建系统(如cmake)来生成

auto make:是一个用于生成Makefile.in文件的工具,Makefile.in是一种用于auto conf的配置文件格式,auto conf是一个用于生成configure脚本的工具。configure脚本是一个用于检测系统环境并生成最终的Makefile文件的脚本Makefile.am是一种用于auto make的配置文件格式,它包含了一些指令和变量,用于定义程序或库的源文件、目标文件、依赖关系和编译选项等。

make:是一个经典而通用的构建工具,它可以根据Makefile中的规则来执行编译和链接命令。make可以支持多种平台和工具,它还提供了一些高级功能,如条件判断、函数调用、模式匹配。

Makefile的使用

编译的四个阶段

编译的四个过程:预处理(Pre-Processing)、编译(Compiling)、汇编 (Assembliang)、链接(Linking)

image-20240502140115245

Makefile的规则

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

Makefile一个示例:

debug:
	@echo "hello world"		# @的作用:是否输出执行的命令

image-20240502143906397

注意:在makefile中使用#号注释

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

#include <stdio.h>

int main(int argc, char *argv[])
{
	printf("hello world!\n");
	return 0;
}

Makefile修改如下:

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

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

注意:如果make后面不跟目标名,默认执行第一个目标,上述Makefile中也即执行debug目标。

image-20240502144544914

也可以在Makefile中加入清理工程的规则:如下所示

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

image-20240502144844832

伪目标

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

示例:在文件中先创建了一个与目标clean同名的文件,执行make clean时提示已是最新,导致clean目标下的shell命令没有执行。

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

image-20240502145826507

修改Makefile文件,将clean说明为伪目标。会强制执行clean目标下的命令,最终输出hello文件

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

image-20240502150221773

基本语法

变量赋值和预定义变量

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
    
    debug:
    	@echo $(VAR_B)
    
    ten@ten-virtual-machine:~/H616/demo$ make
    AA B
    
  • := 表示直接赋值,即变量的值是在定义时就确定,不会受到后面的赋值影响。例如,VAR_A := A, VAR_B := $(VAR_A) B,VAR_A := AA,那么最后VAR_B的值是A B,而不是AA B。

    VAR_A := A
    VAR_B := $(VAR_A) B
    VAR_A := AA
    
    debug:
    	@echo $(VAR_B)
    
    ten@ten-virtual-machine:~/H616/demo$ make
    A B
    
  • ?=表示条件赋值,即只有当变量没有被赋值时,才使用等号后面的值作为变量的值。例如,VAR ?= new_value,如果VAR在之前没有被赋值,那么VAR的值就为new_value,否则保持原来的值不变。

    VAR ?= new_value
    
    debug:
    	@echo $(VAR)
    
    ten@ten-virtual-machine:~/H616/demo$ make
    new_value
    

    变量VAR前面已经赋值

    VAR = old_value
    VAR ?= new_value
    
    debug:
    	@echo $(VAR)
    
    ten@ten-virtual-machine:~/H616/demo$ make
    old_value
    
  • += 表示追加赋值,即将等号后面的值追加到变量原来的值之后,形成一个新的值。例如,VAR += new_value,如果VAR在之前没有被赋值,那么VAR的值就为new_value,如果VAR在之前被赋值为
    old_value,那么VAR的值就为old_value new_value

    VAR = old_value
    VAR += new_value
    
    debug:
    	@echo $(VAR)
    
    ten@ten-virtual-machine:~/H616/demo$ make
    old_value new_value
    

$符的其他用法:

  • $^:表示所有的依赖文件

  • $@:表示生成的目标文件

  • $<:代表第一个依赖文件

注释和换行符

采用#进行一行注释

采用\作为续行符

变量的替换引用

语法格式:

$(var:a=b)或${var:a=b}		#表示把变量var的值中的a后缀替换成b后缀

示例:把变量src的值中的.c后缀替换成.o后缀,赋值给变量obj。

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

debug:
	@echo $(obj) 
ten@ten-virtual-machine:~/H616/demo$ make
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的函数

基本格式:$(<function> <arguments>)或者是${<function> <arguments>}。function:是函数名,arguments:是函数的参数,参数之间要用逗号分隔开,参数和函数名之间使用空格分开。调用函数的时候要使用字符“$”,后面可以跟小括号或者大括号。

wildcard

用于扩展通配符,返回与通配符匹配的文件列表。格式如下

$(wildcard arguments)

通配符

是一种特殊的字符,可以表示多个文件名或目录名,常见的通配符有 *?,分别表示任意长度的任意字符和单个任意字符。

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

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

注意:该函数不会递归查找。只能查当前目下的文件

SRC = $(wildcard src/*.c)

debug:
	@echo $(SRC)
ten@ten-virtual-machine:~/H616/demo$ ls
clean  hello2.c  hello3.c  hello.c  Makefile  test
ten@ten-virtual-machine:~/H616/demo$ make
./hello2.c ./hello3.c ./hello.c

shell

用于执行shell命令

$(shell <cmd> <args>)

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

参数介绍:

  • cmd:需要执行的命令
  • args:参数列表

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

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

debug:
	@echo $(SRC)
ten@ten-virtual-machine:~/H616/demo$ ls
clean  hello2.c  hello3.c  hello.c  Makefile  test
ten@ten-virtual-machine:~/H616/demo$ cd ./test
ten@ten-virtual-machine:~/H616/demo/test$ ls
hello4.c
ten@ten-virtual-machine:~/H616/demo/test$ cd ../
ten@ten-virtual-machine:~/H616/demo$ make
./test/hello4.c ./hello.c ./hello2.c ./hello3.c

patsubst

用于替换文本中的内容。

$(patsubst pattern,replacement,text)

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

参数介绍:

  • pattern:字符串。被替换的字符串,可以包含通配符 % 的模式,表示匹配任意长度的任意字符。
  • replacement:字符串。替换字符串,也可以包含 %,表示用 pattern 中匹配的字符替换。
  • text:是一个要处理的文本,可以包含多个以空格分隔的单词。

示例:

#包含通配符
SRC = a.c b.c c.c
OBJ = $(patsubst %.c,%.o,$(SRC))

debug:
	@echo $(OBJ)
ten@ten-virtual-machine:~/H616/demo$ make
a.o b.o c.o
#不包含通配符
SRC = hello world
OBJ = $(patsubst world,ten,$(SRC))

debug:
	@echo $(OBJ)
ten@ten-virtual-machine:~/H616/demo$ make
hello ten

subst

用于替换文本中的内容。

$(subst from,to,text)

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

参数介绍:

  • from:是要被替换的字符或单词
  • to:是替换后的字符或单词
  • text:是要处理的字符串。

示例

OBJ = $(subst e,E,hello ten)

debug:
	@echo $(OBJ)
ten@ten-virtual-machine:~/H616/demo$ make
hEllo tEn

综合示例

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

/*test.c内容*/
#include <stdio.h>

int main()
{
	printf("hello world\n");
	return 0;
}

执行make compile,生成生成obj/test

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

dir

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

$(dir NAMES...)

优化上述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

suffix

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

$(suffix <names...>)

示例

OBJ = $(suffix hello.c test/hello4.txt ten)

debug:
	@echo $(OBJ)
ten@ten-virtual-machine:~/H616/demo$ make
.c .txt

basename

从文件名序列中取出各个文件名的前缀部分。返回文件名序列的前缀序列,如果文件没有前缀,则返回空字串。

$(basename <names...>)

示例

OBJ = $(basename hello.c test/hello4.txt ten)

debug:
	@echo $(OBJ)
ten@ten-virtual-machine:~/H616/demo$ make
hello test/hello4 ten

addsuffix

把后缀加到文件序列中的每个单词后面。返回加过后缀的文件名序列。

$(addsuffix .c,foo bar)

示例

OBJ = $(addsuffix .c,foo bar)

debug:
	@echo $(OBJ)
ten@ten-virtual-machine:~/H616/demo$ make
foo.c bar.c

addprefix

把前缀加到中的每个单词后面。返回加过前缀的文件名序列。

$(addprefix src/,foo bar)

示例

OBJ = $(addprefix src/,foo bar)

debug:
	@echo $(OBJ)
ten@ten-virtual-machine:~/H616/demo$ make
src/foo src/bar

foreach

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

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

示例

files = ten zzz nb
OBJ = $(foreach file,$(files),$(file).c)

debug:
	@echo $(OBJ)
ten@ten-virtual-machine:~/H616/demo$ make
ten.c zzz.c nb.c

条件判断

ifeq/ifneq

ifeq语句 : 判断参数 是否相等,相等为 true, 否则是 false。可以没有else

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

ifneq语句:判断参数 是否不等,不等为 true, 否则为 false。可以没有else

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

ifdef/ifndef

ifdef 语句: 判断参数 是否有值 ,有值为 true, 否则是 false。可以没有else

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

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

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

标签:ten,make,Makefile,echo,debug,VAR,hello
From: https://www.cnblogs.com/tenzzz/p/18170607

相关文章

  • 自动生成Makefile
    提示:文章文章目录前言一、背景二、2.12.2总结前言前期疑问:本文目标:一、背景最近二、使用autotool生成makefile4.0程序文件建立目录:mkdirincludesrc编写程序:include/str.h#include<stdio.h>intstr(char*string);编写程序:src/str.c#src/str.c......
  • 【Linux】Makefile 基本语法
    make:一般说的是GNUMake,是一个软件,将源代码文件编译成可执行的二进制文件;Makefile:make工具编译的时候需要使用Makefile文件,Makefile文件描述了整个工程的编译、连接规则。接上一节:make工具和Makefile文件的引入Makefile举例注意Makefile的大小写!!!需要......
  • Linux项目自动化构建工具 --- make/Makefile
    文章目录make/Makefile文件1背景2理解2.1创建执行代码2.2创建makefile文件2.3运行make指令2.3.1依赖关系2.3.2依赖方法2.3.3原理2.4项目清理make/Makefile文件1背景会不会写makefile,从一个侧面说明了一个人是否具备完成大型工程的能力一个工程中的源文......
  • 如何高效的编写makefile
    技术笔记!一、简介1.Make        1)工程管理器,顾名思义,是指管理较多的文件。        2)Make工程管理器也就是个“自动编译管理器”,这里的“自动”是指它能够根据文件时间戳,自动发现更新过的文件而减少编译的工作量,同时,它通过读入Makefile文件的内容来执行大......
  • 18_makefile基本语法(下)
    makefile基本语法(下)1.wildcard函数格式:$(wildcardPATTENR)功能:展开指定的目录举例:在/home/test目录有一个a.c的c文件和一个test的文件夹,在/home/test/test文件夹下有一个b.c的文件。​ 我们在当前目录下创建的makefile里面写下如下代码,echo前面加了@符号,echo这......
  • 17_makefile基本语法(上)
    makefile基本语法(上)一.设置vim首行缩进vi/etc/vim/vimrc(rc结尾的一般为配置文件)输入settabstop=4,保存后退出即可。便发现vim打开后的缩进变成4个空格了二.Makefile基本语法语法格式:目标:依赖(tab)命令举例:目标:all依赖:空命令:gcchello.c-ohello上面的例子也......
  • 使用 wsl+makefile+clangd编辑stm32代码环境的搭建
    使用wsl+makefile+clangd编辑stm32代码环境的搭建安装wsl环境可以看看下面的文章安装与换源都提及,相信大家可以安装成功的https://www.cnblogs.com/banmei-brandy/p/16218660.html安装make、bear、clangd、arm-none-eabi-gcc、最新的构建库sudoaptinstallmakebearclang......
  • 学习Makefile
    1.-c代表只编译不链接最后再链接所有.o文件生成main可执行文件2.Version1hello(目标):main.cppprinthello.cpp(依赖)g++...(命令)Version2定义变量TARGET依赖OBJ,先去寻找OBJ里的.o文件,没有就执行下面代码生成最后调用下面命令链接Version3三个变量定义不变:添......
  • makefile编译第二讲
    更多精彩内容在公众号。关注公众号,加v,免费送你两本makefile电子书。轻松掌握makefileGNU 的 make 很强大, 它可以自动推导文件以及文件依赖关系后面的命令,于是我们就没必要去在每一个[.o]文件后都写上类似的命令,因为,我们的 make 会自动识别,并自己推导命令只要 make ......
  • MakeFile学习
    Makefile学习Makefile的规则基本规则target...:prerequisites...<tab缩进>command<tab缩进>...<tab缩进>...target是一个目标文件,可以使ObjectFile,也可以是执行文件。还可以是一个标签(Label),对于标签这种特性,在后续‘“伪目标”中会有叙述。prerequisites就是要生成t......