我编写了一个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;
前言
您正在使用
$(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 $@
main.o: main.cpp $(DEPS)
$(CPP) -c $< $(CPPFLAGS) -o $@
只有当您不使用多个
.cpp
文件进入编译时,这才有效。
$^
是一个自动变量,它保存当前配方的所有先决条件,并用空格分隔,而 $<
仅保存第一个先决条件。这就是为什么您不能将此解决方案用于同一编译中的多个
.cpp
文件。如果您有多个 .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)
。