# ******************************
# *       S E T T I N G S      *
# ******************************

# AspectC++ supports alternative frontends: Puma or Clang
# It can be configured the 'make FRONTEND=...'. Default is Puma.
FRONTEND ?= Puma

# PUMA root
PUMA    ?= ../Puma

# LLVM config command
LLVMCONF ?= llvm-config

# default target
TARGET  ?= linux-release

# include the PUMA configuration variables
# => set _TARGET, RELEASE, CFLAGS, CXXFLAGS, CPPFLAGS, etc.
include $(PUMA)/vars.mk

# check whether we compile for Windows: TARGET-Variable starts with 'win'
ifneq ($(filter win%,$(TARGET)),)
	WIN := yes
	EXT := .exe
endif

# libxml2 settings
LIBXML2_INC := `xml2-config --cflags` 
LIBXML2_LIB := `xml2-config --libs` 

# Check Clang version
ifeq ($(FRONTEND),Clang)
  ifeq ($(shell which $(LLVMCONF)),)
    $(error FRONTEND is 'Clang', but LLVMCONF ($(LLVMCONF)) is not found)
  endif
  LLVM_VERSION := $(shell $(LLVMCONF) --version)
  LLVM_SUPPORTED_VERSIONS := 3.4 3.6.2
  LLVM_SYMBOLS := _ZN5clang4Sema31ActOnStartCXXMemberDeclarationsEPNS_5ScopeEPNS_4DeclENS_14SourceLocationEbS5_ \
                  _ZN5clang4Sema19ActOnBaseSpecifiersEPNS_4DeclEPPNS_16CXXBaseSpecifierEj \
                  _ZN5clang4Sema33ActOnFinishCXXMemberSpecificationEPNS_5ScopeENS_14SourceLocationEPNS_4DeclES3_S3_PNS_13AttributeListE \
                  _ZN5clang4Sema25ActOnEndOfTranslationUnitEv \
                  _ZN5clang12Preprocessor3LexERNS_5TokenE
  LLVM_LIBDIR  := $(shell $(LLVMCONF) --libdir)
  ifneq ($(filter $(LLVM_SUPPORTED_VERSIONS), $(LLVM_VERSION)),)
    $(info Clang version $(LLVM_VERSION): Fine)
  else
    $(warning Warning: UNSUPPORTED Clang version $(LLVM_VERSION) used)
    LLVM_FOUND   := $(foreach SYM, $(LLVM_SYMBOLS), $(shell nm --defined-only $(LLVM_LIBDIR)/libclang*.a | grep -w -c $(SYM)))
    ifneq ($(words $(LLVM_SYMBOLS)),$(words $(filter 1, $(LLVM_FOUND))))
      $(error Incompatible Clang version '$(LLVM_VERSION)': Not all wrapped symbols defined!)
    endif
  endif
endif

# linker settings
ifneq ($(_TARGET),macosx_x86_64)
ifeq ($(SHARED),)
  LDFLAGS += -static
endif
ifeq ($(FRONTEND),Puma)
	LDFLAGS += -Wl,-Bstatic -L$(PUMA)/lib/$(TARGET) -lPuma 
ifneq ($(SHARED),)
	LDFLAGS += -Wl,-Bdynamic
endif
else # FRONTEND is Clang
	LDFLAGS += -L$(PUMA)/lib/$(TARGET) -lMiniPuma 
ifneq ($(SHARED),)
	LDFLAGS += #-Wl,-Bdynamic
endif
endif
else
ifeq ($(FRONTEND),Puma)
	LDFLAGS += -L$(PUMA)/lib/$(TARGET) -lPuma
else # FRONTEND is Clang
	LDFLAGS += -L$(PUMA)/lib/$(TARGET) -lMiniPuma
endif
endif

LDFLAGS += $(LIBXML2_LIB)
ifneq ($(FRONTEND),Puma)
ifneq ($(_TARGET),macosx_x86_64)
  LDFLAGS += -lclangRewriteFrontend -lclangRewriteCore -lclangFrontend -lclangSerialization -lclangDriver -lclangParse -lclangSema -lclangAnalysis -lclangEdit -lclangAST -lclangLex -lclangBasic -lLLVMAsmParser -lLLVMMCParser -lLLVMBitReader -lLLVMTransformUtils -lLLVMCore -lLLVMMC -lLLVMOption -lLLVMSupport
  LDFLAGS += $(patsubst %,-Xlinker --wrap=%,$(LLVM_SYMBOLS))
  LDFLAGS += `$(LLVMCONF) --ldflags`
else
# Mac OS X is special, we patch the clang libraries below.
  LDFLAGS += libclangSema.o libclangLex.o -lclangRewriteFrontend -lclangRewriteCore -lclangFrontend -lclangSerialization -lclangDriver -lclangParse -lclangAnalysis -lclangEdit -lclangAST -lclangBasic -lLLVMAsmParser -lLLVMMCParser -lLLVMBitReader -lLLVMTransformUtils -lLLVMCore -lLLVMMC -lLLVMOption -lLLVMSupport
  LDFLAGS += `$(LLVMCONF) --ldflags`
endif
ifneq ($(LLVM_VERSION),3.4)
  LDFLAGS := $(patsubst -lclangRewriteCore, -lclangRewrite, $(LDFLAGS))
  LDFLAGS += `$(LLVMCONF) --system-libs` # in 3.6 ldflags doesn't contain the system libs anymore
endif
endif

# compiler settings
ifeq ($(WIN),yes)
	RES := windres
	RCFILES := win-ac++.rc
endif

CPPFLAGS += -I$(PUMA)/extern -I$(PUMA)/include $(LIBXML2_INC)
ifeq ($(FRONTEND),Puma)
  CPPFLAGS += -DFRONTEND_PUMA
else
  FILTEROUT :=
ifeq ($(RELEASE),debug)
  # filter-out "-DNDEBUG" added by LLVMCONF, if LLVM is in built in release-mode
  FILTEROUT := -DNDEBUG
endif
  LLVM_CPPFLAGS := $(shell $(LLVMCONF) --cppflags)
  ifeq ($(WIN),yes)
    # llvm-config on Windows generates path name with '\' that mingw g++ can't handle properly
    LLVM_CPPFLAGS := $(subst \,/,$(LLVM_CPPFLAGS))
  endif
  CPPFLAGS += $(filter-out $(FILTEROUT),$(LLVM_CPPFLAGS))
  CPPFLAGS += -DFRONTEND_CLANG
  CXXFLAGS += -fno-rtti -Wno-strict-aliasing # hide annoying warning in Clang header 
  CXXFLAGS += -std=gnu++11 # Clang uses C++11 features; "c++11" does not work -> WIN32 not defined on Windows!
endif

# profiling
ifneq ($(GPROF),)
	LDFLAGS := $(LDFLAGS) -lc_p -lm_p 
	CXXFLAGS += -pg
endif

ifneq ($(PROFILING),)
	CPPFLAGS += -DPROFILING
endif

# use libacmodel
LDFLAGS  += -LACModel/lib/$(TARGET) -lacmodel 


# ******************************
# *       S O U R C E S        *
# ******************************

BINDIR := bin/$(TARGET)

#programm to compile
PROG := $(BINDIR)/ac++$(EXT)

# sources; front-end specific files for the wrong frontend are filtered out
CCSOURCES := $(wildcard *.cc)
ifeq ($(FRONTEND),Puma)
CCSOURCES := $(filter-out Clang%.cc, $(CCSOURCES))
else
CCSOURCES := $(filter-out Puma%.cc, $(CCSOURCES))
endif

OBJECTDIR := ObjFiles/$(TARGET)
OBJECTS := $(addprefix $(OBJECTDIR)/,$(CCSOURCES:.cc=.o) $(RCFILES:.rc=.o))

DEPDIR := DepFiles/$(TARGET)
DEPS := $(addprefix $(DEPDIR)/,$(CCSOURCES:.cc=.d))

DIRS := $(OBJECTDIR) $(DEPDIR) $(BINDIR)

# ******************************
# *  E X A M P L E  / T E S T  *
# ******************************

# tool selection
export AC := $(shell pwd)/$(PROG)
MAKE ?= make

# ac++ settings for tests
ACFLAGS := -k -v 9

ACEXAMPLES = coverage helloworld modules profiling singleton threads


.PHONY: $(ACEXAMPLES) 


# ******************************
# *      T A R G E T S         *
# ******************************

all: libacmodel showtarget $(PROG)
#.NOTPARALLEL: all

strip: $(PROG)
	@$(STRIP) $<

showtarget:
	@echo "---"
	@echo "Making AspectC++ for TARGET=$(TARGET)"
	@echo "  Compiler = $(CXX) $(CPPFLAGS) $(CXXFLAGS)"
	@echo "  Linker   = $(CXX) $(CXXFLAGS) <objects> $(LDFLAGS)"
	@echo "---"


clean:  testclean exampleclean
	@echo Making it clean.
	@$(MAKE) -C ACModel clean
	@rm -rf  core core.* *~ $(PROG) $(OBJECTDIR) $(DEPDIR) $(ERROR_FILE)

cleanall:	doxygen-clean clean

test: $(PROG)$(EXT) testall

example: $(PROG)$(EXT) $(addsuffix .example_make, $(ACEXAMPLES))

examplerun: example $(addsuffix .example_run, $(ACEXAMPLES))

exampleclean:
	@rm -rf examples/*-out
	@rm -rf examples/*.acp

doxygen:
	@echo "Making AspectC++ documentation with doxygen"
	@doxygen doc/doxygen.conf && echo "=> see doc/doxygen/html/index.html"

doxygen-clean:
	@rm -rf doc/doxygen

.PHONY: all test clean cleanall example examplerun exampleclean \
        showtarget doxygen doxygen-clean libacmodel



# ******************************
# *       R U L E S            *
# ******************************




$(DIRS) :
	@mkdir -p $@

libacmodel :
	@$(MAKE) -C ACModel

$(PROG): $(OBJECTS) | libacmodel
# For clang on macosx_x86_64 we create relinked objects with the patched symbols.
ifneq ($(FRONTEND),Puma)
ifeq ($(_TARGET),macosx_x86_64)
	@echo "Creating patched clang libraries ..."
	@$(LD) -r `$(LLVMCONF) --libdir`/libclangSema.a -o libclangSema.o -all_load \
	  -alias __ZN5clang4Sema31ActOnStartCXXMemberDeclarationsEPNS_5ScopeEPNS_4DeclENS_14SourceLocationEbS5_ ___real__ZN5clang4Sema31ActOnStartCXXMemberDeclarationsEPNS_5ScopeEPNS_4DeclENS_14SourceLocationEbS5_ -unexported_symbol __ZN5clang4Sema31ActOnStartCXXMemberDeclarationsEPNS_5ScopeEPNS_4DeclENS_14SourceLocationEbS5_ \
	  -alias __ZN5clang4Sema19ActOnBaseSpecifiersEPNS_4DeclEPPNS_16CXXBaseSpecifierEj ___real__ZN5clang4Sema19ActOnBaseSpecifiersEPNS_4DeclEPPNS_16CXXBaseSpecifierEj -unexported_symbol __ZN5clang4Sema19ActOnBaseSpecifiersEPNS_4DeclEPPNS_16CXXBaseSpecifierEj \
	  -alias __ZN5clang4Sema33ActOnFinishCXXMemberSpecificationEPNS_5ScopeENS_14SourceLocationEPNS_4DeclES3_S3_PNS_13AttributeListE ___real__ZN5clang4Sema33ActOnFinishCXXMemberSpecificationEPNS_5ScopeENS_14SourceLocationEPNS_4DeclES3_S3_PNS_13AttributeListE -unexported_symbol __ZN5clang4Sema33ActOnFinishCXXMemberSpecificationEPNS_5ScopeENS_14SourceLocationEPNS_4DeclES3_S3_PNS_13AttributeListE \
	  -alias __ZN5clang4Sema25ActOnEndOfTranslationUnitEv ___real__ZN5clang4Sema25ActOnEndOfTranslationUnitEv -unexported_symbol __ZN5clang4Sema25ActOnEndOfTranslationUnitEv
	@$(LD) -r `$(LLVMCONF) --libdir`/libclangLex.a -o libclangLex.o -all_load \
	  -alias __ZN5clang12Preprocessor3LexERNS_5TokenE ___real__ZN5clang12Preprocessor3LexERNS_5TokenE -unexported_symbol __ZN5clang12Preprocessor3LexERNS_5TokenE
endif
endif
	@echo "Linking $@."
	@mkdir -p $(BINDIR)
	@$(CXX) -o $@ $(CXXFLAGS) $^ $(LDFLAGS)


$(OBJECTDIR)/%.o : %.cc
	@echo Making dependency file $(DEPDIR)/$(<:%.cc=%.d).
	@mkdir -p $(DEPDIR)
	@$(CXX) $(CPPFLAGS) $(CXXFLAGS) -MM -MP -MT $(OBJECTDIR)/$(<:%.cc=%.o) -MT $(DEPDIR)/$(<:%.cc=%.d) $< > $(DEPDIR)/$(<:%.cc=%.d)
	@echo Making object file $@.
	@mkdir -p $(OBJECTDIR)
	@$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $<

# -Wold-style-cast to find classic C casts "a = (A*)b;"

ifeq ($(WIN),yes)
$(OBJECTDIR)/%.o : %.rc
	@echo Compiling Windows resource file
	@mkdir -p $(OBJECTDIR)
	@$(RES) $(filter -D%,$(CPPFLAGS)) $< $@

endif

test%: 
	$(MAKE) -C tests -s  $*

%.example_make: %
	cd examples/$< ; \
	$(AC) $(ACFLAGS) -I. -p . -d ../$<-out \
	-r ../$<.acp ; \
	cp Makefile ../$<-out/Makefile; \
	cd ../$<-out; g++ -o $< *.cc -lpthread

%.example_run:	%.example_make
	@echo ""
	@echo ---- Running $* ----
	@cd examples/$*-out; `find . -type f -perm -700`

ifneq ($(MAKECMDGOALS),clean)
-include $(DEPS)
endif