C 项目 - 如何管理功能列表?

C Project - How to manage a feature list?

我有多个功能可以在项目构建时启用或禁用。

当前实现使用 #define FEATURE_FOO 等声明。每当我需要做一些与特定功能相关的事情时,我都会使用 pre-processor 指令,例如 #ifdef

功能定义存储在全局 header 文件中。

这种方法有两个缺点:

  1. 它要求在每个文件中 #include 这个全局 header,在任何其他 header 之前。
  2. 我无法轻易禁用 C 文件:

这不是很好:

// file: foo.c
#include <stdio.h>

#include "main_header.h"
#ifdef FEATURE_FOO
...
#endif

因为我更喜欢这个:

// file: foo.c
#ifdef FEATURE_FOO

#include <stdio.h>
...
#endif

因此,解决此问题的另一种方法是在构建时声明我的所有功能:

gcc -DFEATURE_FOO -c %< -o %@

这里我不喜欢的是我需要手动将每个功能传递给我的编译器。

可接受的解决方法是读取包含所有功能的 features.list 文件。在我的 Makefile 中,我将拥有:

DEFINES=$(shell perl -ne 'print "-DFEATURE_ " if /(\w+)/' features.list)

%o: %c
    gcc $(DEFINES) -c %< -o $@

我能找到什么更好的选择?

您可以使用 gcc 的选项 -include myheader.h

它将 myheader.h 的内容添加到当前翻译单元源的最开头。

我的大部分项目都使用基于 GNU make 的构建过程,虽然到目前为止它与功能无关,但我使用的技术也可以帮助您。

首先,拥有一个配置文件的想法非常好,但为什么不在 make 语法和 include 中拥有它呢?

我用的是这样的

# default configuration
CC := gcc
DEBUG := 0
GCC32 := 0
USELTO := 1

# read local configuration
-include defaults.mk

您可以使用它来获得功能列表,例如在你的 defaults.mk

FEATURES := foo bar baz

然后做类似

的事情
FEATUREDEFINES := $(addprefix -DFEATURE_, $(FEATURES))

当您使用 $(eval ...) 函数时,GNU make 有更多的 黑魔法 可能——这可能是一个很好的替代方法,可以从中完全排除源文件编译取决于您的设置。我将其用于特定于平台的实现。例如,我有这个包含用于构建二进制文件的 Makefile:

P:= src
T:= csnake

csnake_SOURCES:= csnake.c utils.c game.c board.c snake.c food.c screen.c
csnake_PLATFORMSOURCES:= ticker.c
csnake_LDFLAGS:= -lm
csnake_posix_LDFLAGS:= -lcurses
csnake_dos_LDFLAGS:= -Wl,-Bstatic -lpdcurses
csnake_win32_LDFLAGS:= -static-libgcc -Wl,-Bstatic -lpdcurses \
    -Wl,-Bdynamic -lwinmm
csnake_win32_RES:= res$(PSEP)csnake.rc

$(eval $(BINRULES))

我的 P 是源代码树中的当前相对路径,T 是要构建的目标而 PSEP 只是一个包含 /\ 以便与 windows 兼容。其余部分应该是不言自明的——对于 $(T)_PLATFORMSOURCES$(BINRULES) 在相对路径 platform/$(PLATFORM)/ 中查找。它是这样工作的:

define BINRULES
BINARIES += $$(BINDIR)$$(PSEP)$(T)$$(EXE)

$(T)_SOURCES_FULL := $$(addprefix $(P)$$(PSEP),$$($(T)_SOURCES))
ifneq ($$(strip $$($(T)_PLATFORMSOURCES)),)
$(T)_SOURCES_FULL += $$(addprefix \
    $(P)$$(PSEP)platform$$(PSEP)$$(PLATFORM)$$(PSEP), \
    $$($(T)_PLATFORMSOURCES))
endif

[...] (... further rules ... )

endef

所有这些双倍美元都在那里,因为 $(eval ...) 将扩展变量——这是 $(T)$(P) 所需要的,但不是所有其他变量所需要的,因此它们受到保护额外的美元。我只是引用了决定在这里编译哪些文件的神奇部分。如果您考虑这样做,see the full example