IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> makefile生成*.d依赖文件,解决“只修改.h头文件,包含了该头文件的.c文件不重新编译的问题” -> 正文阅读

[C++知识库]makefile生成*.d依赖文件,解决“只修改.h头文件,包含了该头文件的.c文件不重新编译的问题”

makefile生成*.d依赖文件,解决“只修改.h头文件,包含了该头文件的.c文件不重新编译的问题”

??最近在使用makefile的过程中发现,当我只更改.h文件,不改任何.c文件的情况下,执行编译操作。.h的改动不会被编译器识别,也就没有编译到最终的可执行文件中。
??接下来,会通过一个简单的demo,来说明.h文件的改动无法被编译器识别的原因,以及如何解决此问题。
??demo中一共包含4个源文件,printHello.h,printHello.c,main.c,config.h。其目的是通过配置config.h中的宏定义,来控制main.c中的输出。源文件如下:

??printHello.h

//printHello.h
#ifndef _PRINTHELLO_H_
#define _PRINTHELLO_H_

void printHello1();
void printHello2();
#endif

??printHello.c

//printHello.c
#include <stdio.h>
#include "printHello.h"

void printHello1() {
	printf("hello1\n");
}

void printHello2() {
	printf("hello2\n");
}

??main.c

//main.c
#include "printHello.h"
#include "config.h"

void main() {
#ifdef HELLO1
	printHello1();
#endif
#ifdef HELLO2
	printHello2();
#endif

}

??config.h

//config.h
#define HELLO1
#define HELLO2

??makefile的代码如下:

CC = gcc
output_path = output
all_objects := $(output_path)/main.o
all_objects += $(output_path)/printHello.o

printHello: $(all_objects)
	$(CC) -o $@ $^
	
$(all_objects):$(output_path)/%.o:%.c
	$(CC) -c $< -o $@	

.PHONY: clean
clean:
	-rm printHello.exe
	del /Q /S output\*

??在接下来的操作中,我会通过改动config.h中的宏定义,来控制main.c中的输出。
??首先,config.h配置如下:

//config.h
#define HELLO1
#define HELLO2

在这里插入图片描述

??然后更改config.h中的宏定义,如下:

//config.h
//#define HELLO1
#define HELLO2

在这里插入图片描述
??我们预期的结果是,只输出hello2.
??但是我们发现,在更改了config.h后,再次运行make时,包含了config.h的main.c并没有被重新编译。所以最后我们的改动,没有更新的到最终的可执行文件中。
??分析原因:
??makefile中的目标文件更新的规则是:如果依赖文件的修改时间比目标文件的修改时间新,那么就会执行后面的命令来生成目标文件。
??实际上,main.o的依赖文件应该是:main.c printHello.h config.h。但在我们前面的makefile中,main.o的依赖文件只有main.c(makefile的9~10行),config.h并没有体现在我们的依赖关系中,所以编译器识别不了config.h的修改。所以我们的优化思路就应该是,想办法在makefile中体现完整的依赖关系。
??解决方案:
??GCC给我提供了自动生成依赖关系的方法。就是在生成目标文件的过程中,自动生成依赖关系文件(*.d)。不过生成的这些依赖关系文件也需要我们将其添加到makefile中。
??自动生成依赖文件的方法主要是给gcc编译器传递相关的参数。
参数定义:

  1. -M
    生成文件的依赖关系,同时也把一些标准库的头文件包含了进来。本质是告诉预处理器输出一个适合 make 的规则,用于描述各目标文件的依赖关系。对于每个源文件,预处理器输出 一个 make 规则,该规则的目标项 (target) 是源文件对应的目标文件名,依赖项 (dependency) 是源文件中 “#include” 引用的所有文件,生成的规则可以是单行,但如果太长,就用’'换行符续成多行。规则显示在标准输出,不产生预处理过的 C 程序。
    注意:该选项默认打开了 -E 选项, -E 参数的用处是使得编译器在预处理结束时就停止编译
  2. -MM
    生成文件的依赖关系,和 -M 类似,但不包含标准库的头文件。
  3. -MD
    等同于 -M -MF File,但是默认关闭了 -E 选项。其输出的文件名是基于 -o 选项,若给定了 -o 选项,则输出的文件名是 -o 指定的文件名,并添加 .d 后缀,若没有给定,则输入的文件名作为输出的文件名,并添加 .d 后缀,同时继续指定的编译工作。
    注意:-MD 不会像 -M 那样阻止正常的编译任务,因为它默认关闭了 -E 选项。比如命令中使用了 -c 选项,其结果要生成 .o 文件。若使用了 -M 选项,则不会生成 .o 文件,若使用的是 -MD 选项,则会生成 .o 文件
  4. -MF File
    当使用了 “-M” 或者 “-MM” 选项时,则把依赖关系写入名为 “File” 的文件中。若同时也使用了 “-MD” 或 “-MMD”,“-MF” 将覆写输出的依赖文件的名称,File中可以包含路径,使得依赖文件可以输出到指定的路径中 。
    例如:
gcc -MD -MF main.d main.c

??-MD所输出的关于main.c的依赖关系的文件,输出在main.d中。

  1. -MMD
    类似于 “-MD”,但是输出的依赖文件中,不包含标准头文件
  2. -MT Target
    在生成的依赖文件中,指定依赖规则中的目标

导入依赖文件:
??makefile中可以使用include关键字把其他文件包含进来,这很像C语言的#include,被包含的文件会原模原样的放在当前文件的包含位置。include的语法是:

include <filename>

可以在include前加一个减号-。如:

-include <filename>

其表示,无论include过程中出现什么错误,都不要报错继续执行。

??修改后的makefile:

CC = gcc
output_path = output
all_objects := $(output_path)/main.o
all_objects += $(output_path)/printHello.o

all_o_depds = $(patsubst %.o, %.o.d, $(all_objects))

printHello: $(all_objects)
	$(CC) -o $@ $^
	
$(all_objects):$(output_path)/%.o:%.c
#	$(CC) -c $< -o $@	
	$(CC) -MMD -MF $(output_path)/$(notdir $@).d -MT $@ -c $< -o $@

-include $(all_o_depds)

.PHONY: clean
clean:
	-rm printHello.exe
	del /Q /S output\*

??编译演示:
??首先,config.h配置如下:

//config.h
#define HELLO1
#define HELLO2

在这里插入图片描述
??然后更改config.h中的宏定义,如下:

//config.h
//#define HELLO1
#define HELLO2

在这里插入图片描述
??我们可以看到,在只更改了config.h后,运行make时,编译器重新生成了main.o目标文件,所以我们对.h文件的修改,就更新到了最后的可执行文件中。

??最后,介绍下demo中的文件结构:
在这里插入图片描述
??makefile会把编译中生成的目标文件(*.o)和依赖文件(*.d)放到output文件夹下。
在这里插入图片描述
其中main.o.d是main.o的依赖文件。其中的内容如下:
在这里插入图片描述

参考文章:
https://blog.csdn.net/qq1452008/article/details/50855810

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-10-31 11:35:33  更:2022-10-31 11:37:03 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 5:05:51-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码