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
    +关注

    关注

    88

    文章

    11628

    浏览量

    217971
  • Makefile
    +关注

    关注

    1

    文章

    125

    浏览量

    20201
  • C文件
    +关注

    关注

    0

    文章

    12

    浏览量

    3217
收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    使用Vivado 2018.2编译E203的mcs文件,遇到的问题求解

    Hi 各位,我在尝试使用Vivado 2018.2编译E203的mcs文件,遇到如下两个问题: 1. 按照书中步骤运行,执行完make mcs之后得到的mcs文件与git中预编译出来的
    发表于 11-11 06:04

    蜂鸟hbird sdk的Makefile架构分析

    Makefile文件之间的关系和主要功能. 二.内容 以application/helloworld 为例进行分析,首先编写完成main.c文件之后,我们会在helloworld
    发表于 10-30 07:15

    头文件保护失效,提示变量重复包含,是什么原因导致的?

    我自定了一个头文件,而且使用了头文件保护机制。但是在两个源文件包含相同的头文件时,依然提示某些变量重复
    发表于 10-09 06:19

    修改Kconfig的配置后,编译基本等于重新编译一遍,怎么解决?

    RT-Thread的配置选项会输出到rtconfig.h文件中,一旦这个文件发生改变,编译的时候,需要重新编译的内容是非常多的. 有没有办法进行优化呢?我只
    发表于 09-28 11:17

    menuconfig修改不使能lvgl后无法编译怎么解决?

    先使用menuconfig配置了启用lvgl 然后变异成功,运行也成功。 通过menuconfig关闭lvgl 重新编译失败了, 提示找不到lvgl相关头文件。我明明已经关闭了为啥还会有引用呢?
    发表于 09-24 07:41

    RTT如何添加文件夹并加入编译

    想把一个新的工程文件夹(包含若干个源文件头文件)放入同一个工程内编译。 试了下把在文件系统添加
    发表于 09-15 07:50

    飞凌嵌入式ElfBoard ELF 1板卡-uboot编译原理介绍

    编译工具将源码文件编译成可执行文件的过程并不是一步到位的,其中要经过一个类似工厂流水线的过程,交叉编译工具中
    发表于 05-22 11:17

    cypress3014在头文件修改编译后,时间不变,这样头文件的程序有被执行吗?

    你好,请问我在头文件修改编译后,时间不变,这样头文件的程序有被执行吗,怎么解决这个问题
    发表于 05-14 06:50

    迅为RK3568开发板内核模块实现-编写 Makefile

    编译驱动程序还需要使用 Makefile 文件。我们为 helloworld.c 编写一个简单的 Makefile
    发表于 04-24 13:36

    redefinition of \'gImage_pic2\'

    重新编译,避免旧缓存导致错误‌58。 ‌验证步骤‌ 确认 pic2.h 已添加头文件保护。 检查 gImage_pic2 仅在 ‌单个源文件‌ 中定义。 重新编译工程,观察错误是否消
    发表于 04-01 11:55

    STM32使用ISp烧录HEX文件运行,代码重新编译之后的产生的新hex文件选择全片擦除就没办法正常运行,为什么?

    我有一套代码需要使用ISp烧录HEX文件运行,代码有一个老的HEX,无论是全片擦除还是擦除重要部分再下载都没有问题。但是代码重新编译之后的产生的新hex文件,如果选择全片擦除,就没办法正常运行。只有
    发表于 03-10 07:42

    嵌入式学习-飞凌嵌入式ElfBoard ELF 1板卡-初识设备树之Makefile修改

    不同而新增加了dts,则需要在这个Makefile的这个位置添加上对应的.dtb文件名参与编译。ELF 1使用的设备树命名为imx6ull-elf1-emmc.dts,是基于NXP官方evk板子的设备树imx6ull-14x14
    发表于 01-13 09:09

    飞凌嵌入式ElfBoard ELF 1板卡-初识设备树之Makefile修改

    不同而新增加了dts,则需要在这个Makefile的这个位置添加上对应的.dtb文件名参与编译。ELF 1使用的设备树命名为imx6ull-elf1-emmc.dts,是基于NXP官方evk板子的设备树imx6ull-14x14
    发表于 01-10 09:23

    嵌入式学习-飞凌嵌入式ElfBoard ELF 1板卡-Linux内核移植之Makefile介绍

    文件的目标文件进行重新编译更新,这就大大减少了编译时间。打开源码目录可以看到,Makefile文件
    发表于 01-04 10:40

    飞凌嵌入式ElfBoard ELF 1板卡-Linux内核移植之Makefile介绍

    文件的目标文件进行重新编译更新,这就大大减少了编译时间。 打开源码目录可以看到,Makefile文件
    发表于 01-03 09:39