#
# Makefile (C) 2006-2008, Aurélien Croc (AP²C)
#
# This project is under the GPL Licence
#
# Available MAKE options:
# * V sets to 1 for verbose mode
#
# Available variables in module.mk :
# * SUBDIRS
# * MODE = {default,debug,optimized}
# * CFLAGS
# * CXXFLAGS
# * LDFLAGS
# * DEBUG_CFLAGS
# * DEBUG_CXXFLAGS
# * OPTIMIZED_CFLAGS
# * OPTIMIZED_CXXFLAGS
# * TARGETS
# * GENERIC_TARGETS
# * PRE_GENERIC_TARGETS
# * directory1_directory2_file_ext_FLAGS
#
# Each project must be listed in the TARGETS variable. Next:
# * project1_SRC
# * project1_LIB
# * project1_CFLAGS
# * project1_LDFLAGS
# * project1_MODULES
# * project1_CXXFLAGS
# * ...
#
# This Makefile will generate:
# * project1_TARGET
# * project1_OBJ
#
#
# In the file rules.mk:
#
# You can add your own rules
#
# To add a new target file support, please add the target extension in the
# TARGET_RULES variable and define the function targetDefinition_{extension}.
# This function will be called with one parameters: the target name
# Like the examples above, use $(value $(1)_TARGET) or _OBJ, .. to access
# to useful informations
#
#
# /!\ = Please do your modifications in the module.mk file = /!\
#
# +--------------------------------------------------------------------------+
# | SUPPORTED LANGUAGES, DIRECTORIES ARCHI AND TOOLS LOCATIONS & FLAGS |
# +--------------------------------------------------------------------------+
LANGUAGES := cpp c
CC := gcc
CXX := g++
RM := rm -f
AR := ar crs
LEX := flex
YACC := bison
LINKER := $(CXX)
DEPDIR := .deps
BUILDDIR := .build
TARGETDIR := .
empty :=
space := $(empty) $(empty)
comma := ,
DEBUG_CFLAGS := -O0 -g
DEBUG_CXXFLAGS := -O0 -g
OPTIM_CFLAGS := -O2
OPTIM_CXXFLAGS := -O2
ARCHI := $(shell uname -s)
ifeq ($(ARCHI),Darwin)
PLUGIN_EXT := bundle
LIBRARY_EXT := dylib
else
PLUGIN_EXT := so
LIBRARY_EXT := so
endif
# +--------------------------------------------------------------------------+
# | DEFINITIONS VARIABLE LOADING |
# +--------------------------------------------------------------------------+
MODE := default
DEFFILE := .defs.mk
-include $(DEFFILE)
# +--------------------------------------------------------------------------+
# | SUBDIRS LOADING |
# +--------------------------------------------------------------------------+
include module.mk
ifeq ($(DEFLOADED),1)
include $(patsubst %, %/module.mk, $(_SUBDIRS))
endif # DEFLOADED == 1
ifeq ($(DEFDONE),1)
# +--------------------------------------------------------------------------+
# | COMPILATION MODE INITIALIZATION |
# +--------------------------------------------------------------------------+
ifeq ($(MAKECMDGOALS),debug)
MODE := debug
endif #MAKECMDGOALS == debug
ifeq ($(MAKECMDGOALS),optimized)
MODE := optimized
endif #MAKECMDGOALS == optimized
ifeq ($(MAKECMDGOALS),default)
MODE := default
endif #MAKECMDGOALS == default
ifeq ($(MODE),debug) # DEBUG
CFLAGS += $(DEBUG_CFLAGS)
CXXFLAGS += $(DEBUG_CXXFLAGS)
BUILDDIR := debug
TARGETDIR := debug
DEPDIR := debug
else
ifeq ($(MODE),optimized) # OPTIMIZED
CFLAGS += $(OPTIM_CFLAGS)
CXXFLAGS += $(OPTIM_CXXFLAGS)
BUILDDIR := optimized
TARGETDIR := optimized
DEPDIR := optimized
endif # MODE == optimized
endif # MODE == debug
# +--------------------------------------------------------------------------+
# | VERBOSE MODE AND INITIALIZATION |
# +--------------------------------------------------------------------------+
V :=
ifeq ($(V),1)
Q :=
else
Q := @
endif
# +--------------------------------------------------------------------------+
# | MAIN RULES AND TARGETS |
# +--------------------------------------------------------------------------+
_TARGETS := $(foreach target,$(TARGETS),$(TARGETDIR)/$(target))
_TARGETS := $(PRE_GENERIC_TARGETS) $(_TARGETS) $(GENERIC_TARGETS)
all: $(_TARGETS)
debug: $(_TARGETS)
optimized: $(_TARGETS)
# +--------------------------------------------------------------------------+
# | MACRO DEFINITIONS |
# +--------------------------------------------------------------------------+
# Function to print smart messages
printCmd = $(if $(filter $(V),1),,$(shell echo "@echo \" $(1)\""))
# Get the target variable name
targetName = $(subst .,_,$(subst /,_,$(1)))
# Specific flags definition
flags = $(value $(subst /,_,$(subst .,_,$(1)))_FLAGS)
flag = $(subst /,_,$(subst .,_,$(1)))_FLAGS
# +--------------------------------------------------------------------------+
# | LOAD AND GENERATE DEPENDENCIES FILES |
# +--------------------------------------------------------------------------+
# Get all the source files in ALLSRC
ALLSRC := $(foreach target,$(call targetName,$(TARGETS)),$(value \
$(target)_SRC))
# Get the dependencies files
DEPENDENCIES := $(foreach lang,$(LANGUAGES),$(patsubst %.$(lang), %.d, \
$(filter %.$(lang),$(ALLSRC))))
# Generate dependencies files for C++ source file
$(DEPDIR)/%.d: %.cpp
@mkdir -p $(dir $@)
@$(CXX) $(CXXFLAGS) -MM -MP -MG -MT "\$$(DEPDIR)/$(basename $<).d \
\$$(BUILDDIR)/$(basename $<).o" -MG "$<" -MF $@
# Load dependencies files
-include $(foreach dep,$(DEPENDENCIES),$(DEPDIR)/$(dep))
# +--------------------------------------------------------------------------+
# | PREVENT LOADING RULES IF DEFFILE IS NOT COMPLET |
# +--------------------------------------------------------------------------+
else
TARGETS := $(empty)
endif # DEFDONE == 1
# +--------------------------------------------------------------------------+
# | OBJECTS AND TARGETS DEFINITIONS |
# +--------------------------------------------------------------------------+
# Define target variables
defineTarget = $(call targetName,$(1))_TARGET := $(TARGETDIR)/$(1)
# Define target object variables
define defineObject
$(1)_OBJ := $(foreach obj,$(foreach lang,$(LANGUAGES),$(patsubst \
%.$(lang),%.o,$(filter %.$(lang),$(value $(1)_SRC)))), \
$(BUILDDIR)/$(obj)) \
$(foreach module,$(value \
$(1)_MODULES),$(TARGETDIR)/$(module)) \
$(foreach obj,$(patsubst %.l,%.l.o,$(filter %.l,$(value \
$(1)_SRC))),$(BUILDDIR)/$(obj)) \
$(foreach obj,$(patsubst %.y,%.yy.o,$(filter %.y,$(value \
$(1)_SRC))),$(BUILDDIR)/$(obj))
$(1)_CLEAN := $(foreach obj,$(patsubst %.y,%.yy.h,$(filter %.y,$(value \
$(1)_SRC))),$(BUILDDIR)/$(obj))
endef
# Create these definitions
$(foreach target,$(TARGETS),$(eval $(call defineTarget,$(strip $(target)))) \
$(eval $(call defineObject,$(strip $(call targetName,$(target))))))
# +--------------------------------------------------------------------------+
# | SMART MESSAGE PRINTING |
# +--------------------------------------------------------------------------+
# Smart messages
cmd_ar_a_o = AR $@
cmd_cc_o_c = CC $<
cmd_cxx_o_cpp = CXX $<
cmd_yacc_h = YACC [H] $<
cmd_yacc_cpp = YACC [CPP] $<
cmd_lex_cpp = LEX $<
cmd_link = LINK $@
cmd_ln_so_o = LINK [M] $@
cmd_rm_clean = RM *.o
cmd_rm_distclean = RM $(_TARGETS) *.d $(DEFFILE)
# +--------------------------------------------------------------------------+
# | TARGET RULES |
# +--------------------------------------------------------------------------+
TARGET_RULES := a so bundle
# Archives
define targetDefinition_a
$(value $(1)_TARGET): $(value $(1)_OBJ)
$$(call printCmd, $$(cmd_ar_a_o))
$$(Q)$$(AR) $$@ $$^
endef
-include rules.mk
# Plugins (for MacOS X)
define targetDefinition_bundle
$(value $(1)_TARGET): $(value $(1)_OBJ) $(value $(1)_LOADER)
$$(call printCmd, $$(cmd_ln_so_o))
$$(Q)$$(LINKER) $$(MODULE_FLAGS) $$(LDFLAGS) -o $$@ $$(value $(1)_OBJ) \
-bundle -bundle_loader $$(value $(1)_LOADER) \
$$(value $(1)_LIBS) $$(value $(1)_FLAGS) $$(LIBS)
endef
# Plugins and libaries (for UNIXes)
define targetDefinition_so
$(value $(1)_TARGET): $(value $(1)_OBJ)
$$(call printCmd, $$(cmd_ln_so_o))
$$(Q)$$(LINKER) $$(MODULE_FLAGS) $$(LDFLAGS) -o $$@ $$(value $(1)_OBJ) \
-rdynamic -shared $$(value $(1)_LIBS) $$(value $(1)_FLAGS) \
$$(LIBS)
endef
rulesTarget := $(foreach rules,$(TARGET_RULES),$(filter \
%.$(rules),$(TARGETS)))
$(foreach target,$(rulesTarget),$(eval $(call targetDefinition_$(subst \
.,,$(suffix $(target))),$(call targetName,$(target)))))
# +--------------------------------------------------------------------------+
# | COMPILATION RULES |
# +--------------------------------------------------------------------------+
# C Files
$(BUILDDIR)/%.o: $(BUILDDIR)/%.c
$(call printCmd, $(cmd_cc_o_c))
@mkdir -p $(dir $@)
$(Q)$(CC) $(CFLAGS) $(call flags,$<) -o $@ -c $<
$(BUILDDIR)/%.o: %.c
$(call printCmd, $(cmd_cc_o_c))
@mkdir -p $(dir $@)
$(Q)$(CC) $(CFLAGS) $(call flags,$<) -o $@ -c $<
# C++ Files
$(BUILDDIR)/%.o: $(BUILDDIR)/%.cpp
$(call printCmd, $(cmd_cxx_o_cpp))
@mkdir -p $(dir $@)
$(Q)$(CXX) $(CXXFLAGS) $(call flags,$<) -o $@ -c $<
$(BUILDDIR)/%.o: %.cpp
$(call printCmd, $(cmd_cxx_o_cpp))
@mkdir -p $(dir $@)
$(Q)$(CXX) $(CXXFLAGS) $(call flags,$<) -o $@ -c $<
# Yacc compilation
$(BUILDDIR)/%.yy.h: %.y
$(call printCmd, $(cmd_yacc_h))
@mkdir -p $(dir $@)
$(Q)$(YACC) $(call flags,$<) -d -b $(basename $(basename $@)) \
-p $(basename $(notdir $<)) $<
$(Q)rm $(basename $(basename $@)).tab.c
$(Q)mv $(basename $(basename $@)).tab.h $(basename $@).h
$(BUILDDIR)/%.yy.cpp: %.y
$(call printCmd, $(cmd_yacc_cpp))
@mkdir -p $(dir $@)
$(Q)$(YACC) $(call flags,$<) -b $(basename $(basename $@)) \
-p $(basename $(notdir $<)) -o $@ $<
# Lex compilation
$(BUILDDIR)/%.l.cpp: %.l $(BUILDDIR)/%.yy.h
$(call printCmd, $(cmd_lex_cpp))
$(Q)$(LEX) $(call flags,$<) -P$(basename $(notdir $<)) -t $< > $@
# +--------------------------------------------------------------------------+
# | CLEAN RULES |
# +--------------------------------------------------------------------------+
.PHONY: clean distclean
clean:
$(call printCmd, $(cmd_rm_clean))
$(Q)$(RM) $(foreach target,$(TARGETS),$(value $(call \
targetName,$(target))_OBJ) $(value $(target)_CLEAN))
distclean: clean
$(call printCmd, $(cmd_rm_distclean))
$(Q)$(RM) $(foreach dep,$(DEPENDENCIES),$(DEPDIR)/$(dep))
$(Q)$(RM) $(_TARGETS)
$(Q)$(RM) $(DEFFILE)
# +--------------------------------------------------------------------------+
# | GET ALL SUBDIRS TO EXPLORE |
# +--------------------------------------------------------------------------+
# Generate the defs.mk file which contains sub directories
$(DEFFILE): Makefile $(patsubst %, %/module.mk, $(SUBDIRS)) module.mk
@echo -n " GEN $(DEFFILE)"
@echo "" > $@
@make -s -C ./ _depsreload
.PHONY: _depsreload
_depsreload:
@echo -n "."
@echo "DEFLOADED := 1" > $(DEFFILE)
@echo "_SUBDIRS := $(SUBDIRS)" >> $(DEFFILE)
@if [ "$(SUBDIRS)" != "$(_SUBDIRS)" ]; then make -j 1 -s -C ./ _depsreload; \
else echo "DEFDONE := 1" >> $(DEFFILE); echo ""; fi