« GNU Make 处理斜杠的问题 | 返回首页 | Freebsd 下 glx 的一点问题 »

让 GNU Make 把中间文件放到独立目录

今天想把项目用的 Makefile 整理一下,主要是让 .o .a 等等这些中间文件生成到独立目录中去。做这项工作的过程中,发现了一些有趣的问题。

当然最头痛的要属路径名的斜杠转换问题,这里暂时不提。为了描述方便,暂且不考虑 windows 平台。

一开始我想到的方法是:把 .o 这些目标指定到一个独立目录下,比如写依赖关系:

$(ODIR)/%.o : %.c

然后让 gcc 直接编译到 $(ODIR) 下。

接着我发现一个问题,我需要用 gcc 去生成 .c 和 .h 的依赖关系文件。但是用 gcc -MM 生成后,格式都是

xxx.o : xxx.c xxx.h

这样的。也就是说,xxx.o 前面没有我需要的目标路径。

当然,我们可以用 sed 进一步处理这个文件。不过我查了下资料,想到另一个方案,即使用 GNU Make 的 vpath 指令。

注:网友 CHU Run-min 提醒,gcc 有个 -MT 选项可以修改 : 前面的 target 。不过如果想一次处理多个源,还是有一定的麻烦。

我们只需要在模式匹配前写上一句:

vpath %o $(ODIR)

那些,下面,符合 %.o 模式的目标,都会去 $(ODIR) 搜索。如此,我们便可以直接接着写:

%.o : %.c

这样的规则了。

下面的链接过程,可以直接写:

SRCS = xxx.c OBJS = $(OBJS:.c=.o)

a.exe : $(OBJS)

看起来好象一切正确,但是在实际工作中,老是有点问题。第一次运行 make 总报告说找不到某个 .o ;可是再运行一次 make 又正常了。

我仔细分析了一下,原来是这样的:

在依赖规则表建立起来的过程中, a.exe 依赖了若干 .o 文件,在上例中就是 xxx.o

因为当前路径以及 vpath 设置的 ODIR 中, xxx.o 均不存在,所以 make 企图构建 xxx.o ,于是它找到了

xxx.o : xxx.c

这条规则。对于这条规则,我让 gcc 直接把 xxx.o 生成到了 ODIR 下。但是这个发生在 a.exe 的依赖关系处理之前。 a.exe 的依赖表里填的仍然是当前目录下的 xxx.o

当第二次运行 make 的时候,由于 ODIR 下的 xxx.o 已存在,根据 vpath 的规则,make 觉得不需要重新构建 xxx.o 。并且, a.exe 的依赖表中, xxx.o 也被自动扩展到了 $(ODIR)/xxx.o 。这样,就可以正确构建 a.exe 了。

我想了个方法解决这个问题:让 a.exe 的构建过程不直接调用 gcc 的链接,而是用 make 重新跑一个伪目标。看起来是这样的:

all : $(OBJS) a.exe

a.exe : $(OBJS)
    $(MAKE) exe

exe : $(OBJS)
    $(LINK) a,exe $^

如此就能正常工作了。

在 Make 的时候,最先构建 OBJS 。然后试图构建 a.exe 。 而 a.exe 需要 fork 一份 make 构建自己。由于是新的子进程,所以这个时候 OBJS 都已经生成好。就不会有上面的问题。

当然,这不是个完美的解决方案,它依赖 all 后面的依赖次序。如果是多进程编译,还是有可能出问题的(仅是猜测,我觉得出不出问题会跟 make 具体实现有关,不过我自己试了是没有问题的)。等明天再想想更好的方法吧。

Comments

好文。学习了。

最近也遇到了这个问题,原来如此。
以前一直用 BSD make,没有出现过这个问题

make 编译目标目录
http://hi.baidu.com/20065562/blog/item/8777467e29a00e0229388a58.html

http://hi.baidu.com/20065562/blog/item/8777467e29a00e0229388a58.html

关于vpath我遇到同样问题,借鉴了博主的方法得以解决,非常感谢!只是觉得多创建了子make进程,不是最好的解决方案,博主后来找到其他解决方案了吗?
我的工程某个子目录编译的结果会被上一层makefile连接,两种情况vpath结果不同:
1.子目录编译出来的.o打包成静态库(.a)供上一层makefile使用
2.子目录编译出来的.o直接供上一层makefile使用

在用了最新的w32版本gnu make 3.81后,对于第一种情况不需要二次make,对于第二种情况需要二次make;以前用的3.74版本两种情况都需要二次make。

简单描述下我的用法:
all : obj a.exe

obj : $(sub_OBJS) $(OBJS)

$(sub_OBJS) :
$(make) obj -C $(sub_dir)

vpath %.o $(sub_dir)
a.exe : $(OBJS) $(sub_OBJS)
$(make) exe

exe : $(OBJS) $(sub_OBJS)
$(LINK) a.exe $^

我一直习惯于Makefile.template加上简单的Makefile,用include包含模板,在Makefile里写简单的语句。百试不爽。

用一个Makefile文件来构建整个工程的方法好不好?也就是说不在各个目录下再写Makefile?

一直觉得auto*之流的东东,不便于移植到别的平台.
于是一次又一次的.configure 转成Makefile再存到仓库里

还在手写Makefile?

看来auto*,cmake之类的在国内不得人心阿

very good

有本书的: <Managing Projects with GNU Make>
http://www.douban.com/subject/1434964/

Post a comment

非这个主题相关的留言请到:留言本