百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

Linux Makefile生成 *d依赖文件以及gcc-M-MF-MP等相关选项说明

lipiwang 2024-11-27 17:16 29 浏览 0 评论


来源:CSDN

作者:Jerry.yl

本文字数:4426,阅读时长:6分钟

1. 为什么要使用后缀名为 .d 的依赖文件?

在 Makefile 中, 目标文件的依赖关系需要包含一系列的头文件。
比如
main.c 源文件内容如下:

#include "stdio.h"
#include "defs.h"

int main(int argc, char *argv[])
{
	printf("Hello, %s!\n", NAME);
	return 0;
} 		

defs.h 头文件如下:

#ifndef _DEFS_H_
#define _DEFS_H_

#define NAME	"makefile"

#endif _DEFS_H_

那么依赖关系如下(依赖的文件省略了绝对路径):

main.o : main.c stdio.h defs.h ...   

假设目标文件的依赖关系缺少了 defs.h 文件,当 defs.h 文件中的内容改变后,根本不会重新编译目标文件,这是致命的,因为目标文件内部引用了 defs.h 文件中的宏定义。

如果是一个比较大型的工程,我们必须清楚每一个源文件都包含了哪些头文件,并且在加入或删除某些头文件时,也需要一并修改 Makefile,这是一个很没有维护性的工作。为了避免这种繁重而又容易出错的事情,可以使用 C/C++ 编译器的 “-M” 选项,即自动获取源文件中包含的头文件,并生成一个依赖关系。例如,执行下面的命令:

gcc -M main.c 	

其输出如下:

main.o : main.c defs.h

由编译器自动生成依赖关系,这样做的好处有以下几点:

  • 不必手动书写若干目标文件的依赖关系,由编译器自动生成
  • 不管是源文件还是头文件有更新,目标文件都会重新编译


2. 使用说明

参数介绍:

  • -M
    生成文件的依赖关系,同时也把一些标准库的头文件包含了进来。本质是告诉预处理器输出一个适合 make 的规则,用于描述各目标文件的依赖关系。对于每个源文件,预处理器输出 一个 make 规则,该规则的目标项 (target) 是源文件对应的目标文件名,依赖项 (dependency) 是源文件中 “#include” 引用的所有文件,生成的规则可以是单行,但如果太长,就用’'换行符续成多行。规则显示在标准输出,不产生预处理过的 C 程序。
    注意:该选项默认打开了 -E 选项, -E 参数的用处是使得编译器在预处理结束时就停止编译 例如: gcc -M main.c

则在终端上输出如下:

 main.o: main.c defs.h \
 /usr/include/stdio.h \
 /usr/include/features.h \ 			                                         
 /usr/include/sys/cdefs.h /usr/include/gnu/stubs.h \         			
 /usr/lib/gcc-lib/i486-suse-linux/2.95.3/include/stddef.h \ 			 
 /usr/include/bits/types.h \
 /usr/include/bits/pthreadtypes.h \ 			
 /usr/include/_G_config.h /usr/include/wchar.h \ 			
 /usr/include/bits/wchar.h /usr/include/gconv.h \ 			
 /usr/lib/gcc-lib/i486-suse-linux/2.95.3/include/stdarg.h \ 			
 /usr/include/bits/stdio_lim.h
  • -MM
    生成文件的依赖关系,和 -M 类似,但不包含标准库的头文件

例如:

gcc -MM main.c

则在终端上输出如下:

main.o: main.c defs.h
  • -MG
    要求把缺失的头文件按存在对待,并且假定他们和源文件在同一目录下,必须和 ‘-M’ 选项一起用。
  • -MF File
    当使用了 “-M” 或者 “-MM” 选项时,则把依赖关系写入名为 “File” 的文件中。若同时也使用了 “-MD” 或 “-MMD”,“-MF” 将覆写输出的依赖文件的名称 。

例如:

gcc -M -MF main.d main.c

则 “-M” 输出的内容就保存在 main.d 文件中了


  • -MD
    等同于 -M -MF File,但是默认关闭了 -E 选项。其输出的文件名是基于 -o 选项,若给定了 -o 选项,则输出的文件名是 -o 指定的文件名,并添加 .d 后缀,若没有给定,则输入的文件名作为输出的文件名,并添加 .d 后缀,同时继续指定的编译工作。
    注意:-MD 不会像 -M 那样阻止正常的编译任务,因为它默认关闭了 -E 选项,比如命令中使用了 -c 选项,其结果要生成 .o 文件,若使用了 -M 选项,则不会生成 .o 文件,若使用的是 -MD 选项,则会生成 .o 文件

例 1:

gcc -E -MD main.c

本目录下生成了以下文件:
	main.d 
同时在终端上输出了 main.c 文件的预处理结果
	
经实测发现,不使用 '-o' 指定输出文件名,以下情况有细微的差别:
gcc -E main.c //不使用 '-o',则把结果输出在终端上
gcc -S main.c //不使用 '-o',则把结果输出到以输入文件名为名称的 .s 文件中,即 main.s
gcc -c main.c //同上
gcc main.o    //不使用 '-o',则把结果默认输出到 a.out 可执行文件中

例 2:

gcc -E -o tmp.i -MD main.c

本目录下生成了以下文件:
	tmp.d tmp.i

例 3:

gcc -c -MD main.c

本目录下生成了以下文件:
	main.d main.o

例 4:

gcc -c -o tmp.o -MD main.c

本目录下生成了以下文件:
	tmp.d tmp.o

例 5:

gcc -MD main.c

本目录下生成了以下文件:
	a.out main.d 

例 6:

gcc -M -MD main.c

本目录下生成了以下文件:
	main.d  

#注释:并不会生成a.out可执行文件,因为 '-M' 默认打开了 '-E' 选项,
#使得编译器在预处理结束后就停止编译
  • -MMD
    类似于 “-MD”,但是输出的依赖文件中,不包含标准头文件
  • -MP
    生成的依赖文件里面,依赖规则中的所有 .h 依赖项都会在该文件中生成一个伪目标,其不依赖任何其他依赖项。该伪规则将避免删除了对应的头文件而没有更新 “Makefile” 去匹配新的依赖关系而导致 make 出错的情况出现。
    (英文描述:This option instructs CPP to add a phony target for each dependency
    other than the main file, causing each to depend on nothing. These
    dummy rules work around errors ‘make’ gives if you remove header
    files without updating the ‘Makefile’ to match.)

例1:

gcc -c -MM -MD main.c

生成的 main.d 文件内容如下:
main.o: main.c defs.h

例 2:

gcc -c -MM -MD main.c -MP

生成的 main.d 文件内容如下:
------------>
main.o: main.c defs.h

#该选项会生成该伪目标,其没有任何依赖项,
#若不使用 '-MP' 选项,则不会生成该伪目标规则
defs.h: 
<------------


  • -MT Target
    在生成的依赖文件中,指定依赖规则中的目标

例:

gcc -MF main.d -MG -MM -MP -MT main.d -MT main.o main.c

$ cat main.d    #查看生成的依赖文件的内容
main.d main.o: main.c
注:依赖规则中 main.d 和 main.o 目标都是通过 “-MT” 选项指定的


3. 使用参考

以上简单介绍了 gcc -M 相关的选项,旨在让 make 自动推导并生成文件的依赖关系。以下提供一个比较好的 gcc -M 选项的参考示例, 它将自动生成依赖文件,并保存在指定目录下的 “.d” 文件中。

Makefile 如下所示:

SRCS=$(wildcard *.c)
OBJS=$(SRCS:.c=.o)
DEPS=$(SRCS:.c=.d)

.PHONY: all clean

all: main

#注释:'-'号的作用:加载错误时,会继续执行 make,
#主要是考虑到首次 make 时,目录中若不存在 '*.d' 文件时,
#加载便会产生错误而停止 make 的执行
-include $(DEPS)	

%.o:%.c
	gcc -c -g -Wall lt; -o $@ -MD -MF $*.d -MP 

main: $(OBJS)
	gcc $^ -o $@   #注释:$^:表示所有的依赖文件 $@:表示目标文件

clean: 
	rm -f  *.d *.o main

仍旧以本篇文章开头的源文件进行 make,将生成如下文件:
main : 可执行文件
main.o : 编译的二进制目标文件
main.d:保存了 main.o 依赖关系的文件

注释: 
$* 表示目标模式中 '%' 及其之前的部分.如果目标是 'dir/a.foo.b',
并且目标的模式为 'a.%.b',那么 '$*' 的值就是 'dir/a.foo'。
如果目标中没有模式的定义,那么 '$*' 就不能被推导出;
但是,如果目标文件是 make 所识别的,那么 '$*' 就是除了后缀的那一部分。

例如:
目标是 'foo.c',因为 '.c' 是 make 所能识别的后缀名,
所以 '$*' 的值就是 'foo',这个特性是 GNU make 的。


4. 延伸说明

Makefile 文件中使用比较多的自动变量:

 - $@ : 表示一个规则中的目标.当规则中有多个目标时,
 		$@ 所指的是其中任何造成规则的命令运行的目标;
 - $^ : 表示规则中的所有依赖项;
 - lt; : 表示规则中的第一个依赖项;

例如:

Makefile 文件内容如下:
---------->
target1 target2:dep1 dep2 dep3
	@echo "Tar:$@, First Dep:lt;, All Dep:$^"

#注释:不写以下行目标规则,执行 make 会报错,
#因为 makefile 文件所在目录下不仅没有 dep1 dep2 dep3 文件,
#makefile 文件中也没有以这些依赖文件做为目标的规则.
dep1 dep2 dep3:   
<----------

==============================================================
运行 make: 
1. make target1 或者 make (把文件中第一个目标当作首要目标)
终端输出如下:
	Tar:target1, First Dep:dep1, All Dep:dep1 dep2 dep3

2. make target2
终端输出如下:
	Tar:target2, First Dep:dep1, All Dep:dep1 dep2 dep3
	
3. make target1 target2
终端输出如下:
	Tar:target1, First Dep:dep1, All Dep:dep1 dep2 dep3
	Tar:target2, First Dep:dep1, All Dep:dep1 dep2 dep3

相信大家在看了以上例子,能够更加理解这三个自动化变量的含义,尤其是 ‘$@’,前面已经说明,这里就不再赘述了。

以上内容若有差错,欢迎指正。

「新品首发」STM32MP157开发板火爆预售!首批仅300套

相关推荐

前端入门——css 网格轨道详细介绍

上篇前端入门——cssGrid网格基础知识整体大概介绍了cssgrid的基本概念及使用方法,本文将介绍创建网格容器时会发生什么?以及在网格容器上使用行、列属性如何定位元素。在本文中,将介绍:...

Islands Architecture(孤岛架构)在携程新版首页的实践

一、项目背景2022,携程PC版首页终于迎来了首次改版,完成了用户体验与技术栈的全面升级。作为与用户连接的重要入口,旧版PC首页已经陪伴携程走过了22年,承担着重要使命的同时,也遇到了很多问题:维护/...

HTML中script标签中的那些属性

HTML中的<script>标签详解在HTML中,<script>标签用于包含或引用JavaScript代码,是前端开发中不可或缺的一部分。通过合理使用<scrip...

CSS 中各种居中你真的玩明白了么

页面布局中最常见的需求就是元素或者文字居中了,但是根据场景的不同,居中也有简单到复杂各种不同的实现方式,本篇就带大家一起了解下,各种场景下,该如何使用CSS实现居中前言页面布局中最常见的需求就是元...

CSS样式更改——列表、表格和轮廓

上篇文章主要介绍了CSS样式更改篇中的字体设置Font&边框Border设置,这篇文章分享列表、表格和轮廓,一起来看看吧。1.列表List1).列表的类型<ulstyle='list-...

一文吃透 CSS Flex 布局

原文链接:一文吃透CSSFlex布局教学游戏这里有两个小游戏,可用来练习flex布局。塔防游戏送小青蛙回家Flexbox概述Flexbox布局也叫Flex布局,弹性盒子布局。它决定了...

css实现多行文本的展开收起

背景在我们写需求时可能会遇到类似于这样的多行文本展开与收起的场景:那么,如何通过纯css实现这样的效果呢?实现的难点(1)位于多行文本右下角的展开收起按钮。(2)展开和收起两种状态的切换。(3)文本...

css 垂直居中的几种实现方式

前言设计是带有主观色彩的,同样网页设计中的css一样让人摸不头脑。网上列举的实现方式一大把,或许在这里你都看到过,但既然来到这里我希望这篇能让你看有所收获,毕竟这也是前端面试的基础。实现方式备注:...

WordPress固定链接设置

WordPress设置里的最后一项就是固定链接设置,固定链接设置是决定WordPress文章及静态页面URL的重要步骤,从站点的SEO角度来讲也是。固定链接设置决定网站URL,当页面数少的时候,可以一...

面试发愁!吃透 20 道 CSS 核心题,大厂 Offer 轻松拿

前端小伙伴们,是不是一想到面试里的CSS布局题就发愁?写代码时布局总是对不齐,面试官追问兼容性就卡壳,想跳槽却总被“多列等高”“响应式布局”这些问题难住——别担心!从今天起,咱们每天拆解一...

3种CSS清除浮动的方法

今天这篇文章给大家介绍3种CSS清除浮动的方法。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。首先,这里就不讲为什么我们要清楚浮动,反正不清除浮动事多多。下面我就讲3种常用清除浮动的...

2025 年 CSS 终于要支持强大的自定义函数了?

大家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发!1.什么是CSS自定义属性CSS自...

css3属性(transform)的一个css3动画小应用

闲言碎语不多讲,咱们说说css3的transform属性:先上效果:效果说明:当鼠标移到a标签的时候,从右上角滑出二维码。实现方法:HTML代码如下:需要说明的一点是,a链接的跳转需要用javasc...

CSS基础知识(七)CSS背景

一、CSS背景属性1.背景颜色(background-color)属性值:transparent(透明的)或color(颜色)2.背景图片(background-image)属性值:none(没有)...

CSS 水平居中方式二

<divid="parent"><!--定义子级元素--><divid="child">居中布局</div>...

取消回复欢迎 发表评论: