【问题标题】:Makefile trigger rebuild for C/C++ preprocessor directives用于 C/C++ 预处理器指令的 Makefile 触发器重建
【发布时间】:2021-03-23 00:01:01
【问题描述】:

假设我有一个 C++ 源文件,如下所示:

int main() {
...
#ifdef flag
    // do something
#endif
...
#ifndef flag
    // do something different
#endif
...
}

我知道对于 gcc,我可以使用-D'flag' 作为预处理器的指令来定义flag,从而确定上面代码的哪一部分实际被编译,并且可以在 Makefile 中使用。

但是如何编写一个 Makefile 以便根据我是否要定义 flag 重新构建源文件(不对任何文件进行其他更改,包括 Makefile 本身)?

我知道我可以,例如(如果有更好的方法,解决方案不需要遵循这个!),当我运行 make 时定义一个变量,例如make flag=1 all,但如果包含 main 的文件(与上面的源代码示例一致)此后未更改,则不会触发重建。

即从一个干净的目录开始并运行make all 将运行构建,但是如果我立即运行make flag=1 all,我希望正确重建包含main(比如main.cpp)的文件,而不必先touch main.cpp .

【问题讨论】:

  • 我建议使用 cmake。如果您使用更改的标志运行 cmake,它会重建目标。
  • 上次构建是否使用标志的“记忆”必须存储在某处。 Make 可以编写一个文件来记录该信息。这种方法可以接受吗?
  • @Beta 有兴趣看看它是如何工作的,谢谢

标签: c++ gcc makefile gnu-make preprocessor-directive


【解决方案1】:

您可以为代码创建对 Makefile 的依赖项。这将在 Makefile 更改时触发编译。

或者使用 cmake,那么您的 IDE 将/应该为您执行此操作...

【讨论】:

  • 我不会更改 Makefile。我希望根据我在命令行中传递给make 的参数来触发重建。我已经编辑了我的问题以更明确地说明这一点。感谢您的 cmake 提示,我目前正在学习 make,打算接下来学习 cmake!
  • 嗯,那么您可以在 Makefile 中创建一个特殊目标,使用“触摸”来触发某些操作。
【解决方案2】:

首先make必须知道标志已经改变。这意味着您必须将标志存储在“某处”,该标志通常是某个文件。接下来你必须告诉make 哪个文件取决于你的标志。除了依赖文件之外,没有必要编译所有的东西。这意味着,您必须指定哪个源取决于哪个标志,或者在这种情况下,取决于存储标志的哪个文件。

好的,你们的 Makefile 可以是这样的:

# define a first rule, that we want, if no target is given, we want to
# compile our program named "go"
all: go

# read the file, where our last given flag was stored.
# the minus in front tells make, that if the file can't be read, simple ignore it!
-include stored_flag.mk

# after that, we compare the old flag and the new one which was given on command
# line or from environment. If it differs or was not present before,
# write the new flag to the file to make it reusable for the next call
ifneq ($(OLDFLAG),$(FLAG))
$(file > stored_flag.mk, OLDFLAG:=$(FLAG))
endif

# now we forward the environment variable to the compiler
# if it is present
ifneq ($(FLAG),)
CXXFLAGS=-DFLAG=$(FLAG)
endif


# as said: we need to make the source files dependent on the flag file,
# if the flag is used in that source. If ot, no need to comoile even if flag has 
# changed
main.o: stored_flag.mk

# standard rule: 
main.o: main.cpp

# build our program "go" 
go: main.o 
    g++ $< -o go

示例源(main.cpp):

#include <iostream>

int main()
{
#ifdef FLAG
    std::cout << "Flag is " << FLAG << std::endl;
#else
    std::cout << "No flag" << std::endl;
#endif
}

执行:

make FLAG=1
make FLAG=1
make FLAG=2
...

测试愉快!

如果你有多个标志,并且你想为每个标志定义独立的依赖关系,你可以使用多个文件来存储标志。

添加:

问:“Make 是通过比较依赖项与目标的时间戳来知道触发重建的唯一方式吗?”

A:这就是make的核心思想!比较文件上的时间戳以确定是否需要做某事。但是:正如您所见,make 可以处理“ifdef”,因此您可以在这样的块内定义规则,根据某些条件进行编译,无需比较时间或定义依赖项。

【讨论】:

  • 这很有帮助,谢谢。我想一个更基本的问题应该是:“only 是 Make 知道通过比较依赖项与目标的时间戳来触发重建的方式吗?”似乎答案是“是”,在这种情况下,我现在明白为什么你必须将以前的标志写入文件并将该文件作为依赖项包含在内!但是,这个答案并不能完全解决我的问题;我必须在源代码中使用#if FLAG==1#if FLAG==0 而不是#ifdef FLAG#ifndef FLAG,因此我不能自己运行make(即离开FLAG 未定义)跨度>
  • @ogb119:我们通常在这里投票而不是“非常有帮助”...
  • @ogb119 为您的问题添加答案
  • @ogb119 现在还更新用例“在命令行上提供更改或打开标志”
  • 更新后的答案正是我想要的,谢谢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-05
  • 2017-09-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多