.ONESHELL:
.SUFFIXES:

# ===================================================================
# =========== First (and default) rule, builds everything ===========
# ===================================================================

.PHONY: first
first : everything

# ===================================================================
# ===== Rules to produce mangle_lapack.h and mangle_blas.h ==========
# ===================================================================

mangle_names.out: myramath/link/mangle_names.cpp
	$(CC) -o $@ $< -lstdc++
	
myramath/link/blas.h : myramath/link/blas.txt mangle_names.out
	./mangle_names.out BLAS $< $@

myramath/link/lapack.h :  myramath/link/lapack.txt mangle_names.out
	./mangle_names.out LAPACK $< $@

# ===================================================================
# ================= Rules for link_blas.out (etc) ===================
# ===================================================================

link_blas.out : myramath/link/link_blas.o
	$(CC) -o $@ $< $(LDFLAGS)

link_lapack.out : myramath/link/link_lapack.o
	$(CC) -o $@ $< $(LDFLAGS)

link_omp.out : myramath/link/link_omp.o
	$(CC) -o $@ $< $(LDFLAGS)

# ===================================================================
# ========================= Library build. ==========================
# ===================================================================

# Add new directories that hold library .cpp files here.
LIB_DIR := \
  myramath/utility \
  myramath/utility/detail \
  myramath/io \
  myramath/expression \
  myramath/dense \
  myramath/dense/detail \
  myramath/sparse \
  myramath/iterative \
  myramath/jobgraph \
  myramath/jobgraph/detail \
  myramath/pdense/detail \
  myramath/pdense \
  myramath/multifrontal/symbolic \
  myramath/multifrontal/symbolic/detail \
  myramath/multifrontal \
  myramath/multifrontal/detail \
  myramath/multifrontal/detail/llt \
  myramath/multifrontal/detail/lu \
  myramath/multifrontal/rcholesky \
  myramath/multifrontal/zcholesky \
  myramath/multifrontal/rldlt \
  myramath/multifrontal/zldlh \
  myramath/multifrontal/zldlt \
  myramath/multifrontal/lu \
  myramath/multifrontal/normal \
  myramath/lowrank \
  myramath/lowrank/detail \
# --- end of list ---

# Expands lists of *.cpp into (*.o, *.d) for each of the LIB_DIR's
LIB_CPP := $(foreach dir,$(LIB_DIR),$(wildcard $(MYRAMATH_HOME)/$(dir)/*.cpp))
LIB_OBJ := $(patsubst $(MYRAMATH_HOME)/%,%,$(patsubst %.cpp,%.o,$(LIB_CPP)))
LIB_DEP := $(patsubst $(MYRAMATH_HOME)/%,%,$(patsubst %.cpp,%.d,$(LIB_CPP)))

# Makes libmyramath.so shared library.
.PHONY: lib
lib : libmyramath.so
libmyramath.so : $(LIB_OBJ)
	$(CC) -o $@ -shared $(LIB_OBJ) $(LDFLAGS)
	
# ===================================================================
# ========================= Build unit tests ========================
# ===================================================================

# Add new test-holding directories here.
TEST_DIR := \
  tests/unit \
  tests/utility \
  tests/io \
  tests/expression \
  tests/dense \
  tests/sparse \
  tests/iterative \
  tests/jobgraph \
  tests/pdense/detail \
  tests/pdense \
  tests/multifrontal/symbolic \
  tests/multifrontal/detail \
  tests/multifrontal/rcholesky \
  tests/multifrontal/zcholesky \
  tests/multifrontal/rldlt \
  tests/multifrontal/zldlh \
  tests/multifrontal/zldlt \
  tests/multifrontal/lu \
  tests/multifrontal/normal \
  tests/matlab \
  tests/lowrank \
# --- end of list ---

# Expands lists of *.cpp into (*.o, *.d) for each of the TEST_DIR's
TEST_CPP := $(foreach dir,$(TEST_DIR),$(wildcard $(MYRAMATH_HOME)/$(dir)/*.cpp))
TEST_OBJ := $(patsubst $(MYRAMATH_HOME)/%,%,$(patsubst %.cpp,%.o  ,$(TEST_CPP)))
TEST_DEP := $(patsubst $(MYRAMATH_HOME)/%,%,$(patsubst %.cpp,%.d  ,$(TEST_CPP)))

# Makes a tests executable, requires linking in all the TEST_OBJ's.
.PHONY: tests
tests : myramath_tests.out
myramath_tests.out : $(TEST_OBJ) libmyramath.so
	$(CC) -o $@ $(CPPFLAGS) $(MYRAMATH_HOME)/tests/myramath_tests.cpp $(TEST_OBJ) libmyramath.so

# ===================================================================
# ======================== Build profiles ===========================
# ===================================================================

# Add new profile-holding directories here.
PROFILE_DIR := \
  profile/dense \
  profile/pdense \
  profile/multifrontal/symbolic \
  profile/multifrontal/rcholesky \
  profile/multifrontal/zcholesky \
  profile/multifrontal/rldlt \
  profile/multifrontal/zldlh \
  profile/multifrontal/zldlt \
  profile/multifrontal/lu \
# --- end of list ---

# Expands lists of *.cpp into (*.o, *.d) for each of the PROFILE_DIR's
PROFILE_CPP := $(foreach dir,$(PROFILE_DIR),$(wildcard $(MYRAMATH_HOME)/$(dir)/*.cpp))
PROFILE_OBJ := $(patsubst $(MYRAMATH_HOME)/%,%,$(patsubst %.cpp,%.o  ,$(PROFILE_CPP)))
PROFILE_DEP := $(patsubst $(MYRAMATH_HOME)/%,%,$(patsubst %.cpp,%.d  ,$(PROFILE_CPP)))

# Makes a profiles executable, requires linking in all the PROFILE_OBJ's.
.PHONY: profiles
profiles : myramath_profile.out
myramath_profile.out : $(PROFILE_OBJ) libmyramath.so
	$(CC) -o $@ $(CPPFLAGS) $(MYRAMATH_HOME)/profile/myramath_profile.cpp $(PROFILE_OBJ) libmyramath.so

# ===================================================================
# ========================= Build tutorials =========================
# ===================================================================

# Add new tutorial-holding directories here.
TUTORIAL_DIR := \
  tutorial/dense \
  tutorial/sparse \
  tutorial/multifrontal \
  tutorial/iterative \
  tutorial/pdense \
  tutorial/expression \
  tutorial/jobgraph \
# --- end of list ---

# Expands lists of *.cpp into (*.o, *.d, *.out *.txt) for each of the TUTORIAL_DIR's
TUTORIAL_CPP := $(foreach dir,$(TUTORIAL_DIR),$(wildcard $(MYRAMATH_HOME)/$(dir)/*.cpp))
TUTORIAL_OBJ := $(patsubst $(MYRAMATH_HOME)/%,%,$(patsubst %.cpp,%.o  ,$(TUTORIAL_CPP)))
TUTORIAL_DEP := $(patsubst $(MYRAMATH_HOME)/%,%,$(patsubst %.cpp,%.d  ,$(TUTORIAL_CPP)))

# Makes a tutorial executable, requires linking in all the TUTORIAL_OBJ's.
.PHONY: tutorials
tutorials : myramath_tutorial.out
myramath_tutorial.out : $(TUTORIAL_OBJ) libmyramath.so
	$(CC) -o myramath_tutorial.out $(CPPFLAGS) $(MYRAMATH_HOME)/tutorial/myramath_tutorial.cpp $(TUTORIAL_OBJ) libmyramath.so

# ===================================================================
# ======================== General recipes ==========================
# ===================================================================

# Add $(MYRAMATH_HOME) to #include search path.
CPPFLAGS += -I$(MYRAMATH_HOME)

# Creates all directories referred to by other targets.
.PHONY : directories
directories: $(LIB_DIR) $(TEST_DIR) $(PROFILE_DIR) $(TUTORIAL_DIR)
	@mkdir -p $(LIB_DIR) $(TEST_DIR) $(PROFILE_DIR) $(TUTORIAL_DIR)

# Compiles a foo.o from foo.cpp, also captures header dependencies to foo.d 
%.o : %.cpp | directories
	$(CC) -o $@ $< $(CPPFLAGS) -MMD -c 

# Links an executable foo.out from foo.o, uses C++ compiler and links against libmyramath.so
%.out : %.o libmyramath.so | directories
	$(CC) -o $@ $< $(LDFLAGS) libmyramath.so

# Captures git commit/sha to revision.txt
revision.txt:
	git rev-parse HEAD > revision.txt

# Include all the .d dependency files we've produced.
-include $(LIB_DEP)
-include $(TEST_DEP)
-include $(PROFILE_DEP)
-include $(TUTORIAL_DEP)

# Secondary targets - prevents the deletion of these .obj's (unless deliberately clean'd)
.SECONDARY : $(TEST_OBJ) $(PROFILE_OBJ) $(TUTORIAL_OBJ)

# ===================================================================
# ====                  Web publishing recipes                   ====
# ====   (these only work in-source, when MYRAMATH_HOME := .     ====
# ===================================================================

# Runs all the tutorials and tests, capturing output to .txt files. Then runs doxygen.
.PHONY: doxygen
doxygen: myramath_tests.out
	./myramath_tests.out -doxygen -all -path=tests/doc -order=sort -out=file
	doxygen doc/Doxyfile

# Rule for packing up files for myracore.com (to publish, copy website.zip into public_html/myramath and unzip it)
.PHONY: website.zip
website.zip:
	rm -f $@
	zip $@ doc/html/* 
	zip $@ $(TUTORIAL_CPP)

# Rule for packing up source distribution.
.PHONY: myramath.zip
myramath.zip:
	rm -f $@
	zip $@ -r myramath/link -x *.d -x *.o
	zip $@ -r myramath/matlab
	zip $@ -r myramath/MYRAMATH_EXPORT.h $(LIB_DIR) -x *.d -x *.o
	zip $@ -r tests/myratest.h tests/myramath_tests.cpp $(TEST_DIR) -x *.d -x *.o
	zip $@ -r profile/myramath_profile.cpp $(PROFILE_DIR) -x *.d -x *.o
	zip $@ -r tutorial/myramath_tutorial.cpp $(TUTORIAL_DIR) -x *.d -x *.o
	zip $@ -r builds Makefile
	zip $@ doc/Doxyfile doc/DoxygenLayout.xml doc/myramath.css doc/*.dox doc/examplebits/*.html
	git rev-parse HEAD > revision.txt
	zip $@ LICENSE revision.txt

# ===================================================================
# ======================== Cleanup recipes ==========================
# ===================================================================

# Cleans up most intermediate build products.
.PHONY: clean
clean:
	-rm -f mangle_names.out
	-rm -f myramath/link/link_omp.o myramath/link/link_omp.d link_omp.out
	-rm -f myramath/link/link_blas.o myramath/link/link_blas.d link_blas.out
	-rm -f myramath/link/link_lapack.o myramath/link/link_lapack.d link_lapack.out
	-rm -f $(LIB_OBJ) $(LIB_DEP) libmyramath.so
	-rm -f $(TEST_OBJ) $(TEST_DEP) myramath_tests.out
	-rm -f $(PROFILE_OBJ) $(PROFILE_DEP) myramath_profile.out
	-rm -f $(TUTORIAL_OBJ) $(TUTORIAL_DEP) myramath_tutorial.out
	-rm -f revision.txt

# Cleans up doxygen output and test outputs files. This stuff takes a long time to produce, so 'make clean' doesn't actually target it.
.PHONY: cleandoxygen
cleandoxygen:
	-rm -rf tests/doc/*.txt
	-rm -rf tests/doc/*.dox
	-rm -rf doc/html

# Useful command, cleans up then builds *everything* (lib, tests, documentation, zipfiles). Used by build server.
.PHONY: everything
everything:
	make clean
	make cleandoxygen
	make myramath.zip	
	make lib -j12
	make tests -j12
	make profiles -j12	
	make tutorials -j12
	make doxygen
	make website.zip
