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

时间:2018-08-27 08:02:11

标签: 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 $@

搜索时,我尝试在CPPFLAGS之后定义值:

DEPS = constants.hpp 

,然后由于main.o依赖它 添加依赖项,如下所示:

main.o: main.cpp $(DEPS)
        $(CPP) $^ $(CPPFLAGS) -o $@

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

我也尝试了this answer,并尝试使用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;
}

以及以下constants.hpp

static const int myValue = 10;

2 个答案:

答案 0 :(得分:2)

如果您只有一个源代码文件,请输入目标文件所依赖的编译文件

main.o: main.cpp $(DEPS)
      $(CPP) -c $< $(CPPFLAGS) -o $@

仅当您不使用多个.cpp文件进入编辑时,此方法才起作用。

$^是一个自动变量,它包含当前配方的所有先决条件,并用空格分隔,而$<仅保留第一个先决条件。这就是为什么您不能对同一编译中的多个.cpp文件使用此解决方案的原因。

答案 1 :(得分:1)

序言

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

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

第一步

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

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

SOURCES := parking.cpp utils.cpp InputStateContext.cpp InputState.cpp InputStateA.cpp

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

SOURCES := $(wildcard *.cpp)

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

OBJECTS := $(patsubst %.cpp,%.o,$(SOURCES))

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

parking: $(OBJECTS)
        $(CXX) $(CXXFLAGS) $^ -o $@

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

%.o: %.cpp Makefile
        $(CXX) $(CXXFLAGS) &< -o $@

使此规则也取决于Makefile可以确保例如$(CXXFLAGS)中的更改也会触发重新编译。 $<解析为 first 依赖项(源文件)。

我们稍后将扩展此规则。

第二步

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

DEPENDS := $(patsubst %.cpp,%.d,$(SOURCES))

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

-include $(DEPENDS)

那里的-意味着make不会抱怨那些文件不存在-因为它们现在不存在。

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

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

%.o: %.cpp Makefile
        $(CXX) $(CXXFLAGS) -MMD -MP -c $< -o $@

-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 $@