【Linux + Makefile】十分钟教你学会Makefile的FORCE「建议收藏」

【Linux + Makefile】十分钟教你学会Makefile的FORCE「建议收藏」Makefile有种FORCE的写法,你了解过它的原理和用途吗?

大家好,又见面了,我是你们的朋友全栈君。

相信大家在使用Linux环境编程的时候,一定接触过Makefile这个玩意。Makefile在搭建自定义的编译环境,尤其是自动化编译、多功能一键编译等功能上,还是发挥了很大的作用。如果接触过Linux内核编译的童鞋,一定会看到编译内核中的各级Makefile中,有很多地方都会有 FORCE 这样的字段出现,那么这个 FORCE 究竟是何方神圣呢?本文将给你答案,通过阅读本文,你讲了解到以下内容:

  • Makefile的基本规则
  • FORCE 在Makefile的含义
  • FORCE在实际工程中的应用

Makefile的基本规则


Makefile的基本形式如下所示:

TARGET : DEPENDENCES
    CMD

# TARGET:生成的目标,可以是一个文件,也可以是一个虚拟符号(非真实文件)
# DEPENDENCES: 生成目标的所有依赖,它是一个集合,可以只有一个文件,或者很多文件;也可以是虚拟符号
# CMD:把所有依赖生成目标的执行命令,其中CMD前面是一个TAB键,而不能是空格

Makefile的基本规则,归纳起来就是一句话:【当一个TARGET (欲生成的目标)比它的任何一个DEPENDENCES(依赖的文件)旧时,这个TARGET就要重新生成】。

这句话读起来很简单,就是Makefile在执行编译的时候,会先去判断TARGET和其依赖文件的时间,如果TARGET的时间比较旧,意味着依赖文件有更新了,所以TARGET要重新生成;反之,如果TARGET的时间比较新,意味着在TARGET生成后,依赖文件是没有改变过的,自然就不用重新去生成TARGET。


FORCE 在Makefile的含义


有了上一小节中介绍的Makefile基本概念后,我们来进一步分析下Makefile中的FORCE,以下是FORCE在Makefile中出现的最简化版本:

file = test.txt

all: generate-a-file

generate-a-file: $(file) 

$(file):
	@echo "Force to generate a test file for every make ..."
	rm -rf $(file) && echo `date "+%Y-%m-%d %H:%M:%S"` > $(file)

FORCE:
.PHONY: FORCE

这个Makefile不是用于真正的编译工程,而是提供一个很简单的功能,生成一个test.txt,并且这个test.txt的内容是记录每次编译的时间。但是使用这个makefile执行make时,发现只有第一次make的时候,才会生成test.txt,而其他时候只要test.txt还存在都不会重新生成,如下所示:

【Linux + Makefile】十分钟教你学会Makefile的FORCE「建议收藏」

很明显,它没有达到我们期望的“每次编译都重新生成test.txt”。这个时候 FORCE 就发挥作用了,我们在 test.txt目标的后面添加 FORCE作为它的依赖试试看,即如下所示:

file = test.txt

all: generate-a-file

generate-a-file: $(file) 

$(file): FORCE  #FORCE表示每次这段都要执行
	@echo "Force to generate a test file for every make ..."
	rm -rf $(file) && echo `date "+%Y-%m-%d %H:%M:%S"` > $(file)

FORCE:
.PHONY: FORCE

执行输出如下所示: 我们可以看到,这达到了我们的目的,每次test.txt都是重新生成了,它记录了每次make的时间。

【Linux + Makefile】十分钟教你学会Makefile的FORCE「建议收藏」

这个就是要归功于 FORCE 的功劳了。我们来分析下,为何加了 FORCE 就能实现这样的功能。

我们可以注意到FORCE这个目标,它的DEPENDENCES是空的,CMD部分也是空的;这个比较特殊了,在Makefile里,像这样依赖为空、执行命令也为空的TARGET,则需要每次都重新生成,而这个TARGET不一定是一个文件,可以是任意的符号,而 FORCE 只是我们最常用的符号,理论上它可以换成任意符号,比如NO-FORCE、SOMETHING等等。


FORCE在实际工程中的应用


上一小节,我们讲到可以用Makefile配合shell命令来自动生成一些文件,自然我们很容易想到,在我们实际的编译工程中,往往需要动态生成一些配置项,然后嵌入到代码中,比如编译版本号、编译时间等。

假设我们有以下一个main.c:

#include <stdio.h>
#include "build_info.h"  #这个头文件需要每次编译时自动生成

int main(int argc, const char *argv[])
{
    printf("%s >>> APP_TIME=%s\n", __func__, APP_TIME);
    return 0;
}

示例代码很简单,就是再main函数中打印一个 build_info.h中的一个宏定义APP_TIME,这个build_info.h要求每次编译的时候都重新生成。我们给出的Makefile示例如下:

##拷贝时,注意tab键
SHELL           = /bin/bash #指定shell使用/bin/bash,否则echo -e可能会出问题
ECHO            = echo
BIN             = test
BUILG_INFO_H    = build_info.h
SRC-C-y         = main.c
SRC-O           = $(patsubst %.c, $(O)%.o, $(SRC-C-y))

all: gen_build_info $(BIN)

$(BIN) : $(SRC-O)
    gcc -o $(O)"$@" $(SRC-O)
	
%.o : %.c
    gcc -c "$<" -o "$@"
	
gen_build_info: $(BUILG_INFO_H)

$(BUILG_INFO_H): FORCE     #强制生成build_info.h
    @$(RM) $@
    @$(ECHO) '  GEN     $@'
    @$(ECHO) -e   " #ifndef __BUILD_INFO_H__\n"\
				"#define __BUILD_INFO_H__\n"\
				"#define APP_TIME        	\"`date "+%Y-%m-%d %H:%M:%S"`\"\n"\
				"#endif"  > $@

FORCE:
.PHONY: FORCE

make执行输出测试如下:

【Linux + Makefile】十分钟教你学会Makefile的FORCE「建议收藏」

从输出的测试,我们可以看出,make的每次执行都触发了生成build_info.h,但是运行编译出来的test可执行程序,我们发现并不是每次生成的build_info.h的内容都传递到了test里面;也就是当build_info.h改变的时候,test没有被重新编译。这里先留下点疑问,为何会产生这样的问题。博主将会在后续的文章中解决这个问题。


不管怎么样,经过对上文的学习,我们至少掌握了 FORCE的基本用法,而在实际项目工程中,我们也见证了它的威力;那么,你学会了吗?如果还有疑问,欢迎在评论席提出你的问题和看法,博主定会尽力解决你的困惑。

版权申明:本文为博主原创文章,转载请注明出处!【Linux + Makefile】十分钟教你学会Makefile的FORCE_架构师李肯-CSDN博客

原创作者:recan

电子邮箱:recan.szu@foxmail.com


延伸阅读:

【Linux + Makefile】Makefile的高阶用法:解决C文件包含的头文件修改了,但C文件不重新编译的问题

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/131032.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号