【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文件不重新编译的问题

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

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

(0)
上一篇 2022年6月12日 下午11:36
下一篇 2022年6月12日 下午11:46


相关推荐

  • PyCharm退回上一步,前进下一步

    PyCharm退回上一步,前进下一步PyCharm 版本 PyCharm2020 1 3 菜单 View gt Appearance gt Toolbar lt 退回上一步 快捷键 Ctrl Alt 左箭头 gt 前进下一步 快捷键 Ctrl Alt 右箭头

    2026年3月27日
    2
  • SAE J1939 – 简短介绍[通俗易懂]

    SAE J1939 – 简短介绍[通俗易懂]SAEJ1939–简短介绍在商用车辆领域,标准化的,串行的协议用于单个电子控制单元(ECU)和传动系统组件之间的通讯已有一段时间。通过使用标准化的串行协议,可具有以下优势:组件制造商只需要采用一个协议;这主要是商用车辆才会涉及的问题,因为生产量低。商用车辆制造商可依靠不同供应商的组件。可确保组件之间的互操作性,来自不同制造商的组件不用调整就可一同工作。由国际汽车工程师协会…

    2022年5月1日
    57
  • java链表排序方法_java链表排序

    java链表排序方法_java链表排序插入排序    对链表进行插入排序,是最简单的一种链表排序算法,用于插入排序是迭代的,所以每次只移动一个元素,直到所有元素可以形成一个有序的输出列表。    每次迭代中,插入排序只从输入数据中移除一个待排序的元素,找到它在序列中适当的位置,并将其插入。重复直到所有输入数据插入完为止。    插入排序的时间复杂度为O(N^2),空间复杂度为O(1)cla

    2022年10月9日
    7
  • OpenClaw超全指南!是什么?能干嘛?怎么部署?

    OpenClaw超全指南!是什么?能干嘛?怎么部署?

    2026年3月13日
    2
  • jvm垃圾回收详解_java 垃圾回收器

    jvm垃圾回收详解_java 垃圾回收器JVM垃圾回收1.概述JVM会自动帮程序员进行垃圾回收,并不需要程序员手动的进行垃圾回收(C++等语言需要自己手动回收垃圾),了解JVM的垃圾回收,可以帮程序员写出占用内存更小、更高效的程序。1.1什么是垃圾?垃圾是指运行程序中没有任何指针指向的对象,这个对象就是需要被回收的垃圾。1.2什么区域需要进行垃圾回收JVM的内存结构包括五大区域:程序计数器、虚拟机栈、本地方法栈、堆区、方法区。其中程序计数器、虚拟机栈、本地方法栈3个区域随线程而生、随线程而灭,因此这几个区域的内存分配和回

    2025年10月31日
    5
  • 电商架构图_电商运营方案

    电商架构图_电商运营方案电商架构图

    2022年4月20日
    62

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

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