更改头文件时,如何使Makefile重新编译?

Fra*_*Boi 1 makefile compilation header-files

我编写了一个Makefile来在OSX(Unix系统中更通用)上编译一个openCV程序。

该代码有一个标头constants.hpp,其中定义了一些常量。

我想使Makefile在此头文件更改时重新编译程序,因为其中的常量值会更改程序行为。

我的Makefile如下

CPP = g++
CPPFLAGS = -std=c++11

all: main.o

main.o: main.cpp
    $(CPP) $^ $(CPPFLAGS) -o $@
Run Code Online (Sandbox Code Playgroud)

搜索后,我试图定义CPPFLAGS以下值:

DEPS = constants.hpp 
Run Code Online (Sandbox Code Playgroud)

然后由于main.o依赖于它,所以添加依赖关系如下:

main.o: main.cpp $(DEPS)
        $(CPP) $^ $(CPPFLAGS) -o $@
Run Code Online (Sandbox Code Playgroud)

但是我得到的错误是clang: error: cannot specify -o when generating multiple output files

我也尝试了这个答案,并尝试使用M MM标志,但是我丢失了一些东西。

更改头文件时,如何使Makefile重新编译?

编辑:在对DevSolar的评论之后,我不得不完全修改问题,并且他还询问了源代码。由于我发现这里无用地复制所有源代码,因此我用一个简单的hello world程序对其进行了简化。

以下是main.cpp

#include<iostream>
#include"constants.hpp"

int main()
{

  std::cout<<"Hello world, the value is: " <<  myValue <<"\n";
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

以及以下内容constants.hpp

static const int myValue = 10;
Run Code Online (Sandbox Code Playgroud)

Dev*_*lar 6

前言

您正在使用$(CPP)$(CPPFLAGS)...这是用于预处理程序的。您要使用的是$(CXX)$(CXXFLAGS),用于C ++编译器。

以下内容假定使用GNU make和与GCC兼容的编译器(使用clang即可)。

步骤1

使用通用规则,而不是每个源文件一个规则-后者很快就会变得毫无用处,并且容易出错。

手动列出您的源文件...

SOURCES := parking.cpp utils.cpp InputStateContext.cpp InputState.cpp InputStateA.cpp
Run Code Online (Sandbox Code Playgroud)

...或自动列出所有源文件:

SOURCES := $(wildcard *.cpp)
Run Code Online (Sandbox Code Playgroud)

您还需要一个目标文件列表(每个.cpp一个.o):

OBJECTS := $(patsubst %.cpp,%.o,$(SOURCES))
Run Code Online (Sandbox Code Playgroud)

现在,根据所有目标文件为可执行文件提供规则。

parking: $(OBJECTS)
        $(CXX) $(CXXFLAGS) $^ -o $@
Run Code Online (Sandbox Code Playgroud)

...以及关于如何将单个源文件转换为目标文件的通用规则:

%.o: %.cpp Makefile
        $(CXX) $(CXXFLAGS) &< -o $@
Run Code Online (Sandbox Code Playgroud)

使该规则也依赖于Makefile可以确保例如更改也$(CXXFLAGS)触发重新编译。$<解析为第一个依赖项(源文件)。

我们将在以后扩展此规则。

第二步

每个源文件都有一个依赖文件。(在这里与我在一起。)我们可以生成这些文件的列表...

DEPENDS := $(patsubst %.cpp,%.d,$(SOURCES))
Run Code Online (Sandbox Code Playgroud)

...并将它们包含在Makefile中:

-include $(DEPENDS)
Run Code Online (Sandbox Code Playgroud)

-那里意味着,make如果不存在这些文件不会抱怨-因为他们不这样做,在这一点上。

第三步(问题答案的核心)

编译器我们生成那些依赖文件-因为它最了解。为此,我们扩展了构建规则:

%.o: %.cpp Makefile
        $(CXX) $(CXXFLAGS) -MMD -MP -c $< -o $@
Run Code Online (Sandbox Code Playgroud)

-MMD标志生成依赖项文件(%.d),该文件将保存(以Makefile语法)规则,以使所生成的文件(%.o在这种情况下)取决于源文件及其包括的任何非系统头文件。这意味着只要触摸相关源,就会自动重新创建目标文件。如果您还希望依赖系统头文件(即,检查它们的每次编译更新),请-MD改用。

-MP选项添加了空的虚拟规则,可以避免在从文件系统中删除头文件时出错。

在第一次编译运行时,没有依赖项信息-但是由于目标文件也不存在,因此编译器仍然必须运行。对于每个后续运行,make将包括自动生成的依赖文件,并“做正确的事”。


多合一(添加了更多语法糖):

SOURCES := $(wildcard *.cpp)
OBJECTS := $(patsubst %.cpp,%.o,$(SOURCES))
DEPENDS := $(patsubst %.cpp,%.d,$(SOURCES))

# ADD MORE WARNINGS!
WARNING := -Wall -Wextra

# .PHONY means these rules get executed even if
# files of those names exist.
.PHONY: all clean

# The first rule is the default, ie. "make",
# "make all" and "make parking" mean the same
all: parking

clean:
        $(RM) $(OBJECTS) $(DEPENDS) parking

# Linking the executable from the object files
parking: $(OBJECTS)
        $(CXX) $(WARNING) $(CXXFLAGS) $^ -o $@

-include $(DEPENDS)

%.o: %.cpp Makefile
        $(CXX) $(WARNING) $(CXXFLAGS) -MMD -MP -c $< -o $@
Run Code Online (Sandbox Code Playgroud)

  • 感谢您详细解释清楚的答案。假设您发布步骤的顺序在 Makefile 中是相同的,我不明白的一件事是为什么在可执行规则之后指定如何将单个源文件转换为目标文件的通用规则?之前不应该定义它以便在生成可执行文件时已经知道吗? (2认同)