【问题标题】:Using GNU Make to build both debug and release targets at the same time使用 GNU Make 同时构建调试和发布目标
【发布时间】:2011-05-01 09:42:54
【问题描述】:

我正在开发一个中等规模的项目,其中包含几个相互依赖的库,我最近将它们转换为使用非递归 makefile 构建。我的下一个目标是同时在同一个源代码树中构建调试和发布版本(make debug;make release)。我的第一步是制作包含正确构建标志的调试和发布目标。我使用目标特定变量做到了这一点,如下所示: CXXFLAGS=-Wall -Wextra -Werror -DLINUX

CXX_DEBUG_FLAGS=-g3 -DDEBUG_ALL
CXX_RELEASE_FLAGS=-O3

.PHONY: debug 
debug: CXXFLAGS+=$(CXX_DEBUG_FLAGS) 
debug: build

.PHONY: release 
release: CXXFLAGS+=$(CXX_RELEASE_FLAGS) 
release: build

这工作正常,但您只能构建调试或发布,不能同时构建。同时,我的意思不是在同一个构建期间,我的意思是在同一个源代码树中背靠背(进行调试;进行发布)。为了做到这一点,我需要将目标文件放在调试/发布特定目录中,这样它们就不会相互覆盖,并且我需要使用“D”来破坏调试目标二进制名称。我虽然这很容易,因为我可以再次使用目标特定变量,如下所示: CXXFLAGS=-Wall -Wextra -Werror -DLINUX

CXX_DEBUG_FLAGS=-g3 -DDEBUG_ALL
CXX_RELEASE_FLAGS=-O3

.PHONY: debug 
debug: CXXFLAGS+=$(CXX_DEBUG_FLAGS) 
debug: MODULE_BLD_TYPE=D
debug: OUT_DIR=debug_obj
debug: build

.PHONY: release 
release: CXXFLAGS+=$(CXX_RELEASE_FLAGS) 
release: MODULE_BLD_TYPE:=
release: OUT_DIR=release_obj
release: build

.PHONY: build
build: TARGET_NAME=HelloWorld$(MODULE_BLD_TYPE)
build: TARGET_BUILD_DIR=$(PROJECT_ROOT_DIR)/$(OUT_DIR)
build: TARGET_BUILD_OBJS=$(addprefix $(TARGET_BUILD_DIR)/,$(SOURCES:.cpp=.o))
build: $(TARGET_NAME)

您让阅读本文的专家已经知道这是行不通的,因为您无法使用目标特定变量来创建实际目标。它们适用于我的 CXXFLAGS var,因为该变量未在目标名称中使用。

是否有使用非递归 makefile 管理调试/发布版本的设计模式和/或最佳实践?具体来说,如何构建目标文件目录路径和目标名称(基于目标构建目标)?

【问题讨论】:

标签: makefile gnu-make


【解决方案1】:

Make 最持久的问题之一是它无法一次处理多个通配符。没有真正干净的方法来做你所要求的(不诉诸递归,我认为这并不是那么糟糕)。这是一个合理的方法:

CXXFLAGS=-Wall -Wextra -Werror -DLINUX
CXX_DEBUG_FLAGS=-g3 -DDEBUG_ALL 
CXX_RELEASE_FLAGS=-O3 

.PHONY: debug  
debug: CXXFLAGS+=$(CXX_DEBUG_FLAGS)  
debug: HelloWorldD

.PHONY: release  
release: CXXFLAGS+=$(CXX_RELEASE_FLAGS)
release: HelloWorld

DEBUG_OBJECTS = $(addprefix $(PROJECT_ROOT_DIR)/debug_obj/,$(SOURCES:.cpp=.o))
RELEASE_OBJECTS = $(addprefix $(PROJECT_ROOT_DIR)/release_obj/,$(SOURCES:.cpp=.o))

HelloWorldD: $(DEBUG_OBJECTS)
HelloWorld: $(RELEASE_OBJECTS)

# And let's add three lines just to ensure that the flags will be correct in case
# someone tries to make an object without going through "debug" or "release":

CXX_BASE_FLAGS=-Wall -Wextra -Werror -DLINUX
$(DEBUG_OBJECTS): CXXFLAGS=$(CXX_BASE_FLAGS) $(CXX_DEBUG_FLAGS)
$(RELEASE_OBJECTS): CXXFLAGS=$(CXX_BASE_FLAGS) $(CXX_RELEASE_FLAGS)

【讨论】:

  • 这是一个有趣的解决方案,我会考虑一下,看看是否可以将其应用于实际问题。这就是将复杂问题归结为简单示例的问题。所以我不能马上说这是否可行,但看起来很有希望。
  • @Richard:如果它不起作用,请发布更新,我们会再试一次。
  • 我相信我有一些工作。一旦您向我确认我正在尝试的方法行不通,我就退后一步并采取了不同的方法。您对使用递归的评论提醒我,我的 makefile 是由一个更高级别的 makefile 包装的,该 makefile 会递归调用我的 makefile,所以我只是将构建目录路径的逻辑移到其中,这似乎是有效的。但是,我现在已经复杂化了我的“干净”目标,因为它必须同时清除调试和发布目标文件,但这似乎是一个更简单的问题。
【解决方案2】:

使用VPATH 使调试和发布版本使用同一组源文件。调试和发布版本可以有自己的目录,这意味着它们的目标文件将分开。

或者,使用原生支持外源构建的构建工具,例如 automake 或(呃)cmake。

如果启用自动生成选项subdir-objects(如AM_INIT_AUTOMAKE([foreign subdir-objects])),则可以编写非递归Makefile.am

【讨论】:

  • 不幸的是,其他构建工具现在不是一个选项。我已经在使用 VPATH(虽然我没有在我的示例中展示它)来查找我的源文件。但是,我不清楚这如何帮助我将目标文件放在哪里。直到运行时选择“调试”或“发布”目标时,才知道将 obj 文件放在哪个目录中。但是,我认为我有一个可行的解决方案。请参阅我之前的评论。
  • 在生成调试和发布文件的目录中创建单独的 makefile。使用 VPATH 将它们指向公共源。使用顶级 makefile 来决定基于 debugrelease 目标递归调用哪个 makefile。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-01
  • 2010-12-17
  • 2013-10-05
  • 2020-11-17
  • 1970-01-01
相关资源
最近更新 更多