未正确考虑Makefile中的依赖项

时间:2012-10-23 12:11:24

标签: c++ class include makefile gnu

我对GNU makefile的了解有限,但目前还没有让我失望。

我有一个类声明文件:mdfTree.h,类mdfTree.cpp的一个实现,以及一个带有main的mdfTree_x.cpp文件,我创建了一个类mdfTree的对象并调用它的公共函数,然后调用它们mdfTree的一些私有成员变量。

我正在用Makefile编译它:

CXX=g++
CXXFLAGS=-g -Wall -W -Wconversion -Wshadow -Wcast-qual -Wwrite-strings $(shell root-config --cflags --gl\
ibs)
LDFLAGS=-g $(shell root-config --ldflags)
LDLIBS=$(shell root-config --libs)

mdfTree_x: mdfTree_x.o
        g++ $(LDFLAGS) -o mdfTree_x mdfTree_x.o $(LDLIBS)

mdfTree_x.o: mdfTree_x.cpp
        g++ $(LDFLAGS) $(CXXFLAGS) -c mdfTree_x.cpp

mdfTree.cpp和mdfTree_x.cpp中有#include mdfTree.h。 mdfTree_x.cpp也需要#include mdfTree.cpp吗?

我认为我的Makefile是错误的,因为当我尝试编译时,类mdfTree中的公共函数无法看到同一个类的私有变量。此外,当我在mdfTree.h中插入语法错误时,编译器不会提取它。我怎么告诉Makefile mdfTree_x需要使用/编译mdfTree.h / .cpp?

3 个答案:

答案 0 :(得分:2)

不需要调整你的make文件来编译和链接mdfTree.cpp。

像这样的东西

mdfTree.o: mdfTree.cpp
        g++ $(LDFLAGS) $(CXXFLAGS) -c mdfTree.cpp

并将链接步骤更改为

mdfTree_x: mdfTree_x.o mdfTree.o
        g++ $(LDFLAGS) -o mdfTree_x mdfTree_x.o mdfTree.o $(LDLIBS)

答案 1 :(得分:1)

您需要显式添加所有依赖项,在这种情况下,您的可执行文件依赖于已构建的mdfTree.o,mdfTree.o和mdfTree_x.o依赖于mdfTree.h。您的依赖应该是

mdfTree_x:mdfTree_x.o mdfTree.o
mdfTree.o:mdfTree.cpp mdfTree.h
mdfTree_x.o:mdfTree_x.cpp mdfTree.h

某些编译器(例如gnu& intel)能够自动列出make格式的标头依赖项。例如,当mdfTree_x.cppmdfTree.cppmdfTree.h中的任何一个发生更改时,此makefile将重新生成可执行文件:

# Compiler flags...
CPPFLAGS+=-MMD -MP
mdfTree_x:mdfTree_x.o mdfTree.o
-include mdfTree_x.d mdfTree.d

CPPFLAGS将在编译时创建带有标头依赖性的.d文件,-include行将尝试将这些文件包含到makefile中(如果它们不存在,则无论如何都要重新编译源文件,所以额外的依赖无关紧要)。 .d文件的内容类似于

mdfTree.o:mdfTree.h

此外,在大多数情况下,您不需要显式编写make规则 - 例如,如果您将文件foo.o列为依赖gnu make的默认规则将运行

$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o foo.o foo.cpp

(和其他语言类似)。类似地,如果可执行文件名称与一个目标文件匹配,则链接是自动的。

foo:foo.o

将运行(注意CC不是CXX)

$(CC) $(LDFLAGS) -o foo foo.o $(LDLIBS)

使用默认规则可以使makefile更简单,还可以使用环境变量来设置默认编译器。

答案 2 :(得分:1)

  

我有一个类声明文件:mdfTree.h,类mdfTree.cpp的一个实现,以及一个带有main的mdfTree_x.cpp文件,我创建了一个类mdfTree的对象并调用它的公共函数,然后调用它们mdfTree的一些私有成员变量。

您的程序需要mdfTree.cpp和mdfTree_x.cpp中定义的功能。所以你需要编译这两个文件,你的链接需要包含mdfTree.o和mdfTree_x.o。

您可以编写两个规则,一个用于编译mdfTree.cpp,另一个用于编译mdfTree_x.cpp。这很快就会失控。是邪恶的。 一个规则来构建它们,一条规则来找到它们,一条规则将它们全部带入并在黑暗中绑定它们。

objects = mdfTree_x.o mdfTree.o
$(objects): %.o: %.cpp
   g++ $(CXXFLAGS) -c $<

请注意,上述编译规则中没有$(LDFLAGS)。编译时不应指定链接选项。

您需要链接对象以形成可执行文件:

mdfTree_x: $(objects)
   g++ $(LDFLAGS) -o $@ $^ $(LDLIBS)

请注意,现在您需要指定链接器选项;你正在联系。

下一步之前的最后一点:一般来说,在makefile中指定规则的顺序无关紧要。但是有一个例外。第一个规则是默认规则,如果您未在make命令中指定任何目标,则将使用该规则。明确地将默认规则放在makefile的最顶层附近是个好主意。例如,将以下内容放在makefile顶部附近,在任何其他规则之前:

default: mdfTree_x
  

mdfTree.cpp和mdfTree_x.cpp中包含#include mdfTree.h。 mdfTree_x.cpp是否也需要#include mdfTree.cpp?

从不 #include源文件。决不。让链接器做到这一点。

然而,这里缺少一些东西。假设您更改了mdfTree.h。你需要重建。到目前为止,makefile不会这样做。缺少的是对mdfTree.h的依赖。在这种情况下,可以轻松解决此依赖性问题:只需指定依赖项。

$(objects): mdfTree.h

通常,此标头依赖性问题是一个非常难的问题。最好不要将这些头文件依赖项放在makefile中。让一些自动化工具(例如makedepend)为您找出那些依赖项。