#!/bin/sh
## configure for the rgrpc R package
##
## System-linked build: finds the distro gRPC C++ library via pkg-config
## and proves it links with R's own C++ compiler. There is no vendored
## fallback by design; see PLAN.md. When gRPC C++ is missing or does not
## link (e.g. a libstdc++ gRPC under a libc++ toolchain), the package
## installs as a stub whose functions error and grpc_available() is
## FALSE. Set RGRPC_REQUIRE_GRPC=true to make that an install error.

: "${PKG_CONFIG=pkg-config}"
: "${R_HOME=$(R RHOME)}"

stub() {
    echo "------------------------------------------------------------"
    echo "rgrpc: $1"
    echo "  Installing WITHOUT gRPC: every call errors and"
    echo "  grpc_available() returns FALSE."
    echo "  Ubuntu/Debian: sudo apt install libgrpc++-dev libprotobuf-dev"
    echo "  (libgrpc++-dev, not libgrpc-dev: the C-only package leaves"
    echo "   the C++ runtime and grpc++.pc missing. libgrpc++-dev does"
    echo "   not pull libprotobuf-dev under --no-install-recommends.)"
    echo "  Fedora: sudo dnf install grpc-cpp grpc-devel protobuf-devel"
    echo "  macOS (Homebrew): brew install grpc protobuf pkgconf"
    echo "------------------------------------------------------------"
    case "${RGRPC_REQUIRE_GRPC}" in
        [Tt][Rr][Uu][Ee]|1)
            echo "ERROR: RGRPC_REQUIRE_GRPC is set; not building a stub." >&2
            exit 1 ;;
    esac
    sed -e "s|@GRPC_CFLAGS@||" \
        -e "s|@GRPC_LIBS@||" \
        -e "s|@GRPC_OBJECTS@|stub.o init.o|" \
        src/Makevars.in > src/Makevars
    exit 0
}

if ! "${PKG_CONFIG}" --version >/dev/null 2>&1; then
    stub "pkg-config not found."
fi

for mod in "grpc++" "protobuf"; do
    if ! "${PKG_CONFIG}" --exists "${mod}"; then
        stub "pkg-config cannot find '${mod}'."
    fi
done

GRPC_VERSION=$("${PKG_CONFIG}" --modversion grpc++)
GRPC_CFLAGS=$("${PKG_CONFIG}" --cflags grpc++ protobuf)
GRPC_LIBS=$("${PKG_CONFIG}" --libs grpc++ protobuf)

## Keep pkg-config's include directories and defines, not its warning
## options: abseil's .pc files carry the -Wno-... flags abseil builds
## itself with, which R CMD check reports as non-portable. Include
## directories go in as -isystem, so warnings raised inside gRPC's and
## abseil's own headers (deprecations between gRPC and a newer abseil)
## are not reported against this package.
cflags=""
for flag in ${GRPC_CFLAGS}; do
    case "${flag}" in
        -W*) ;;
        -I/usr/include) cflags="${cflags} ${flag}" ;;
        -I*) cflags="${cflags} -isystem ${flag#-I}" ;;
        *) cflags="${cflags} ${flag}" ;;
    esac
done
GRPC_CFLAGS="${cflags}"

## abseil's time zone code calls CoreFoundation on macOS. A shared
## abseil (Homebrew) carries that dependency; the static one in CRAN's
## macOS recipes does not, and its .pc files do not list it.
if [ "$(uname -s)" = "Darwin" ]; then
    GRPC_LIBS="${GRPC_LIBS} -framework CoreFoundation"
fi

## Link, not just compile: a shared object links with unresolved symbols
## and only fails at load time, so build an executable with the flags R
## will use.
CXX=$("${R_HOME}/bin/R" CMD config CXX17)
CXXSTD=$("${R_HOME}/bin/R" CMD config CXX17STD)
CXXFLAGS=$("${R_HOME}/bin/R" CMD config CXX17FLAGS)
## PIC as for the package's own objects; also keeps a -pie link in
## LDFLAGS working when CXXFLAGS lack a matching hardening spec.
CXXPICFLAGS=$("${R_HOME}/bin/R" CMD config CXX17PICFLAGS)
CPPFLAGS=$("${R_HOME}/bin/R" CMD config CPPFLAGS)
LDFLAGS=$("${R_HOME}/bin/R" CMD config LDFLAGS)

CONFTEST=$(mktemp -d 2>/dev/null || echo "./rgrpc-conftest.$$")
mkdir -p "${CONFTEST}"
cat > "${CONFTEST}/conftest.cpp" <<'EOF'
#include <grpcpp/grpcpp.h>
int main() {
    auto ch = grpc::CreateChannel("localhost:1",
                                  grpc::InsecureChannelCredentials());
    return ch ? 0 : 1;
}
EOF
if ${CXX} ${CXXSTD} ${CPPFLAGS} ${GRPC_CFLAGS} ${CXXPICFLAGS} ${CXXFLAGS} \
    "${CONFTEST}/conftest.cpp" -o "${CONFTEST}/conftest" \
    ${LDFLAGS} ${GRPC_LIBS} > "${CONFTEST}/conftest.log" 2>&1; then
    rm -rf "${CONFTEST}"
else
    tail -n 5 "${CONFTEST}/conftest.log"
    rm -rf "${CONFTEST}"
    stub "gRPC C++ ${GRPC_VERSION} was found but a test program does not link with R's C++ compiler (${CXX}); it may be built for a different C++ standard library."
fi

echo "using gRPC C++ ${GRPC_VERSION} via ${PKG_CONFIG}"

sed -e "s|@GRPC_CFLAGS@|${GRPC_CFLAGS}|" \
    -e "s|@GRPC_LIBS@|${GRPC_LIBS}|" \
    -e "s|@GRPC_OBJECTS@|client.o server.o shim.o init.o|" \
    src/Makevars.in > src/Makevars

exit 0
