当 header 文件被更改时如何使 Makefile 重新编译?
How to make Makefile recompile when a header file is changed?
我已经编写了一个 Makefile 来编译 OSX 上的 openCV 程序(在 Unix 系统中更通用)。
该代码有一个名为 constants.hpp
的 header,其中定义了一些常量。
我想让 Makefile 在这个 header 文件发生变化时重新编译程序,因为其中常量的值改变了程序行为。
我的 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
标志,但我遗漏了一些东西。
如何在 header 文件更改后重新编译 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;
如果你只有一个源代码文件进入编译目标文件依赖try
main.o: main.cpp $(DEPS)
$(CPP) -c $< $(CPPFLAGS) -o $@
只有当您不使用多个 .cpp
文件进入编译时,这才会起作用。
$^
是一个自动变量,它保存当前配方的所有先决条件,用空格分隔,而 $<
只保存第一个先决条件。这就是为什么您不能将此解决方案用于同一编译中的多个 .cpp
文件。
前言
您正在使用 $(CPP)
和 $(CPPFLAGS)
...这是用于 预处理器 。对于 C++ 编译器,您要使用的是 $(CXX)
和 $(CXXFLAGS)
。
以下假定 GNU make 和 GCC-compatible 编译器(clang 会做)。
第一步
使用通用规则,而不是每个源文件一个 - 后者很快就会变得笨拙,并且非常 error-prone。
要么手动列出您的源文件...
SOURCES := parking.cpp utils.cpp InputStateContext.cpp InputState.cpp InputStateA.cpp
...或自动列出所有源文件:
SOURCES := $(wildcard *.cpp)
您还需要 object 个文件的列表(每个 .cpp 一个 .o):
OBJECTS := $(patsubst %.cpp,%.o,$(SOURCES))
现在根据所有 object 文件为可执行文件提供规则,并使用将所有这些 object 文件 ($^
) 链接到同一个可执行文件 ($@
)...
parking: $(OBJECTS)
$(CXX) $(CXXFLAGS) $^ -o $@
...以及关于如何从相应的源文件(%.cpp
):
%.o: %.cpp Makefile
$(CXX) $(CXXFLAGS) $< -o $@
使 object 文件依赖于 Makefile 以及确保 Makefile 中的更改,例如$(CXXFLAGS)
,同时触发重新编译。 $<
解析为 first 依赖项(源文件)。
我们稍后会扩展此规则。
第二步
我们将为每个源文件提供一个依赖文件。 (请耐心等待。)我们可以像生成 object 个文件一样生成这些文件的列表...
DEPENDS := $(patsubst %.cpp,%.d,$(SOURCES))
...并将它们包含到 Makefile 中:
-include $(DEPENDS)
那里的 -
意味着如果这些文件不存在,make
将不会抱怨 -- 因为此时它们不存在。
第三步(你问题的核心答案)
让编译器为我们生成那些依赖文件——因为它最了解。为此,我们扩展了 object 文件构建规则:
%.o: %.cpp Makefile
$(CXX) $(CXXFLAGS) -MMD -MP -c $< -o $@
-MMD
标志生成依赖文件 (%.d
),它将包含(在 Makefile 语法中)使生成的文件(在本例中为 %.o
)依赖于源文件 及其包含的任何 non-system header。这意味着 object 文件会在触及相关来源时自动重新创建。如果你还想依赖于系统 headers(即,在每次编译时检查它们的更新),请改用 -MD
。
-MP
选项添加了空的虚拟规则,这避免了应该 header 文件从文件系统中删除的错误。
在第一次编译 运行 时,没有依赖信息 -- 但由于 object 文件也不存在,编译器必须 运行 无论如何。对于每个后续 运行,make
将包含 auto-generated 依赖文件,并“做正确的事”。
All-In-One(添加了一些语法糖):
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 $@
如果您有多个 .cpp 和 .hpp 文件,并且希望在其中一个发生更改时重新编译,则以下内容应该适用于 GNU make:
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)
。
我已经编写了一个 Makefile 来编译 OSX 上的 openCV 程序(在 Unix 系统中更通用)。
该代码有一个名为 constants.hpp
的 header,其中定义了一些常量。
我想让 Makefile 在这个 header 文件发生变化时重新编译程序,因为其中常量的值改变了程序行为。
我的 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
标志,但我遗漏了一些东西。
如何在 header 文件更改后重新编译 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;
如果你只有一个源代码文件进入编译目标文件依赖try
main.o: main.cpp $(DEPS)
$(CPP) -c $< $(CPPFLAGS) -o $@
只有当您不使用多个 .cpp
文件进入编译时,这才会起作用。
$^
是一个自动变量,它保存当前配方的所有先决条件,用空格分隔,而 $<
只保存第一个先决条件。这就是为什么您不能将此解决方案用于同一编译中的多个 .cpp
文件。
前言
您正在使用 $(CPP)
和 $(CPPFLAGS)
...这是用于 预处理器 。对于 C++ 编译器,您要使用的是 $(CXX)
和 $(CXXFLAGS)
。
以下假定 GNU make 和 GCC-compatible 编译器(clang 会做)。
第一步
使用通用规则,而不是每个源文件一个 - 后者很快就会变得笨拙,并且非常 error-prone。
要么手动列出您的源文件...
SOURCES := parking.cpp utils.cpp InputStateContext.cpp InputState.cpp InputStateA.cpp
...或自动列出所有源文件:
SOURCES := $(wildcard *.cpp)
您还需要 object 个文件的列表(每个 .cpp 一个 .o):
OBJECTS := $(patsubst %.cpp,%.o,$(SOURCES))
现在根据所有 object 文件为可执行文件提供规则,并使用将所有这些 object 文件 ($^
) 链接到同一个可执行文件 ($@
)...
parking: $(OBJECTS)
$(CXX) $(CXXFLAGS) $^ -o $@
...以及关于如何从相应的源文件(%.cpp
):
%.o: %.cpp Makefile
$(CXX) $(CXXFLAGS) $< -o $@
使 object 文件依赖于 Makefile 以及确保 Makefile 中的更改,例如$(CXXFLAGS)
,同时触发重新编译。 $<
解析为 first 依赖项(源文件)。
我们稍后会扩展此规则。
第二步
我们将为每个源文件提供一个依赖文件。 (请耐心等待。)我们可以像生成 object 个文件一样生成这些文件的列表...
DEPENDS := $(patsubst %.cpp,%.d,$(SOURCES))
...并将它们包含到 Makefile 中:
-include $(DEPENDS)
那里的 -
意味着如果这些文件不存在,make
将不会抱怨 -- 因为此时它们不存在。
第三步(你问题的核心答案)
让编译器为我们生成那些依赖文件——因为它最了解。为此,我们扩展了 object 文件构建规则:
%.o: %.cpp Makefile
$(CXX) $(CXXFLAGS) -MMD -MP -c $< -o $@
-MMD
标志生成依赖文件 (%.d
),它将包含(在 Makefile 语法中)使生成的文件(在本例中为 %.o
)依赖于源文件 及其包含的任何 non-system header。这意味着 object 文件会在触及相关来源时自动重新创建。如果你还想依赖于系统 headers(即,在每次编译时检查它们的更新),请改用 -MD
。
-MP
选项添加了空的虚拟规则,这避免了应该 header 文件从文件系统中删除的错误。
在第一次编译 运行 时,没有依赖信息 -- 但由于 object 文件也不存在,编译器必须 运行 无论如何。对于每个后续 运行,make
将包含 auto-generated 依赖文件,并“做正确的事”。
All-In-One(添加了一些语法糖):
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 $@
如果您有多个 .cpp 和 .hpp 文件,并且希望在其中一个发生更改时重新编译,则以下内容应该适用于 GNU make:
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)
。