C++ 依赖于#定义的最佳实践?
是否有一种最佳实践来支持对C/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
-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