

################## FXT makefile ##########################

PROJ=fxt


include flags.mk

#### choose the FLAG combo here:
FXT_CXXFLAGS=
#FXT_CXXFLAGS += -std=c++11
FXT_CXXFLAGS += -std=c++17
FXT_CXXFLAGS += -pipe
#FXT_CXXFLAGS += -fno-exceptions
FXT_CXXFLAGS += $(OFLAGS) # uncomment for OPTIMIZATION
#FXT_CXXFLAGS += -O3 # test -O3
FXT_CXXFLAGS += $(GFLAGS) # uncomment for DEBUGGING
#FXT_CXXFLAGS += $(PFLAGS) # uncomment for PROFILING
#
FXT_CXXFLAGS += $(WFLAGS) # uncomment for WARNINGS
#FXT_CXXFLAGS += -Werror # make all warnings ERRORS
#FXT_CXXFLAGS += -fmax-errors=1 # clang chokes on this one

#FXT_CXXFLAGS += -D__NO_MATH_INLINES
FXT_CXXFLAGS += $(FXT_EXTRA_FLAGS)

#FXT_CXXFLAGS += $(CLANG_FLAGS)  ## for clang++

#FXT_CXXFLAGS += -fprofile-arcs -ftest-coverage # for gcov
#FXT_CXXFLAGS += -fno-unroll-loops # for valgrind/sgcheck

#FXT_CXXFLAGS += -Wstrict-overflow=5

# Uncomment to use AMD64 popcnt (bit-count) instruction
# (should better be auto-detected):
#FXT_CXXFLAGS += -DHAVE_AMD64_POPCNT



## set CXXFLAGS only if not supplied:
CXXFLAGS ?= $(FXT_CXXFLAGS)


#.PHONY: foo
#foo:
#	echo 'CXXFLAGS=$(CXXFLAGS)'
#	echo 'FXT_CXXFLAGS=$(FXT_CXXFLAGS)'

#CXXFLAGS += -DTIMING

## set CXX only if not supplied:
CXX ?= c++
## export CXX=/usr/bin/g++
## export CXX=/usr/bin/clang++
## export CXX=/usr/local/sage/local/bin/g++
#export CXX=/opt/gcc13/bin/g++ -Wl,-rpath -Wl,/opt/gcc13/lib64/

PPLIBS = -lm -lstdc++

# gcc:
#IFLAGS =-I. -Isrc
IFLAGS =-Isrc
# llvm/clang++ (for versions <= 2.9 define CLANG for file fxtalloca.h):
#IFLAGS = \
#-Isrc \
# -I/usr/local/lib/clang/3.4/include
# -DCLANG


#### aux: ####

#TMPDIR=/tmp
TMPDIR=./

#-------------------------------------

FXTLIB=libfxt.a
LIBS = $(FXTLIB) $(PPLIBS)

SCRIPTDIR=./scripts

FXTIDIR=./src

REPLACE=$(SCRIPTDIR)/replace

TNT=test "$$?" -eq "1" ## tricky: The Not-Trick

#-------------------------------------

.PHONY: default  ## the default: build the lib
default: lib

#-------------------------------------

include files.mk

#-------------------------------------

# 'cp -u' unknown option on apple/mac/osx/darwin
# OS detection:
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Darwin) #apple/mac/osx/darwin
	CP_U=
else #every normal cp system
	CP_U=u
endif

#-------------------------------------
DEP=depend.mk

include $(DEP)

.PHONY: dep depend  ## create depend.mk
dep depend:
	rm -f $(DEP)
	$(MAKE) $(DEP)

$(DEP):
	: '[$@]'
	: 'File by file generation to have full pathname with dependencies:'
	@for f in $(SRC); do  $(CXX) -MM -MT $${f%.cc}.o $(IFLAGS) $$f;  done > $(DEP)
#	cp $(DEP) sss-dep.mk ## to compare
#	@$(CXX) -MM $(IFLAGS) $(SRC) > $(DEP)
#	cp $(DEP) tmp-dep
#	@echo 'FIXING $(DEP), sigh ...'
#	@$(SCRIPTDIR)/fixdepend.pl '$(strip $(OBJS))' < depend.mk > depend.fixed
#	mv depend.fixed depend.mk

## compare:
##  replace -f -o depend-nn.mk '\\\\\\n' '' depend.mk
##  replace -f -o sss-dep-nn.mk '\\\\\\n' '' sss-dep.mk
##  diff -dbw depend-nn.mk sss-dep-nn.mk
##  mgdiff -g 1200x1000+0+0 -args "-dbw" depend-nn.mk sss-dep-nn.mk &

TMPDEPH=tmp-depend-h.mk
.PHONY: invdep ##x
invdep:
	: '[$@]'
	: 'List of headers and files depending on them:'
	cp $(DEP) $(TMPDEPH)
	@for f in $(SRC); do  $(CXX) -MM -MT $${f} $(IFLAGS) $$f;  done > $(TMPDEPH)
#	: 'Ignore the error messages "Do not include directly, ..." '
	@for f in $(FXTHDRS); do $(CXX) -DFXT_NO_WARN_HDR -MM -MT $${f} $(IFLAGS) $$f >> $(TMPDEPH) ;  done
	: 'Creating depend.inv (list of headers and files depending on them) ...'
	$(SCRIPTDIR)/mkinvdep.sh
	: '[$@ OK]'

.PHONY: whodep ##x list which files depend on what is given as PAT
whodep:
	@test -n "$(PAT)" || { echo "Usage:  make whodep PAT=filename" 1>&2; exit 1;}
#	$(SCRIPTDIR)/whodep.sh "$(PAT)"
	grep --color '.*$(PAT).*:' depend.inv

#-------------------------------------

%.o : %.cc
#	$(CXX) $< $(CXXFLAGS) $(IFLAGS) -E > ./pre.cc
	$(CXX) $< $(CXXFLAGS) $(IFLAGS) -c -o $@
#	rm -f ./pre.cc

.PHONY: lib  ## build the library
lib: $(FXTLIB)

.PHONY: all  ## build the library
all: lib

$(FXTLIB): $(OBJS)
	: '[$@]'
#	@echo ' ===== making FXT lib ====='
	@ar rcs $(FXTLIB) $(OBJS)
#	nm -C $(FXTLIB)
#	@echo ' ===== make FXT lib DONE. ====='


.PHONY: obj objs  ## compile *.cc --> *.o
obj objs: $(OBJS)


#### be BSD-aware (2015-July-19: breaks on FreeBSD):
#ifeq ($(strip $(BSD_INSTALL_DATA)),)
### if ( variable empty ) simply use copy (unless variable FXT_INSTALL is supplied):
FXT_INSTALL ?= cp -p$(CP_U)v
#else
### use the given mechanism:
#FXT_INSTALL = $(BSD_INSTALL_DATA)
#endif
#### end "BSD-aware"

## PREFIX is only set if not supplied
## for other locations use e.g.  "make install PREFIX=/opt/"
PREFIX ?= /usr/local/
LIBDIR=$(PREFIX)/lib
INCDIR=$(PREFIX)/include/fxt
.PHONY: install  ## install to $(PREFIX)/lib/ and $(PREFIX)/include/fxt/
install: lib
	: '[$@]'
	@echo 'PREFIX=$(PREFIX)  LIBDIR=$(LIBDIR)  INCDIR=$(INCDIR)'
	@:
	@test -d $(INCDIR)  ||  mkdir $(INCDIR)
	@$(FXT_INSTALL) $(FXTIDIR)/*.h $(INCDIR)/
	@cd src && for f in $(SHFXTSRCDIRS); do mkdir -p $(INCDIR)/$$f; done
	@cd src && for f in $(SHFXTSRCDIRS); do $(FXT_INSTALL) $$f/*.h $(INCDIR)/$$f; done
	@:
	@test -d $(LIBDIR)  ||  mkdir $(LIBDIR)
	@$(FXT_INSTALL) $(FXTLIB) $(LIBDIR)/
	: '[$@ OK]'

.PHONY: chk-install ##x print whether installed header files are up to date
chk-install:
	@(cd src  &&  find . -maxdepth 2 -name \*.h | sort) > tmp-hdr1
	@(cd $(INCDIR)/  &&  find . -name \*.h | sort) > tmp-hdr2
	@-diff tmp-hdr1 tmp-hdr2
	@rm tmp-hdr1 tmp-hdr2

.PHONY: uninstall  ## uninstall headers and lib, but leave directory $(INCDIR)
uninstall:
	: '[$@]'  ## note: we do not remove $(INCDIR)
	rm -rf $(INCDIR)/*
	rm -f $(LIBDIR)/$(FXTLIB)

.PHONY: reinstall  ## uninstall and install
reinstall: # uninstall install
	-$(MAKE) uninstall # ignore if we cannot remove the lib ...
	$(MAKE) install    # ... as long as we can overwrite it


#-------------------------------------

#ADEF=-Dstatic= -Dinline=
.PHONY: asm ##x usage: make asm ASRC=dir/file
asm:
	@test -n "$(ASRC)" ||  { echo 'USAGE:  make $@ ASRC=dir/some-file.cc'; exit 1; }
	$(CXX) $(ASRC) -fverbose-asm $(ADEF) -S -g $(CXXFLAGS) $(IFLAGS) -c -o asm.s
	echo '// -*- C++ -*-' > asm.lst
	as -alhnd asm.s >> asm.lst
	rm -f asm.s

#-------------------------------------

#GCOVno=$(OBJS:.o=.gcno)
.PHONY: rmgcov  ##x remove gcov files (from program runs)
rmgcov:
	: '[$@]'
#	ls $(GCOVno)
#	@rm -f $(GCOVno)
	rm -f $$(find . -name '*.gcov')
	rm -f $$(find . -name '*.gcda')

.PHONY: rmgcno  ##x remove gcov (graph) files (from compiling)
rmgcno:
	: '[$@]'
	rm -f $$(find . -name '*.gcno')

.PHONY: clean  ## cleanup
clean: rmgcov rmgcno
	: '[$@]'
	rm -f  a.out core .gdb_history gmon.out $(TESTBIN) $(GENBIN) $(DEMOBIN)
	-find . -name \*.o -exec rm -f {} \;
	rm -f $(CALLSRC)
	rm -f asm.lst asm.s asm.cpp
	rm -fv tmp-*

.PHONY: clobber ## cleanup incl. lib
clobber: clean
	: '[$@]'
	rm -f $(FXTLIB)
	rm -f $(SCRIPTDIR)/depend.nn depend.nn
	rm -f depend.inv
	rm -f bench-log-new.txt bench-log-eval.txt
	rm -f $(TESTLOG)

.PHONY: new  ## cleanup and recompile everything
new: clobber depend all


#-------------------------------------
#-------------------------------------

## files that depend on BITS_USE_ASM:
# export CH='*/*.h */*.cc'
# grep -l BITS_USE_ASM $CH
# make -n  $(grep -l BITS_USE_ASM $CH | (while read a; do echo "-W $a"; done))
#
# make -n test 2>&1 | g 1test
#RUNTEST=$(TESTBIN)
#NOBITASM="$(CXXFLAGS) -DDISABLE_BIT_ASM"
#.NOTPARALLEL: test
.PHONY: test  ##x run several self tests (demos are much more useful now)
test: lib
	: '[$@]'
	CXXFLAGS="$(CXXFLAGS)" $(SCRIPTDIR)/run-tests.sh

#
TESTBIN=./bin
RUNTEST=MALLOC_CHECK_=2 $(TESTBIN)
#.NOTPARALLEL: 1test
.PHONY: 1test  ## called by 'test', usage:  make 1test TESTSRC=test/test-WHAT.cc
1test:
	: '[$@]'
	@test -n "$(TESTSRC)" || { echo 'USAGE:  make $@ TESTSRC=test/some-test.cc'; exit 1; }
#	@echo '========== routines tested: =========='
#	grep -E '(CHECK)|(ECHO)|(EQUIV)' $(TESTSRC)
	$(CXX) test/aux-test.cc $(TESTSRC) $(CXXFLAGS) $(IFLAGS) $(LIBS) -o $(TESTBIN)
	$(RUNTEST)  >  /dev/null  ||  $(MAKE) failedtest
	: '[$@ OK  (with TESTSRC=$(TESTSRC))]'
	:
#	@echo '==========  OK. ($(TESTSRC)) =========='
#	@echo

TESTLOG=failed-test.log
.PHONY: failedtest  ## called if a test failed, hopefully never
failedtest:
	: '[$@]'
	@echo -e '\n\n*********** AAARGH !  something went wrong ***********'
	@echo '  repeating with logfile := $(TESTLOG)'
	$(CXX) test/aux-test.cc $(TESTSRC) -DVERBOSE_TEST $(CXXFLAGS) $(IFLAGS) $(LIBS) -o $(TESTBIN)
	-$(RUNTEST)  >  $(TESTLOG)
	grep -B5 -A9999 "FAIL" $(TESTLOG)
	grep 'FAIL'  $(TESTLOG)
	@echo -e '\n********* =--> please contact the author <--= *********'
	false # finally fail and start being depressive



.PHONY: chkdtim ##x make sure no demo has TIMING defined
chkdtim:
	: '---------- [$@] ----------'
	@if grep -E '^#define[ ]+TIMING' $(DEMOS); then false; else true; fi

DEMOS=demo/*/*.cc
.PHONY: chkdemo  ## some pedantic checks for the demos
chkdemo:
	: '---------- [$@] ----------'
	@echo "Checking Emacs-targets of demos ..."
	$(SCRIPTDIR)/chkdemo-etargets.sh
	:
	@echo "Checking demos ..."
	$(SCRIPTDIR)/chkdemo.sh
	:
	@echo "Checking includes of demos ..."
	$(SCRIPTDIR)/chkdemoincl.sh
	:
	$(MAKE) chkdtim


.PHONY: chkincl   ## some pedantic checks for correct #includes
chkincl:
	: '---------- [$@] ----------'
	: 'Doing some pedantic checks about #includes ...'
	:
	@echo "Running $(SCRIPTDIR)/chkifdef.sh ..."
	@bash $(SCRIPTDIR)/chkifdef.sh $(FXTHDRS)
	:
	@echo "Running $(SCRIPTDIR)/chkallincl.sh ..."
	@NONSRCDIRS="$(NONSRCDIRS)" FXTSRCDIRS="$(FXTSRCDIRS)" bash $(SCRIPTDIR)/chkallincl.sh
	:
	@echo "Running $(SCRIPTDIR)/chkinclex.sh ..."
	@FXTSRCDIRS="$(FXTSRCDIRS)"  bash $(SCRIPTDIR)/chkinclex.sh
	:
	@echo "Running $(SCRIPTDIR)/chkinclpath.sh ..."
	@FXTSRCDIRS="$(FXTSRCDIRS)"  bash $(SCRIPTDIR)/chkinclpath.sh
	:
#	@echo "Running $(SCRIPTDIR)/chkdirs.sh ..."
#	@NONSRCDIRS="$(NONSRCDIRS)" FXTSRCDIRS="$(FXTSRCDIRS)"  bash $(SCRIPTDIR)/chkdirs.sh
#	:
	:
	: '[$@ OK]'

.PHONY: chkascii ## list files containing non-ASCII chars (or tabs)
chkascii:
	: '---------- [$@] ----------'
	LC_ALL=C grep -l -r '[^ -~]' . | grep -vF './.git/'  ## only makefile should be listed

.PHONY: check chk  ## some pedantic checks for the code and makefile
check chk: chkcopyright chkincl chksrc
	: '---------- [$@] ----------'
	@echo 'CXX is $(CXX)'
	:
	@echo 'CXXFLAGS are $(CXXFLAGS)'
	:
	@echo 'IFLAGS are $(IFLAGS)'
	@echo 'OFLAGS are $(OFLAGS)'
	: '[$@ OK]'

.PHONY: chkopt  ##x check whether optimization is turned off
chkopt:
	: '---------- [$@] ----------'
#	@$(SCRIPTDIR)/chkopt.zsh
	@$(SCRIPTDIR)/chkopt.sh

.PHONY: xchk ## do all (non-interactive) checks
xchk:   chkopt chk doc chkdemo dep
	: '[$@ OK]'


OKFILES =
#OKFILES += 00readme.txt
OKFILES += makefile COPYING.txt
OKFILES += depend.mk files.mk flags.mk # fxt.lsm
OKFILES += depend.inv
OKFILES += INSTALL.txt
OKFILES += 00-todo.txt
OKDIRS =
#OKDIRS += data doc demo scripts src test
OKDIRS += doc demo scripts src test
.PHONY: chkxfiles ##x check whether there are spurious files
chkxfiles:
	: '---------- [$@] ----------'
	@OKFILES='$(OKFILES)' OKDIRS='$(OKDIRS)' $(SCRIPTDIR)/chkxfiles.sh


.PHONY: chksrc ## check whether all sources referenced in comments exist
chksrc:
	: '---------- [$@] ----------'
	: 'check whether all sources referenced in comments exist:'
	@echo "Running $(SCRIPTDIR)/chksrc.sh ..."
	@SRC='$(SRC)' $(SCRIPTDIR)/chksrc.sh
	: '[$@ OK]'

.PHONY: chkcopyright ## check copyright statements
chkcopyright:
	: '---------- [$@] ----------'
	: 'checking copyright statements:'
	$(SCRIPTDIR)/chk-copyright.zsh
	: '[$@ OK]'

TMP1=$(TMPDIR)/tmp-1
TMP2=$(TMPDIR)/tmp-2
.PHONY: chkdblfiles  ##x find filenames that exist more than once
chkdblfiles:
	: '---------- [$@] ----------'
	find . -type f  | grep -v attic | grep -vF '.git/' | grep -E -v '.*\.o$$' > $(TMP1)
	-for f in $$(cat $(TMP1)); do echo $$(basename $$f); done  | sort | uniq -d > $(TMP2)
	-for f in $$(cat $(TMP2)); do echo "$$f:"; find . -name $$f; echo ; done
	rm -fv  $(TMP1) $(TMP2)

.PHONY: chkts  ##x check trailing spaces
chkts:
	: '---------- [$@] ----------'
	@grep -lE ' +$$' $(FXTHDRS) ; true
	@grep -lE ' +$$' $(DEMOSRC) ; true
	@grep -lE ' +$$' $(SRC) ; true
# try: sed -ri 's/[ ]+$//;' $(SRC) $(DEMOSRC) $(SRC)

.PHONY: showglobals ##x show global vars in lib, omit class-global vars
showglobals: #$(FXTLIB)
	: '---------- [$@] ----------'
	-nm -n -A -C $(FXTLIB) | cat -n | grep -P ' [RdDbB] ' | grep -v '::'

.PHONY: chkdef  ##x heuristic check for includes
chkdef:
	: '---------- [$@] ----------'
	@SRC='$(SRC)' FXTHDRS='$(FXTHDRS)' bash $(SCRIPTDIR)/chkdef.sh


THINK=' !*THINK*!   ctrl-C to interrupt, enter for next check'
.PHONY: ichk  ##x some interactive checks for the code (don't do that)
ichk:
	: '---------- [$@] ----------'
	$(SCRIPTDIR)/chk-demo-xref.zsh | grep -v 'return' | grep -vF '=' || true
	-@read -p '(---------- done demo crossrefs) $(THINK)' dummy
	bash $(SCRIPTDIR)/alloca.sh | grep -v 'return' | grep -vF '=' || true
	-@read -p '(---------- done ALLOCA) $(THINK)' dummy
	@echo; echo; echo;
#	@$(MAKE) rts
#	-@read -p '(---------- done TRAILING SPACES) $(THINK)' dummy
	@echo; echo; echo;
	$(MAKE) chkundoc
	-@read -p '(---------- done UNDOC)  $(THINK)' dummy
	@echo; echo; echo;
	$(MAKE) mrtdoc
	-@read -p '(---------- done MRTDOC)  $(THINK)' dummy
	@echo; echo; echo;
	$(MAKE) chkdef
	-@read -p '(---------- done CHKDEF)  $(THINK)' dummy
	@echo; echo; echo;
	$(MAKE) showsrc
	-@read -p '(---------- done SHOWSRC)  $(THINK)' dummy
	@echo; echo; echo;
	$(MAKE) chkts
	@echo '(---------- done chkts)'
	@echo; echo; echo;
	$(MAKE) chkascii
	@echo '(---------- done chkascii)'
	@echo; echo; echo;
	$(MAKE) showglobals
	-@read -p '(---------- done SHOWGLOBALS)  $(THINK)' dummy
	@echo; echo; echo;
	@grep -C1 -F '/*=' $(FXTHDRS) || true
	@echo '(---------- done grep func arg defaults)'
	$(MAKE) chkdblfiles
	-@read -p '(---------- done DBLFILES) $(THINK)' dummy
	-$(MAKE) chkxfiles
	-@read -p '(---------- done XFILES) $(THINK)' dummy
	@echo; echo; echo;
	: '---------- [$@ DONE] ----------'


############## DEMO ##############

DEMOSCRIPT=$(SCRIPTDIR)/demo.zsh
DEMOBIN=./bin
RUNDEMO=$(DEMOBIN)
DEMODIR=./demo
DEMOSRC=$(DEMODIR)/*/*-demo.cc


#.NOTPARALLEL: demo
.PHONY: demo  ## refresh all demos (also useful as test)
demo: lib chkdtim
	WHICH='all' $(DEMOSCRIPT)


#.NOTPARALLEL: vgdemo
.PHONY: vgdemo ## check all demos with valgrind (use DSRC to select files)
vgdemo: lib chkdtim
	@echo 'With anything suspicious: disable optimization, enable debugging, and repeat.'
	WHICH='vg' $(DEMOSCRIPT)
	@echo 'With anything suspicious: disable optimization, enable debugging, and repeat.'


## use DEMOFLAGS e.g. for -DTIMING
## limit filesize, virtual memory, and time to prevent runaway:
#D_ULIMIT=ulimit -f 500 -v 16000 -t 100
## portability: limit only filesize:
D_ULIMIT=ulimit -f 500
#.NOTPARALLEL: 1demo
.PHONY: 1demo  ## called by 'demo', usage:  make 1demo DSRC=demo/WHICH-demo.cc
1demo: lib
	@test -n "$(DSRC)" || { echo 'USAGE:  make $@ DSRC=demo/dir/some-demo.cc'; exit 1; }
#	@echo "CXX=$(CXX)" 1>&2 ## show compiler executable
#	-$(CXX) --version 1>&2 ## show compiler version
	$(CXX) $(DSRC) $(CXXFLAGS) $(DEMOFLAGS) $(IFLAGS) $(LIBS) -o $(DEMOBIN)
	@echo "// output of $(DSRC):"; echo "// Description:"; grep '^//%' $(DSRC); echo "";
#	@grep '^#define TIMING' $(DSRC) || true
	$(D_ULIMIT);  $(RUNDEMO)
#	ulimit -f 500; MALLOC_CHECK_=2  $(RUNDEMO);  ## slow...
#	@echo $(CXX) 1>&2


CPUMHZ ?= 3700
## optional pattern:  make bench PAT=foo # all where filename matches $(PAT)
## example: make bench PAT=/perm-lex-demo.cc
#.NOTPARALLEL: bench
.PHONY: bench  ## run benchmarks (demo files that contain a 'BENCHARGS=' line)
bench: chkopt lib
#	PAT='$(PAT)' $(SCRIPTDIR)/bench.zsh
	PAT='$(PAT)' $(SCRIPTDIR)/bench.zsh 2>&1 | tee bench-log-new.txt
	ls -l bench-log-new.txt
	@echo 'CPUMHZ = $(CPUMHZ)  <--= is this correct?'
	rm -f bench-log-eval.txt
	cat /proc/cpuinfo | grep -F 'model name' | head -n 1 >> bench-log-eval.txt
	$(CXX) --version | head -n 1 >> bench-log-eval.txt
	@echo >> bench-log-eval.txt
	$(SCRIPTDIR)/eval-bench-log.pl $(CPUMHZ) < bench-log-new.txt >> bench-log-eval.txt


#.NOTPARALLEL: odemo
.PHONY: odemo  ##x do demos, starting with oldest
odemo: lib
	WHICH='old' $(DEMOSCRIPT)

#.NOTPARALLEL: ndemo
.PHONY: ndemo  ##x do "New" (i.e. last modified) demo
ndemo: lib
	$(MAKE) 1demo DSRC=$$(\ls -1t $(DEMOSRC) | head -1);

#.NOTPARALLEL: ndemo2
.PHONY: ndemo2  ##x do "almost New" (i.e. second-last modified) demo
ndemo2: lib
	$(MAKE) 1demo DSRC=$$(\ls -1t $(DEMOSRC) | head -2 | tail -1);

#.NOTPARALLEL: ntdemo
.PHONY: ntdemo  ##x do demos with sources Newer Than their output
ntdemo: lib chkdtim
	WHICH='nt' $(DEMOSCRIPT)

#.PHONY: Etarget2
#Etarget2:
#	@$(MAKE) -s --no-print-directory ntdemo;  ## tricky: no garbage in output

#.NOTPARALLEL: hdemo
.PHONY: hdemo  ##x do demos that include Header matching $PAT, usage: make hdemo PAT=cyclic
hdemo: lib
	@test -n "$(PAT)" || { echo 'USAGE:  make $@ PAT=some-pattern'; exit 1; }
	WHICH='header' PAT='$(PAT)' $(DEMOSCRIPT)

#.NOTPARALLEL: fdemo
.PHONY: fdemo  ##x do demos whose Filename contains pattern $PAT, usage: make fdemo PAT=pol
fdemo: lib
	@test -n "$(PAT)" || { echo 'USAGE:  make $@ PAT=some-pattern'; exit 1; }
	WHICH='files' PAT='$(PAT)' $(DEMOSCRIPT)

#.NOTPARALLEL: gdemo
.PHONY: gdemo  ##x do demos "grep -El $PAT demo/*/*-demo.cc", usage: make gdemo PAT=irreduc
gdemo: lib
	@test -n "$(PAT)" || { echo 'USAGE:  make $@ PAT=some-pattern'; exit 1; }
	WHICH='grep' PAT='$(PAT)' $(DEMOSCRIPT)


.PHONY: ctdemo  ##x how many demo programs do we have?
ctdemo:
	find ./demo/ -name \*-demo.cc | wc -l

.PHONY: ctsrc  ##x how many source files do we have?
ctsrc:
	echo ./src/*/*.cc ./src/*/*.h  | wc -w



############## AUTODOC ##############


#-------------------------------------

#DATADOCFILE=../data-fxt/00data-doc.txt
#.PHONY: datadoc  ##x extract doc from files in data/
#datadoc:
#	: '[$@]'
#	echo 'Generated file: doc for tables in the directory data-fxt/' > $(DATADOCFILE)
#	echo  >> $(DATADOCFILE)
#	for f in ../data-fxt/*.txt; do grep -F '#.' $$f >/dev/null || exit 1; done
#	LC_ALL=C; for f in ../data-fxt/*.txt; do \
# echo '----- '$$(basename $$f)":"; grep -B25 -F '#.' $$f ; echo; \
# done >> $(DATADOCFILE)


#ADOC=zsh $(SCRIPTDIR)/autodoc.zsh
AUTODOCLOG=$(TMPDIR)/tmp-autodoc.log
#REDIRECT1=1> /dev/null
#REDIRECT2=2>> $(AUTODOCLOG)

DOCTMP=$(TMPDIR)/autodoc/
.PHONY: doc autodoc  ##x generate some documentation-oids
doc autodoc: # datadoc
	$(MAKE) doautodoc  ||  { tail -55 $(AUTODOCLOG); echo 1>&2 " *** There is a problem. See $(AUTODOCLOG)." ; exit 1; }
	rm -f $(AUTODOCLOG)


.PHONY: doautodoc  ##x called by autodoc
doautodoc:
	: '[$@]'
	-mkdir -p $(DOCTMP)
	-cp -a doc/* $(DOCTMP)
#	rm -f doc/*-doc.txt
	rm -f $(AUTODOCLOG)
	AUTODOCLOG='$(AUTODOCLOG)' FXTSRCDIRS='$(FXTSRCDIRS)' scripts/doc-all.zsh
	-diff -rd  $(DOCTMP) doc/
	-diff -rq  $(DOCTMP) doc/
	rm -rf  $(DOCTMP)
	: '[$@ OK]'


# format in docfiles:
#// ========== HEADER FILE bpol/bitpol.h: ==========
.PHONY: chkundoc  ##x find possibly undocumented headers
chkundoc:
	: '---------- [$@] ----------'
	$(SCRIPTDIR)/chkundoc.zsh | grep -v -- '-all.h'

.PHONY: mrtdoc  ##x find funcs with missing return types in documentation
mrtdoc:
	: '---------- [$@] ----------'
	-grep '^[a-zA-Z0-9_]\+(' doc/*.txt



############## get OEIS A-numbers: ##############

.PHONY: oeis ## show OEIS sequence numbers mentioned in files, see http://oeis.org
oeis:
#	@$(SCRIPTDIR)/extract-oeis-anums.pl src/*/*.{h,cc} # dash (Debian) does not grok this
	@$(SCRIPTDIR)/extract-oeis-anums.pl src/*/*.h
	@$(SCRIPTDIR)/extract-oeis-anums.pl src/*/*.cc
	@$(SCRIPTDIR)/extract-oeis-anums.pl demo/*/*.cc

.PHONY: ctoeis ## count OEIS sequence numbers mentioned in files, see http://oeis.org
ctoeis:
	: 'total OEIS refs:'
	$(MAKE) oeis | wc -l
	: 'unique OEIS refs:'
	$(MAKE) oeis | cut -s -d: -f1 | sort -u | wc -l

############## MKCOMPL: ##############

MKCOMPL=../../$(SCRIPTDIR)/mkcompl.sh
.PHONY: compl mkcompl  ##x generate type complex versions of some routines
compl mkcompl:
	: '[$@]'
	export NAME=fhtdit.cc;    export CNAME=c$$NAME;  cd src/fht  && $(MKCOMPL)
	export NAME=fhtdif.cc;    export CNAME=c$$NAME;  cd src/fht  && $(MKCOMPL)
	export NAME=fht0.cc;      export CNAME=c$$NAME;  cd src/fht  && $(MKCOMPL)
	export NAME=fhtcnvla.cc;  export CNAME=c$$NAME;  cd src/convolution  && $(MKCOMPL)
	export NAME=fhtcnvl.cc;   export CNAME=c$$NAME;  cd src/convolution  && $(MKCOMPL)
	export NAME=fhtcnvlcore.cc; export CNAME=c$$NAME;  cd src/convolution  && $(MKCOMPL)
	export NAME=fhtcnvlacore.cc; export CNAME=c$$NAME;  cd src/convolution  && $(MKCOMPL)


#.PHONY: pre  ##x dump defines
#pre:
#	$(CXX) -E -dM  $(CXXFLAGS) $(IFLAGS) test/test.h 2>&1

#-------------------------------------

ALLHDRS=$$(shell find . -name \*.h)
CCFILES=$$(find .  -maxdepth 2 -name \*.cc)
.PHONY: showsrc  ##x find *.cc files that are unused for the library
showsrc:
	: '---------- [$@] ----------'
	@SRC='$(SRC)' zsh $(SCRIPTDIR)/showsrc.zsh


.PHONY: showtodo ##x show comments that contain "todo" or "TBD"
showtodo:
	-@grep -iP '\b(todo)|(TBD)\b' $(CCFILES) $(FXTHDRS)

.PHONY: aall  ##x more than all  ;-)
aall: clobber xchk check doc dep mkcompl
	$(MAKE) lib
#	$(MAKE) test
	$(MAKE) -s demo ## option -s to avoid changing demo's output with make's message "entering dir blah"
#	-$(MAKE) invdep
	: '[$@ OK]'


############# BACKUP ###########


RELDIR:=$${PWD\#$$HOME}
MYBAKDIR=$(BAKDIR)/$(RELDIR)
.PHONY: testbakdir ##x called by bak and fbak
testbakdir:
	test -n "$(BAKDIR)"
	test -d $(BAKDIR)
	test -n "$(MYBAKDIR)"
	test -d $(MYBAKDIR)

.PHONY: bak  ##x copy files to some backup dir
bak: clobber testbakdir
	cp -ar$(CP_U) doc/* $(MYBAKDIR)/doc # copy doc silently
	cp -ar$(CP_U) demo/* $(MYBAKDIR)/demo # copy demo output silently
	cp -ar$(CP_U)v * $(MYBAKDIR)


.PHONY: syncdry ##x rsync incl. delete (dry run)
syncdry: testbakdir
	rsync -avW ./ $(MYBAKDIR) --delete --dry-run

.PHONY: sync  ##x rsync incl. delete
sync: testbakdir
	rsync -avW ./ $(MYBAKDIR) --delete

EXCL= --exclude '.git/*' ## files to exclude: --exclude 'filepat'
ZBAKDATE=$(shell date '+%Y.%m.%d')
MYZBAKDIR=$(ZBAKDIR)/$(PROJ)
#ZBAK=$(MYZBAKDIR)/$(PROJ)-$(ZBAKDATE).tgz
ZBAK=$(MYZBAKDIR)/$(PROJ)-$(ZBAKDATE).tar.gz
.PHONY: zbak dist  ##x create archive with date (for backup and distribution)
zbak dist:
	$(MAKE) clobber chkxfiles check xchk
	grep -En '^CXX_FLAGS \+= -Werror' makefile; $(TNT)
	test -n "$(ZBAKDIR)"
	test -d $(ZBAKDIR)
	test -n "$(MYZBAKDIR)"
	test -d $(MYZBAKDIR)
	cd ..  &&  tar -czf $(ZBAK) $(EXCL) $(PROJ)
	ls -ltr $(MYZBAKDIR)/ | tail -5

.PHONY: snapshot ##x create snapshot ~/fxt.tar.gz
snapshot: clobber
	cd ..  &&  tar -czf $(HOME)/fxt.tar.gz $(EXCL) $(PROJ)


#CHKZBAK=$(HOME)/bin/chkzbak.sh
.PHONY: chkzbak  ##x check how up-to-date the last archived backup is
chkzbak: diff-old
#	test -x $(CHKZBAK)
#	-$(CHKZBAK)

.PHONY: diff-old ##x compare to old version (unpacked in /run/shm)
diff-old:
	test -d "${MYZBAKDIR}"
#	ls -hF $(WEBLOC)/fxt-20*.tgz
#	( cd $(WEBLOC) && ls -hF ./fxt-20*.tgz; )
	( cd $(WEBLOC) && ls -hF ./fxt-20*.tar.gz; )
	diff-old.zsh

.PHONY: build-old ##x build old version in /run/shm
build-old:
	test -d "${MYZBAKDIR}"
	BDATE=$(BDATE) $(SCRIPTDIR)/build-old.zsh

.PHONY: tzbak  ##x zbak to /tmp
tzbak:
	$(MAKE) zbak ZBAKDIR=/tmp MYZBAKDIR=/tmp ZBAKDATE=$(shell date '+%Y.%m.%d-%H.%M.%S')



WEBLOC=$(WEBDIR)/fxt/
.PHONY: chkweb ##x check whether $(WEBLOC) exists
chkweb:
	: '---------- [$@] ----------'
	test -n "$(WEBDIR)"
	test -d $(WEBLOC)

.PHONY: 2web 4web  ##x copy stuff where it gets rsync'd to my website
2web 4web: chkweb
	: '---------- [$@] ----------'
	test -f $(ZBAK)  # version of today
	:
	test -n "$(WEBLOC)"
	test -d $(WEBLOC)
	:
	cp -p$(CP_U)v $(ZBAK) $(WEBLOC)
#	$(REPLACE) 'fxt-[0-9]{4}\.[0-9]{2}\.[0-9]{2}\.tgz' "fxt-$(ZBAKDATE).tgz" $(WEBLOC)/fxtpage.html
	$(REPLACE) 'fxt-[0-9]{4}\.[0-9]{2}\.[0-9]{2}\.tar.gz' "fxt-$(ZBAKDATE).tar.gz" $(WEBLOC)/fxtpage.html
	:
	$(MAKE) doc2web
	:
	$(MAKE) demo2web
#	:
#	$(MAKE) data2web
	:
	$(MAKE) bw2web

.PHONY: doc2web ##x called by 2web: put documentation to web
doc2web:
	: '---------- [$@] ----------'
	cp -p$(CP_U)v doc/*   $(WEBLOC)/doc/
	diff -q doc/  $(WEBLOC)/doc/  ;  true
#	cp -p$(CP_U)v fxt.lsm $(WEBLOC)
#	grep -A999 DESCRIPTION 00readme.txt > $(WEBLOC)/description.txt
	cd $(WEBLOC)/doc/ && lshtml > index.html

#.PHONY: data2web  ##x called by 2web:  put math data to web
#data2web:
#	: '---------- [$@] ----------'
#	test -d $(WEBDIR)/mathdata/
#	cp -$(CP_U)v ./data/* $(WEBDIR)/mathdata/
#	$(SCRIPTDIR)/datahtmldoc.zsh > $(WEBDIR)/mathdata/index.html

.PHONY: bw2web  ##x called by 2web:  put bitwizardry stuff to web
bw2web:
	: '---------- [$@] ----------'
	WEBDIR=$(WEBDIR) $(SCRIPTDIR)/bw2web.zsh


#D2WDIR=/tmp/demo2web/
WEBDEMOLOC=$(WEBDIR)/fxt/demo/
## script demo2web.sh uses firsthdrs.pl and make-demo-list.sh
.PHONY: demo2web  ##x called by 2web:  put demos to web (topics from demo/topics.txt)
demo2web: chkdemo
	: '---------- [$@] ----------'
	test -n $(WEBDIR)
	test -d $(WEBDIR)
	test -d $(WEBDEMOLOC)
	rsync --version > /dev/null ## make sure we have rsync
	perl -v > /dev/null ## make sure we have perl
	rsync -avW --exclude='attic/' demo/  $(WEBDEMOLOC)/
	rsync -aW  --exclude='attic/' demo/  $(WEBDEMOLOC)/ --delete # why does this delete so much?
	WEBDEMOLOC=$(WEBDEMOLOC) FXTLOC=$$PWD scripts/demo2web.sh


NLAST=50
.PHONY: last ##x list src-files changed most recently
last:
	ls -ltr $$(find . -name \*.cc -o -name \*.h) | tail -n $(NLAST)

.PHONY: elast ##x select a recently modified file to edit
elast:
	select F in $$(ls -tr $$(find . -name \*.cc -o -name \*.h) | tail -n $(NLAST)); do ( $$EDITOR $$F &); break ; done



#.PHONY: cat ##x cat all src files to /dev/null so their contents are in memory
#cat:
#	: '[$@]'
#	cat $$(find . -name \*.cc) > /dev/null
#	cat $$(find . -name \*.h) > /dev/null
#	cat $$(find . -name \*-out.txt) > /dev/null


.PHONY: help h  ## help on make targets
h help:
	grep '^\.PHONY' *akefile* | grep -v '##x'

.PHONY: xhelp xh  ## help on all make targets including the weird and dangerous
xh xhelp:
	grep '^\.PHONY' *akefile*


# Emacs:
# Local Variables:
# frame-title-format: "FXT makefile"
# End:

################## end FXT makefile ##########################
