#!/usr/bin/env make

#####################################################################
# PARPP3D++                                                         #
# A parallel 3D Navier-Stokes-solver.                               #
#                                                                   #
# PARALLEL VERSION OF PP3D                                          #
# FROM THE FINITE ELEMENT ANALYSIS & SOLUTION PACKAGE               #
# FEATFLOW (www.featflow.de)                                        #
#                                                                   #
# WRITTEN IN C++                                                    #
#                                                                   #
# Authors of PP3D++:                                                #
#          S.Buijssen                                               #
#          Institute of Applied Mathematics & Simulation            #
#          University of Dortmund                                   #
#          D-44227 DORTMUND                                         #
#                                                                   #
# Authors of FEATFLOW:                                              #
#          S.Turek et.al.                                           #
#          Institute of Applied Mathematics & Simulation            #
#          University of Dortmund                                   #
#          D-44227 DORTMUND                                         #
#                                                                   #
# Makefile, Version 2.0                                             #
#                                                                   #
# Note:                                                             #
# change settings like compiler, optimisation flags, include paths, #
# library paths etc in Makefile.in.                                 #
#                                                                   #
#####################################################################

# Application name
APP     = parpp3d++

# Location of parpp3d++
SRCDIR  = .

# Location of Makefile.inc
MAKEFILEINC = $(SRCDIR)/Makefile.inc

# For development and debugging turn this to "NO",
# for full optimisation to "YES".
OPT     = YES

# Set build architecture
# If commented out, a guess will be taken in Makefile.in
#ID    := pc-opteron-linux-lammpi32-gnu32

include $(MAKEFILEINC)

# List of source files for application
SRC_APP = $(SRCLIST_APP)

# List of all object files
OBJ     = $(SRC_APP:%.cc=$(OBJDIR)/%.o)

# Set additional include path
INC_ADD = -I$(SRCDIR) -I$(PARLINLIB) -I$(LINLIB) -I$(FINITELIB) -I$(COMTOOL)

# List of library files
LIB     = $(BUILDLIB:%=$(OBJDIR)/lib%.a)

# Current date
DATE    = $(shell date +'%Y-%m-%d')



# default target: compile the parpp3d++ application
all:	| symlinks
all:	greeting testsettings $(APP)

# 'make debug' will compile without optimisation and with debug symbols
debug:
	@$(MAKE) OPT=NO

# Link statement for application
$(APP): | symlinks
$(APP): $(OBJDIR) $(LIB) $(OBJ)
	@echo "Linking <$(APP)> ..."
	$(LD) $(LDFLAGS) $(OBJ) $(LIBDIR) $(LIBS) -o $(APP)
	@echo
	@echo "<$(APP)> has been successfully built."
	@echo
	@echo "The invocation command depends on your MPI environment, but"
	@echo "in general will be similar to"
	@echo "  mpirun -np 4 ./$(APP)"
	@echo
	@echo "Make sure there is a configuration file named <$(APP).dat>"
	@echo "within the same directory as <$(APP)> when invoking the program."


# Create object directories
$(OBJDIR):  | verify_id
	@test -d $(OBJDIR) || (rm -f $(OBJDIR); mkdir -p $(OBJDIR))

# Create all libraries
lib:	$(LIB)

# Create own library: linear algebra
liblin:             $(OBJDIR)/liblin.a
$(OBJDIR)/liblin.a: | $(OBJDIR)
$(OBJDIR)/liblin.a: \
	$(SRC_LINLIB) $(HDRS_LINLIB)
	(cd $(LINLIB) && $(MAKE) ID="$(ID)" OPT="$(OPT)" liblin.a) || exit 1;

# Create own library: finite element method
libfinite:             $(OBJDIR)/libfinite.a
$(OBJDIR)/libfinite.a: | $(OBJDIR)
$(OBJDIR)/libfinite.a: \
	$(SRC_FINITELIB) $(HDRS_FINITELIB) $(HDRS_LINLIB) \
	$(SRCDIR)/cinput.h $(SRCDIR)/coutput.h $(SRCDIR)/Errorcodes.h \
	$(SRCDIR)/Global.h $(SRCDIR)/MyTypes.h $(SRCDIR)/typecasts.h
	(cd $(FINITELIB) && $(MAKE) ID="$(ID)" OPT="$(OPT)" libfinite.a) || exit 1;

# Create own library: parallel linear algebra
libparlin:             $(OBJDIR)/libparlin.a
$(OBJDIR)/libparlin.a: | $(OBJDIR)
$(OBJDIR)/libparlin.a: \
	$(SRC_PARLINLIB) $(HDRS_PARLINLIB) $(HDRS_COMTOOL) \
	$(HDRS_FINITELIB) $(HDRS_LINLIB) $(SRCDIR)/CCoarseGrid.h \
	$(SRCDIR)/cinput.h $(SRCDIR)/coutput.h $(SRCDIR)/Daten.h \
	$(SRCDIR)/Errorcodes.h $(SRCDIR)/FeatDomainBound.h $(SRCDIR)/Global.h \
	$(SRCDIR)/MyTypes.h $(SRCDIR)/ParGrid.h $(SRCDIR)/PostGrid.h \
	$(SRCDIR)/String.h $(SRCDIR)/Task.h $(SRCDIR)/typecasts.h
	(cd $(PARLINLIB) && $(MAKE) ID="$(ID)" OPT="$(OPT)" libparlin.a) || exit 1;

# Create own library: communication
libcom:	            $(OBJDIR)/libcom.a
$(OBJDIR)/libcom.a: | $(OBJDIR)
$(OBJDIR)/libcom.a: \
	$(SRC_COMTOOL) $(HDRS_COMTOOL) $(HDRS_LINLIB) \
	$(SRCDIR)/coutput.h $(SRCDIR)/Errorcodes.h $(SRCDIR)/Global.h \
	$(SRCDIR)/MyTypes.h $(SRCDIR)/typecasts.h
	(cd $(COMTOOL) && $(MAKE) ID="$(ID)" OPT="$(OPT)" libcom.a) || exit 1;

# Create 3rd party library: metis
libmetis:             $(OBJDIR)/libmetis.a
$(OBJDIR)/libmetis.a: | $(OBJDIR)
	@echo "# Building metis library"
	(cd $(METIS) && \
	    $(MAKE) libmetis.a CC="$(CC)" COPTIONS="$(CFLAGSC)" \
		AR="$(AR)" RANLIB="$(RANLIB)")
	cp -p $(METIS)/libmetis.a $(OBJDIR)
	(cd $(METIS) && $(MAKE) clean)

# Create 3rd party library: party
libparty:             $(OBJDIR)/libparty.a
$(OBJDIR)/libparty.a: | $(OBJDIR)
	@echo "# Building party library"
	(cd $(PARTY) && \
	    $(MAKE) libparty.a CC="$(CC)" COPTIONS="$(CFLAGSC)" \
		AR="$(AR)" RANLIB="$(RANLIB)")
	cp -p $(PARTY)/libparty.a $(OBJDIR)
	(cd $(PARTY) && $(MAKE) clean)

# Clean all object files for current architecture
clean:	clean_app clean_lib
	-rm $(APP)
	-rmdir $(OBJDIR)

# Clean all parpp3d++ object files for current architecture
clean_app:
	-rm -f $(APPNAME) $(OBJ)
ifneq ($(findstring -pgi,$(ID)),)
	-rm -f $(SRC_APP:%.cc=$(OBJDIR)/%.ti)
endif
	@for dir in $(LIBDIRS); do \
	    echo making $@ in $$dir; \
		(cd $$dir && $(MAKE) clean) \
	done
	-rmdir $(OBJDIR)

# Clean all external libraries for current architecture
clean_lib:
	@for dir in $(PARTITIONLIBDIRS); do \
	    echo making $@ in $$dir; \
		(cd $$dir && $(MAKE) clean) \
	done
	-rm -f $(OBJDIR)/libparty.a $(OBJDIR)/libmetis.a
	-rmdir $(OBJDIR)

# Clean all object files for all architectures
purge:	clean
	@for dir in $(LIBDIRS) $(PARTITIONLIBDIRS); do \
	    echo making $@ in $$dir; \
		(cd $$dir && $(MAKE) $@) \
	done
	-(cd $(dir $(OBJDIR)) && \
	    rm -f $(SRCLIST_APP:%.cc=*/%.o) $(SRCLIST_APP:%.cc=*/%.ti) \
		  */libparty.a */libmetis.a && \
	    rmdir *-*-*-*-*)

# shortcut to delete an object file in the current object directory
# (useful if you do not want to remember that directory name)
delete:
	@if test -z "$(FILE)"; then \
	    echo "ERROR: No file specified to delete in directory"; \
	    echo "       $(OBJDIR)."; \
	    echo "Usage: $(MAKE) delete FILE=<module.o>"; \
	else \
	    if test -f "$(OBJDIR)/$(FILE)"; then \
		echo "# Removing $(OBJDIR)/$(FILE)"; \
	        rm -f $(OBJDIR)/$(FILE); \
	    else \
		echo "No file found that could be removed!"; \
	    fi; \
	fi;

# Dependencies of each object file
TYPICAL_DEP_LIST = \
	CCoarseGrid.h cinput.h Const.h coutput.h Daten.h Errorcodes.h Global.h MyTypes.h \
	String.h typecasts.h

$(OBJDIR)/Bound.o: \
	Bound.cc FeatDomainBound.h ParGrid.h PostGrid.h Task.h \
	$(TYPICAL_DEP_LIST) $(HDRS_COMTOOL) $(HDRS_FINITELIB) \
	$(HDRS_LINLIB) $(HDRS_PARLINLIB) $(HDRS_PARTY)

$(OBJDIR)/BuildMatrices.o: \
	BuildMatrices.cc FeatDomainBound.h ParGrid.h PostGrid.h Task.h \
	$(TYPICAL_DEP_LIST) $(HDRS_COMTOOL) $(HDRS_FINITELIB) \
	$(HDRS_LINLIB) $(HDRS_PARLINLIB) $(HDRS_PARTY)

$(OBJDIR)/CCoarseGrid.o: \
	CCoarseGrid.cc $(TYPICAL_DEP_LIST) $(HDRS_COMTOOL) \
	$(FINITELIB)/FiniteElement_3D.h $(FINITELIB)/NonParamElement_3D.h \
	$(FINITELIB)/NumIntegration_3D.h $(FINITELIB)/RotatedElement_3D.h \
	$(FINITELIB)/SquareGrid_3D.h $(FINITELIB)/TrilinearElement_3D.h \
	$(HDRS_LINLIB) $(PARLINLIB)/MG_Info.h $(PARLINLIB)/ParCompactMatrix.h \
	$(PARLINLIB)/ParFiniteElement_3D.h $(HDRS_PARTY)

$(OBJDIR)/CProcessApp.o: \
	CProcessApp.cc CProcessApp.h FeatDomainBound.h ParGrid.h PostGrid.h \
	Task.h $(TYPICAL_DEP_LIST) $(HDRS_COMTOOL) $(HDRS_FINITELIB) \
	$(HDRS_LINLIB) $(HDRS_PARLINLIB) $(HDRS_PARTY)

$(OBJDIR)/Communicate.o: \
	Communicate.cc $(TYPICAL_DEP_LIST) $(HDRS_COMTOOL) $(HDRS_FINITELIB) \
	$(HDRS_LINLIB) $(HDRS_PARLINLIB) $(HDRS_PARTY)

$(OBJDIR)/FeatDomainBound.o: \
	FeatDomainBound.cc FeatDomainBound.h ParGrid.h PostGrid.h \
	$(TYPICAL_DEP_LIST) $(HDRS_COMTOOL) $(FINITELIB)/FiniteElement_3D.h \
	$(FINITELIB)/MultiGrid_3D.h $(FINITELIB)/NonParamElement_3D.h \
	$(FINITELIB)/NumIntegration_3D.h $(FINITELIB)/RotatedElement_3D.h \
	$(FINITELIB)/SquareGrid_3D.h $(FINITELIB)/TrilinearElement_3D.h \
	$(HDRS_LINLIB) $(HDRS_PARLINLIB) $(HDRS_PARTY)

$(OBJDIR)/OutputAVS.o: \
	OutputAVS.cc $(TYPICAL_DEP_LIST) $(HDRS_COMTOOL) $(HDRS_FINITELIB) \
	$(HDRS_LINLIB) $(HDRS_PARLINLIB) $(HDRS_PARTY)

$(OBJDIR)/OutputGMV.o: \
	OutputGMV.cc $(TYPICAL_DEP_LIST) $(HDRS_COMTOOL) $(HDRS_FINITELIB) \
	$(HDRS_LINLIB) $(HDRS_PARLINLIB) $(HDRS_PARTY)

$(OBJDIR)/ParGrid.o: \
	ParGrid.cc ParGrid.h PostGrid.h $(TYPICAL_DEP_LIST) $(HDRS_COMTOOL) \
	$(FINITELIB)/FiniteElement_3D.h $(FINITELIB)/MultiGrid_3D.h \
	$(FINITELIB)/NonParamElement_3D.h $(FINITELIB)/NumIntegration_3D.h \
	$(FINITELIB)/RotatedElement_3D.h $(FINITELIB)/SquareGrid_3D.h \
	$(FINITELIB)/TrilinearElement_3D.h $(HDRS_LINLIB) $(HDRS_PARLINLIB) \
	$(HDRS_PARTY)

$(OBJDIR)/PostGrid.o: \
	PostGrid.cc cinput.h Const.h coutput.h Daten.h Errorcodes.h Global.h \
	MyTypes.h PostGrid.h String.h typecasts.h $(COMTOOL)/blist.h \
	$(COMTOOL)/CComTool.h $(FINITELIB)/FiniteElement_3D.h \
	$(FINITELIB)/MultiGrid_3D.h $(FINITELIB)/NumIntegration_3D.h \
	$(FINITELIB)/SquareGrid_3D.h $(HDRS_LINLIB) $(PARLINLIB)/MG_Info.h \
	$(PARLINLIB)/ParCompactMatrix.h $(PARLINLIB)/ParFiniteElement_3D.h

$(OBJDIR)/PostProcess.o: \
	PostProcess.cc $(TYPICAL_DEP_LIST) $(HDRS_COMTOOL) $(HDRS_FINITELIB) \
	$(HDRS_LINLIB) $(HDRS_PARLINLIB) $(HDRS_PARTY)

$(OBJDIR)/Smoother.o: \
	Smoother.cc $(TYPICAL_DEP_LIST) $(HDRS_COMTOOL) $(HDRS_FINITELIB) \
	$(HDRS_LINLIB) $(HDRS_PARLINLIB) $(HDRS_PARTY)

$(OBJDIR)/String.o: \
	String.cc String.h

$(OBJDIR)/Task.o: \
	Task.cc $(TYPICAL_DEP_LIST) $(HDRS_COMTOOL) $(HDRS_FINITELIB) \
	$(HDRS_LINLIB) $(HDRS_PARLINLIB) $(HDRS_PARTY)

$(OBJDIR)/TimeStep.o: \
	TimeStep.cc $(TYPICAL_DEP_LIST) $(HDRS_COMTOOL) $(HDRS_FINITELIB) \
	$(HDRS_LINLIB) $(HDRS_PARLINLIB) $(HDRS_PARTY)

$(OBJDIR)/Transform_3D.o: \
	Transform_3D.cc $(TYPICAL_DEP_LIST) $(HDRS_COMTOOL) $(HDRS_FINITELIB) \
	$(HDRS_LINLIB) $(HDRS_PARLINLIB) $(HDRS_PARTY)

$(OBJDIR)/Util.o: \
	Util.cc $(TYPICAL_DEP_LIST) $(HDRS_COMTOOL) $(HDRS_FINITELIB) \
	$(HDRS_LINLIB) $(HDRS_PARLINLIB) $(HDRS_PARTY)

$(OBJDIR)/cinput.o: \
	cinput.cc cinput.h coutput.h Errorcodes.h typecasts.h

$(OBJDIR)/coutput.o: \
	coutput.cc coutput.h Errorcodes.h typecasts.h

$(OBJDIR)/typecasts.o: \
	typecasts.cc typecasts.h

# For ease of use only:
# 'make coutput.o' should be possible, regardless of the fact that
# it will be created in $(OBJDIR). Till now, only $(OBJDIR)/coutput.o
# is as target defined.
$(notdir $(OBJ)):
	@$(MAKE) $(OBJDIR)/$@

# Compile statement for each object file
$(OBJ): | symlinks $(OBJDIR)
$(OBJ):
	$(CPP) $(DEFINES) $(CFLAGSCPP) $(INC_ADD) $(INC) -c $< -o $@

greeting:
	@echo "# Detected build target id <$(ID)>."
	@echo "# Compiling <$(APP)> ..."

# Restore symbolic links (necessary after a plain CVS checkout)
symlinks:



# Test whether given ID (possibly hard-coded in top section or given
# on command line) is valid
verify_id:
ifeq (,$(findstring $(REALID), $(ID)))
	@echo;
	@echo "# Build target id $(ID) is *not* valid";
	@echo "# for current host!";
	@echo "# ../../bin/guess_id reported a host of type $(REALID).";
	@echo "# Cowardly refusing compilation.";
	@exit 1;
endif

# Test whether at least compiler and linker are defined
testsettings:
ifeq ($(CPP),)
	@echo "No C++ compiler defined.";
	@echo "Adjust Makefile.inc first for this architecture.";
	@echo;
	@exit 1;
endif
ifeq ($(CC),)
	@echo "No C Compiler defined."
	@echo "Adjust Makefile.inc first for this architecture.";
	@echo;
	@exit 1;
endif
ifeq ($(LD),)
	@echo "No linker defined.";
	@echo "Adjust Makefile.inc first for this architecture.";
	@echo;
	@exit 1;
endif

# aliases for global target .id & .idonly
# show current build target id and all compile settings applied
id:       .id

# print a list of all valid build target ids
listids:  list_ids
list.ids: list_ids
list_ids:
	@echo "The following are valid build target ids:"
	@perl -ne 'if (m/^ifeq \(\$$\(ID\),\s*(\w+-\w+-\w+-\w+-\w+)\)\s*$$/) { \
		       my $$string = $$1; \
		       if ($$string =~ m/^$(ID)/) { print "  $$string (default)\n"; } \
		       elsif ($$string =~ m/^$(LIBID)/) { print "  $$string\n"; } \
		   }' \
	    $(MAKEFILEINC) | sort;

# print a list of all valid build target ids
listallids:   list_all_ids
list.all.ids: list_all_ids
list_all_ids:
	@echo "The following are all valid build target ids:"
	@perl -ne 'if (m/^ifeq \(\$$\(ID\),\s*(\w+-\w+-\w+-\w+-\w+)\)\s*$$/) { \
		       my $$string = $$1; \
		       if ($$string =~ m/^$(ID)/) { print "  $$string (default)\n"; } \
		       else { print "  $$string\n"; } \
		   }' \
	    $(MAKEFILEINC) | sort;

# Help
help:
	@echo "Usage: make [targets...]"
	@echo ""
	@echo "where targets include:"
	@echo ""
	@echo "  help              display this help"
	@echo "  all               compile all (default)"
	@echo "  debug             compile all without optimisation, include debugging symbols"
	@echo "  lib               compile all needed libraries"
	@echo "                      (umfpack, metis. if used locally, blas & lapack, too)"
	@echo "  clean             remove all object files for current build target id"
	@echo "  clean_app         remove only $(APP)'s object files"
	@echo "  clean_libs        remove only libraries and the object files they are built from"
	@echo "  purge             remove all object files for all build targets"
	@echo ""
	@echo "Additional auxiliary targets include:"
	@echo "  id                show current build target id and all compile settings applied"
	@echo "  list_ids          print a list of valid build target ids for current host"
	@echo "  list_all_ids      print a list of all valid build target ids"
	@echo "  symlinks          restore all symbolic links"
	@echo "                      (necessary after a plain CVS checkout)"
	@echo "  <library>         compile only <library>, where <library> is one of"
	@echo "                      'metis', 'party', 'finite', 'lin', 'com', 'parlin'"
	@echo "  <object file>     compile <object file>, where <object file> is one of"
	@echo "                      $(APP)'s object files. (The object directory can be"
	@echo "                      omitted). Those other object files <object file> depends"
	@echo "                      on will be built if necessary."
	@echo "  delete FILE=<object file>"
	@echo "                    removes <object file> in the object directory currently active;"
	@echo "                    no need to remember or know the name of that directory"
	@echo ""
	@echo "Targets for creating distributable packages:"
	@echo "  dist              create a compressed tar ball containing minimal $(APP)"
	@echo "  tar.gz            create a compressed tar ball of current $(APP) installation"
	@echo "                      difference to 'dist':"
	@echo "                      include all additional source, header and configuration files"

# Create distribution
dist:	DIST = $(shell echo $(APP).$(DATE).dist.tar.gz)
dist:
	@echo "Creating $(APP) distribution";
	@tar -czf $(DIST) \
	    $(SRC_APP)       $(HEADERS_APP) \
	    $(SRC_COMTOOL)   $(HDRS_COMTOOL)   $(COMTOOL)/Makefile \
	    $(SRC_FINITELIB) $(HDRS_FINITELIB) $(FINITELIB)/Makefile \
	    $(SRC_LINLIB)    $(HDRS_LINLIB)    $(LINLIB)/Makefile \
	    $(SRC_PARLINLIB) $(HDRS_PARLINLIB) $(PARLINLIB)/Makefile\
	    $(dir $(PARTY)) $(dir $(METIS)) \
	    Grids/ samples parpp3d++.dat \
	    HISTORY guess.id Makefile Makefile.inc;
	@echo $(DIST) "created";

# Create package
tar.gz:	PACKAGE = $(shell echo $(APP).$(DATE).tar)
tar.gz:
	@echo "Creating $(APP) package (including all .cc/.h files)"
	@tar -cf $(PACKAGE) \
	    `find . -name "*.cc" -o -name "*.c" -o -name "*.h" -o \
	  	    -name "Makefile*" -o -name "*.dat" -o -name "Grids/" | \
	     egrep -v "(/CVS/|/ver/)" | sort`;
	@tar -rf $(PACKAGE) ./Grids ./guess_id;
	@gzip $(PACKAGE);
	@echo $(PACKAGE)".gz created";



# For explanation of phony targets see
# http://www.gnu.org/software/make/manual/html_mono/make.html#SEC41
.PHONY: .id .idonly id idonly list.all.ids list.ids list_all_ids \
        list_ids listallids listids verify_id delete

.PHONY: $(APP) $(notdir $(OBJ)) all clean clean_app clean_lib debug dist \
	greeting lib libcom libfinite liblin libmetis libparlin libparty purge \
	tar.gz testsettings


