.PHONY: debug
.PHONY: all all_generator all_modules all_cpp_modules all_java_modules all_java_modules all_libraries all_main all_utils
.PHONY: clean clean_generator clean_modules clean_cpp_modules clean_java_modules clean_libraries clean_main clean_utils
.PHONY: cpp java test


#---------------------------------------------------------------------------
# Settings

# General

MAKEFLAGS += --no-builtin-rules --no-builtin-variables

BIN := ../bin
MOD_CPP := modules/cpp
MOD_JAVA := modules/java

#--------------
# C++ specific

CC := g++
CC_OPTS := -Wall -Iinclude
LD := g++
LD_OPTS :=

ifeq ($(MAKECMDGOALS),debug)
CC_OPTS += -O0 -g
export BUILD_CC_OPTS := -O0 -g
LD_OPTS += -g
else
CC_OPTS += -O3
export BUILD_CC_OPTS := -O3
LD_OPTS +=
endif

#---------------
# Java specific

# A module directory, where all the modules reside.
JAVA_MOD_DIR := $(MOD_JAVA)

# An output directory, where all the compiled classes are placed.
JAVA_OUTPUT_DIR := $(BIN)/javapart

# A template directory, containing the template sources and other non-module classes.
JAVA_TEMPLATE_DIR := templates/java

# Redistributable jar files included in the compilation.
JAVA_REDIST_PATH := $(MOD_JAVA)/redistributables/jars
JAVA_REDIST_BASES := jl1.0.jar Tidy.jar xom-1.1.jar
JAVA_REDIST_TARGET := $(addprefix $(JAVA_OUTPUT_DIR)/jars/, $(JAVA_REDIST_BASES))
JAVA_REDIST_CLASS_PATH := $(JAVA_REDIST_PATH)/jl1.0.jar:$(JAVA_REDIST_PATH)/Tidy.jar:$(JAVA_REDIST_PATH)/xom-1.1.jar

# Classpath is set because the program uses some redistributable packages there.
# Also JAVA_OUTPUT_DIR is included in the classpath, so that compiled classes
# can find their dependencies there, since output directory is redirected.
JFLAGS := -g -classpath .:$(JAVA_REDIST_CLASS_PATH):$(JAVA_OUTPUT_DIR)/common
JC := javac

# Since javac requires the output directory to exist, it is unfortunately
# necessary to check its existence before every java compilation, and create
# it if it is not there.
CREATE_JAVA_OUTPUT_DIR := mkdir -p $(JAVA_OUTPUT_DIR)


#---------------------------------------------------------------------------
# Common targets

all: all_generator all_modules all_libraries all_main all_utils
all_modules: all_cpp_modules all_java_modules 
clean: clean_generator clean_modules clean_libraries clean_main clean_utils
clean_modules: clean_cpp_modules clean_java_modules

cpp: all_generator all_cpp_modules
java: all_generator all_java_modules
debug: all


#---------------------------------------------------------------------------
# C++ shared libraries

#------
# SFMT

# Note that SFMT requires special compilation options.

SHR_SFMT_BASES := thirdparty/SFMT/SFMT
SHR_SFMT_OBJS := $(addsuffix .o, $(SHR_SFMT_BASES))
SHR_SFMT_DEPS := $(addsuffix .dep, $(SHR_SFMT_BASES))

SHR_SFMT_CC_OPTS := -finline-functions -fomit-frame-pointer -fno-strict-aliasing -msse2 -DHAVE_SSE2 -DMEXP=19937 -DNDEBUG 

# Objects

$(SHR_SFMT_OBJS): %.o: %.c
	$(CC) $(CC_OPTS) $(SHR_SFMT_CC_OPTS) -c -o $@ $<

#--------
# Common

# Parsers

# Note that the pattern rule syntax must be used to
# guarantee that multiple targets are generated
# correctly by a single command. 

common/%_tokenizer.cpp: common/%.l include/%_parser.h
	flex --prefix=$*_ --outfile=$@ $<

common/%_parser.cpp include/%_parser.h: common/%.y
	bison --name-prefix=$*_ --defines=include/$*_parser.h --output=common/$*_parser.cpp $< 	 

# Objects

SHR_BASES := config config_parser config_tokenizer error
SHR_BASES := $(addprefix common/, $(SHR_BASES))
SHR_OBJS := $(addsuffix .o, $(SHR_BASES))
SHR_DEPS := $(addsuffix .dep, $(SHR_BASES))

# C++ Specific Objects
# Note that common.cpp, even though it contains base module classes, cannot go
# under modules/cpp, because its symbols would get renamed. 

CPP_SHR_BASES := common
CPP_SHR_BASES := $(addprefix common/cpp/, $(CPP_SHR_BASES))
CPP_SHR_OBJS := $(addsuffix .o, $(CPP_SHR_BASES))
CPP_SHR_DEPS := $(addsuffix .dep, $(CPP_SHR_BASES))

$(SHR_OBJS) $(CPP_SHR_OBJS): %.o: %.cpp
	$(CC) $(CC_OPTS) -c -o $@ $<

# Library

all_libraries: $(BIN)/shared.a

$(BIN)/shared.a: $(SHR_SFMT_OBJS) $(SHR_OBJS) $(CPP_SHR_OBJS)
	ar rcs $@ $^

# Dependencies

ifneq ($(MAKECMDGOALS),clean)
include $(SHR_SFMT_DEPS)
include $(SHR_DEPS)
include $(CPP_SHR_DEPS)
endif

$(SHR_SFMT_DEPS): %.dep: %.c
	$(CC) -MM $(CC_OPTS) $(SHR_SFMT_CC_OPTS) -MT "$@ $(basename $@).o" -o $@ $<

$(SHR_DEPS) $(CPP_SHR_DEPS): %.dep: %.cpp
	$(CC) -MM $(CC_OPTS) -MT "$@ $(basename $@).o" -o $@ $<
	
# Cleanup

clean_libraries:
	rm -f $(BIN)/shared.a
	rm -f $(SHR_SFMT_OBJS) $(SHR_OBJS) $(CPP_SHR_OBJS)
	rm -f $(SHR_SFMT_DEPS) $(SHR_DEPS) $(CPP_SHR_DEPS)
	rm -f include/config_parser.h
	rm -f common/config_parser.cpp
	rm -f common/config_tokenizer.cpp


#---------------------------------------------------------------------------
# Java shared libraries

#------------------
# Redistributables

# Copy the desitributable jars to the compiled classes.
$(JAVA_REDIST_TARGET): $(JAVA_OUTPUT_DIR)/jars/%.jar: $(JAVA_REDIST_PATH)/%.jar
	$(CREATE_JAVA_OUTPUT_DIR)/jars
	cp $< $@

# Set classes that can be compiled without (do not depend on) a template file
# (Worker.java) that needs completing by the program generator. These are also
# used by the modules.
JAVA_SHR_BASES := ConfigReader Module ModuleBase ModuleClassLoader ModuleTimed Rand SessionState SessionStateHolder StopBenchmarkException Synchronized 
JAVA_SHR_SRCS := $(addprefix common/java/rpg/, $(JAVA_SHR_BASES))
JAVA_SHR_SRCS := $(addsuffix .java, $(JAVA_SHR_SRCS))

# A timestamp file for serializing the creation of module class files.
JAVA_SHR_CLASSES_TS := $(JAVA_OUTPUT_DIR)/shared.ts

$(JAVA_SHR_CLASSES_TS): $(JAVA_SHR_SRCS)
	$(CREATE_JAVA_OUTPUT_DIR)/common
	$(JC) $(JFLAGS) -d $(JAVA_OUTPUT_DIR)/common/ $^
	touch $@

#----------
# Wrappers

# A library for working with the config file, needs to be compiled and added to
# the finished java program. Contains shared objects from the generator and
# additional JNI code from the template

JNI_LIB := $(JAVA_OUTPUT_DIR)/common/rpg/libConfigReader.so

JNI_LIB_BASE := $(JAVA_TEMPLATE_DIR)/rpg/ConfigReader
JNI_LIB_SRC := $(addsuffix .cpp, $(JNI_LIB_BASE))
JNI_LIB_OBJ := $(addsuffix .o, $(JNI_LIB_BASE))

JNI_CC_OPTS := $(CC_OPTS) -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/linux -fPIC -c

# SFMT has to be also included

JNI_SFMT_BASES := thirdparty/SFMT/SFMT
JNI_SFMT_OBJS := $(subst thirdparty/SFMT/, $(JAVA_TEMPLATE_DIR)/rpg/, $(addsuffix .o, $(SHR_SFMT_BASES)))

# Objects

$(JNI_SFMT_OBJS): $(JAVA_TEMPLATE_DIR)/rpg/%.o: thirdparty/SFMT/%.c
	$(CC) $(JNI_CC_OPTS) $(SHR_SFMT_CC_OPTS) -c -o $@ $<

# Objects shared by the generator and the generated program through JNI.

JNI_SHR_BASES := $(subst common/,$(JAVA_TEMPLATE_DIR)/rpg/, $(SHR_BASES))
JNI_SHR_OBJS := $(addsuffix .o, $(JNI_SHR_BASES))

$(JNI_SHR_OBJS): $(JAVA_TEMPLATE_DIR)/rpg/%.o: common/%.cpp
	$(CC) $(JNI_CC_OPTS) -o $@ $<

$(JNI_LIB_OBJ): $(JNI_LIB_SRC)
	$(CC) $(JNI_CC_OPTS) -o $@ $<

$(JNI_LIB): $(JNI_SHR_OBJS) $(JNI_LIB_OBJ) $(JNI_SFMT_OBJS)
	$(LD) $(LD_OPTS) -shared -o $@ $^


#---------------------------------------------------------------------------
# Generator

# Objects

GEN_BASES := main gener lang
GEN_BASES := $(addprefix generator/, $(GEN_BASES))
GEN_OBJS := $(addsuffix .o, $(GEN_BASES))
GEN_DEPS := $(addsuffix .dep, $(GEN_BASES))

$(GEN_OBJS): %.o: %.cpp
	$(CC) $(CC_OPTS) -c -o $@ $<

# Executable

all_generator: $(BIN)/rpg

$(BIN)/rpg: $(GEN_OBJS) $(BIN)/shared.a
	$(LD) $(LD_OPTS) -o $@ $^

# Dependencies

ifneq ($(MAKECMDGOALS),clean)
include $(GEN_DEPS)
endif

$(GEN_DEPS): %.dep: %.cpp
	$(CC) -MM $(CC_OPTS) -MT "$@ $(basename $@).o" -o $@ $<

# Cleanup

clean_generator:
	rm -f generator/*.o
	rm -f generator/*.dep
	rm -f $(BIN)/rpg


#---------------------------------------------------------------------------
# C++ modules

# To keep module dependencies in module directories, modules have
# their own makefile fragments. There are two points that connect
# the fragments with this makefile:
#
#  - List of standard sources. Sources that are compiled
#    in a standard way are added to the MOD_SRCS list,
#    this makefile will use standard rules to
#    compile them.
#
#  - List of additional objects. Sources are compiled by
#    the fragments, objects are added to the MOD_OBJS
#    list.   
#
# Both lists should use base names only.

MOD_SRCS :=
MOD_OBJS :=

# A list of modules is a list of module subdirectories.
MOD_INCS := $(wildcard $(MOD_CPP)/*/_makefile.inc)
include $(MOD_INCS)

# All files reside in the module subdirectories.
MOD_BASES_SRCS := $(addprefix $(MOD_CPP)/, $(MOD_SRCS))
MOD_BASES_OBJS := $(addprefix $(MOD_CPP)/, $(MOD_OBJS))
# All sources produce objects.
MOD_OBJS := $(addsuffix .o, $(MOD_BASES_SRCS) $(MOD_BASES_OBJS))
# Source extension detected.
MOD_BASES_SRCS_CCC := $(basename $(wildcard $(addsuffix .c, $(MOD_BASES_SRCS))))
MOD_BASES_SRCS_CPP := $(basename $(wildcard $(addsuffix .cpp, $(MOD_BASES_SRCS))))
# Helper variables.
MOD_OBJS_CCC := $(addsuffix .o, $(MOD_BASES_SRCS_CCC))
MOD_OBJS_CPP := $(addsuffix .o, $(MOD_BASES_SRCS_CPP))
MOD_DEPS_CCC := $(addsuffix .dep, $(MOD_BASES_SRCS_CCC))
MOD_DEPS_CPP := $(addsuffix .dep, $(MOD_BASES_SRCS_CPP))
MOD_DEPS := $(MOD_DEPS_CCC) $(MOD_DEPS_CPP)

# A header including all module headers.
MOD_LIST := include/modules.h

# Includes

# Internally, the list generation script uses the module headers.
# We therefore use the module headers as the target dependency.
MOD_HEAD := $(wildcard $(MOD_CPP)/*/_module.h)
$(MOD_LIST): $(MOD_HEAD)
	$(BIN)/internal-generate-includes-cpp.sh

# Objects

$(MOD_OBJS_CCC): %.o: %.c
	$(CC) $(CC_OPTS) -I$(dir $@) -x c -c -o $@ $<

$(MOD_OBJS_CPP): %.o: %.cpp
	$(CC) $(CC_OPTS) -I$(dir $@) -x c++ -c -o $@ $<

# Library

all_cpp_modules: $(BIN)/modules.a $(MOD_LIST)

$(BIN)/modules.a: $(MOD_OBJS)
	ar rcs $@ $^

# Dependencies

ifneq ($(MAKECMDGOALS),clean)
include $(MOD_DEPS)
endif

$(MOD_DEPS_CCC): %.dep: %.c
	$(CC) -MM $(CC_OPTS) -MT "$@ $(basename $@).o" -I$(dir $@) -x c -o $@ $<

$(MOD_DEPS_CPP): %.dep: %.cpp
	$(CC) -MM $(CC_OPTS) -MT "$@ $(basename $@).o" -I$(dir $@) -x c++ -o $@ $<

# Cleanup

clean_cpp_modules:
	rm -f $(BIN)/modules.a
	rm -f $(MOD_LIST)
	rm -f $(MOD_OBJS)
	rm -f $(MOD_DEPS)


#---------------------------------------------------------------------------
# Java modules

JAVA_MOD_OUTPUT_DIR := $(JAVA_OUTPUT_DIR)

# A list of all modules is collected by running several makefile parts in the
# module directory. This also takes care of compiling the modules.

JAVA_MOD_BASES :=

include $(MOD_JAVA)/rpg/modules/_makefile.inc

JAVA_MOD_SRCS := $(addsuffix .java, $(JAVA_MOD_BASES))
JAVA_MOD_SRCS := $(addprefix modules/java/, $(JAVA_MOD_SRCS))  

# A timestamp file for serializing the creation of module class files.
JAVA_MOD_CLASSES_TS := $(JAVA_MOD_OUTPUT_DIR)/modules.ts

all_java_modules: $(JAVA_SHR_CLASSES_TS) $(JAVA_MOD_CLASSES_TS) $(JNI_LIB) $(JAVA_REDIST_TARGET)

$(JAVA_MOD_CLASSES_TS): $(JAVA_MOD_SRCS) $(JAVA_SHR_CLASSES_TS)
	$(CREATE_JAVA_OUTPUT_DIR)/modules
	$(JC) $(JFLAGS) -d $(JAVA_OUTPUT_DIR)/modules $(JAVA_MOD_SRCS)
	touch $@

# Cleanup

# The deletion of the whole javapart directory in bin may be too
# drastic, may delete more than what was generated, if somebody
# added some file there after generation, but ... oh well.

clean_java_modules:
	rm -f $(JNI_LIB_OBJ)
	rm -f $(JNI_SHR_OBJS)
	rm -f $(JNI_SFMT_OBJS)
	rm -rf $(BIN)/javapart


#---------------------------------------------------------------------------
# Main template
# 
# The template is compiled with dummy macro expansions for easier tracking
# of compilation errors, resulting main.o is however not used anywhere.
#
# A full testing binary is also generated, compilation errors are displayed. 

MAIN_BASES := main
MAIN_BASES := $(addprefix templates/cpp/, $(MAIN_BASES))
MAIN_OBJS := $(addsuffix .o, $(MAIN_BASES))
MAIN_DEPS := $(addsuffix .dep, $(MAIN_BASES))

MAIN_CC_OPTS := -DTEMPLATE_TEST -Imodules/cpp/ -Iinclude/

# Objects

$(MAIN_OBJS): %.o: %.cpp
	$(CC) $(CC_OPTS) $(MAIN_CC_OPTS) -c -o $@ $<

# Dependencies

ifneq ($(MAKECMDGOALS),clean)
include $(MAIN_DEPS)
endif

$(MAIN_DEPS): %.dep: %.cpp $(MOD_LIST)
	$(CC) -MM $(CC_OPTS) $(MAIN_CC_OPTS) -MT "$@ $(basename $@).o" -o $@ $<

all_main: $(MAIN_OBJS)

# Cleanup

clean_main:
	rm -f templates/cpp/*.o
	rm -f templates/cpp/*.dep


#---------------------------------------------------------------------------
# Generate testing architecture
#
# This uses rpg to generate a testing architecture from test.conf in a
# special rpg mode which generates simple architecture where each module
# appears at least once. This is good to spot linking errors and and some
# runtime errors of all modules, in a simple way.
#
# The generated app is currently not executed by the make itself.

TEST_DIR := $(BIN)/test

$(TEST_DIR)/main: $(BIN)/rpg $(BIN)/shared.a $(BIN)/modules.a 
	@$(MAKE) clean_test
	$(BIN)/generate-architecture-cpp.sh test $(TEST_DIR)

all_test: $(TEST_DIR)/main
	
clean_test:
	rm -rf $(TEST_DIR)

test: clean_test all_test


#---------------------------------------------------------------------------
# Miscellaneous utilities

all_utils:	$(BIN)/utils/timer_test

clean_utils:
	rm -f $(BIN)/utils/timer_test

$(BIN)/utils/timer_test: utils/timer_test.cpp
	$(CC) $(CC_OPTS) -o $@ -lrt $<
