#!/bin/bash
# Copyright 2020 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

#
# DO NOT EDIT: automatically generated by ./ci/generate-builder.sh
#

set -eu

echo "---> Functions Framework C++ Buildpack"

layers="$1"

echo "-----> Setup vcpkg"
export VCPKG_DEFAULT_BINARY_CACHE="${layers}/vcpkg-cache"
export VCPKG_DEFAULT_TRIPLET=x64-linux-nodebug
export VCPKG_ROOT="${layers}/vcpkg"

if [[ -d "/usr/local/share/gcf/build_scripts/vcpkg-overlays" ]]; then
  export VCPKG_OVERLAY_PORTS="/usr/local/share/gcf/build_scripts/vcpkg-overlays"
fi

if [[ ! -d "${VCPKG_ROOT}" ]]; then
  echo "-----> Install vcpkg"
  mkdir -p "${VCPKG_ROOT}"
  curl -sSL https://github.com/Microsoft/vcpkg/archive/9b9a6680b25872989c8eb0303d670f32e5cfe6a4.tar.gz | \
    tar -C "${VCPKG_ROOT}" -xzf - --strip-components=1
  cat >"${VCPKG_ROOT}/triplets/x64-linux-nodebug.cmake" <<_EOF_
set(VCPKG_BUILD_TYPE release)
set(VCPKG_CMAKE_SYSTEM_NAME Linux)
set(VCPKG_CRT_LINKAGE dynamic)
set(VCPKG_LIBRARY_LINKAGE static)
set(VCPKG_TARGET_ARCHITECTURE x64)
_EOF_
  cp -r /usr/local/bin/vcpkg "${VCPKG_ROOT}"
cat >"${layers}/vcpkg.toml" <<_EOF_
build = true
cache = true
launch = false
_EOF_
fi

if [[ ! -d "${layers}/vcpkg-cache" ]]; then
  echo "-----> Restore cache from build image"
  cp -r /var/cache/vcpkg-cache "${layers}/vcpkg-cache"
cat >"${layers}/vcpkg-cache.toml" <<_EOF_
build = true
cache = true
launch = false
_EOF_
fi

echo "-----> Setup build directory"
cat >"${layers}/source.toml" <<_EOF_
build = true
cache = false
launch = false
_EOF_

mkdir -p "${layers}/source"
cat >"${layers}/source/CMakeLists.txt" <<'_EOF_'
cmake_minimum_required(VERSION 3.10)
project(functions-framework-application CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(functions_framework_cpp REQUIRED)

function (functions_framework_cpp_define_target_with_glob directory)
    file(GLOB_RECURSE application_sources "${directory}/*.cc"
         "${directory}/*.cpp" "${directory}/*.cxx")
    add_library(functions_framework_cpp_function ${application_sources})
    target_link_libraries(functions_framework_cpp_function
                          PUBLIC functions-framework-cpp::framework)
endfunction ()

if (EXISTS "${CNB_APP_DIR}/CMakeLists.txt")
    add_subdirectory("${CNB_APP_DIR}" "cnb_app_dir")
else ()
    functions_framework_cpp_define_target_with_glob("${CNB_APP_DIR}")
endif ()

if (NOT TARGET functions_framework_cpp_function)
    message(
        ERROR_FATAL
        [===[No `functions_framework_cpp_function` target found

The application is expected to define a CMake library target called
`functions_framework_cpp_function` which contains the target function. The
framework automatically defines this target, *UNLESS* you include a top-level
CMakeLists.txt file in your code. If you do include this file you must either:
- Define the target manually, using the standard CMake commands.
- Use the the `functions_framework_cpp_define_target_with_glob` function to
  define the target.
            ]===])
endif ()

add_executable(functions_framework_cpp_application main.cc)
set_target_properties(functions_framework_cpp_application PROPERTIES OUTPUT_NAME
                                                                     function)
target_link_libraries(functions_framework_cpp_application
                      PRIVATE functions_framework_cpp_function)

install(TARGETS functions_framework_cpp_application
        RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
_EOF_


if [[ -r vcpkg.json ]]; then
  cp vcpkg.json "${layers}/source"
else
  cat >"${layers}/source/vcpkg.json" <<_EOF_
{
  "name": "auto-generated-vcpkg-json",
  "version-string": "unversioned",
  "dependencies": [ "functions-framework-cpp" ]
}
_EOF_
fi

generate_http_main_no_namespace() {
  local function="${1}"
  cat <<_EOF_
#include <google/cloud/functions/framework.h>

namespace gcf = ::google::cloud::functions;

extern gcf::HttpResponse ${function}(gcf::HttpRequest);

int main(int argc, char* argv[]) {
  return gcf::Run(argc, argv, gcf::UserHttpFunction(${function}));
}
_EOF_
}

generate_http_main_with_namespace() {
  local function="${1}"
  local namespace="${2}"

  cat <<_EOF_
#include <google/cloud/functions/framework.h>

namespace gcf = ::google::cloud::functions;

namespace ${namespace} {
  extern gcf::HttpResponse ${function}(gcf::HttpRequest);
} // namespace

int main(int argc, char* argv[]) {
  return gcf::Run(argc, argv, gcf::UserHttpFunction(::${namespace}::${function}));
}
_EOF_
}

generate_cloud_event_main_no_namespace() {
  local function="${1}"
  cat <<_EOF_
#include <google/cloud/functions/framework.h>

namespace gcf = ::google::cloud::functions;

extern void ${function}(gcf::CloudEvent);

int main(int argc, char* argv[]) {
  return gcf::Run(argc, argv, gcf::UserCloudEventFunction(${function}));
}
_EOF_
}

generate_cloud_event_main_with_namespace() {
  local function="${1}"
  local namespace="${2}"

  cat <<_EOF_
#include <google/cloud/functions/framework.h>

namespace gcf = ::google::cloud::functions;

namespace ${namespace} {
  extern void ${function}(gcf::CloudEvent);
} // namespace

int main(int argc, char* argv[]) {
  return gcf::Run(argc, argv, gcf::UserCloudEventFunction(::${namespace}::${function}));
}
_EOF_
}

generate_main() {
  local signature="${2}"
  local full
  full="${1//:://}"
  full="${full#/}"
  local dir
  dir="$(dirname "${full}")"
  local namespace
  namespace="${dir//\//::}"
  local function
  function="$(basename "${full}")"

  if [[ "${signature}" == "http" ]]; then
    if [[ -z "${namespace}" || "${namespace}" == "." ]]; then
      generate_http_main_no_namespace "${function}"
      return
    fi
    generate_http_main_with_namespace "${function}" "${namespace}"
    return
  fi

  if [[ "${signature}" == "cloudevent" ]]; then
    if [[ -z "${namespace}" || "${namespace}" == "." ]]; then
      generate_cloud_event_main_no_namespace "${function}"
      return
    fi
    generate_cloud_event_main_with_namespace "${function}" "${namespace}"
    return
  fi

  >&2 echo "Unknown function signature type: ${signature}"
  exit 1
}

generate_main \
    "${TARGET_FUNCTION}" "${FUNCTION_SIGNATURE_TYPE:-http}" >"${layers}/source/main.cc"

echo "-----> Configure Function"
cat >"${layers}/binary.toml" <<_EOF_
build = true
cache = true
launch = false
_EOF_

/usr/local/bin/cmake -S "${layers}/source" -B "${layers}/binary" -GNinja -DCMAKE_MAKE_PROGRAM=/usr/local/bin/ninja \
  -DCNB_APP_DIR="${PWD}" \
  -DCMAKE_BUILD_TYPE=Release \
  -DCMAKE_INSTALL_PREFIX="${layers}/local" \
  -DVCPKG_TARGET_TRIPLET="${VCPKG_DEFAULT_TRIPLET}" \
  -DCMAKE_TOOLCHAIN_FILE="${VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake"
/usr/local/bin/cmake --build "${layers}/binary" --target install

cat >"${layers}/local.toml" <<_EOF_
launch = true
cache = false
build = false
_EOF_

cat >"${layers}/launch.toml" <<_EOF_
[[processes]]
type = "web"
command = "${layers}/local/bin/function"
_EOF_
