每个模块虽然对整个应用程序至关重要,但它们可以单独使用,也可以与其他模块以任何合理的组合方式使用 – 始终暴露最具特色的API,这些API是由为特定构建选择的其他模块提供的组件的可用性产生的.
为了一个最小和完整的例子,让我们假设我们的程序有三个模块(红色,绿色和蓝色),所有可能的条件功能都通过条件编译来切换.每个模块具有两个这样的条件块,每个条件块由两个可能的邻居之一的存在来启用.这为我们提供了三种可能的单一构建(红色,绿色,蓝色),三种双重构建(青色,洋红色,黄色)和一种三重构建(白色) – 每个构建都包含一个专用的主程序(Core),构建在一组构建的特征.
期望的情况
图1显示了三个模块(mod_red.c,mod_green.c和mod_blue.c«RGB»);在相邻模块内实现三个跨模块功能区域(青色,品红色和黄色«CMY»);和三个核心(白色,具有物理依赖性«RGB»在大的,锐化的顶部和逻辑依赖性«CMY»在小顶部).每个方向(六个中的一个)表示功能方面,因此CMY顶部指向主三角形表明协同作用可以提供额外的特征.
期望的Makefile应该为所有可能的构建提供配方,因此使用三个模块和七个不同核心中的每一个的四个版本.它应该足够聪明,以避免残酷的解决方案(每个配方的完整gcc命令块)并保持单独编译的优势.
没有单独的编译,问题很容易(至少对于单边依赖):主程序包括必要的源,并且依赖块由预处理器标志启用,例如,由其他模块设置的那些包括警卫.但是,通过单独编译,编译器不知道包含特定构建的模块集.
手动方法
可以使用下面列出的shell命令手动实现所需的情况.
# Single objects: gcc -c -o mod_green.o mod_green.c # Double objects gcc -c -include mod_blue.h -o mod_red+B.o mod_red.c gcc -c -include mod_red.h -o mod_blue+R.o mod_blue.c # Triple objects gcc -c -include mod_green.h -include mod_blue.h -o mod_red+G+B.o mod_red.c gcc -c -include mod_red.h -include mod_blue.h -o mod_green+R+B.o mod_green.c gcc -c -include mod_red.h -include mod_green.h -o mod_blue+R+G.o mod_blue.c # Builds gcc -o green green.c mod_green.o gcc -o magenta magenta.c mod_red+B.o mod_blue+R.o gcc -o white white.c mod_red+G+B.o mod_green+R+B.o mod_blue+R+G.o
至于期望的情况,这个例子只显示了三个代表性的版本:绿色,洋红色和白色.其他类似的形成.
经典的方法
使用经典的Makefile解决方案,Green构建保持不变,但其他两个缺少逻辑依赖性(即CMY提供的符号).之所以如此,是因为建筑过程目前(通常)定义如下:
white: white.c mod_red.o mod_green.o mod_blue.o gcc -o $@ $^ magenta: magenta.c mod_blue.o mod_red.o gcc -o $@ $^ green: green.c mod_green.o gcc -o $@ $^ %.o: %.c gcc -c -o $@ $<
这里明显暴露了问题:最后一条规则没有区分特定的构建 – 上下文丢失了.此外,我需要最终得到每个模块的不同二进制版本,以满足不同的构建.这样做的正确方法是什么?
这应该工作.虽然没试过.我认为从这一点开始制定其他规则会很容易.BINARY = build CC = gcc SOURCES_RGB = rgb.c mod_red.c mod_green.c mod_blue.c OBJECTS_RGB = $(SOURCES_RGB:.c=_rgb.o) BINARY_RGB = $(addprefix RGB-,$(BINARY)) CFLAGS_RGB = -include mod_rgb.h $(BINARY_RGB): $(OBJECTS_RGB) $(CC) -o $@ $^ %_rgb.o: %.c $(CC) -c $(CFLAGS_RGB) -o $@ $<