- makefile文件三要素
- 伪目标
- 三类变量
- makefile 当中 常用函数
编写Makefile并执行make
# Makefile
main : main.c
gcc main.c -o main
(makefile 三要素)
make file 文件中的:.PHONY( 伪目标**)**
Phony Targets
A phony target is one that is not really the name of a file; rather it is just a name for a recipe to be executed when you make an explicit request. There are two reasons to use a phony target: to avoid a conflict with a file of the same name, and to improve performance.
伪目标的使用:
在 Makefile 中,伪目标(phony target)或称为伪目标规则(phony target rule)是一种特殊的规则,它本身不对应任何实际的文件。伪目标通常用于执行一系列命令或作为其他目标的依赖,以确保特定的任务被执行。在 GNU Make 中,伪目标通常通过 .PHONY
声明来指定。
以下是一些关于伪目标的要点和示例:
声明伪目标
在 Makefile 中,你可以使用 .PHONY
声明一个或多个伪目标。例如:
bash
.PHONY: clean all test
这表示 clean
、all
和 test
是伪目标,它们不会与文件名冲突。
使用伪目标
伪目标通常用作默认目标或作为其他目标的依赖。以下是一些示例:
示例 1:默认目标
bash
.PHONY: all
all: program
program: main.o utils.o
gcc -o program main.o utils.o
main.o: main.c
gcc -c main.c
utils.o: utils.c
gcc -c utils.c
clean:
rm -f *.o program
在这个示例中,all
是一个伪目标,它依赖于 program
。当你运行 make
时,默认会执行 all
目标,进而构建 program
。
示例 2:执行命令
bash
.PHONY: hello
hello:
@echo "Hello, World!"
在这个示例中,hello
是一个伪目标,当你运行 make hello
时,它会输出 "Hello, World!"。
示例 3:组合多个目标
bash
.PHONY: all clean test
all: program
program: main.o utils.o
gcc -o program main.o utils.o
main.o: main.c
gcc -c main.c
utils.o: utils.c
gcc -c utils.c
test:
./program --test-option
clean:
rm -f *.o program
在这个示例中,all
、clean
和 test
都是伪目标。你可以通过运行 make all
来构建程序,通过 make test
来运行测试,通过 make clean
来清理构建文件。
注意事项
- 避免与文件名冲突 :伪目标通常不会与文件名冲突,但使用
.PHONY
声明可以避免潜在的命名冲突。 - 默认目标 :如果你没有指定目标,
make
会尝试构建第一个目标。如果你希望指定一个默认目标,可以将其命名为all
或其他你喜欢的名字,并确保它是第一个目标或在 Makefile 中明确指定。 - 命令前缀 :在伪目标的命令中,通常使用
@
前缀来抑制命令本身的输出(仅显示命令的输出结果)。
变量:分为系统内置变量、自定义变量、自动化变量
内置变量
Makefile中的内置变量(也称为系统变量)在构建过程中非常有用,它们提供了一些预定义的值,用于简化Makefile的编写。以下是一些常见的Makefile内置变量及其使用示例:
1. $@
表示当前规则中的目标文件名,包含扩展名。
示例:
bash
# 定义目标文件和依赖文件
TARGET = myprogram
OBJS = main.o utils.o
# 规则: 编译目标文件
$(TARGET): $(OBJS)
$(CC) $(CFLAGS) -o $@ $^
在这个示例中,$@
会被替换为myprogram
。
2. $^
表示当前规则中所有的依赖文件名,以空格隔开,不重复。
示例:
继续上面的例子,$^
会被替换为main.o utils.o
。
3. $<
表示当前规则中的第一个依赖文件名。
示例:
bash
# 规则: 编译单个.o文件
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
在这个示例中,$<
会被替换为第一个依赖文件,例如main.c
(如果正在编译main.o
)。
4. $*
表示目标文件的名称,不包含扩展名。
示例:
bash
# 假设有一个目标文件名为 main.o
# 那么 $* 会被替换为 main
5. $?
表示所有比目标文件新的依赖文件。这个变量在决定是否需要重新编译目标文件时非常有用。
示例:
由于$?
的使用场景比较特殊,它通常与条件语句结合使用,以确定哪些依赖文件已经更新,从而决定是否需要重新编译。在简单的Makefile示例中可能不太容易直接展示其用法。
6. CC 和 CXX
分别表示C语言编译器和C++语言编译器的名称。
示例:
bash
# 定义编译器
CC = gcc
CXX = g++
# 使用编译器编译目标文件
$(TARGET): $(OBJS)
$(CC) $(CFLAGS) -o $@ $^
在这个示例中,CC
被设置为gcc
,用于编译C语言源文件。
7. RM
表示删除文件程序的名称。
示例:
bash
# 定义删除文件的命令
RM = rm -f
# 清理规则
clean:
$(RM) $(TARGET) $(OBJS)
在这个示例中,RM
被设置为rm -f
,用于删除目标文件和依赖文件。
8. CFLAGS 和 CXXFLAGS
分别表示C语言编译器和C++语言编译器的编译选项。
示例:
bash
# 定义删除文件的命令
RM = rm -f
# 清理规则
clean:
$(RM) $(TARGET) $(OBJS)
在这个示例中,CFLAGS
和CXXFLAGS
分别被设置为C语言和C++语言的编译选项。
自定义变量:
= 是延迟赋值
:= 是立即赋值
?= 是空赋值
+= 是追加赋值
自动化变量:
$< 第一个依赖文件
$^ 全部依赖文件
$@ 目标
模式匹配
% 匹配任意多个字符
* 通配符
默认规则
.o文件默认.使用.c文件进行编译
条件分支1
ifeq(var1, var2)
...
else
..
endif
ifeq(<arg1>, <arg2>)
语句1
else ifeq(<arg3>, <arg4>)
语句2
else
语句3
endif
条件分支2
ifneq(<arg1>, <arg2>)
语句1
else
语句2
endif
cc
命令时通过 -I
选项指定头文件所在路径
# Makefile
INCS := -I./func
SRCS := $(wildcard *.c)
main : $(SRCS)
gcc $(INCS) $(SRCS) -o mainmal
例子解读:
# Makefile
SRCS := $(wildcard *.c)
main : $(SRCS)
gcc $(SRCS) -o main
SRCS := $(wildcard *.c)
这行代码出现在使用Makefile文件构建项目的上下文中,特别是在GNU Make工具中。Makefile是一个包含了一系列规则和指令的文件,用于自动化编译和构建过程。让我们分解并详细说明这行代码的含义和作用:
SRCS
:SRCS
是一个变量名,在这里被用作一个容器,用于存储源文件的列表。变量名在Makefile中是自定义的,SRCS
通常代表"Source Files"(源文件)。
:=
::=
是Makefile中的赋值操作符,用于给变量赋值。这种赋值方式称为简单扩展赋值,它在赋值时立即对右侧的值进行扩展(即解析其中的变量和函数)。
$(wildcard *.c)
:$(wildcard pattern)
是GNU Make的一个函数,用于匹配并返回符合特定模式的文件名列表。*.c
是一个文件模式,表示当前目录下所有以.c
结尾的文件。这意味着,这个函数会查找当前目录(Makefile所在的目录)下所有C语言源文件。
综合起来,SRCS := $(wildcard *.c)
这行代码的作用是:查找当前目录下所有以.c
结尾的文件,并将这些文件的列表赋值给变量SRCS
。这样,SRCS
变量就包含了所有C语言源文件的名称,可以在Makefile的其他部分使用这些文件名来进行编译和链接等操作。
例如,如果当前目录下有main.c
、utils.c
和test.c
三个C语言源文件,那么执行这行代码后,SRCS
变量的值将是这三个文件名的列表。
$(wildcard pattern)
在Makefile的上下文中,$(wildcard pattern)
是一个函数,而wildcard
是这个函数的名称。这个函数的作用是根据给定的模式(pattern)匹配文件名,并返回匹配到的文件名列表。
具体来说,wildcard
函数的工作原理如下:
- 它接受一个参数,即文件模式(pattern),这个模式可以包含通配符,比如
*
、?
等。 - 函数会搜索当前工作目录(或者通过指定路径搜索其他目录,但这里没有涉及到)中所有符合该模式的文件。
- 对于每个匹配的文件,函数都会将其完整路径(或相对路径,取决于Makefile的位置和上下文)添加到返回列表中。
- 最终,函数返回一个包含所有匹配文件名的列表。
makefile 当中 常用函数
在Makefile中,有许多常用的函数,这些函数可以帮助自动化构建过程,提高构建效率和灵活性。以下是一些常用的Makefile函数及其功能:
一、字符串操作函数
- subst :字符串替换函数。把字符串
<text>
中的<from>
字符串替换成<to>
。 - patsubst :模式字符串替换函数。查找
<text>
中的单词(单词以空格、Tab或回车、换行分隔)是否符合模式<pattern>
,如果匹配则用<replacement>
替换。<pattern>
可以包括通配符%
,表示任意长度的字符串。 - strip :去空格函数。去掉
<string>
字符串开头和结尾的空字符,并将其中多个连续空字符合并为一个空字符。 - findstring :查找字符串函数。在字符串
<in>
中查找<find>
字符串,如果找到则返回<find>
,否则返回空字符串。 - filter :过滤函数。以
<pattern>
模式过滤<text>
字符串中的单词,保留符合<pattern>
模式的单词。可以有多个模式。 - filter-out :反过滤函数。与
filter
函数相反,过滤掉<text>
字符串中所有符合<pattern>
模式的单词,保留所有不符合此模式的单词。 - sort :排序函数。给字符串
<list>
中的单词排序(升序),并去掉重复的单词。 - word :取单词函数。取字符串
<text>
中第<n>
个单词(从1开始),若<n>
比<text>
大则返回空字符串。 - wordlist :取单词串函数。取字符串
<text>
中从<s>
开始到<e>
的单词串。<s>
和<e>
表示单词在字符串中位置的数字。 - words :单词个数统计函数。统计
<text>
中字符串的单词个数。 - firstword :首单词函数。取字符串
<text>
中的第一个单词。
二、文件名操作函数
- dir :取目录函数。从文件名序列
<names...>
中取出目录部分,目录部分是指最后一个反斜杠(/)之前的部分。如果没有反斜杠,则返回./
。 - notdir :取文件函数。从文件名序列
<names...>
中取出非目录部分,非目录部分是指最后一个反斜杠(/)之后的部分。 - suffix:取后缀函数。从文件名中取出后缀部分。
- basename:取前缀函数。从文件名中取出除去后缀的信息。
- addsuffix:加后缀函数。往文件名序列中添加后缀信息。
- addprefix:加前缀函数。往文件名序列中添加前缀信息。
- join :连接函数。把
<list2>
中的单词对应地加到<list1>
的单词后面,返回连接后的字符串。 - wildcard:通配符函数。展开为已经存在的、使用空格分开的、匹配此模式的所有文件列表。如果不存在任何符合此模式的文件,函数会忽略模式字符并返回空。
三、其他常用函数
- foreach:循环函数。用于循环遍历列表中的每个元素,并对每个元素执行指定的操作。
- if:条件函数。根据条件判断执行不同的代码块。
- call:调用函数。调用一个用户自定义的函数,并传递参数给它。
- origin:起源函数。返回变量的来源,即变量是在哪个Makefile中定义的,或者是通过哪个命令设置的。
- shell:执行shell命令函数。执行一个shell命令,并将命令的输出结果赋值给变量。