Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added special target EXECUTABLE_PER_SOURCE and colorization features #12

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions MANUAL
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,13 @@ TARGET
SOURCES variable or any "TGT_*" variable specified in the child makefile
applies to the target named "bar".

EXECUTABLE_PER_SOURCE can be used as a special target. When this value is
used as the target, each file in SOURCES is assumed to be an independent
the sole source for a single executable using the same build settings.
Targets are automatically generated using the basename of each source file.
This allows a series of these executables to be constructed using a single
.mk file.

TARGET_DIR
----------
Specifies the directory in which *all* final target files will be placed.
Expand Down
187 changes: 137 additions & 50 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,50 @@
# instances of "$" within them need to be escaped with a second "$" to
# accomodate the double expansion that occurs when eval is invoked.

##############################
# Colorized output features
COLOR_STATE ?= ON

COLOR_TERMINAL_NONE = "\\033[0m"
COLOR_TERMINAL_BLACK = "\\033[0;30m"
COLOR_TERMINAL_GRAY = "\\033[1;30m"
COLOR_TERMINAL_RED = "\\033[0;31m"
COLOR_TERMINAL_LIGHT_RED = "\\033[1;31m"
COLOR_TERMINAL_GREEN = "\\033[0;32m"
COLOR_TERMINAL_LIGHT_GREEN = "\\033[1;32m"
COLOR_TERMINAL_YELLOW = "\\033[0;33m"
COLOR_TERMINAL_LIGHT_YELLOW = "\\033[1;33m"
COLOR_TERMINAL_BLUE = "\\033[0;34m"
COLOR_TERMINAL_LIGTH_BLUE = "\\033[1;34m"
COLOR_TERMINAL_PURPLE = "\\033[0;35m"
COLOR_TERMINAL_LIGHT_PURPLE = "\\033[1;35m"
COLOR_TERMINAL_CYAN = "\\033[0;36m"
COLOR_TERMINAL_LIGHT_CYAN = "\\033[1;36m"
COLOR_TERMINAL_WHITE = "\\033[1;37m"
COLOR_TERMINAL_LIGHT_GRAY = "\\033[0;37m"


color_text = $(call COLOR_TEXT_$(COLOR_STATE),$(1),$(2))
color_text = $(call COLOR_TEXT_$(COLOR_STATE),$(1),$(2))
COLOR_TEXT_ON = $(COLOR_TERMINAL_$(strip $(1)))$(strip $(2))$(COLOR_TERMINAL_NONE)
COLOR_TEXT_OFF = $(2)

GREEN_OK = $(call color_text, GREEN, OK)
RED_ERROR = $(call color_text, RED, ERROR)
section_title = $(call color_text, BLUE, $(1))
subsection_title = $(call color_text, CYAN, " $(1)")


toInt = $(shell echo "$(1)" | awk -F. '{print $$1+0}')
getPercentColor = $(call color_text, $(call GOOD_COLOR_TEST, $(call toInt,$(1))), $(call toInt, $(1)))

GOOD_COLOR_TEST = $(shell if [ $(1) -ge 95 ]; then echo "GREEN"; else if [ $(1) -ge 75 ]; then echo "YELLOW"; else if [ $(1) -ge 50 ]; then echo "PURPLE"; else echo "RED"; fi fi fi)
GOOD_COLOR_TEST1 = if [ $(1) -ge 95 ]; then echo "GREEN"; else if [ $(1) -ge 75 ]; then echo "YELLOW"; else if [ $(1) -ge 50 ]; then echo "PURPLE"; else echo "RED"; fi fi fi

# End of colorization features
################################


# ADD_CLEAN_RULE - Parameterized "function" that adds a new rule and phony
# target for cleaning the specified target (removing its build-generated
# files).
Expand All @@ -52,7 +96,7 @@ endef
# USE WITH EVAL
#
define ADD_OBJECT_RULE
${1}/%.o: ${2}
${1}/%.o: ${2} $${${4}_PREREQS}
${3}
endef

Expand All @@ -68,6 +112,7 @@ define ADD_TARGET_RULE
# Add a target for creating a static library.
$${TARGET_DIR}/${1}: $${${1}_OBJS}
@mkdir -p $$(dir $$@)
@echo -e $(call color_text, LIGHT_CYAN, "Linking $${TARGET_DIR}/${1}")
$$(strip $${AR} $${ARFLAGS} $$@ $${${1}_OBJS})
$${${1}_POSTMAKE}
else
Expand All @@ -89,6 +134,7 @@ define ADD_TARGET_RULE

$${TARGET_DIR}/${1}: $${${1}_OBJS} $${${1}_PREREQS}
@mkdir -p $$(dir $$@)
@echo -e $(call color_text, LIGHT_CYAN, "Linking $${TARGET_DIR}/${1}")
$$(strip $${${1}_LINKER} -o $$@ $${LDFLAGS} $${${1}_LDFLAGS} \
$${${1}_OBJS} $${LDLIBS} $${${1}_LDLIBS})
$${${1}_POSTMAKE}
Expand All @@ -108,6 +154,7 @@ endef
# COMPILE_C_CMDS - Commands for compiling C source code.
define COMPILE_C_CMDS
@mkdir -p $(dir $@)
@echo -e $(call color_text, LIGHT_GREEN, "Building $<")
$(strip ${CC} -o $@ -c -MD ${CFLAGS} ${SRC_CFLAGS} ${INCDIRS} \
${SRC_INCDIRS} ${SRC_DEFS} ${DEFS} $<)
@cp ${@:%$(suffix $@)=%.d} ${@:%$(suffix $@)=%.P}; \
Expand All @@ -120,6 +167,7 @@ endef
# COMPILE_CXX_CMDS - Commands for compiling C++ source code.
define COMPILE_CXX_CMDS
@mkdir -p $(dir $@)
@echo -e $(call color_text, LIGHT_GREEN, "Building $<")
$(strip ${CXX} -o $@ -c -MD ${CXXFLAGS} ${SRC_CXXFLAGS} ${INCDIRS} \
${SRC_INCDIRS} ${SRC_DEFS} ${DEFS} $<)
@cp ${@:%$(suffix $@)=%.d} ${@:%$(suffix $@)=%.P}; \
Expand Down Expand Up @@ -181,35 +229,31 @@ define INCLUDE_SUBMAKEFILE
# used to keep track of which target is the "current" target as we
# recursively include other submakefiles.
ifneq "$$(strip $${TARGET})" ""
# This makefile defined a new target. Target variables defined by this
# makefile apply to this new target. Initialize the target's variables.
TGT := $$(strip $${TARGET})
ALL_TGTS += $${TGT}
$${TGT}_CFLAGS := $${TGT_CFLAGS}
$${TGT}_CXXFLAGS := $${TGT_CXXFLAGS}
$${TGT}_DEFS := $${TGT_DEFS}
$${TGT}_DEPS :=
TGT_INCDIRS := $$(call QUALIFY_PATH,$${DIR},$${TGT_INCDIRS})
TGT_INCDIRS := $$(call CANONICAL_PATH,$${TGT_INCDIRS})
$${TGT}_INCDIRS := $${TGT_INCDIRS}
$${TGT}_LDFLAGS := $${TGT_LDFLAGS}
$${TGT}_LDLIBS := $${TGT_LDLIBS}
$${TGT}_LINKER := $${TGT_LINKER}
$${TGT}_OBJS :=
$${TGT}_POSTCLEAN := $${TGT_POSTCLEAN}
$${TGT}_POSTMAKE := $${TGT_POSTMAKE}
$${TGT}_PREREQS := $$(addprefix $${TARGET_DIR}/,$${TGT_PREREQS})
$${TGT}_SOURCES :=

# The special target below can be used to indicate that each of the
# supplied sources should be compiled as an executable
ifneq "$$(strip $${TARGET})" "EXECUTABLE_PER_SOURCE"
# This makefile defined a new target. Target variables defined by this
# makefile apply to this new target. Initialize the target's variables.
$$(eval $$(call INIT_TGT,$${TARGET}))
else
# This is a special variant makfile where each source is
# assumed to be a lone object for an executable.
# Init targets for each source file.
$$(foreach TGT,$$(basename $${SOURCES}),\
$$(eval $$(call INIT_TGT,$${TGT})))
endif

else
# The values defined by this makefile apply to the the "current" target
# as determined by which target is at the top of the stack.
TGT := $$(strip $$(call PEEK,$${TGT_STACK}))
$${TGT}_CFLAGS += $${TGT_CFLAGS}
$${TGT}_CXXFLAGS += $${TGT_CXXFLAGS}
$${TGT}_DEFS += $${TGT_DEFS}
TGT_INCDIRS := $$(call QUALIFY_PATH,$${DIR},$${TGT_INCDIRS})
TGT_INCDIRS := $$(call CANONICAL_PATH,$${TGT_INCDIRS})
$${TGT}_INCDIRS += $${TGT_INCDIRS}
TGT_INCDIRSa := $$(call QUALIFY_PATH,$${DIR},$${TGT_INCDIRS})
TGT_INCDIRSb := $$(call CANONICAL_PATH,$${TGT_INCDIRSa})
$${TGT}_INCDIRS := $${TGT_INCDIRSb}
$${TGT}_LDFLAGS += $${TGT_LDFLAGS}
$${TGT}_LDLIBS += $${TGT_LDLIBS}
$${TGT}_POSTCLEAN += $${TGT_POSTCLEAN}
Expand All @@ -220,27 +264,86 @@ define INCLUDE_SUBMAKEFILE
# Push the current target onto the target stack.
TGT_STACK := $$(call PUSH,$${TGT_STACK},$${TGT})

ifneq "$$(strip $${SOURCES})" ""
ifneq "$$(strip $${TARGET})" "EXECUTABLE_PER_SOURCE"
$$(eval $$(call PREP_SOURCES,$${SOURCES},$${TGT}))
else
$$(foreach SRC,$${SOURCES},\
$$(eval $$(call PREP_SOURCES,$${SRC},$$(basename $${SRC}))))
endif

ifneq "$$(strip $${SUBMAKEFILES})" ""
# This makefile has submakefiles. Recursively include them.
$$(foreach MK,$${SUBMAKEFILES},\
$$(eval $$(call INCLUDE_SUBMAKEFILE,\
$$(call CANONICAL_PATH,\
$$(call QUALIFY_PATH,$${DIR},$${MK})))))
endif

# Reset the "current" target to it's previous value.
TGT_STACK := $$(call POP,$${TGT_STACK})
TGT := $$(call PEEK,$${TGT_STACK})

# Reset the "current" directory to it's previous value.
DIR_STACK := $$(call POP,$${DIR_STACK})
DIR := $$(call PEEK,$${DIR_STACK})
endef

# INIT_TGT - This was isolated from INCLUDE_SUBMAKEFILE so it could be
# called for individual source file in the case where EXECUTABLE_PER_SOURCE
# is used at the target.
#
# USE WITH EVAL
#
define INIT_TGT
TGT := $$(strip ${1})
ALL_TGTS += $${TGT}
$${TGT}_CFLAGS := $${TGT_CFLAGS}
$${TGT}_CXXFLAGS := $${TGT_CXXFLAGS}
$${TGT}_DEFS := $${TGT_DEFS}
$${TGT}_DEPS :=
TGT_INCDIRSa := $$(call QUALIFY_PATH,$${DIR},$${TGT_INCDIRS})
TGT_INCDIRSb := $$(call CANONICAL_PATH,$${TGT_INCDIRSa})
$${TGT}_INCDIRS := $${TGT_INCDIRSb}
$${TGT}_LDFLAGS := $${TGT_LDFLAGS}
$${TGT}_LDLIBS := $${TGT_LDLIBS}
$${TGT}_LINKER := $${TGT_LINKER}
$${TGT}_OBJS :=
$${TGT}_POSTCLEAN := $${TGT_POSTCLEAN}
$${TGT}_POSTMAKE := $${TGT_POSTMAKE}
$${TGT}_PREREQS := $$(call CANONICAL_PATH,$$(addprefix $${TARGET_DIR}/,$${TGT_PREREQS}))
$${TGT}_SOURCES :=
endef

# PREP_SOURCES - This was isolated from INCLUDE_SUBMAKEFILE so it could be
# called for individual source file in the case where EXECUTABLE_PER_SOURCE
# is used at the target.
#
# USE WITH EVAL
#
define PREP_SOURCES
LOCALSOURCES := ${1}
TGT := ${2}
ifneq "$$(strip $${LOCALSOURCES})" ""
# This makefile builds one or more objects from source. Validate the
# specified sources against the supported source file types.
BAD_SRCS := $$(strip $$(filter-out $${ALL_SRC_EXTS},$${SOURCES}))
BAD_SRCS := $$(strip $$(filter-out $${ALL_SRC_EXTS},$${LOCALSOURCES}))
ifneq "$${BAD_SRCS}" ""
$$(error Unsupported source file(s) found in ${1} [$${BAD_SRCS}])
endif

# Qualify and canonicalize paths.
SOURCES := $$(call QUALIFY_PATH,$${DIR},$${SOURCES})
SOURCES := $$(call CANONICAL_PATH,$${SOURCES})
SRC_INCDIRS := $$(call QUALIFY_PATH,$${DIR},$${SRC_INCDIRS})
SRC_INCDIRS := $$(call CANONICAL_PATH,$${SRC_INCDIRS})
LOCALSOURCES := $$(call QUALIFY_PATH,$${DIR},$${LOCALSOURCES})
LOCALSOURCES := $$(call CANONICAL_PATH,$${LOCALSOURCES})
SRC_INCDIRSa := $$(call QUALIFY_PATH,$${DIR},$${SRC_INCDIRS})
SRC_INCDIRSb := $$(call CANONICAL_PATH,$${SRC_INCDIRSa})

# Save the list of source files for this target.
$${TGT}_SOURCES += $${SOURCES}
$${TGT}_SOURCES += $${LOCALSOURCES}

# Convert the source file names to their corresponding object file
# names.
OBJS := $$(addprefix $${BUILD_DIR}/$$(call CANONICAL_PATH,$${TGT})/,\
$$(addsuffix .o,$$(basename $${SOURCES})))
$$(addsuffix .o,$$(basename $${LOCALSOURCES})))

# Add the objects to the current target's list of objects, and create
# target-specific variables for the objects based on any source
Expand All @@ -251,24 +354,8 @@ define INCLUDE_SUBMAKEFILE
$${OBJS}: SRC_CXXFLAGS := $${$${TGT}_CXXFLAGS} $${SRC_CXXFLAGS}
$${OBJS}: SRC_DEFS := $$(addprefix -D,$${$${TGT}_DEFS} $${SRC_DEFS})
$${OBJS}: SRC_INCDIRS := $$(addprefix -I,\
$${$${TGT}_INCDIRS} $${SRC_INCDIRS})
$${$${TGT}_INCDIRS} $${SRC_INCDIRSb})
endif

ifneq "$$(strip $${SUBMAKEFILES})" ""
# This makefile has submakefiles. Recursively include them.
$$(foreach MK,$${SUBMAKEFILES},\
$$(eval $$(call INCLUDE_SUBMAKEFILE,\
$$(call CANONICAL_PATH,\
$$(call QUALIFY_PATH,$${DIR},$${MK})))))
endif

# Reset the "current" target to it's previous value.
TGT_STACK := $$(call POP,$${TGT_STACK})
TGT := $$(call PEEK,$${TGT_STACK})

# Reset the "current" directory to it's previous value.
DIR_STACK := $$(call POP,$${DIR_STACK})
DIR := $$(call PEEK,$${DIR_STACK})
endef

# MIN - Parameterized "function" that results in the minimum lexical value of
Expand Down Expand Up @@ -357,13 +444,13 @@ $(foreach TGT,${ALL_TGTS},\
$(foreach TGT,${ALL_TGTS},\
$(foreach EXT,${C_SRC_EXTS},\
$(eval $(call ADD_OBJECT_RULE,${BUILD_DIR}/$(call CANONICAL_PATH,${TGT}),\
${EXT},$${COMPILE_C_CMDS}))))
${EXT},$${COMPILE_C_CMDS},${TGT}))))

# Add pattern rule(s) for creating compiled object code from C++ source.
$(foreach TGT,${ALL_TGTS},\
$(foreach EXT,${CXX_SRC_EXTS},\
$(eval $(call ADD_OBJECT_RULE,${BUILD_DIR}/$(call CANONICAL_PATH,${TGT}),\
${EXT},$${COMPILE_CXX_CMDS}))))
${EXT},$${COMPILE_CXX_CMDS},${TGT}))))

# Add "clean" rules to remove all build-generated files.
.PHONY: clean
Expand Down