Makefile 自动生成头文件的依赖关系 .

最近在看一本书《Windows游戏编程大师技巧》 (Tricks of Windows Game Programming Gurus). 第一章给出了一个打砖块小游戏的示例程序. 包括三个文件: blackbox.h, blackbox.cpp和freakout.cpp (600行代码, 对于Windows C++程序来说还好, 没有让我freak out…). blackbox.cpp封装了部分DirectDraw, 提供了一些更傻瓜化的初始化DirectDraw, 画点, 画方框的工具函数. blackbox.h包括了这些函数的声明. freakout.cpp引用了blackbox.h文件, 包括WinMain主函数和主要的游戏逻辑.

How to build?

和我一样还在使用N年前的垃圾电脑的看官们, 多半也不愿意用Visual Studio来挑战自己的耐心. 但是所以让我们选择小米加步枪: GCC (MinGW) + Makefile. 写个简单的makefile例如:

all:    freakout.exe
 
freakout.exe: freakout.o blackbox.o
    g++ freakout.o blackbox.o -o stupid.exe -L D:gamedevdx81sdkDXFDXSDKlib -lddraw -mwindows
 
freakout.o: freakout.cpp blackbox.h
    g++ -c freakout.cpp -o freakout.o -I D:gamedevdx81sdkDXFDXSDKinclude
 
blackbox.o: blackbox.cpp blackbox.h
    g++ -c blackbox.cpp -o blackbox.o -I D:gamedevdx81sdkDXFDXSDKinclude

Problem?

上面的代码当然有很多问题, 比如”-L <DX lib path>”, “-I <DX inc path>”可以被定义在类似$(LIB), $(INC)的变量里面, 比如我没有按照makefile的习惯写一个clean目标清理生成的东东…不过我的重点是在红色高亮的blackbox.h: 两个.o文件都需要依赖于blackbox.h.

假设我们修改freakout.cpp引用更多的头文件, 每加一条#include “somefile.h”指令, 就需要相应地在makefile里为freakout.o增加一个依赖项somefile.h.

假设我们修改stupid.h文件, 让stupid.h也引用一个头文件someotherfile.h, 那么我们也需要相应地在makefile里为所有依赖stupid.h的.o文件 (在这个例子中是freakout.o和blackbox.o) 增加依赖项someotherfile.h.

所以问题是: 在.cpp和.h被修改的情况下, 怎么维护.o目标文件和.h头文件的依赖关系.

Solution – 自动生成依赖关系

Google一下”makefile 头文件 依赖”会发现大多数编译器都提供了一个选项生成.o目标文件所依赖的文件列表. 比如GCC的”-MM”选项. 运行GCC –MM freakout.cpp blackbox.cpp <库文件和头文件选项>得到输出:

freakout.o: freakout.cpp blackbox.h D:/gamedev/dx81sdk/DXF/DXSDK/include/ddraw.h
blackbox.o: blackbox.cpp blackbox.h D:/gamedev/dx81sdk/DXF/DXSDK/include/ddraw.h

所以一个简单的处理依赖关系的办法是把这些编译器生成的依赖关系写入一个文件里, 然后在makefile中用include指令包含这个文件:

CPP = g++
OBJ = freakout.o blackbox.o
LIB = -L D:gamedevdx81sdkDXFDXSDKlib -mwindows -l ddraw
INC = -I D:gamedevdx81sdkDXFDXSDKinclude
 
all: freakout.exe
 
freakout.exe: ${OBJ}
    ${CPP} ${OBJ} -o freakout.exe ${LIB}
 
include depend
 
# note the symbols: $< and ‘[email protected]
%.o: %.cpp
    ${CPP} -c $< -o [email protected] ${INC}
 
# generate depend file
depend:
    ${CPP} -MM ${OBJ:.o=.cpp} ${INC} > depend

注意红色的部分, depend是GCC生成的包含了依赖关系的文件 (也注意上面的makefile中有”$<”, “[email protected]”这种perl风格的bt匹配字符…) . 有了这样的makefile, 我们就能用两个步骤来build项目:

1) 在需要更新依赖关系的时候 (比如在某个文件里多加了一条#include指令) 运行make depend.

2) 运行make.

Makefile Auto Dependency – 只运行一次make

懒惰是程序员的一大美德, 所以GNU make的手册里提供了一个运行一次make就能更新所有依赖关系并且按照依赖关系build的办法 (http://www.gnu.org/software/make/manual/make.html#Automatic-Prerequisites): 与整体引入一个depend目标不同, 我们为每一个.o/.cpp文件引入一个.d依赖关系文件. 依赖关系文件由GCC的”-MM”选项生成, 并且在GCC输出的基础上把自己本身加入到目标列表中. 比如:

# freakout.d
freakout.o freakout.d: freakout.cpp D:/gamedev/dx81sdk/DXF/DXSDK/include/ddraw.h blackbox.h
 
# blackbox.d
blackbox.o blackbox.d: blackbox.cpp D:/gamedev/dx81sdk/DXF/DXSDK/include/ddraw.h blackbox.h

注意红色的部份: .d文件被加入到了目标列表, 依赖相关的.h和.cpp文件. 那么怎样自动生成这些.d文件呢? 类似的, makefile:

CPP = g++
OBJ = freakout.o blackbox.o
LIB = -L D:gamedevdx81sdkDXFDXSDKlib -mwindows -l ddraw
INC = -I D:gamedevdx81sdkDXFDXSDKinclude
all: freakout.exe
freakout.exe: ${OBJ}
    ${CPP} ${OBJ} -o freakout.exe ${LIB}
include ${OBJ:.o=.d} 
%.o: %.cpp
    ${CPP} -c $< -o [email protected] ${INC} 
%.d: %.cpp
    rm -f [email protected] & 
    ${CPP} -MM $< ${INC} > [email protected]$$ & 
    insertdfile.exe $< [email protected]$$ > [email protected] & 
    rm -f [email protected]$$

这个makefile和上一节给出的makefile大致差不多, 不同的是两个红色的部分: 第一部分我们用include指令include相关的所有.d文件; 第二部分定义了生成.d文件的规则 (又是[email protected], $<符号…): 第一行首先删除原有的.d文件; 第二行运行g++ -MM生成依赖关系写入一个临时文件([email protected]$$)里; 第三行把.d文件加入到第二行生成的依赖关系中并把最终结果写到.d文件中; 第四行删除临时文件.

P.S. insertdfile.exe是我自己写的一个小程序, 用来把.d文件加入到第二行生成的依赖关系中, 例如把”blackbox.o: blackbox.cpp blackbox.h”转换为”blackbox.o blackbox.d: blackbox.cpp blackbox.h”. 我为什么要自己写一个字符串替换的程序? 因为我没有装sed工具…如果有, 当然用官方推荐的办法, 把第三行换成神奇的符咒: sed ’s,($*).o[ :]*,1.o [email protected] : ,g’ < [email protected]$$ > [email protected] .

好了, 运行make:

1) make尝试去包含blackbox.d和freakout.d文件, 没有发现, 所以检查有没有规则可以生成这些.d文件, 发现我们%.d: %.cpp的这条规则, 于是运行这条规则生成所有的.d文件并且包含进来.
2) 按照正常规则生成.o文件, 然后生成.exe.

然后我们做一些修改, 比如增加一个stupid.h文件, 然后修改blackbox.h文件以包含stupid.h. 运行make:

1) make把第一个目标all加入到需要生成的目标列表中.
2) make包含blackbox.d和freakout.d文件. 注意在这个过程中这两个.d文件也会被加入到make的需要生成的目标列表中 – 因为可能有规则能更新这两个.d文件.
3) 根据blackbox.d和freakout.d包含的规则: .d文件 (已经被入到了make的需要生成目标列表中) 需要被更新, 于是相应地运行%.d: %.cpp规则更新.d文件: 新加入的 stupid.h文件被加入到两个.d文件的依赖项中.
4) make发现包含的两个.d文件在第2)步中都被更新, 于是重新包含这两个.d文件.
5) make根据在第2)步生成在第3)步被包含进来的规则, 发现stupid.h的日期比两个.o文件的日期都要新, 于是重新生成.o文件.
6) 根据规则生成.exe.

时间: 2024-12-23 20:35:51

Makefile 自动生成头文件的依赖关系 .的相关文章

Makefile自动生成头文件依赖

前言 Makefile自动生成头文件依赖是很常用的功能,本文的目的是想尽量详细说明其中的原理和过程. Makefile模板 首先给出一个本人在小项目中常用的Makefile模板,支持自动生成头文件依赖. CC = gcc CFLAGS = -Wall -O INCLUDEFLAGS = LDFLAGS = OBJS = seq.o TARGETS = test_seq .PHONY:all all : $(TARGETS) test_seq:test_seq.o $(OBJS) $(CC) -o

Makefile中自动生成头文件依赖

为什么需要自动生成头文件依赖? 编译单个源文件时,需要获取文件中包含的头文件的信息,但是一般的Makefile不会在规则中明确写明文件依赖的头文件,所以单独修改头文件后,不会导致包含头文件的源文件重新编译.如果每次手动的添加头文件依赖,又会非常的繁琐,所以需要一种自动生成依赖的方法. 编译器中神奇的选项 使用$(CC)中的-M命令就可以完美的解决问题,因为-M选项可以将源文件依赖的所有头文件,自动解析出来. 例子:在当前路径下,编辑test.c和test.h文件,test.c如下所示,test.

makefile 自动处理头文件的依赖关系 (zz)

现在我们的Makefile写成这样: all: main main: main.o stack.o maze.ogcc $^ -o [email protected] main.o: main.h stack.h maze.hstack.o: stack.h main.hmaze.o: maze.h main.h clean:-rm main *.o .PHONY: clean按照惯例,用all做缺省目标.现在还有一点比较麻烦,在写main.o.stack.o和maze.o这三个目标的规则时要查

Makefile中头文件在依赖关系中作用

摘于:http://bbs.csdn.net/topics/120024677 (1)在makefile的依赖关系中用不用体现.h头文件?(2)如果在依赖关系中要体现.h头文件,应该体现到什么层次?==============================(1)在makefile的依赖关系中用不用体现.h头文件?============================== 下面是我的一些认识: 头文件中定义的是接口(函数接口,文件外全局变量和宏定义),它的作用是向调用文件封装函数的实现过程.在

设置pycharm 创建文件时自动生成头文件

找到该路径并添加以下信息 File->settings->Editor->File and Code Templates->Python Script  #!/usr/bin/env python# -*- coding: utf-8 -*-# @Time    : ${DATE} ${TIME}# @Author  : Aries# @Site    : ${SITE}# @File    : ${NAME}.py# @Software: ${PRODUCT_NAME}

自动编译Makefile,原版升级版,支持自动推倒头文件依赖关系、创建目标路径

################################################################################### # 简易多模块程序自动编译/清除Makefile v2.0 #简介: 自动编译.清除功能,不需要每次编译输入gcc或者rm指令 #使用方法: 先配置Makefile中的配置项.目标名称等,然后在终端执行make即可 # #使用说明:1. 本Makefile可以自动推倒源程序与头文件之间的依赖关系,并生成.depend文件 # 2.

php学习之道:php中soap的使用实例以及生成WSDL文件,提供自动生成WSDL文件的类库——SoapDiscovery.class.php类

1. web service普及: Webservice soap wsdl区别之个人见解 Web Service实现业务诉求:  Web Service是真正"办事"的那个,提供一种办事接口的统称. WSDL提供"能办的事的文档说明":  对要提供的服务的一种描述格式.我想帮你的忙,但是我要告诉你我都能干什么,以及干这些事情需要的参数类型. SOAP提供"请求"的规范:  向服务接口传递请求的格式,包括方法和参数等.你想让人家办事,总得告诉人家

程序自动生成dump文件

作用 可以再程序崩溃后保留崩溃现场,方便事后查找程序的崩溃原因. 前提是奔溃程序所对应的代码和PDB文件都是匹配的(发布程序的时候,保留源代码和PDB文件). 代码示例 原理是使用DbgHelp的自定义未处理异常来保留崩溃现场到文件的,使用直接包含该头文件,然后再main函数的开始就调用EnableAutoDump函数. // pro_dump.h // 包含本文件是用来处理程序崩溃时自动生成dump文件的 // 生成的dump文件在程序的工作目录下 // 文件名格式是:年月日_时分秒.dmp

程序自动生成Dump文件()

前言:通过drwtsn32.NTSD.CDB等调试工具生成Dump文件, drwtsn32存在的缺点虽然NTSD.CDB可以完全解决,但并不是所有的操作系统中都安装了NTSD.CDB等调试工具.了解了mini dump文件格式后,完全可以程序自动生成Dump文件. 本文主要讨论以下内容: 1.  运行原理 2.  程序修改 3.  注意事项 一.   运行原理 当程序遇到未处理异常(主要指非指针造成)导致程序崩溃死,如果在异常发生之前调用了SetUnhandledExceptionFilter(