# Makefile for parallel package for Octave
#
# Copyright (C) 2015-2020 Olaf Till <i7tiol@t-online.de>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.


# before cross building, pre-build at least $(TEXIFILE) natively,
# e.g. with targets 'doc', 'html', or 'prebuild'


# Uncomment this for a release.
RELEASE := yes
ifdef RELEASE
# Currently this does nothing.
RELEASE_CXXFLAGS = -Wno-deprecated
endif

# This is set by configure to the default used by mkoctfile, possibly
# enhanced with an option to enforce C++11 features. I see no clean
# way to make this overridable by the user with an environment
# variable, since make itself sets a default for CXX.
CXX = @CXX@

CXXCPP = @CXXCPP@

MKOCTFILE ?= @MKOCTFILE@
PKG_CONFIG ?= @PKG_CONFIG@
ifndef CXXFLAGS
CXXFLAGS := $(shell $(MKOCTFILE) -p CXXFLAGS)
endif
ifndef CPICFLAG
CPICFLAG := $(shell $(MKOCTFILE) -p CPICFLAG)
endif
# not for calling mkoctfile
CC = @CC@
# not for calling mkoctfile
CFLAGS = -g -O2

HAVE_GNUTLS := @HAVE_GNUTLS@
HAVE_GNUTLS_EXTRA := @HAVE_GNUTLS_EXTRA@

REDUCED_BUILD := @REDUCED_BUILD@

octs = $(cluster_oct) $(parallel_local_oct) $(generate_srp_data_oct) $(local_octs)

# the server is a standalone binary
ifeq ($(REDUCED_BUILD), no)
  standalone := octave-pserver
endif

# All compiled user functions for parallel cluster must be in one
# file, otherwise registration of new octave_base_value derived type
# is not seen by the other functions. Even the server function must be
# included, since the server, too, will usually run some other user
# functions which accept a parallel connections object.
cluster_oct := parallel_interface.oct

# Currently only for Testing.
parallel_local_oct :=  __parcellfun_interface__.oct
parallel_local_oobjs := parcellfun_set_nproc.o \
                        __parcellfun_set_nproc_used__.o \
                        __parcellfun_initialize_job__.o \
                        __parcellfun_send_next_args__.o \
                        __parcellfun_get_next_result__.o

local_octs := fload.oct fsave.oct __exit__.oct __rfeval_slave__.oct \
              select.oct

cluster_objs = $(cluster_oobjs) $(cluster_lobjs)

parallel_local_objs = $(parallel_local_oobjs) $(parallel_local_lobjs)

cluster_user_oobjs := pconnect.o select_sockets.o network_get_info.o \
                      network_set.o sclose.o reval.o precv.o psend.o

ifeq ($(REDUCED_BUILD), yes)

  cluster_oobjs = $(cluster_user_oobjs)

else

  cluster_oobjs = $(cluster_user_oobjs) __octave_server__.o

endif

cluster_lobjs := p-connection.o p-streams.o

parallel_local_lobjs := p-control.o make-cmd-string.o

generate_srp_data_source := parallel_generate_srp_data.cc

ifeq ($(HAVE_GNUTLS), yes)
  generate_srp_data_oct := parallel_generate_srp_data.oct

  cluster_oobjs := $(cluster_oobjs) parallel_generate_srp_data.o

  cluster_lobjs := $(cluster_lobjs) gnutls-callbacks.o sensitive-data.o

  ifeq ($(HAVE_GNUTLS_EXTRA), yes)
    GNUTLS_CFLAGS := $(shell $(PKG_CONFIG) --cflags gnutls-extra) -Wno-int-to-pointer-cast
    GNUTLS_LIBS :=  $(shell $(PKG_CONFIG) --libs gnutls-extra)
  else
    GNUTLS_CFLAGS := $(shell $(PKG_CONFIG) --cflags gnutls) -Wno-int-to-pointer-cast
    GNUTLS_LIBS :=  $(shell $(PKG_CONFIG) --libs gnutls)
  endif
endif


INFOFILE := ../doc/parallel.info
TEXIFILE := $(addsuffix .texi,$(basename $(INFOFILE)))
TXIFILE := $(addsuffix .txi,$(basename $(INFOFILE)))
HTMLDIR := ../doc/html/

DEFUNDLDFILES := $(addsuffix .cc,$(basename \
                 $(cluster_user_oobjs) $(local_octs) $(parallel_local_oobjs))) \
                 $(generate_srp_data_source)

DSFILES := $(addsuffix .docstrings,$(DEFUNDLDFILES))

MFILES := $(wildcard *.copy_to_m) $(wildcard ../inst/*.m)

.PHONY: all gnulib clean distclean maintainer-clean doc prebuild html

.INTERMEDIATE: MFDOCSTRINGS $(DSFILES)

all: doc $(octs) $(standalone)

gnulib:
	$(MAKE) -C gl AM_CFLAGS='-DEXTERNAL_BINARY $(CPICFLAG)'

gl/libgnu.a: gnulib
	true # without any recipe, make seemingly doesn't always consider the target to be really re-made after this rule was triggered

prebuild: doc html

# the following variables are substituted by m4 macros of gnulib
LIBSOCKET := @LIBSOCKET@
GETADDRINFO_LIB := @GETADDRINFO_LIB@
HOSTENT_LIB := @HOSTENT_LIB@
INET_NTOP_LIB := @INET_NTOP_LIB@
LIBINTL := @LIBINTL@ # when linking with libtool, LTLIBINTL instead of this
SERVENT_LIB := @SERVENT_LIB@
LIB_POLL := @LIB_POLL@
LIB_SELECT := @LIB_SELECT@
LIBTHREAD := @LIBTHREAD@
LIB_PTHREAD_SIGMASK := @LIB_PTHREAD_SIGMASK@

gnulib_libs := $(LIBSOCKET) $(GETADDRINFO_LIB) $(HOSTENT_LIB) \
               $(INET_NTOP_LIB) $(LIBINTL) $(SERVENT_LIB) \
               $(LIB_POLL) $(LIB_SELECT) $(LIBTHREAD) \
               $(LIB_PTHREAD_SIGMASK)

# gl/libgnu.a is included into prerequisites as a marker that some of
# the various gnulib header files under gl/ might have been updated,
# to avoid having to mention them all here
gnulib-wrappers.o: gnulib-wrappers.c gl/libgnu.a
	$(CC) -c gnulib-wrappers.c -Igl $(CFLAGS) -fPIC

error-helpers.o: error-helpers.cc error-helpers.h
	CXXFLAGS="$(CXXFLAGS) $(RELEASE_CXXFLAGS)" $(MKOCTFILE) -c error-helpers.cc

minimal-load-save.o: minimal-load-save.cc
	CXXFLAGS="$(CXXFLAGS) $(RELEASE_CXXFLAGS)" $(MKOCTFILE) -c minimal-load-save.cc

$(octs): error-helpers.h minimal-load-save.h

__rfeval_slave__.oct: p-streams.h parallel-gnutls.h \
                      parallel-gnutls-nonoctave.h

$(cluster_objs) $(parallel_local_objs): \
                 parallel-gnutls.h parallel-gnutls-nonoctave.h \
                 p-connection.h p-streams.h \
                 sensitive-data.h p-sighandler.h \
                 gnulib-wrappers.h

$(standalone): parallel-gnutls-nonoctave.h common.h

# CXX default is set by configure to the default used by mkoctfile,
# possibly enhanced with an option to enforce C++11 features.
$(cluster_objs): %.o: %.cc config.h
	CXX="$(CXX)" CXXFLAGS="$(CXXFLAGS) $(GNUTLS_CFLAGS) $(RELEASE_CXXFLAGS)" $(MKOCTFILE) -c $<

select.o: select.cc config.h gnulib-wrappers.h
	CXXFLAGS="$(CXXFLAGS) $(RELEASE_CXXFLAGS)" $(MKOCTFILE) -c $<

$(parallel_local_objs): %.o: %.cc p-control.h p-pipes.h config.h
	CXX="$(CXX)" CXXFLAGS="$(CXXFLAGS) $(RELEASE_CXXFLAGS)" $(MKOCTFILE) -c $<

%.o: %.cc config.h
	CXXFLAGS="$(CXXFLAGS) $(RELEASE_CXXFLAGS)" $(MKOCTFILE) -c $<

$(generate_srp_data_oct): %.oct: %.o $(cluster_lobjs) \
                          gnulib-wrappers.o gl/libgnu.a error-helpers.o
	$(MKOCTFILE) $< $(cluster_lobjs) gnulib-wrappers.o error-helpers.o $(GNUTLS_LIBS) $(gnulib_libs) gl/libgnu.a

# $(GNUTLS_LIBS) is also necessary for all oct-files which link to
# p-streams.o.

$(cluster_oct): $(cluster_objs) error-helpers.o minimal-load-save.o gnulib-wrappers.o gl/libgnu.a
	$(MKOCTFILE) -pthread -o $(cluster_oct) $(cluster_objs) error-helpers.o minimal-load-save.o $(GNUTLS_LIBS) $(gnulib_libs) gl/libgnu.a gnulib-wrappers.o

$(parallel_local_oct): $(parallel_local_objs) p-streams.o error-helpers.o minimal-load-save.o gnulib-wrappers.o gl/libgnu.a
	$(MKOCTFILE) -o $@ $(parallel_local_objs) p-streams.o error-helpers.o minimal-load-save.o $(GNUTLS_LIBS) $(gnulib_libs) gl/libgnu.a gnulib-wrappers.o

__rfeval_slave__.oct: __rfeval_slave__.o p-streams.o error-helpers.o minimal-load-save.o gnulib-wrappers.o gl/libgnu.a
	$(MKOCTFILE) __rfeval_slave__.o p-streams.o error-helpers.o minimal-load-save.o gl/libgnu.a gnulib-wrappers.o $(GNUTLS_LIBS) $(gnulib_libs)

%.oct: %.o error-helpers.o minimal-load-save.o gnulib-wrappers.o gl/libgnu.a
	$(MKOCTFILE) $< error-helpers.o minimal-load-save.o gl/libgnu.a gnulib-wrappers.o $(gnulib_libs)

$(standalone): octave-pserver.cc octave-pserver.h
	$(CXX) -pthread -Wall -o octave-pserver octave-pserver.cc
	install -D octave-pserver ../bin/octave-pserver

doc: $(INFOFILE)

$(INFOFILE): $(TEXIFILE)
	makeinfo $(TEXIFILE) -o $(INFOFILE)

html: $(TEXIFILE)
	makeinfo --html -o $(HTMLDIR) $(TEXIFILE)

$(TEXIFILE): $(TXIFILE) MFDOCSTRINGS $(DSFILES) munge-texi.pl
	./munge-texi.pl MFDOCSTRINGS $(DSFILES) < $(TXIFILE) > $(TEXIFILE)

MFDOCSTRINGS: $(MFILES) mkdoc.pl
	./mkdoc.pl $(MFILES) > MFDOCSTRINGS

# take long CPP macro names, unlikely to be present in package code
# (which is checked) or to be defined by Octave (which can't be
# checked)
RDEFUN_DLD := DEFUN_DLD_REPLACEMENT_FOR_PACKAGE_DOCS
RDEFUNX_DLD := DEFUNX_DLD_REPLACEMENT_FOR_PACKAGE_DOCS
# Docstrings defined as C strings are obtained from a compiled C
# program. This should be the cleanest way to correctly get all
# special characters defined in these strings.
%.cc.docstrings: %.bin
	(echo "### This file is generated automatically from the"; echo "### corresponding .cc file by a Makefile rule."; echo ""; ./$<) > $@
# Explanation of the command after the checks: Macro names are changed
# by `sed' so that a different definition can be given to them on the
# `cpp' commandline. After running the `cpp' command, one can be sure
# that there are not more than one `$(RDEFUN_DLD)' resulting from a
# DEFUN(X)_DLD invocation in the original code at one line, that every
# occurance of `$(RDEFUN_DLD)' indeed corresponds to an original macro
# invocation, and not to `DEFUN(X)_DLD' within a comment or a string,
# and that we have all necessary information within one line of
# text. The package code is expected to give the function
# documentation as a string constant directly in the macro invocation,
# as is normally done.
%.bin: %.cc
	if grep -q $(RDEFUN_DLD) $<; then echo "The string '$(RDEFUN_DLD)' must not be present in source code, but is in $<."; exit 1; fi
	if grep -q $(RDEFUNX_DLD) $<; then echo "The string '$(RDEFUNX_DLD)' must not be present in source code, but is in $<."; exit 1; fi
	(echo "#include <stdio.h>"; echo "int main () {"; sed -e s/DEFUN_DLD/$(RDEFUN_DLD)/g -e s/DEFUNX_DLD/$(RDEFUNX_DLD)/g $< | $(CXXCPP) `$(MKOCTFILE) -p INCFLAGS` -x c++ -iquote '.' -D'$(RDEFUN_DLD)(name,args,nargout,doc)=$(RDEFUN_DLD)(name,doc)' -D'$(RDEFUNX_DLD)(name,fname,gname,args,nargout,doc)=$(RDEFUN_DLD)(name,doc)' - | sed -e '/.*$(RDEFUN_DLD)/!D'; echo "}";) | $(CXX) -x c++ -D'$(RDEFUN_DLD)(name,doc)=printf("%c" #name "\n@c " #name " $<\n" doc "\n\n", 0x1D);' -o $@ -

clean:
	$(RM) $(standalone) *.o *.oct *.m octave-core *.cc.docstrings MFDOCSTRINGS *~

distclean: clean
	$(MAKE) -C gl distclean
	$(RM) config.h config.log config.status
	$(RM) Makefile

maintainer-clean: distclean
	$(RM) $(INFOFILE) $(TEXIFILE)
