Skip to content

Instantly share code, notes, and snippets.

@spott
Created March 25, 2013 04:00
Show Gist options
  • Save spott/5234842 to your computer and use it in GitHub Desktop.
Save spott/5234842 to your computer and use it in GitHub Desktop.
#####
#
# Dynamic Makefile for C++11 Projects.
# Recursively builds all files in a project and its test suite.
#
# Standard build targets are:
# - (none): all
# - all: testing
# - primary: build primary targets
# - testing: build testing targets
# - run: build and link primary targets, execute primary binary
# - test: build and link testing targets, execute testing binary
# - clean: remove objects created from primary and testing builds
# - remove remove main objects and executables from primary and testing links
# and perform a clean
#
# Build modes can be speficified on the command-line (mode=XXXX):
# - debug: debug symbols, minor optimizations, defines DEBUG
# - release: massive optimizations, defines NDEBUG
# - profile: debug and profiling symbols, safe optimizations, defines NDEBUG
# - default: set to value of DEFAULT_BUILD_MODE
#
# Variables expected to be edited (and their default values):
# - LIBRARIES
# - INCLUDES
# - DEFINES
# - WARNINGS (all extra)
# - CXXFLAGS (--std=c++11 -pipe)
# - LXXFLAGS
#
# Oher variables that can be changed (and their default values):
# - SOURCE_DIR (src)
# - OBJECT_DIR (obj)
# - PRIMARY_DIR (project)
# - TESTING_DIR (testing)
# - DEFAULT_BUILD_MODE (debug)
#
# Requires separate project and testing directories and mains:
# |- ./
# |- Makefile
# |- project/
# |- main.cpp
# |- src/
# |- ... project files and directories
# |- testing/
# |- main.cpp
# |- src/
# |- ... testing files and directories
#
#####
.PHONY: all run test primary testing \
prepare prepare_primary prepare_testing \
build build_primary build_testing \
link link_primary link_testing \
clean clean_primary clean_testing \
remove remove_primary remove_testing
.DEFAULT_GOAL: all
###
# TARGETS
###
all: testing
primary: build_primary
testing: build_testing
###
# PATHS
###
# Common
SOURCE_DIR = src
OBJECT_DIR = obj
# Primary
PRIMARY_DIR = project
PRIMARY_SOURCE_DIR = $(PRIMARY_DIR)/$(SOURCE_DIR)
PRIMARY_OBJECT_DIR = $(PRIMARY_DIR)/$(OBJECT_DIR)
PRIMARY_SOURCE_SUB_DIRS = $(shell find $(PRIMARY_SOURCE_DIR) -type d)
PRIMARY_OBJECT_SUB_DIRS = \
$(patsubst $(PRIMARY_SOURCE_DIR)%,$(PRIMARY_OBJECT_DIR)%,$(PRIMARY_SOURCE_SUB_DIRS))
# Testing
TESTING_DIR = testing
TESTING_SOURCE_DIR = $(TESTING_DIR)/$(SOURCE_DIR)
TESTING_OBJECT_DIR = $(TESTING_DIR)/$(OBJECT_DIR)
TESTING_SOURCE_SUB_DIRS = $(shell find $(TESTING_SOURCE_DIR) -type d)
TESTING_OBJECT_SUB_DIRS = \
$(patsubst $(TESTING_SOURCE_DIR)%,$(TESTING_OBJECT_DIR)%,$(TESTING_SOURCE_SUB_DIRS))
###
# FILES
###
# Primary
PRIMARY_SOURCE_FILES = $(shell find $(PRIMARY_SOURCE_DIR) -name "*.cpp")
PRIMARY_HEADER_FILES = $(shell find $(PRIMARY_SOURCE_DIR) -name "*.hpp")
PRIMARY_OBJECT_FILES = \
$(patsubst $(PRIMARY_SOURCE_DIR)/%.o,$(PRIMARY_OBJECT_DIR)/%.o,$(PRIMARY_SOURCE_FILES:.cpp=.o))
PRIMARY_SOURCE_MAIN = $(PRIMARY_DIR)/main.cpp
PRIMARY_OBJECT_MAIN = $(OBJECT_DIR)/$(patsubst %.cpp,%.o,$(PRIMARY_SOURCE_MAIN))
PRIMARY_EXEC = $(shell basename `pwd`)
# Testing
TESTING_SOURCE_FILES = $(shell find $(TESTING_SOURCE_DIR) -name "*.cpp")
TESTING_HEADER_FILES = $(shell find $(TESTING_SOURCE_DIR) -name "*.hpp")
TESTING_OBJECT_FILES = \
$(patsubst $(TESTING_SOURCE_DIR)/%.o,$(TESTING_OBJECT_DIR)/%.o,$(TESTING_SOURCE_FILES:.cpp=.o))
TESTING_SOURCE_MAIN = $(TESTING_DIR)/main.cpp
TESTING_OBJECT_MAIN = $(OBJECT_DIR)/$(patsubst %.cpp,%.o,$(TESTING_SOURCE_MAIN))
TESTING_EXEC = $(PRIMARY_EXEC)_test
###
# VARIABLES
###
# Common
LIBRARIES =
INCLUDES =
DEFINES =
WARNINGS = all extra
CXX = g++
CXXFLAGS = --std=c++11 -pipe
LXX = g++
LXXFLAGS =
# Target-specific
MODE ?= default
BUILD_MODE = $(shell echo $(MODE) | tr '[:upper:]' '[:lower:]')
DEFAULT_BUILD_MODE = debug
ifeq ($(BUILD_MODE),default)
BUILD_MODE = $(DEFAULT_BUILD_MODE)
endif
ifeq ($(BUILD_MODE),debug)
CXXFLAGS += -g -O
DEFINES += DEBUG
else
ifeq ($(BUILD_MODE),release)
CXXFLAGS += -Ofast
DEFINES += NDEBUG
else
ifeq ($(BUILD_MODE),profile)
CXXFLAGS += -g -pg -Og
LXXFLAGS += -pg
DEFINES += NDEBUG
else
$(warning Unrecognized build mode: $(BUILD_MODE))
endif
endif
endif
# Primary
PRIMARY_LIBRARIES = $(LIBRARIES)
PRIMARY_INCLUDES = $(INCLUDES)
PRIMARY_DEFINES = $(DEFINES)
PRIMARY_WARNINGS = $(WARNINGS)
PRIMARY_CXXFLAGS = $(CXXFLAGS) $(foreach d,$(PRIMARY_DEFINES),-D$d) \
$(foreach d,$(PRIMARY_WARNINGS),-W$d) $(foreach d,$(PRIMARY_INCLUDES),-I$d)
PRIMARY_LXXFLAGS =
# Testing
TESTING_LIBRARIES = $(LIBRARIES)
TESTING_INCLUDES = $(INCLUDES)
TESTING_DEFINES = $(DEFINES)
TESTING_WARNINGS = $(WARNINGS)
TESTING_CXXFLAGS = $(CXXFLAGS) $(foreach d,$(TESTING_DEFINES),-D$d) \
$(foreach d,$(TESTING_WARNINGS),-W$d) $(foreach d,$(TESTING_INCLUDES),-I$d)
TESTING_LXXFLAGS =
###
# TARGETS
###
# Common
build: build_primary build_testing
link: link_primary link_testing
clean: clean_primary clean_test
remove: remove_primary remove_test
rm -rf $(OBJECT_DIR)
# Primary
$(PRIMARY_OBJECT_DIR)/%.o: $(PRIMARY_SOURCE_DIR)/%.cpp
$(CXX) $(PRIMARY_CXXFLAGS) -c -o $@ $<
$(PRIMARY_OBJECT_MAIN): $(PRIMARY_SOURCE_MAIN)
$(CXX) $(PRIMARY_CXXFLAGS) -c -o $@ $<
prepare_primary:
@echo " --------"
@echo "| creating primary build directories..."
@echo " --------"
mkdir -p $(PRIMARY_OBJECT_SUB_DIRS) $(OBJECT_DIR)/$(PRIMARY_DIR)
declare_primary_build:
@echo " --------"
@echo "| building primary object files..."
@echo " --------"
build_primary: prepare_primary declare_primary_build $(PRIMARY_OBJECT_FILES)
link_primary: $(PRIMARY_EXEC)
$(PRIMARY_EXEC): build_primary $(PRIMARY_OBJECT_MAIN)
@echo " --------"
@echo "| linking primary executable..."
@echo " --------"
$(LXX) $(LXXFLAGS) -o $@ $(PRIMARY_OBJECT_FILES) $(PRIMARY_OBJECT_MAIN) \
$(foreach l,$(PRIMARY_LIBRARIES),-l$l)
run: link_primary
@echo " --------"
@echo "| running primary executable..."
@echo " --------"
./$(PRIMARY_EXEC)
clean_primary:
@echo " --------"
@echo "| cleaning primary build..."
@echo " --------"
rm -f $(PRIMARY_OBJECTS)
rm -rf $(PRIMARY_OBJECT_SUB_DIRS)
remove_primary: clean_primary
@echo " --------"
@echo "| removing primary executable..."
@echo " --------"
rm -f $(PRIMARY_OBJECT_MAIN) $(PRIMARY_EXEC)
rm -rf $(OBJECT_DIR)/$(PRIMARY_DIR)
# Testing
$(TESTING_OBJECT_DIR)/%.o: $(TESTING_SOURCE_DIR)/%.cpp
$(CXX) $(TESTING_CXXFLAGS) -c -o $@ $<
$(TESTING_OBJECT_MAIN): $(TESTING_SOURCE_MAIN)
$(CXX) $(TESTING_CXXFLAGS) -c -o $@ $<
prepare_testing:
@echo " --------"
@echo "| creating testing build directories..."
@echo " --------"
mkdir -p $(TESTING_OBJECT_SUB_DIRS) $(OBJECT_DIR)/$(TESTING_DIR)
declare_testing_build:
@echo " --------"
@echo "| building testing object files..."
@echo " --------"
build_testing: build_primary prepare_testing declare_testing_build $(TESTING_OBJECT_FILES)
link_testing: $(TESTING_EXEC)
$(TESTING_EXEC): build_testing $(TESTING_OBJECT_MAIN)
@echo " --------"
@echo "| linking testing executable..."
@echo " --------"
$(LXX) $(LXXFLAGS) -o $@ $(PRIMARY_OBJECT_FILES) $(TESTING_OBJECT_FILES) \
$(TESTING_OBJECT_MAIN) $(foreach l,$(TESTING_LIBRARIES),-l$l)
test: link_testing
@echo " --------"
@echo "| running testing executable..."
@echo " --------"
./$(TESTING_EXEC)
clean_test:
@echo " --------"
@echo "| cleaning testing build..."
@echo " --------"
rm -f $(TESTING_OBJECTS) $(TESTING_OBJECT_MAIN)
rm -rf $(TESTING_OBJECT_SUB_DIRS)
remove_test: clean_test
@echo " --------"
@echo "| removing testing executable..."
@echo " --------"
rm -f $(TESTING_OBJECT_MAIN) $(TESTING_EXEC)
rm -rf $(OBJECT_DIR)/$(TESTING_DIR)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment