依赖#defines的最佳实践?

时间:2011-07-28 18:12:28

标签: c++ c c-preprocessor conditional-compilation

是否支持对-DCOMPILE_WITHOUT_FOO等C / C ++预处理程序标志的依赖关系进行最佳实践?这是我的问题:

> setenv COMPILE_WITHOUT_FOO
> make <Make system reads environment, sets -DCOMPILE_WITHOUT_FOO>
  <Compiles nothing, since no source file has changed>

我想要做的是让所有依赖#ifdef语句的文件重新编译:

> setenv COMPILE_WITHOUT_FOO
> make
  g++ FileWithIfdefFoo.cpp

如果COMPILE_WITHOUT_FOO的值没有改变,我不想重新编译所有内容。

我有一个原始的Python脚本工作(见下文),它基本上写了一个头文件FooDefines.h,然后对它进行区分以查看是否有任何不同。如果是,则替换FooDefines.h,然后传统的源文件依赖关系接管。使用-D在命令行上传递 not 。缺点是我现在必须在使用FooDefines.h的任何源文件中包含#ifdef,并且每个#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)

6 个答案:

答案 0 :(得分:3)

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

find . -name '*cpp' -o -name '*h' -exec grep -l COMPILE_WITHOUT_FOO {} \; | xargs touch

这将查看宏COMPILE_WITHOUT_FOO的源代码,并“触摸”每个文件,这将更新时间戳。然后当你运行make时,这些文件将重新编译。

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

ack -l --cpp COMPILE_WITHOUT_FOO | xargs touch

答案 1 :(得分:2)

我不相信可以自动确定。预处理程序指令不会被编译成任何东西。一般来说,如果我依赖于一个定义,我希望完全重新编译。 DEBUG是一个熟悉的例子。

我认为没有正确的方式来实现它。如果你不能以正确的方式做到这一点,那么最愚蠢的方式可能是你最好的选择。 COMPILE_WITH_FOO的文本搜索并以这种方式创建依赖项。我会将其归类为shenanigan,如果您正在编写共享代码,我建议您从同事那里寻求非常重要的购买。

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

Cheetah是另一种可能有用的工具。

如果是我,我想我会完全重建。

答案 2 :(得分:1)

您的问题似乎是根据autoconfautoheader来定制的,将变量的值写入config.h文件。如果那是不可能的,请考虑从文件中读取“-D”指令并将标志写入该文件。

在所有情况下,您都必须避免仅依赖于环境变量的构建。您无法确定环境何时发生变化。将变量存储在文件中是绝对必要的,最简单的方法是使用autoconf,autoheader以及源和多个构建树;对于每个编译上下文切换,重新configure的第二个最干净的方法;以及第三种最简洁的方式,包含所有可变编译器的文件切换所有依赖于这些开关的对象依赖于它们。

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

答案 3 :(得分:1)

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

这是一个例子。如果file.o更改或变量file.c与上次不同,它将更新COMPILE_WITHOUT_FOO。它使用$(shell )将当前值与存储在文件envvars/COMPILE_WITHOUT_FOO中的值进行比较。如果它们不同,则会为该文件创建一个命令,该命令取决于force,该命令始终更新。

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

如果要支持未定义宏,则需要使用ifdefifndef条件,并在文件中指示上次运行时值未定义。

答案 4 :(得分:0)

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

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

唯一棘手的问题是如果你足够聪明地解析include guards。由于包含文件名冲突和包含目录搜索的顺序,我看到了编译问题。

现在您提到它我应该检查并查看我的IDE是否足够智能以自动为我创建这些依赖项。添加到IDE听起来非常棒。

答案 5 :(得分:0)

杰伊指出“在文件上设置日期时间戳的触发器”。

理论上,你可以拥有你的主要makefile,称之为m1,包含来自第二个名为m2的makefile的变量。 m2将包含所有预处理器标志的列表。

你的程序的制作规则取决于m2是最新的。

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

诀窍是,制作m2的规则将检测是否存在来自先前版本的差异。如果是这样,它将启用一个变量,该变量将强制“make all”和/或为主目标清理。否则,它只会更新m2上的时间戳而不会触发完全重制。

最后,正常目标(make all)的规则将从m2中的预处理程序指令中获取并根据需要应用它们。

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