0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

【Makefile】C文件包含的头文件修改,但不重新编译?

嵌入式物联网开发 来源:嵌入式物联网开发 作者:嵌入式物联网开发 2022-09-08 08:53 次阅读

在上一篇 《【Linux + Makefile】十分钟教你学会Makefile的FORCE》文章的最后,笔者就FORCE的用法在一个示例工程中使用,提出了一个问题:为何build_info.h每次都是新生成的(有修改过),而main.c又是有include “build-info.h”,但main.c却不是每次都重新编译呢?这个到底是不是违反了Makefile的基本规则呢?本文将给你答案,通过阅读本文,你将了解到以下内容:

  • 如何保证在C文件中包含的头文件修改了的时候,C文件每次都会被重新编译?

为了更好地展示上诉描述的问题,我们将之前的示例工程稍微复杂化一点点:

整个工程有3个.c文件,a.c/b.c/main.c,其中main.c会调用a.c/b.c中的两个接口,同时main.c会include头文件build_info.h;这个build_info.h每次编译都会重新生成,按照我们之前的写法,我们Makefile可能就是这样:

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

all: gen_build_info $(BIN)

clean: 
    rm -rf $(SRC-O) $(BIN) $(BUILG_INFO_H)

$(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
poYBAGDYdXCAWkKMAAAAK8RNs4s030.png

执行make,我们会发现,跟我们的预期不一样:它虽然会每次都生成build_info.h,但是main.c包含了build_info.h却不会每次都重新编译。这个问题发生的原因,我们来分析下:

在我们的Makefile规则中,main.o只依赖于main.c (Makefile 第18-19行),而在第二次执行make的时候,main.c显然并没有被修改,所以main.o不会重新生成,自然可执行文件就不会重新生成。这里的问题根源在于,main.c它是依赖于build_info.h的,而这个依赖关系并没有体现在Makefile中,所以整个编译流程达不到我们的预期想法。我们尝试下,将main.c的依赖头文件也写入到Makefile中,怎么实现呢?

恰好,GCC给了我们强大的支持,它有个非常有用的选项 -MD -MF,它可以在生成一个.o的同时也生成它的依赖文件列表,修改后的Makefile如下所示:

SHELL           = /bin/bash #指定shell使用/bin/bash,否则echo -e可能会出问题
ECHO            = echo
BIN             = test
BUILG_INFO_H    = build_info.h
SRC-C-y         += a.c
SRC-C-y         += b.c
SRC-C-y         += main.c
SRC-O           = $(patsubst %.c, $(O)%.o, $(SRC-C-y))
SRC-C-DEPS      = $(patsubst %.c, $(O).%.o.d, $(SRC-C-y))  ## 由 a.c ==> .a.o.d

all: gen_build_info $(BIN)

clean: 
    rm -rf $(SRC-O) $(BIN) $(BUILG_INFO_H) $(SRC-C-DEPS)

$(BIN) : $(SRC-O)
    gcc -o $(O)"$@" $(SRC-O)
	
%.o : %.c
#	生成xxx.o的时候,同时生成它的依赖列表,放在文件.xxx.o.d中
	gcc -c "$<" -o "$@" -MD -MF "$(dir $@).$(notdir $@).d" -MT "$@"
	
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

# 在Makefile末尾强制包含这些依赖文件
-include $(SRC-C-DEPS)

测试结果如下所示:

再次执行make,多试几次,一样的结果。

由上可知,经过改造后的Makefile是实现了我们的需求,每次build_info.h重新生成,导致main.c包含了build_info.h也会重新编译,而a.c和b.c没有被修改,所以在未执行make clean的情况下,a.c和b.c是不会被重新编译的,每次都是仅仅main.c被再次编译,从而重新生成新的test可执行文件。这样就是已经达到了【当C文件包含的头文件修改了的时候,C文件必须重新编译】的目的。


以上就是关于Makefile的高阶用法,基本满足了我们日常工程实践的需求。如果你对该Makefile有疑问,欢迎在评论席提出你的疑问,博主很乐意为你解答。


延伸阅读:

【Linux + Makefile】十分钟教你学会Makefile的FORCE

​审核编辑:汤梓红

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • Linux
    +关注

    关注

    87

    文章

    11030

    浏览量

    207259
  • Makefile
    +关注

    关注

    1

    文章

    124

    浏览量

    19117
  • C文件
    +关注

    关注

    0

    文章

    12

    浏览量

    2803
收藏 人收藏

    评论

    相关推荐

    文件工程的编译

    文件工程的编译
    的头像 发表于 06-26 13:35 48次阅读
    多<b class='flag-5'>文件</b>工程的<b class='flag-5'>编译</b>

    IDF-V4.3环境下包含了库的头文件编译报错,为什么?

    hello_world_main.c里面包含头文化 #include "xtensa/core-macros.h",编译则报错:找不到头文件; Pss:CMakeL
    发表于 06-21 08:12

    编译components文件夹内容引入的头文件不对怎么处理?

    最近在移植LVGL,需要自己写RGB驱动 我把LVGL放在components文件夹中,但编译的时候提示缺少esp_lcd下头文件的引用 仔细找了一下发现,编译components内容
    发表于 06-17 07:35

    components包含头文件错误是怎么回事?

    我新建了一个工程,添加了一个BLE组件,现在我在BLE组件的头文件包含了如下文件Code: Select all #include \"api/esp_gatt_common_api.h
    发表于 06-06 07:21

    快来用Makefile管理工程,提高工作效率!

    一、makefile简介Makefile是一种特别设计用来帮助项目的构建管理的文件。它定义了编译器和IDE工程管理系统自动执行的命令集合,主要用于自动化
    的头像 发表于 05-18 08:10 135次阅读
    快来用<b class='flag-5'>Makefile</b>管理工程,提高工作效率!

    请问头文件能不能定义变量呢?

    最近在编译一个工程的时候,突然遇到了变量重复定义的问题,根据提示打开这几个 C 文件,并没有发现定义变量的地方。后来再找一找,原来变量定义在了头文件里面。
    的头像 发表于 04-28 09:33 368次阅读

    HighTec Tricore编译速度优化策略探讨

    HighTec在编译时默认是根据工程源文件文件架构首先生成makefile文件,然后执行“make ma
    的头像 发表于 04-10 12:44 569次阅读
    HighTec Tricore<b class='flag-5'>编译</b>速度优化策略探讨

    已经将文件的路径添加了,头文件包含了,为什么编译会提示未定义符号错误?

    我准备添加TFT显示屏的驱动,已经将文件的路径添加了,头文件包含了,但在编译时却提示未定义符号错误。
    发表于 03-07 07:36

    rt-thread studio执行清空项目的时候发现系统找不到指定的文件怎么解决?

    在基于rt-thread studio开发时,出于尝试添加了一些组件,后来又删掉了一些不需要的组件,重新编译的时候抛出错误,错误指出删掉的组件的相关.o文件还是存在,执行清空项目的时候发现系统找不到
    发表于 03-05 06:18

    Makefile可以做什么?Makefile的基本格式

    Makefile可以根据指定的依赖规则和文件是否有修改来执行命令。常用来编译软件源代码,只需要重新编译
    的头像 发表于 01-25 11:18 383次阅读

    C语言必备知识头文件包含

    头文件在C语言中是非常重要的组成部分。
    的头像 发表于 12-01 18:20 1096次阅读

    什么是头文件头文件编写的一般格式要求是怎样?

    本文介绍头文件的定义、编写、保存及引用等方面的内容,包括了一般的格式要求、例程等。
    的头像 发表于 11-08 16:25 1184次阅读
    什么是<b class='flag-5'>头文件</b>?<b class='flag-5'>头文件</b>编写的一般格式要求是怎样?

    浅谈Linux内核源码的Makefile、Kconfig和.config文件

    Linux内核源码文件繁多,搞不清Makefile、Kconfig、.config间的关系,不了解内核编译体系,编译修改内核有问题无从下手,
    发表于 10-17 16:19 1927次阅读
    浅谈Linux内核源码的<b class='flag-5'>Makefile</b>、Kconfig和.config<b class='flag-5'>文件</b>

    和我一起写makefile

    来源于 gnu的 make 使用手册,在这个示例中,我们的工程有 8 个 c 文件,和 3 个头文件,我们要写一个 makefile来告诉 make 命令如何编译和链接这几个
    发表于 10-07 11:23 0次下载

    helloword程序如何修改makefile

    按照芯来官方教程使用命令make mcs 的确是可以生成mcs文件,这个文件就是helloword程序,请问如何修改makefile,将别的文件
    发表于 08-12 07:39