热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

自动生成依赖关系

值得思考的问题目标文件(.o)是否只依赖于源文件(.c)?编译器如何编译源文件和头文件?编译行为带来的缺陷预处理器将头文件中的代码直接插入源文件编译器只通过预处理后的源文件产生目标


值得思考的问题

目标文件 (.o) 是否只依赖于源文件 (.c)?

编译器如何编译源文件和头文件?


编译行为带来的缺陷

预处理器将头文件中的代码直接插入源文件

编译器只通过预处理后的源文件产生目标文件

因此,


  • 规则中以源文件为依赖,命令可能无法执行


下面的 makefile 有没有问题?


问题的提出 

makefile

TARGET := hello.out
CC := gcc
SRCS := $(wildcard *.c)
OBJS := $(SRCS:.c=.o)
$(TARGET) : $(OBJS)
$(CC) -o $@ $^
$(OBJS) : %.o : %.c
$(CC) -o $@ -c $<

当前的目录文件

 当我们修改了 foo.h 中的内容后,结果如下

并没有再次进行编译,这导致最终可执行文件没有重新生成。 


实验中的解决方案

头文件作为依赖条出现于每个目标对应的规则中

当头文件改动,任何源文件都将被重新编译 (编译低效)

当项目中头文件数量巨大时,makefile 将很难维护


疯狂的想法

通过命令自动生成对头文件的依赖

将生成的依赖自动包含进 makefile 中

当头文件改动后,自动确认需要重新编译的文件


预备工作 (原材料)

Linux 命令 sed

编译器依赖生成选项 gcc -MM (gcc -M)


Linux 中的 sed 命令

sed 是一个流编辑器,用于流文件的修改 (增/删/查/改)

sed 可用于流文本中的字符替换

sed 的字符串替换方式为 sed 's:src:des:g'


sed 的正则表达式支持 

在 sed 中可以用正则表达式匹配替换目标

并且可以使用匹配的目标生成替换结果


gcc 关键编译选项 

生成依赖关系


  • 获取目标的完整依赖关系

gcc -M test.c



  • 获取目标的部分依赖关系

gcc -MM test.c



小技巧:拆分目标的依赖

将目标的完整依赖拆分为多个多个部分依赖


预备工作 

makefile

.PHONY : test a b c
test : a b
test : b c
test :
@echo "$^"

运行结果如下所示:

我们将 test 目标的依赖拆分成两部分,和直接将 test 目标的依赖写成一起是一样的。 


makefile 中的 include 关键字

类似 C 语言中的 include

将其它文件的内容原封不动的搬入当前文件


make 对 include 关键字的处理 

在当前目录搜索或指定目录搜索目标文件


  • 搜索成功:将文件搬入当前 makefile 中
  • 搜索失败:产生警告
    • 以文件名作为目标查找并执行对应规则
    • 当文件名对应的规则不存在时,最终产生了错误


初探 include 关键字 

makefile


.PHONY : all
include test.txt
all :
@echo "this is all"
test.txt :
@echo "this is test.txt"
@touch test.txt

test.txt

other :
@echo "this is $@"

执行结果如下所示:

首先 make 会查找当前目录中是否存在 test.txt 文件,当前目录中存在该文件,所以 make 会将 test.txt 中的内容插入到 include 的地方,other 成为了顶层目标,所以我们在执行 make 的时候,其实是在执行 other 所对应的命令。

我们将当前目录下的 test.txt 文件删除,再次执行 make,运行结果如下所示: 

此时 make 查找不到对应的文件,所以首先会产生一个警告,然后去查找当前 makefile 中是否存在对应的目标,当前 makefile 中存在 test.txt 目标,所以会执行目标所对应的命令,这时顶层的目标为 all,所以又会去执行 all 所对应的命令。


makefile 中命令的执行机制

规则中的每个命令默认是在一个新的进程中执行 (Shell)

可以通过接续符 (;) 将多个命令组合成一个命令

组合的命令依次在同一个进程中被执行

set -e 指定发生错误后立即退出执行


下面的代码想要实现功能?有没有问题?

每个命令都是 Shell 开启一个新的进程执行的,进程之间不会相互影响,所以 subtest 是创建在当前目录下的,而不是 test 目录下的。 


makefile 的命令执行

makefile


.PHONY : all
all :
set -e; \
mkdir test; \
cd test; \
mkdir subtest

set -e 指定发生错误后立即退出执行,并通过接续符将四个命令组合成一个命令,Shell 此时只会创建一个进程去执行这些命令,所以 subtest 是创建在 test 目录下的。


解决方案的初步思路

通过 gcc -MM 和 sed 得到 .dep 依赖文件 (目标的部分依赖)


  • 技术点:规则中命令的连续执行

通过 include 指令包含所有的 .dep 依赖文件


  • 技术点:当 .dep 依赖文件不存在时,使用规则自动生成


解决方案原型

makefile


.PHONY : all clean
CC := gcc
MKDIR := mkdir
RM := rm -rf
SRCS := $(wildcard *.c)
DEPS := $(SRCS:.c=.dep)
-include $(DEPS)
all :
@echo "this is $@"
%.dep : %.c
@echo "creating $@ ..."
@set -e; \
$(CC) -MM -E $^ | sed 's,\(.*\)\.o[ :]*,objs/\1.o : ,g' > $@
clean :
$(RM) $(DEPS)

.dep 依赖文件记录了一个目标文件所对应的依赖关系;如果对应的依赖文件不存在,则通过 include 去执行目标所对应的命令,gcc -MM 生成目标文件的依赖关系,通过 sed 将这些依赖关系都添加 objs/ 前缀,并重定向到对应的依赖文件中。



推荐阅读
  • 对于以压缩包形式发布的软件,其目录中通常包含一个配置脚本 `configure`。该脚本的主要功能是确定编译所需的各项参数,如头文件的位置和链接库的路径,并生成相应的 `Makefile` 以供编译使用。通过运行此脚本,开发者可以确保软件在不同环境下的正确编译与安装。此外,该脚本还能够检测系统依赖项,进一步提高编译过程的可靠性和兼容性。 ... [详细]
  • 个人博客https:juejin.cnuser176366088104638和http:blog.wuzhenyu.com.cncmake编译动态库和链接动态库cmake中&#x ... [详细]
  • 本文详细介绍了如何在Linux系统中搭建51单片机的开发与编程环境,重点讲解了使用Makefile进行项目管理的方法。首先,文章指导读者安装SDCC(Small Device C Compiler),这是一个专为小型设备设计的C语言编译器,适合用于51单片机的开发。随后,通过具体的实例演示了如何配置Makefile文件,以实现代码的自动化编译与链接过程,从而提高开发效率。此外,还提供了常见问题的解决方案及优化建议,帮助开发者快速上手并解决实际开发中可能遇到的技术难题。 ... [详细]
  • 在 CentOS 7 上部署和配置 RabbitMQ 消息队列系统时,首先需要安装 Erlang,因为 RabbitMQ 是基于 Erlang 语言开发的。具体步骤包括:安装必要的依赖项,下载 Erlang 源码包(可能需要一些时间,请耐心等待),解压源码包,解决可能出现的错误,验证安装是否成功,并将 Erlang 添加到环境变量中。接下来,下载 RabbitMQ 的 tar.xz 压缩包,并进行解压和安装。确保每一步都按顺序执行,以保证系统的稳定性和可靠性。 ... [详细]
  • 深入分析 Linux 内核链表转
    引用地址:http:www.ibm.comdeveloperworkscnlinuxkernell-chainindex.html一、链表数据结构简介链表是一种常用的 ... [详细]
  • ubuntu下基于c++的opencv学习
    一、环境配置1、安装opencv2、makefile编写makefile模板,与c文件在同一个目录下,用make指令生成可执行文件,然后运 ... [详细]
  • 在进行网络编程时,准确获取本地主机的IP地址是一项基本但重要的任务。Winsock作为20世纪90年代初由Microsoft与多家公司共同制定的Windows平台网络编程接口,为开发者提供了一套高效且易用的工具。通过Winsock,开发者可以轻松实现网络通信功能,并准确获取本地主机的IP地址,从而确保应用程序在网络环境中的稳定运行。此外,了解Winsock的工作原理及其API函数的使用方法,有助于提高开发效率和代码质量。 ... [详细]
  • 在CentOS上部署和配置FreeSWITCH
    在CentOS系统上部署和配置FreeSWITCH的过程涉及多个步骤。本文详细介绍了从源代码安装FreeSWITCH的方法,包括必要的依赖项安装、编译和配置过程。此外,还提供了常见的配置选项和故障排除技巧,帮助用户顺利完成部署并确保系统的稳定运行。 ... [详细]
  • 在《PHP应用性能优化实战指南:从理论到实践的全面解析》一文中,作者分享了一次实际的PHP应用优化经验。文章回顾了先前进行的一次优化项目,指出即使系统运行时间较长后出现的各种问题和性能瓶颈,通过采用一些通用的优化策略仍然能够有效解决。文中不仅详细阐述了优化的具体步骤和方法,还结合实例分析了优化前后的性能对比,为读者提供了宝贵的参考和借鉴。 ... [详细]
  • 本题库精选了Java核心知识点的练习题,旨在帮助学习者巩固和检验对Java理论基础的掌握。其中,选择题部分涵盖了访问控制权限等关键概念,例如,Java语言中仅允许子类或同一包内的类访问的访问权限为protected。此外,题库还包括其他重要知识点,如异常处理、多线程、集合框架等,全面覆盖Java编程的核心内容。 ... [详细]
  • 在 Debian 11 系统中部署 CMake 的详细步骤与最佳实践
    CMake是一个免费、开源、跨平台的工具系列,旨在构建、测试和打包软件. CMake用于使用简单的平台和独立于编译器的配置文件来控制软件编译过程,并生成可在您选择的编译器环境中使用 ... [详细]
  • PyFasterRCNN配置详解与优化指南
    本文主要讲解Faster-RCNN的配置过程,以及配置过程中遇到问题的解决方案。 1.下载工程gitclone--recursivehttps:github.comr ... [详细]
  • 如何判断电容好坏?
    关注+星标公众号,不错过精彩内容来源 | 电子电路怎样测量小容量电容的好坏?1、检测10pF以下的小电容,因10pF以下的固定电容器容量太小,如果用指针式用万用表进行测量,只能定性 ... [详细]
  • openssh其他版本有漏洞,需要升级,本文以我升级的机器centos6为例一、准备工作1.1检查openssh版本查看系统版本catetcre ... [详细]
  • Linux下Google Test (GTest)测试环境搭建步骤
    nsitionalENhttp:www.w3.orgTRxhtml1DTDxhtml1-transitional.dtd ... [详细]
author-avatar
金婉山_461
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有