views:

50

answers:

2

I'm working on a medium sized project which contains several libraries with interdependence's which I've recently converted over to build using a non-recursive makefile. My next goal is to enable building of both debug and release builds out of the same source tree at the same time (make debug;make release). My first step was to make debug and release targets which contained the correct build flags. I did this using target specific variables, like this: 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

This worked fine, but you could only build debug, or release, not both at the same time. And by same time, I don't mean during the same build I mean back to back in the same source tree (make debug;make release). In order to do this I need to place the object files in a debug/release specific directory so they don't overwrite each other and I need to mangle the debug target binary name with a 'D'. I though this would be easy as I could just use target specific variables again, like this: 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)

You make experts reading this already know this won't work because you can't use target specific variables to create actual targets. They worked fine for my CXXFLAGS var because the variable wasn't used in a target name.

Is there a design pattern and or best practice to managing debug/release builds using non-recursive makefiles? Specificly, how do I build the object file directory path and target name (build a target based on a target)?

+1  A: 

One of the most persistent problems with Make is its inability to handle more than one wildcard at a time. There is no really clean way to do what you ask (without resorting to recursion, which I don't think is really so bad). Here is a reasonable approach:

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)
Beta
This is an interesting solution and I'll think on it and see if I can apply it to the actual problem. That's the problem with boiling complex problems down to a simple example. So I can't say right away whether this will work or not but it looks promising.
Richard
@Richard: If it doesn't work, post an update and we'll try again.
Beta
I believe I have something working. Once you confirmed for me that what I was trying just wasn't going to work I stepped back and took a different approach. You comment about using recursion reminded me that my makefile is wrapped by a higher level makefile which does recursively call my makefile, so I just moved the logic which builds the directory path in to it which seems to be working. However, I've now complicated my 'clean' target as it has to clean both the debug and release object files, but that appears to be a much simpler problem.
Richard
A: 

Use VPATH to make debug and release builds use the same set of source files. The debug and release build can have their own directory, which means they'll have their object files separated.

Alternatively, use a build tool that supports out-of-source builds natively, like automake or (ugh) cmake.

If you enable the automake option subdir-objects (as in AM_INIT_AUTOMAKE([foreign subdir-objects])), you can write a non-recursive Makefile.am.

Jack Kelly
Unfortunately other build tools are not an option right now. I'm already using VPATH (although I didn't show it in my example) to find my source files. However, I'm not clear how this helps me with where to put my object files. Which directory to put the obj files in isn't known until run-time when a 'debug' or 'release' target is chosen. However, I think I have a solution that is working. See my previous comment.
Richard
Create separate makefiles in the directories where the debug and release files are generated. Use VPATH to point them back to the common source. Use a top-level makefile to decide which makefile to recursively invoke based on the `debug` or `release` target.
Jack Kelly