cpp-cheat/Makefile_many
2017-12-23 19:29:58 +00:00

182 lines
5.5 KiB
Plaintext

# `make help` for documentation.
-include Makefile_params
# C/CPP
# Don't use the standard names like `CC` and `CXX` to avoid `?=` getting overridden.
# by the Makefile default values.
ALL_DEPEND ?=
MYCC ?= gcc
MYCXX ?= g++
G ?= gdb3
I ?= #-I/usr/include
O ?= 0
STD ?= c11
PEDANTIC ?= -pedantic-errors
CFLAGS ?= -g$(G) -march=native -O$(O) -pthread -std=$(STD) -Wall -Wextra $(PEDANTIC) $(CFLAGS_EXTRA) #-pg
MYCXXFLAGS ?= -g$(G) -march=native -O$(O) -pthread -std=c++17 -Wall -Wextra $(PEDANTIC) $(CXXFLAGS_EXTRA) #-pg
LIBS ?= -lm -lrt $(LIBS_EXTRA) #-lGL -lGLU -lglut
# Fortran
FF ?= gfortran
FFLIBS ?= #-l:lapack/liblapack.so
FFLAGS ?= -O$(O) -std=f2003 -Wextra $(PEDANTIC) $(FFLAGS_EXTRA)
# Paths
IN_DIR ?= ./
IN_EXTS ?= .c .cpp .f
# Unlike in Makefile_one, there is no TMP_DIR because there are no .o files generated.
OUT_DIR ?= ./
OUT_EXT ?= .out
OBJECT_EXT ?= .o
TMP_EXT ?= .tmp
TMP_PREF ?= tmp.
# Basename without extension of file to run on run target.
RUN ?= main
# If exists, will be symlinked into the same directory from which the executable will be run.
# This way, the executable can suppose that this file or directory is in the current directory.
RUN_INPUT ?= input
TEST ?= test
ASSEMBLER_NOEXT ?= $(IN_DIR)$(RUN)
RUN_BNAME := $(RUN)$(OUT_EXT)
INS := $(foreach IN_EXT, $(IN_EXTS), $(wildcard $(IN_DIR)*$(IN_EXT)))
INS_NODIR := $(notdir $(INS))
OUTS_NODIR_NOEXT := $(basename $(INS_NODIR))
OUTS_NOEXT := $(addprefix $(OUT_DIR), $(OUTS_NODIR_NOEXT))
OUTS := $(addsuffix $(OUT_EXT), $(OUTS_NOEXT))
.PHONY: all asm clean debug help mkdir objdump set_objdump_flags profile set_profile_flags test $(PHONY)
all: mkdir $(OUTS)
@#TODO ignore errors if not present
@if [ -e "$(RUN_INPUT)" ] && [ ! -e "$(OUT_DIR)$(RUN_INPUT)" ]; then ln -s ../$(RUN_INPUT) "$(OUT_DIR)$(RUN_INPUT)"; fi
ifneq ($(strip $(run)),)
@echo
cd $(OUT_DIR) && ./"$(run)"
endif
$(OUT_DIR)%$(OUT_EXT): $(IN_DIR)%.c $(ALL_DEPEND)
$(MYCC) $(CFLAGS) $(PROFILE_DEFINE) $(PROFILE_FLAGS) $(I) -o "$@" "$<" $(LIBS)
$(OUT_DIR)%$(OUT_EXT): $(IN_DIR)%.cpp $(ALL_DEPEND)
$(MYCXX) $(MYCXXFLAGS) $(PROFILE_DEFINE) $(PROFILE_FLAGS) $(I) -o "$@" "$<" $(LIBS)
$(OUT_DIR)%$(OUT_EXT): $(IN_DIR)%.f $(ALL_DEPEND)
$(FF) $(FFLAGS) $(PROFILE_DEFINE) $(PROFILE_FLAGS) -o "$@" "$<" $(FFLIBS)
# Make assembly intermingled with original C code to stdout>
# TODO0: how not to rewrite the make rules?
# For bare asm: $(MYCC) $(PROFILE_DEFINE) $(PROFILE_FLAGS) $(CFLAGS) -fverbose-asm -S "$(ASSEMBLER_NOEXT)$$EXT"
asm: mkdir set_asm_flags
for EXT in $(IN_EXTS); do \
if [ -f "$(ASSEMBLER_NOEXT)$$EXT" ]; then \
case "$$EXT" in \
.c)\
$(MYCC) $(PROFILE_DEFINE) $(PROFILE_FLAGS) $(CFLAGS) -c -fverbose-asm -Wa,-adhln "$(ASSEMBLER_NOEXT)$$EXT" $(LIBS) -o $(OUT_DIR)asm.o\
;;\
.cpp)\
$(MYCXX) $(MYCXXFLAGS) $(PROFILE_DEFINE) $(PROFILE_FLAGS) -c -fverbose-asm -Wa,-adhln "$(ASSEMBLER_NOEXT)$$EXT" $(LIBS)\
;;\
.f)\
;;\
esac;\
break;\
fi;\
done
clean:
if [ ! '$(OUT_DIR)' = './' ]; then \
rm -rf '$(OUT_DIR)' ;\
else \
rm -rf *'$(OBJECT_EXT)' *'$(OUT_EXT)' *'$(TMP_EXT)' '$(TMP_PREF)'* callgrind.out.* ;\
fi
if [ -x 'clean' ]; then ./clean; fi
debug: clean set_debug_flags all
cd $(OUT_DIR) && gdb "$(RUN_BNAME)"
mkdir:
@mkdir -p "$(OUT_DIR)"
objdump: mkdir set_objdump_flags all
cd '$(OUT_DIR)' && objdump -CSr '$(RUN_BNAME)'
profile: clean set_profile_flags all run
cd '$(OUT_DIR)' && gprof -b '$(RUN_BNAME)' gmon.out | tee "$(RUN_BNAME).profile_out" | less
run: all
cd $(OUT_DIR) && ./$(RUN_BNAME)
time: all
cd $(OUT_DIR) && time -p ./$(RUN_BNAME)
set_profile_flags:
$(eval PROFILE_FLAGS := -p -pg)
$(eval PROFILE_DEFINE := -DPROFILE)
test: all
@\
if [ -x $(TEST) ]; then \
./$(TEST) '$(OUT_EXT)' ;\
else\
fail=false ;\
for t in *"$(OUT_EXT)"; do\
if ! ./"$$t"; then \
fail=true ;\
break ;\
fi ;\
done ;\
if $$fail; then \
echo "TEST FAILED: $$t" ;\
exit 1 ;\
else \
echo 'ALL TESTS PASSED' ;\
exit 0 ;\
fi ;\
fi ;\
-include Makefile_targets
help:
@echo 'Compiles C files with GCC, and C++ files with g++, Fortran files with gfortran one by one separatelly.'
@echo ''
@echo 'Executables have the same name without extension as the file that originated it:'
@echo ''
@echo ' c.c -> c'
@echo ' cpp.c -> cpp'
@echo ' fortran.f -> fortran'
@echo ''
@echo 'Therefore dont use files with the same basename without extension: e.g. `main.c` and `main.cpp`.'
@echo ''
@echo '# Most useful invocations'
@echo ''
@echo 'Build all:'
@echo ''
@echo ' make'
@echo ''
@echo 'Build and run output with the default basename:'
@echo ''
@echo ' make run'
@echo ''
@echo 'Build and run output with given basename:'
@echo ''
@echo ' make run=c'
@echo ''
@echo 'The `=` sign is *not* optional.'
@echo ''
@echo '# Targets'
@echo ''
@echo 'all ................. Build all.'
@echo 'asm [RUN=name] ...... Print generated assembly code for the file with given basename without extension.'
@echo 'clean ............... Clean built files.'
@echo 'debug ............... Run with `gdb`.'
@echo 'help ................ Print help to stdout.'
@echo 'profile ............. Run with `gprof`.'
@echo 'run[=name] .......... Run a file with the given basename withotu extension or the default not given.'
@echo 'test ................ Run `./test <output-directory> <output-basename> ...`'