如何让Makefile在头文件改变时重新编译?

问题描述 投票:0回答:3

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

代码有一个名为

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

我也尝试过这个答案并尝试使用

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;
makefile compilation header-files
3个回答
66
投票

前言

您正在使用

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

以下假设 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
) 的
通用
规则:

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

使目标文件依赖于 Makefile 并确保 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 $@

如果你只有一个目标文件所依赖的源代码文件进入编译试试

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

只有当您不使用多个
.cpp
文件进入编译时,这才有效。

$^

是一个自动变量,它保存当前配方的所有先决条件,并用空格分隔,而

$<

 仅保存第一个先决条件。这就是为什么您不能将此解决方案用于同一编译中的多个 
.cpp
 文件。

如果您有多个 .cpp 和 .hpp 文件,并且希望在其中一个文件发生更改时重新编译,则以下内容应该适用于 GNU make:

0
投票
main: main.cpp foo.cpp constants.hpp bar.hpp $(LINK.cpp) $(filter-out %.hpp,$^) $(LDLIBS) -o $@

$(filter-out %.hpp,$^)
从编译器调用中删除

.hpp

 文件名。
在 GNU make 中,变量 
$(LINK.cpp)

扩展为

$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)

© www.soinside.com 2019 - 2024. All rights reserved.