C++ 依赖于#定义的最佳实践?

C++ 依赖于#定义的最佳实践?,c++,c,c-preprocessor,conditional-compilation,C++,C,C Preprocessor,Conditional Compilation,是否有一种最佳实践来支持对C/C++预处理器标志的依赖,如-DCOMPILE\u而不使用\u FOO?我的问题是: > setenv COMPILE_WITHOUT_FOO > make <Make system reads environment, sets -DCOMPILE_WITHOUT_FOO> <Compiles nothing, since no source file has changed> 我不希望的是,如果COMPILE\u WIT

是否有一种最佳实践来支持对C/C++预处理器标志的依赖,如
-DCOMPILE\u而不使用\u FOO
?我的问题是:

> setenv COMPILE_WITHOUT_FOO
> make <Make system reads environment, sets -DCOMPILE_WITHOUT_FOO>
  <Compiles nothing, since no source file has changed>
我不希望的是,如果COMPILE\u WITHOUT\u FOO的值没有更改,那么必须重新编译所有内容

我有一个基本的Python脚本(见下文),它基本上编写了一个头文件
FooDefines.h
,然后对它进行区分,看看是否有什么不同。如果是,它将替换
FooDefines.h
,然后传统的源文件依赖性将接管。在命令行上不使用
-D
传递定义。缺点是我现在必须在使用
#ifdef
的任何源文件中包含
FooDefines.h
,而且我还为每个
#ifdef
都有一个新的动态生成的头文件。如果有一个工具可以做到这一点,或者有一种避免使用预处理器的方法,我洗耳恭听

import os, sys
def makeDefineFile(filename, text):
    tmpDefineFile = "/tmp/%s%s"%(os.getenv("USER"),filename) #Use os.tempnam?
    existingDefineFile = filename

    output = open(tmpDefineFile,'w')
    output.write(text)
    output.close()

    status = os.system("diff -q %s %s"%(tmpDefineFile, existingDefineFile))

    def checkStatus(status):
        failed = False
        if os.WIFEXITED(status):
            #Check return code
            returnCode = os.WEXITSTATUS(status)
            failed = returnCode != 0
        else:
            #Caught a signal, coredump, etc.
            failed = True
        return failed,status

    #If we failed for any reason (file didn't exist, different, etc.)
    if checkStatus(status)[0]:
        #Copy our tmp into the new file
        status = os.system("cp %s %s"%(tmpDefineFile, existingDefineFile))
        failed,status  = checkStatus(status)
        print failed, status
        if failed:
            print "ERROR: Could not update define in makeDefine.py"
            sys.exit(status)

在文件上的日期时间戳上创建触发器。依赖文件比依赖文件更新会触发它重新编译。您必须将每个选项的定义放在一个单独的.h文件中,并确保在makefile中表示这些依赖项。然后,如果您更改一个选项,依赖它的文件将自动重新编译

如果将包含文件的包含文件考虑在内,则不必更改源的结构。您可以包含一个“BuildSettings.h”文件,其中包含所有单独的设置文件

唯一困难的问题是,如果您足够聪明地解析。我已经看到编译的问题,因为include文件名冲突和include目录搜索的顺序


既然您提到了它,我应该检查一下,看看我的IDE是否足够智能,可以自动为我创建这些依赖项。听起来像是一个很好的添加到IDE中的东西。

我不相信自动确定是可能的。预处理器指令不会被编译成任何东西。一般来说,如果依赖于define,我希望进行完整的重新编译。调试是一个熟悉的例子

我认为没有正确的方法来做这件事。如果你不能用正确的方法,那么最愚蠢的方法可能是你最好的选择。用_FOO编译_并以这种方式创建依赖项的文本搜索。我会将其归类为shenanigan,如果您正在编写共享代码,我建议您从您的同事那里寻求相当重要的认同

有一些设施可以使这更容易。您可以创建一个自定义目标来执行此操作。不过,您可以在这里交换问题,维护依赖于符号的文件列表。如果文件发生更改,您的文本搜索可能会生成该文件。我使用了类似的技术来检查是否需要基于wget时间戳重建静态数据存储库

是另一个可能有用的工具


如果是我,我想我会进行全面重建。

这当然不是最好的方法,但它会起作用:

find . -name '*cpp' -o -name '*h' -exec grep -l COMPILE_WITHOUT_FOO {} \; | xargs touch
这将在源代码中查找宏
COMPILE\u WITHOUT\u FOO
,并“触摸”每个文件,这将更新时间戳。然后,当您运行make时,这些文件将重新编译

如果安装了
ack
,则可以简化此命令:

ack -l --cpp COMPILE_WITHOUT_FOO | xargs touch
Jay指出,“在文件的日期时间戳上制作触发器”

理论上,您可以让您的主makefile(称为m1)包含来自第二个makefile(称为m2)的变量。m2将包含所有预处理器标志的列表

根据m2的最新情况,您可以为您的程序制定规则

创建m2的规则是导入所有环境变量(以及#include指令)

诀窍是,制作m2的规则将检测是否与以前的版本存在差异。如果是这样,它将启用一个变量,该变量将强制对主目标执行“全部生成”和/或清除。否则,它只会更新m2上的时间戳,而不会触发完全重拍

最后,普通目标(makeall)的规则将源于m2的预处理器指令,并根据需要应用它们


这在理论上听起来很容易/可能,但在实践中GNU Make很难让这类东西工作。我相信这是可以做到的。

您的问题似乎是专门针对
autoconf
autoheader
来处理的,将变量的值写入config.h文件。如果这是不可能的,考虑从文件中读取“-d”指令并将标志写入该文件。 在所有情况下,都必须避免只依赖于环境变量的构建。你无法判断环境何时发生了变化。确实需要将变量存储在文件中,最干净的方法是通过autoconf、autoheader和一个源和多个构建树;第二种最干净的方法是重新配置编译上下文的每个开关;第三种最干净的方法是创建一个包含所有可变编译器开关的文件,所有依赖于这些开关的对象都依赖于这些开关


当您选择实现第三种方式时,请记住不要不必要地更新此文件,例如,在临时位置构建此文件并在diff上有条件地复制它,然后make rules将能够根据标志有条件地重建文件。

一种方法是将每个#define的上一个值存储在文件中,并在makefile中使用条件,在当前值与上一个值不匹配时强制更新该文件。依赖于该宏的任何文件都将包含该文件作为依赖项

这里有一个例子。如果
file.c
发生更改,或者变量
COMPILE\u而不带\u FOO
与上次不同,它将更新
file.o
。它使用
$(shell)
将当前值与存储值进行比较
ack -l --cpp COMPILE_WITHOUT_FOO | xargs touch
file.o: file.c envvars/COMPILE_WITHOUT_FOO
    gcc -DCOMPILE_WITHOUT_FOO=$(COMPILE_WITHOUT_FOO) $< -o $@

ifneq ($(strip $(shell cat envvars/COMPILE_WITHOUT_FOO 2> /dev/null)), $(strip $(COMPILE_WITHOUT_FOO)))
force: ;
envvars/COMPILE_WITHOUT_FOO: force
    echo "$(COMPILE_WITHOUT_FOO)" > envvars/COMPILE_WITHOUT_FOO
endif