# Makefile for parallel package for Octave
#
# Copyright (C) 2015-2018 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 =
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
# not for calling mkoctfile
CC ?= @CC@
# not for calling mkoctfile
CFLAGS ?= -g -O2

HAVE_WINSOCK := @HAVE_WINSOCK@
ifeq ($(HAVE_WINSOCK), yes)
  WINSOCKLIBS = -lws2_32
else
  WINSOCKLIBS = 
endif

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

REDUCED_BUILD := @REDUCED_BUILD@

ifeq ($(REDUCED_BUILD), yes)
  octs = $(local_octs_set1)
else
  octs = $(cluster_oct) $(generate_srp_data_oct) $(local_octs)
  ms = install_vars.m netarrayfun.m __netcellfun_guardfun__.m \
       netcellfun.m __pserver_exit__.m rfeval.m scloseall.m server.m
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.
cluster_oct := parallel_interface.oct

local_octs_set1 := fload.oct fsave.oct __exit__.oct

local_octs_set2 := select.oct __visglobal__.oct

local_octs := $(local_octs_set1) $(local_octs_set2)

cluster_objs = $(cluster_oobjs) $(cluster_lobjs)

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

cluster_lobjs := p-connection.o p-streams.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 := $(t_cluster_oobjs) parallel_generate_srp_data.o

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

  ifneq ($(HAVE_GETPASS), yes)
    getpass_obj := getpass.o
  endif

  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
else
  cluster_oobjs := $(t_cluster_oobjs)
endif

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

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

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

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

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

.INTERMEDIATE: MFDOCSTRINGS $(DSFILES)

all: doc $(octs) $(ms)

prebuild: doc html

getpass.o: getpass.c
	$(CC) -c getpass.c $(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

$(cluster_objs): parallel-gnutls.h p-connection.h p-streams.h sensitive-data.h \
                 p-sighandler.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 $<

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

$(generate_srp_data_oct): %.oct: %.o $(cluster_lobjs) $(getpass_obj) \
                          error-helpers.o
	$(MKOCTFILE) $< $(cluster_lobjs) $(getpass_obj) error-helpers.o $(GNUTLS_LIBS)

$(cluster_oct): $(cluster_objs) $(getpass_obj) error-helpers.o minimal-load-save.o
	$(MKOCTFILE) -o $(cluster_oct) $(cluster_objs) $(getpass_obj) error-helpers.o minimal-load-save.o $(GNUTLS_LIBS) $(WINSOCKLIBS)

%.oct: %.o error-helpers.o minimal-load-save.o
	$(MKOCTFILE) $< error-helpers.o minimal-load-save.o

$(ms): %.m: %.copy_to_m
	cp $< $(addsuffix .m,$(basename $<))

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) *.o *.oct *.m octave-core *.cc.docstrings MFDOCSTRINGS *~

distclean:
	$(RM) *.o *.oct *.m octave-core *.cc.docstrings config.h config.log config.status MFDOCSTRINGS *~

maintainer-clean:
	$(RM) *.o *.oct *.m octave-core *.cc.docstrings config.h config.log config.status MFDOCSTRINGS *~ $(INFOFILE) $(TEXIFILE)
