1. Makefile 条件判断作用
条件语句可以根据一个变量的值来控制 make
执行或者时忽略 Makefile
的特定部分,条件语句可以是两个不同的变量或者是常量和变量之间的比较。
下面是条件判断中使用到的一些关键字:
关键字 | 功能 |
---|---|
ifeq | 判断参数是否不相等,相等为 true,不相等为 false |
ifneq | 判断参数是否不相等,不相等为 true,相等为 false |
ifdef | 判断是否有值,有值为 true,没有值为 false |
ifndef | 判断是否有值,没有值为 true,有值为 false |
2. ifeq 和 ifneq
条件判断的使用方式如下:
ifeq (first, second)
ifeq 'first' 'second'
ifeq `first` `second`
ifeq `first` 'second'
ifeq 'first' `second`
示例:
first = $(CXX)
second = g++
all:
ifeq ($(first), $(second))
echo `first == second`
else
echo `first != second`
endif
执行 make 之后的输出
wohu@ubuntu:~/cpp/ifeq$ make
echo `first == second`
first == second
wohu@ubuntu:~/cpp/ifeq$
条件语句中使用到三个关键字 ifeq
、else
、endif
。其中:
ifeq
表示条件语句的开始,并指定一个比较条件(相等)。括号和关键字之间要使用空格分隔,两个参数之间要使用逗号分隔。参数中的变量引用在进行变量值比较的时候被展开。ifeq
后面的是条件满足的时候执行的,条件不满足忽略;else
表示当条件不满足的时候执行的部分,不是所有的条件语句都要执行此部分;endif
是判断语句结束标志,Makefile 中条件判断的结束都要有;
其实 ifneq
和 ifeq
的使用方法是完全相同的,只不过是满足条件后执行的语句正好相反。
3. ifdef 和 ifndef
使用方式如下:
ifdef VARIABLE_NAME
它的主要功能是判断变量的值是不是为空,示例 1
a =
b = $(a)
all:
ifdef b
echo yes
else
echo no
endif
执行 make 之后的输出
wohu@ubuntu:~/cpp/ifeq$ make
echo yes
yes
wohu@ubuntu:~/cpp/ifeq$
示例 2
a =
all:
ifdef a
echo yes
else
echo no
endif
执行 make 之后的输出
wohu@ubuntu:~/cpp/ifeq$ make
echo no
no
wohu@ubuntu:~/cpp/ifeq$
通过两个实例对比说明:我们执行 make 可以看到示例 1 打印的结果是 yes
,实例 2 打印的结果是 no
。
其原因就是在实例 1 中,变量 b
的定义是 b = $(a)
。虽然变量 a
的值为空,但是 ifdef
的判断结果为真,这种方式判断显然是有不行的,因此当我们需要判断一个变量的值是否为空的时候需要使用ifeq
而不是 ifdef
。
注意:在条件表达式中不能使用自动化变量,自动化变量在规则命令执行时才有效,更不能将一个完整的条件判断语句分卸在两个不同的
Makefile
的文件中。
在一个 Makefile
中使用指示符 include
包含另一个 Makefile
文件。
4. 定义命令包
命令包有点像是个函数, 将连续的相同的命令合成一条, 减少 Makefile
中的代码量, 便于以后维护。
语法:
define <command-name>
command
...
endef
示例:
# Makefile 内容
define run_demo_makefile
@echo -n "Hello"
@echo " Makefile!"
@echo "这里可以执行多条 Shell 命令!"
endef
all:
$(run_demo_makefile)
执行 make
wohu@ubuntu:~/cpp/func$ make
Hello Makefile!
这里可以执行多条 Shell 命令!
wohu@ubuntu:~/cpp/func$
5. Makefile 伪目标
所谓的伪目标可以这样来理解,它并不会创建目标文件,只是想去执行这个目标下面的命令。伪目标的存在可以帮助我们找到命令并执行。
如果需要书写这样一个规则,规则所定义的命令不是去创建文件,而是通过 make 命令明确指定它来执行一些特定的命令。实例:
clean:
rm -rf *.o test
规则中 rm
命令不是创建文件 clean
的命令,而是执行删除任务,删除当前目录下的所有的 .o
结尾和文件名为 test
的文件。
当工作目录下不存在以 clean
命令的文件时,在 shell
中输入 make clean
命令,命令 rm -rf *.o test
总会被执行 ,这也是我们期望的结果。
如果当前目录下存在文件名为 clean
的文件时情况就会不一样了,当我们在 shell
中执行命令 make clean
,由于这个规则没有依赖文件,所以目标被认为是最新的而不去执行规则所定义的命令。因此命令 rm
将不会被执行。
为了解决这个问题,删除 clean
文件或者是在 Makefile
中将目标 clean
声明为伪目标。
将一个目标声明称伪目标的方法是将它作为特殊的目标 .PHONY
的依赖,如下:
.PHONY:clean
这样 clean
就被声明成一个伪目标,无论当前目录下是否存在 clean
这个文件,当我们执行 make clean
后 rm
都会被执行。
而且当一个目标被声明为伪目标之后,make
在执行此规则时不会去试图去查找隐含的关系去创建它。这样同样提高了 make
的执行效率,同时也不用担心目标和文件名重名而使我们的编译失败。
在书写伪目标的时候,需要声明目标是一个伪目标,之后才是伪目标的规则定义。目标 clean
的完整书写格式如下:
.PHONY:clean
clean:
rm -rf *.o test
示例:
目录结构
wohu@ubuntu:~/cpp/ifeq$ tree
.
└── Makefile
0 directories, 1 file
wohu@ubuntu:~/cpp/ifeq$
只有一个 Makefile
文件,内容如下:
a = 123
bbb:
echo "a is $(a)"
执行 make 输出结果如下:
wohu@ubuntu:~/cpp/ifeq$ make
echo "a is 123"
a is 123
wohu@ubuntu:~/cpp/ifeq$ make bbb
echo "a is 123"
a is 123
wohu@ubuntu:~/cpp/ifeq$
手工创建文件 bbb
之后再执行 make
,echo
语句就不会执行
wohu@ubuntu:~/cpp/ifeq$ ls
Makefile
wohu@ubuntu:~/cpp/ifeq$ touch bbb
wohu@ubuntu:~/cpp/ifeq$ make
make: 'bbb' is up to date.
wohu@ubuntu:~/cpp/ifeq$
添加伪目标后
a = 123
.PHONY: bbb
bbb:
echo "a is $(a)"
执行 make
输出结果如下,即无论当前目录下是否存在 bbb
这个文件,当我们执行 make bbb
后 echo
都会被执行。
wohu@ubuntu:~/cpp/ifeq$ ls
bbb Makefile
wohu@ubuntu:~/cpp/ifeq$ make bbb
echo "a is 123"
a is 123
wohu@ubuntu:~/cpp/ifeq$