【问题标题】:Dependencies in Makefile not properly taken into account未正确考虑 Makefile 中的依赖关系
【发布时间】:2012-10-23 12:11:23
【问题描述】:

我对 GNU makefile 的了解有限,目前令我失望。

我有一个类声明文件:mdfTree.h,一个 mdfTree.cpp 类的实现,以及一个 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?

【问题讨论】:

    标签: c++ class include makefile gnu


    【解决方案1】:

    你不需要调整你的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)
    

    【讨论】:

      【解决方案2】:

      您需要显式添加所有依赖项,在这种情况下,您的可执行文件依赖于已构建的 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 更简单,还允许您使用环境变量来设置默认编译器。

      【讨论】:

        【解决方案3】:

        我有一个类声明文件:mdfTree.h,一个 mdfTree.cpp 类的实现,以及一个 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)
        

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

        下一步之前的最后一点:在生成文件中指定规则的顺序通常无关紧要。然而,有一个例外。第一条规则是默认规则,如果您在 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 等自动化工具为您找出这些依赖关系。

        【讨论】:

          猜你喜欢
          • 2023-04-02
          • 2018-09-07
          • 1970-01-01
          • 2014-11-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-01-01
          • 1970-01-01
          相关资源
          最近更新 更多