头文件改变时如何使Makefile重新编译? [英] How to make Makefile recompile when a header file is changed?

查看:121
本文介绍了头文件改变时如何使Makefile重新编译?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have written a Makefile to compile an openCV program on OSX (more general in Unix systems).

代码有一个名为 constants.hpp 的头文件,其中定义了一些常量.

The code has a header named constants.hpp where some constants are defined.

当这个头文件改变时,我想让 Makefile 重新编译程序,因为其中的常量值改变了程序的行为.

I would like to make the Makefile recompile the program when this header file changes because the values of the constants in it change the program behavior.

我的 Makefile 如下

My Makefile is the following

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

all: main.o

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

四处搜索我试图在 CPPFLAGS 之后定义值:

Searching around I tried to define after CPPFLAGS the value:

DEPS = constants.hpp 

然后因为 main.o 依赖于它添加依赖如下:

and then since main.o depends on it adding the dependency as follows:

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

但我得到的错误是clang: error: cannot specified -o when generate multiple output files.

我也尝试了 这个答案 并尝试使用 MMM 标志,但我遗漏了一些东西.

I tried also this answer and tried to use th e M MM flags but I am missing something.

当头文件改变时如何使Makefile重新编译?

How to make Makefile recompile when a header file is changed?

根据对 DevSolar 的评论,我不得不完全修改问题,他还询问了源代码.由于我发现在这里复制所有源代码没有用,所以我用一个简单的 hello world 程序对其进行了简化.

Following the comments to DevSolar I had to modify completely the question and he asked also the source code. Since I found useless to copy all the source code here I simplified it with a simple hello world program.

以下是main.cpp:

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

int main()
{

  std::cout<<"Hello world, the value is: " <<  myValue <<"
";
  return 0;
}

以及以下 constants.hpp:

static const int myValue = 10;

推荐答案

前言

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

You are using $(CPP) and $(CPPFLAGS)... that's for the preprocessor. What you want to use is $(CXX) and $(CXXFLAGS), for the C++ compiler.

以下假设使用 GNU make 和 GCC 兼容的编译器(clang 可以).

The following assumes GNU make and a GCC-compatible compiler (clang will do).

第一步

使用通用规则,而不是每个源文件一个——后者很快就会变得笨拙,而且很容易出错.

Use generic rules, not one per source file -- the latter will very quickly become unwieldly, and is very error-prone.

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

Either list your source files manually...

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

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

...or have all source files listed automatically:

SOURCES := $(wildcard *.cpp)

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

You also need a list of the object files (one .o per .cpp):

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

现在为可执行文件提供一个规则,取决于所有目标文件,使用一个命令将所有这些目标文件($^)链接到同一个可执行文件($@)代码>)...

Now provide a rule for the executable, depending on all the object files, with a command that links all those object files ($^) into that same executable ($@)...

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

...以及关于如何从相应的源文件(%.cpp)生成单个目标文件(%.o)的通用规则代码>):

...and a generic rule on how to generate individual object files (%.o) from the corresponding source file (%.cpp):

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

使目标文件依赖于 Makefile 也可以确保 Makefile 中的更改,例如$(CXXFLAGS),也触发重新编译.$< 解析为first 依赖项(源文件).

Making the object file depend on the Makefile as well ensures that changes in the Makefile, e.g. $(CXXFLAGS), trigger a recompilation as well. $< resolves to the first dependency (the source file).

我们稍后会扩展此规则.

We will extend this rule later.

第二步

我们将为每个源文件提供一个依赖文件.(请耐心等待.)我们可以像生成目标文件一样生成这些文件的列表...

We will have a dependency file for each source file. (Bear with me here.) We can generate a list of those files the same as we did for object files...

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

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

...and include them into the Makefile:

-include $(DEPENDS)

- 意味着如果这些文件不存在,make 不会抱怨——因为此时它们不存在.

The - there means that make will not complain if those files do not exist -- because they do not, at this point.

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

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

Have the compiler generate those dependency files for us -- because it knows best. For that, we extend our object file build rule:

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

-MMD 标志生成依赖文件 (%.d),它将保存(在 Makefile 语法中)生成生成文件 (%.o 在这种情况下)依赖于源文件和它包含的任何非系统头.这意味着只要接触到相关源,就会自动重新创建目标文件.如果您还想依赖系统头文件(即,在每次编译时检查它们的更新),请改用 -MD.

The -MMD flag generates the dependency file (%.d), which will hold (in Makefile syntax) rules making the generated file (%.o in this case) depend on the source file and any non-system headers it includes. That means the object file gets recreated automatically whenever relevant sources are touched. If you want to also depend on system headers (i.e., checking them for updates on each compile), use -MD instead.

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

The -MP option adds empty dummy rules, which avoid errors should header files be removed from the filesystem.

在第一次编译运行时,没有依赖信息——但由于目标文件也不存在,编译器无论如何都必须运行.对于每次后续运行,make 将包含自动生成的依赖文件,并做正确的事".

At the first compile run, there is no dependency information -- but since the object files don't exist either, the compiler must run anyway. For every subsequent run, make will include the auto-generated dependency files, and "do the right thing".

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

All-In-One (with some more syntactic sugar added):

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

这篇关于头文件改变时如何使Makefile重新编译?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆