From af4c1e662fd53af7fc1e255437c6e6a54971a1fd Mon Sep 17 00:00:00 2001
From: Daniel Lyubomirov <daniel.lyubomirov@vereign.com>
Date: Fri, 12 Jun 2020 11:04:54 +0300
Subject: [PATCH] Flat C API

---
 .gitignore                                |   2 +-
 cpp/.clang-tidy                           |   2 +-
 cpp/docs/doxygen.config                   |   3 +-
 cpp/include/vereign/vereign.h             | 105 ++++++++++++++++
 cpp/src/CMakeLists.txt                    |  13 +-
 cpp/src/vereign/grpc/server.cc            |  10 +-
 cpp/src/vereign/grpc/server.hh            |  14 +++
 cpp/src/vereign/vereign.cc                |  60 +++++++++
 cpp/tests/CMakeLists.txt                  |   1 +
 cpp/tests/integration/CMakeLists.txt      |  26 ++++
 cpp/tests/integration/integration_test.cc | 147 ++++++++++++++++++++++
 cpp/tests/util/protobuf.cc                |   2 +-
 cpp/vendor/CMakeLists.txt                 |  71 +++++++++++
 cpp/vendor/boost.cmake                    |  38 ++++++
 cpp/vendor/boring_ssl.cmake               |  44 +++++++
 cpp/vendor/fmt.cmake                      |  48 +++++++
 cpp/vendor/grpc.cmake                     |  26 ++++
 cpp/vendor/nlohmann.cmake                 |  27 ++++
 18 files changed, 629 insertions(+), 10 deletions(-)
 create mode 100644 cpp/include/vereign/vereign.h
 create mode 100644 cpp/src/vereign/vereign.cc
 create mode 100644 cpp/tests/integration/CMakeLists.txt
 create mode 100644 cpp/tests/integration/integration_test.cc
 create mode 100644 cpp/vendor/CMakeLists.txt
 create mode 100644 cpp/vendor/boost.cmake
 create mode 100644 cpp/vendor/boring_ssl.cmake
 create mode 100644 cpp/vendor/fmt.cmake
 create mode 100644 cpp/vendor/grpc.cmake
 create mode 100644 cpp/vendor/nlohmann.cmake

diff --git a/.gitignore b/.gitignore
index fccfd58..02bce2e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,7 +2,7 @@
 *.iml
 bin/
 Gopkg.lock
-vendor/
+/vendor
 temp/
 yarn-error.log
 /.project
diff --git a/cpp/.clang-tidy b/cpp/.clang-tidy
index 756a03e..09a43d1 100644
--- a/cpp/.clang-tidy
+++ b/cpp/.clang-tidy
@@ -1,5 +1,5 @@
 ---
-Checks:          'clang-diagnostic-*,clang-analyzer-*,modernize-*,readability-*,-readability-container-size-empty,-readability-magic-numbers'
+Checks:          'clang-diagnostic-*,clang-analyzer-*,modernize-*,-modernize-use-nodiscard,readability-*,-readability-container-size-empty,-readability-magic-numbers'
 WarningsAsErrors: ''
 HeaderFilterRegex: ''
 AnalyzeTemporaryDtors: false
diff --git a/cpp/docs/doxygen.config b/cpp/docs/doxygen.config
index 89459d3..2be5290 100644
--- a/cpp/docs/doxygen.config
+++ b/cpp/docs/doxygen.config
@@ -792,6 +792,7 @@ WARN_LOGFILE           =
 # Note: If this tag is empty the current directory is searched.
 
 INPUT                  = src
+INPUT                 += include
 
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
@@ -816,8 +817,8 @@ INPUT_ENCODING         = UTF-8
 # *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
 # *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf.
 
+                         # *.cc \
 FILE_PATTERNS          = *.c \
-                         *.cc \
                          *.cxx \
                          *.cpp \
                          *.c++ \
diff --git a/cpp/include/vereign/vereign.h b/cpp/include/vereign/vereign.h
new file mode 100644
index 0000000..2a25ecc
--- /dev/null
+++ b/cpp/include/vereign/vereign.h
@@ -0,0 +1,105 @@
+#ifndef VEREIGN_VEREIGN_H_
+#define VEREIGN_VEREIGN_H_
+
+#ifdef _WIN32
+  #ifdef WIN_EXPORT
+    #define PUBLIC_API __declspec(dllexport)
+  #else
+    #define PUBLIC_API __declspec(dllimport)
+  #endif
+#else
+  #define PUBLIC_API
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Error codes.
+ */
+enum vereign_error_code {
+  VEREIGN_ERR_INTERNAL = 1,
+  VEREIGN_ERR_GRPC_BIND_FAILED = 2
+};
+
+/**
+ * Error object used for the error handling of the APIs.
+ *
+ * The error has a code and error message, which can be obtained by using the corresponding
+ * methods ...
+ */
+typedef struct vereign_error vereign_error;
+
+/**
+ * Destroys ::vereign_error object.
+ *
+ * Error objects are created by the API calls as result of failures.
+ *
+ * It is safe to call it with null err.
+ *
+ * @param err The error to destroy.
+ */
+PUBLIC_API void vereign_error_free(vereign_error* err);
+
+/**
+ * Provides the gRPC API services.
+ *
+ * The object is created with ::vereign_service_start method, and shutdown with
+ * ::vereign_service_shutdown.
+ *
+ * All object's methods are named with prefix `vereign_service_`.
+ */
+typedef struct vereign_service vereign_service;
+
+/**
+ * Creates ::vereign_service object and starts the gRPC API.
+ *
+ * The returned ::vereign_service object must be destroyed with ::vereign_service_shutdown method.
+ * After successful start, the gRPC bind port can be retrieved with the
+ * ::vereign_service_selected_port method.
+ *
+ * When the gRPC server cannot bind, `err` will have code VEREIGN_ERR_GRPC_BIND_FAILED.
+ * Any other failures will set `err` with code VEREIGN_ERR_INTERNAL.
+ *
+ * **NOTE: On failure the `err` object must be freed with vereign_error_free method.**
+ *
+ * @param listen_address gRPC listen address, for example "localhost:".
+ * @param vereignHost Vereign restapi host.
+ * @param vereignPort Vereign restapi port - https, 443...
+ * @param err On failure err is initialized with the reason of the failure,
+ *    otherwise err is set to nullptr.
+ * @returns vereign_service object if the gRPC is up and running, otherwise returns nullptr.
+ */
+PUBLIC_API vereign_service* vereign_service_start(
+  const char* listen_address,
+  const char* vereign_host,
+  const char* vereign_port,
+  const char* public_key,
+  vereign_error** err
+);
+
+/**
+ * Retruns the port number selected by the gRPC server of the service.
+ *
+ * Must be called after the service is created with ::vereign_service_start, and before
+ * it is destroyed with ::vereign_service_shutdown.
+ *
+ * @returns the port number selected by the gRPC server of the service.
+ */
+PUBLIC_API int vereign_service_selected_port(vereign_service* service);
+
+/**
+ * Stops and destroys the vereign service.
+ *
+ * It is safe to call with null `service`.
+ *
+ * @param service The object returned by the ::vereign_service_start function.
+ */
+PUBLIC_API void vereign_service_shutdown(vereign_service* service);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif // VEREIGN_VEREIGN_H_
diff --git a/cpp/src/CMakeLists.txt b/cpp/src/CMakeLists.txt
index c986d16..20c17f3 100644
--- a/cpp/src/CMakeLists.txt
+++ b/cpp/src/CMakeLists.txt
@@ -26,10 +26,10 @@ list(APPEND PROTO_SRC
   ${CMAKE_SOURCE_DIR}/proto/cpp/code.vereign.com/code/viam-apis/passport-generation-agent/api/api.pb.cc
 )
 add_library(vereignproto STATIC ${PROTO_SRC})
+set_property(TARGET vereignproto PROPERTY POSITION_INDEPENDENT_CODE ON)
 
 target_link_libraries(
   vereignproto
-  fmt::fmt
   protobuf::libprotobuf
   OpenSSL::SSL
   OpenSSL::Crypto
@@ -54,6 +54,7 @@ file(GLOB GENERATED_SERVICES_SRC vereign/service/gen/*.cc)
 list(APPEND VEREIGNLIB_SRC ${GENERATED_SERVICES_SRC})
 
 add_library(vereignlib STATIC ${VEREIGNLIB_SRC})
+set_property(TARGET vereignlib PROPERTY POSITION_INDEPENDENT_CODE ON)
 target_link_libraries(vereignlib PRIVATE nlohmann_json::nlohmann_json)
 target_link_libraries(vereignlib PUBLIC
   vereignproto
@@ -61,6 +62,14 @@ target_link_libraries(vereignlib PUBLIC
   gRPC::grpc++
 )
 
+add_library(vereign SHARED
+  vereign/vereign.cc
+)
+target_include_directories(vereign
+  PRIVATE ${CMAKE_SOURCE_DIR}/include
+)
+target_link_libraries(vereign PRIVATE vereignlib)
+
 set(csandbox_sources
   csandbox.cc
 )
@@ -71,7 +80,7 @@ set(csandbox_sources
 add_executable(csandbox ${csandbox_sources})
 
 target_link_libraries(csandbox
-  vereignlib
+  vereign
   # fmt::fmt
   # Boost::regex
   # Threads::Threads
diff --git a/cpp/src/vereign/grpc/server.cc b/cpp/src/vereign/grpc/server.cc
index c89a274..fd3c911 100644
--- a/cpp/src/vereign/grpc/server.cc
+++ b/cpp/src/vereign/grpc/server.cc
@@ -32,14 +32,16 @@ public:
     const std::string& vereignPort,
     // FIXME: the public key must come from a storage
     const std::string& publicKey
-  ) : work_guard_{asio::make_work_guard(ioc_)},
+  ) : selected_port_{0},
+    work_guard_{asio::make_work_guard(ioc_)},
     ssl_context_{asio::ssl::context::tlsv12_client},
     client_{std::make_unique<restapi::Client>(
       ioc_, ssl_context_, vereignHost, vereignPort
     )},
     client_session_{std::make_unique<restapi::ClientSession>(
       *client_, publicKey
-    )}
+    )},
+    server_{nullptr}
   {
 
     // FIXME: Verify the remote server's certificate
@@ -60,8 +62,8 @@ public:
     services_registry_.RegisterIntoBuilder(builder);
 
     server_ = builder.BuildAndStart();
-    if (server_ == nullptr) {
-      throw std::runtime_error("server start failed");
+    if (server_ == nullptr || selected_port_ == 0) {
+      throw BindError{};
     }
 
     server_thread_ = std::thread([this]() {
diff --git a/cpp/src/vereign/grpc/server.hh b/cpp/src/vereign/grpc/server.hh
index d2c4cba..bcadb25 100644
--- a/cpp/src/vereign/grpc/server.hh
+++ b/cpp/src/vereign/grpc/server.hh
@@ -7,6 +7,16 @@
 namespace vereign {
 namespace grpc {
 
+/**
+ * BindError is thrown when the Server::Server could not start listening.
+ */
+class BindError: public virtual std::exception {
+public:
+  auto what() const noexcept -> const char* override {
+    return "gRPC listen failed";
+  }
+};
+
 /**
  * Server is a grpc server that provides the Vereign services.
  *
@@ -18,9 +28,13 @@ public:
   /**
    * Constructs and bootstraps the server.
    *
+   * When the gRPC could not start listening a BindError is thrown.
+   * This can happen for number of reasons like invalid hostname or the listen port is already in use.
+   *
    * @param listenAddress gRPC listen address, for example "localhost:".
    * @param vereignHost Vereign restapi host.
    * @param vereignPort Vereign restapi port - https, 443...
+   * @throws BindError when the gRPC server could not start listening.
    */
   explicit Server(
     const std::string& listenAddress,
diff --git a/cpp/src/vereign/vereign.cc b/cpp/src/vereign/vereign.cc
new file mode 100644
index 0000000..aa69b13
--- /dev/null
+++ b/cpp/src/vereign/vereign.cc
@@ -0,0 +1,60 @@
+#include <vereign/vereign.h>
+#include <vereign/grpc/server.hh>
+
+struct vereign_error {
+  int code;
+  std::string msg;
+};
+
+void vereign_error_free(vereign_error* err) {
+  delete err;
+}
+
+struct vereign_service {
+  std::unique_ptr<vereign::grpc::Server> impl;
+};
+
+auto vereign_service_start(
+  const char* listen_address,
+  const char* vereign_host,
+  const char* vereign_port,
+  const char* public_key,
+  vereign_error** err
+) -> vereign_service* {
+  std::unique_ptr<vereign::grpc::Server> serviceImpl;
+  try {
+    serviceImpl = std::make_unique<vereign::grpc::Server>(
+      listen_address,
+      vereign_host,
+      vereign_port,
+      public_key
+    );
+
+    return new vereign_service{std::move(serviceImpl)};
+  } catch (const vereign::grpc::BindError& e) {
+    if (err != nullptr) {
+      *err = new vereign_error{VEREIGN_ERR_GRPC_BIND_FAILED, e.what()};
+    }
+  } catch (const std::exception& e) {
+    if (err != nullptr) {
+      *err = new vereign_error{VEREIGN_ERR_INTERNAL, e.what()};
+    }
+  }
+
+  return nullptr;
+}
+
+
+auto vereign_service_selected_port(vereign_service* service) -> int {
+  return service->impl->SelectedPort();
+}
+
+void vereign_service_shutdown(vereign_service* service) {
+  if (service == nullptr) {
+    return;
+  }
+
+  service->impl->Shutdown();
+  delete service;
+}
+
diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt
index 522c633..62bd6f4 100644
--- a/cpp/tests/CMakeLists.txt
+++ b/cpp/tests/CMakeLists.txt
@@ -6,3 +6,4 @@ include_directories(
 
 add_subdirectory("protobuf")
 add_subdirectory("vereign")
+add_subdirectory("integration")
diff --git a/cpp/tests/integration/CMakeLists.txt b/cpp/tests/integration/CMakeLists.txt
new file mode 100644
index 0000000..9efb102
--- /dev/null
+++ b/cpp/tests/integration/CMakeLists.txt
@@ -0,0 +1,26 @@
+include_directories(
+  ${CMAKE_SOURCE_DIR}/src
+  ${CMAKE_SOURCE_DIR}/include
+  # ${VENDOR_INSTALL_DIR}/include
+  ${CMAKE_SOURCE_DIR}/proto/cpp
+)
+
+list(APPEND tests_src
+  ../init_tests.cc
+
+  integration_test.cc
+)
+add_executable(integration_test ${tests_src})
+
+target_link_libraries(integration_test
+  vereign
+  fmt::fmt
+  vereignproto
+  gRPC::grpc++_reflection
+  gRPC::grpc++
+)
+
+add_test(
+  NAME integration_test
+  COMMAND integration_test
+)
diff --git a/cpp/tests/integration/integration_test.cc b/cpp/tests/integration/integration_test.cc
new file mode 100644
index 0000000..d89c6d2
--- /dev/null
+++ b/cpp/tests/integration/integration_test.cc
@@ -0,0 +1,147 @@
+#include <vereign/vereign.h>
+#include <util/env.hh>
+#include <vereign/client_library/types.gen.pb.h>
+#include <vereign/client_library/passport_api.gen.grpc.pb.h>
+#include <vereign/core/scope_guard.hh>
+#include <grpcpp/create_channel.h>
+
+#include <catch2/catch.hpp>
+
+TEST_CASE("C API integration", "[.integration]") {
+  auto publicKey = vereign::test::RequireEnv("TEST_VEREIGN_PUB_KEY");
+  auto host = vereign::test::RequireEnv("TEST_VEREIGN_API_HOST");
+  auto port = vereign::test::GetEnv("TEST_VEREIGN_API_PORT", "https");
+
+  vereign_error* err = nullptr;
+  auto service = vereign_service_start(
+    "localhost:",
+    host.data(),
+    port.data(),
+    publicKey.data(),
+    &err
+  );
+  CHECK(service != nullptr);
+  CHECK(err == nullptr);
+  auto on_exit = vereign::core::MakeScopeGuard([service] {
+    vereign_service_shutdown(service);
+  });
+
+  int listen_port = vereign_service_selected_port(service);
+
+  auto channel = ::grpc::CreateChannel(
+    "localhost:" + std::to_string(listen_port),
+    ::grpc::InsecureChannelCredentials()
+  );
+
+  auto client = vereign::client_library::PassportAPI::NewStub(channel);
+
+  vereign::client_library::ListPassportsForm req;
+  vereign::client_library::ListPassportsFormResponse resp;
+  ::grpc::ClientContext ctx;
+  auto status = client->ListPassports(&ctx, req, &resp);
+
+  // std::cout << vereign::test::ProtobufToJson(resp) << std::endl;
+
+  REQUIRE(status.error_message() == "");
+  REQUIRE(resp.error() == "");
+  CHECK(resp.status() == "OK");
+  CHECK(resp.code() == "200");
+  CHECK(resp.data().size() > 0);
+  for (auto& passport : resp.data()) {
+    CHECK(passport.uuid().size() == 36);
+  }
+
+  req.Clear();
+  resp.Clear();
+  ::grpc::ClientContext manually_ctx;
+  status = client->ListPassportsManually(&manually_ctx, req, &resp);
+
+  // std::cout << vereign::test::ProtobufToJson(resp) << std::endl;
+
+  REQUIRE(status.error_message() == "");
+  REQUIRE(resp.error() == "");
+  CHECK(resp.status() == "OK");
+  CHECK(resp.code() == "200");
+  CHECK(resp.data().size() > 0);
+  for (auto& passport : resp.data()) {
+    CHECK(passport.uuid().size() == 36);
+  }
+
+  vereign::client_library::GetInteractionsForm getInterReq;
+  getInterReq.set_uuid(resp.data().at(0).uuid());
+  vereign::client_library::GetInteractionsFormResponse getInterResp;
+  ::grpc::ClientContext getInterCtx;
+  status = client->GetInteractions(&getInterCtx, getInterReq, &getInterResp);
+  CHECK(status.error_message() == "");
+  CHECK(getInterResp.error() == "");
+  CHECK(getInterResp.status() == "OK");
+  CHECK(getInterResp.code() == "200");
+  for (auto& interaction : getInterResp.data()) {
+    CHECK(interaction.subject().size() > 0);
+    CHECK(interaction.passport().size() == 36);
+  }
+
+  // std::cout << vereign::test::ProtobufToJson(getDIDsResp) << std::endl;
+
+  grpc_shutdown();
+  google::protobuf::ShutdownProtobufLibrary();
+}
+
+TEST_CASE("vereign_service_start") {
+  SECTION("success") {
+    vereign_error* err = nullptr;
+    auto service = vereign_service_start(
+      "localhost:",
+      "",
+      "",
+      "",
+      &err
+    );
+
+    CHECK(service != nullptr);
+    CHECK(err == nullptr);
+
+    vereign_service_shutdown(service);
+  }
+
+  SECTION("invalid listen address") {
+    vereign_error* err = nullptr;
+    auto service = vereign_service_start(
+      "invalid",
+      "",
+      "",
+      "",
+      &err
+    );
+
+    CHECK(service == nullptr);
+    CHECK(err != nullptr);
+
+    vereign_error_free(err);
+    vereign_service_shutdown(service);
+  }
+}
+
+TEST_CASE("vereign_service_selected_port") {
+  vereign_error* err = nullptr;
+  auto service = vereign_service_start(
+    "localhost:",
+    "",
+    "",
+    "",
+    &err
+  );
+
+  REQUIRE(service != nullptr);
+  REQUIRE(err == nullptr);
+
+  auto port = vereign_service_selected_port(service);
+  CHECK((port > 0 && port <= 65535));
+
+  vereign_service_shutdown(service);
+}
+
+TEST_CASE("vereign_service_shutdown") {
+  // shutdown must not fail with nullptr
+  vereign_service_shutdown(nullptr);
+}
diff --git a/cpp/tests/util/protobuf.cc b/cpp/tests/util/protobuf.cc
index 544c577..5350ab1 100644
--- a/cpp/tests/util/protobuf.cc
+++ b/cpp/tests/util/protobuf.cc
@@ -1,4 +1,4 @@
-#include "vereign/grpc/json/encoder.hh"
+#include <vereign/grpc/json/encoder.hh>
 #include <util/protobuf.hh>
 
 #include <google/protobuf/util/json_util.h>
diff --git a/cpp/vendor/CMakeLists.txt b/cpp/vendor/CMakeLists.txt
new file mode 100644
index 0000000..79268ff
--- /dev/null
+++ b/cpp/vendor/CMakeLists.txt
@@ -0,0 +1,71 @@
+cmake_minimum_required (VERSION 3.16.5)
+
+if(WIN32)
+  set(CMAKE_IGNORE_PATH "C:/Strawberry/c/bin")
+endif()
+
+project (vereign-vendor)
+
+if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
+  add_definitions(-D_WIN32_WINNT=0x0601)
+
+  set(CMAKE_C_FLAGS "/DNDEBUG /DWIN32 /D_WINDOWS /W3")
+  set(CMAKE_CXX_FLAGS "/DNDEBUG /DWIN32 /D_WINDOWS /W3 /GR /EHsc")
+
+  set(CMAKE_C_FLAGS_DEBUG "/MTd /Zi /Ob0 /Od /RTC1")
+  set(CMAKE_CXX_FLAGS_DEBUG "/MTd /Zi /Ob0 /Od /RTC1")
+
+  set(CMAKE_CXX_FLAGS_RELEASE "/Gd /MT /O2 /Oi /Ot /Gy /Zi /GL")
+  set(CMAKE_C_FLAGS_RELEASE "/Gd /MT /O2 /Oi /Ot /Gy /Zi /GL")
+
+  set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded")
+  if(CMAKE_BUILD_TYPE STREQUAL "Debug")
+    set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreadedDebug")
+  endif()
+endif()
+
+if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
+  set(CMAKE_C_FLAGS_RELEASE "-g -O3 -Wall -Wextra -pedantic")
+  set(CMAKE_C_FLAGS_DEBUG "-g -O0 -Wall -Wextra -pedantic")
+  set(CMAKE_CXX_FLAGS_RELEASE "-g -O3 -Wall -Wextra -pedantic")
+  set(CMAKE_CXX_FLAGS_DEBUG "-g -O0 -Wall -Wextra -pedantic")
+endif()
+
+if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+  set(CMAKE_C_FLAGS_RELEASE "-g -O3 -Wall -Wextra -pedantic")
+  set(CMAKE_C_FLAGS_DEBUG "-g -O0 -Wall -Wextra -pedantic")
+  set(CMAKE_CXX_FLAGS_RELEASE "-g -O3 -Wall -Wextra -pedantic")
+  set(CMAKE_CXX_FLAGS_DEBUG "-g -O0 -Wall -Wextra -pedantic")
+endif()
+
+set(CMAKE_CXX_STANDARD 14)
+set(CMAKE_CXX_EXTENSIONS OFF)
+
+string(TOLOWER "${CMAKE_BUILD_TYPE}" _build_type)
+set(VENDOR_INSTALL_DIR ${CMAKE_SOURCE_DIR}/../cmake-install-vendor-${_build_type} CACHE STRING "vendor directory")
+set(CMAKE_PREFIX_PATH ${VENDOR_INSTALL_DIR} CACHE STRING "")
+option(CMAKE_FIND_USE_CMAKE_SYSTEM_PATH NO)
+
+include(FetchContent)
+include(ExternalProject)
+
+include(fmt.cmake)
+include(boring_ssl.cmake)
+include(boost.cmake)
+include(grpc.cmake)
+include(nlohmann.cmake)
+
+string(TOUPPER "${CMAKE_BUILD_TYPE}" _build_type)
+message(STATUS "Summary:
+
+  Vendor install dir: ${VENDOR_INSTALL_DIR}
+  Compiler:
+    Build type:     ${CMAKE_BUILD_TYPE}
+    C compiler:     ${CMAKE_C_COMPILER}
+    CFLAGS:         ${CMAKE_C_FLAGS_${_build_type}} ${CMAKE_C_FLAGS}
+    C++ compiler:   ${CMAKE_CXX_COMPILER}
+    CXXFLAGS:       ${CMAKE_CXX_FLAGS_${_build_type}} ${CMAKE_CXX_FLAGS}
+    WARNCFLAGS:     ${WARNCFLAGS}
+    CXX1XCXXFLAGS:  ${CXX1XCXXFLAGS}
+    CMAKE_MSVC_RUNTIME_LIBRARY: ${CMAKE_MSVC_RUNTIME_LIBRARY}
+")
diff --git a/cpp/vendor/boost.cmake b/cpp/vendor/boost.cmake
new file mode 100644
index 0000000..e0dfc1c
--- /dev/null
+++ b/cpp/vendor/boost.cmake
@@ -0,0 +1,38 @@
+include(ExternalProject)
+
+set(_boost_libs regex system thread)
+
+if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
+  list(TRANSFORM _boost_libs PREPEND --with-)
+
+  set(_configure_command <SOURCE_DIR>/bootstrap.bat)
+  set(_build_command <SOURCE_DIR>/b2 install --prefix=<INSTALL_DIR> ${_boost_libs} link=static)
+  set(_install_command "")
+elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
+  set(_boost_toolset clang)
+  set(_boost_cxx_flags "-std=c++${CMAKE_CXX_STANDARD} -fPIC")
+  set(_boost_c_flags "-fPIC")
+  string(REPLACE ";" "," _boost_libs "${_boost_libs}")
+
+  set(_configure_command <SOURCE_DIR>/bootstrap.sh --prefix=<INSTALL_DIR> --with-toolset=${_boost_toolset} --with-libraries=${_boost_libs})
+  set(_build_command <SOURCE_DIR>/b2 toolset=${_boost_toolset} cxxflags=${_boost_cxx_flags} cflags=${_boost_c_flags})
+  set(_install_command <SOURCE_DIR>/b2 install)
+endif()
+
+ExternalProject_Add(boostlib
+  PREFIX boost
+  URL https://dl.bintray.com/boostorg/release/1.72.0/source/boost_1_72_0.zip
+  URL_HASH SHA256=8c20440aaba21dd963c0f7149517445f50c62ce4eb689df2b5544cc89e6e621e
+  BUILD_IN_SOURCE ON
+  INSTALL_DIR ${VENDOR_INSTALL_DIR}/boost
+
+  USES_TERMINAL_DOWNLOAD ON
+  USES_TERMINAL_UPDATE ON
+  USES_TERMINAL_CONFIGURE ON
+  USES_TERMINAL_BUILD ON
+  USES_TERMINAL_INSTALL ON
+
+  CONFIGURE_COMMAND "${_configure_command}"
+  BUILD_COMMAND "${_build_command}"
+  INSTALL_COMMAND "${_install_command}"
+)
diff --git a/cpp/vendor/boring_ssl.cmake b/cpp/vendor/boring_ssl.cmake
new file mode 100644
index 0000000..91cfaa7
--- /dev/null
+++ b/cpp/vendor/boring_ssl.cmake
@@ -0,0 +1,44 @@
+
+if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
+  set(_install_command
+    ${CMAKE_COMMAND} -E copy crypto.lib <INSTALL_DIR>/lib/crypto.lib
+    COMMAND ${CMAKE_COMMAND} -E copy ssl.lib <INSTALL_DIR>/lib/ssl.lib
+    COMMAND ${CMAKE_COMMAND} -E copy_directory <SOURCE_DIR>/src/include <INSTALL_DIR>/include
+  )
+elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
+  set(_install_command
+    ${CMAKE_COMMAND} -E copy libcrypto.a <INSTALL_DIR>/lib/libcrypto.a
+    COMMAND ${CMAKE_COMMAND} -E copy libssl.a <INSTALL_DIR>/lib/libssl.a
+    COMMAND ${CMAKE_COMMAND} -E copy_directory <SOURCE_DIR>/src/include <INSTALL_DIR>/include
+  )
+endif()
+
+ExternalProject_Add(boringssllib
+  PREFIX boringssl
+  GIT_REPOSITORY git@github.com:google/boringssl.git
+  GIT_TAG 1c2769383f027befac5b75b6cedd25daf3bf4dcf
+  INSTALL_DIR ${VENDOR_INSTALL_DIR}/boringssl
+
+  USES_TERMINAL_DOWNLOAD ON
+  USES_TERMINAL_UPDATE ON
+  USES_TERMINAL_CONFIGURE ON
+  USES_TERMINAL_BUILD ON
+  USES_TERMINAL_INSTALL ON
+
+  CMAKE_CACHE_ARGS
+  -DCMAKE_C_COMPILER:STRING=${CMAKE_C_COMPILER}
+  -DCMAKE_CXX_COMPILER:STRING=${CMAKE_CXX_COMPILER}
+  -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
+  -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}
+  -DCMAKE_MSVC_RUNTIME_LIBRARY:STRING=${CMAKE_MSVC_RUNTIME_LIBRARY}
+  -DCMAKE_C_FLAGS:STRING=${CMAKE_C_FLAGS}
+  -DCMAKE_CXX_FLAGS:STRING=${CMAKE_CXX_FLAGS}
+  -DCMAKE_C_FLAGS_DEBUG:STRING=${CMAKE_C_FLAGS_DEBUG}
+  -DCMAKE_CXX_FLAGS_DEBUG:STRING=${CMAKE_CXX_FLAGS_DEBUG}
+  -DCMAKE_C_FLAGS_RELEASE:STRING=${CMAKE_C_FLAGS_RELEASE}
+  -DCMAKE_CXX_FLAGS_RELEASE:STRING=${CMAKE_CXX_FLAGS_RELEASE}
+  -DBUILD_SHARED_LIBS:BOOL=OFF
+  -DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=ON
+
+  INSTALL_COMMAND "${_install_command}"
+)
diff --git a/cpp/vendor/fmt.cmake b/cpp/vendor/fmt.cmake
new file mode 100644
index 0000000..3fd86d8
--- /dev/null
+++ b/cpp/vendor/fmt.cmake
@@ -0,0 +1,48 @@
+#  set(FETCHCONTENT_QUIET OFF)
+#  set(FETCHCONTENT_BASE_DIR "${CMAKE_SOURCE_DIR}/cmake-build-vendor")
+#  FetchContent_Declare(
+#    fmt
+#    GIT_REPOSITORY git@github.com:fmtlib/fmt.git
+#    GIT_TAG 6.2.0
+#  )
+#  FetchContent_MakeAvailable(fmt)
+
+ExternalProject_Add(fmtlib
+  PREFIX fmt
+  GIT_REPOSITORY git@github.com:fmtlib/fmt.git
+  GIT_TAG 6.2.0
+  INSTALL_DIR ${VENDOR_INSTALL_DIR}/fmt
+
+  USES_TERMINAL_DOWNLOAD ON
+  USES_TERMINAL_UPDATE ON
+  USES_TERMINAL_CONFIGURE ON
+  USES_TERMINAL_BUILD ON
+  USES_TERMINAL_INSTALL ON
+
+  CMAKE_CACHE_ARGS
+  -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}
+  -DCMAKE_MSVC_RUNTIME_LIBRARY:STRING=${CMAKE_MSVC_RUNTIME_LIBRARY}
+  -DCMAKE_C_COMPILER:STRING=${CMAKE_C_COMPILER}
+  -DCMAKE_CXX_COMPILER:STRING=${CMAKE_CXX_COMPILER}
+  -DCMAKE_C_FLAGS:STRING=${CMAKE_C_FLAGS}
+  -DCMAKE_CXX_FLAGS:STRING=${CMAKE_CXX_FLAGS}
+  -DCMAKE_C_FLAGS_DEBUG:STRING=${CMAKE_C_FLAGS_DEBUG}
+  -DCMAKE_CXX_FLAGS_DEBUG:STRING=${CMAKE_CXX_FLAGS_DEBUG}
+  -DCMAKE_C_FLAGS_RELEASE:STRING=${CMAKE_C_FLAGS_RELEASE}
+  -DCMAKE_CXX_FLAGS_RELEASE:STRING=${CMAKE_CXX_FLAGS_RELEASE}
+  -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
+  -DFMT_TEST:BOOL=OFF
+)
+
+#  ExternalProject_Get_Property(fmtlib INSTALL_DIR)
+#  set(FMT_LIB ${INSTALL_DIR} PARENT_SCOPE)
+#
+#  add_library(fmt STATIC IMPORTED GLOBAL)
+#  set_property(TARGET fmt PROPERTY IMPORTED_LOCATION_DEBUG ${INSTALL_DIR}/lib/libfmtd.a)
+#  set_property(TARGET fmt PROPERTY IMPORTED_LOCATION_RELEASE ${INSTALL_DIR}/lib/libfmt.a)
+#  target_include_directories(fmt INTERFACE
+#    $<BUILD_INTERFACE:${INSTALL_DIR}/include>
+##    $<INSTALL_INTERFACE:include/mylib>  # <prefix>/include/mylib
+#  )
+#  add_dependencies(fmt fmtlib)
+
diff --git a/cpp/vendor/grpc.cmake b/cpp/vendor/grpc.cmake
new file mode 100644
index 0000000..db85269
--- /dev/null
+++ b/cpp/vendor/grpc.cmake
@@ -0,0 +1,26 @@
+
+ExternalProject_Add(grpclib
+  PREFIX grpc
+  GIT_REPOSITORY git@github.com:grpc/grpc.git
+  GIT_TAG v1.28.1
+  GIT_SHALLOW ON
+  INSTALL_DIR ${VENDOR_INSTALL_DIR}/grpc
+  DEPENDS boringssllib
+
+  USES_TERMINAL_DOWNLOAD ON
+  USES_TERMINAL_UPDATE ON
+  USES_TERMINAL_CONFIGURE ON
+  USES_TERMINAL_BUILD ON
+  USES_TERMINAL_INSTALL ON
+
+  CMAKE_CACHE_ARGS
+  -DCMAKE_C_COMPILER:STRING=${CMAKE_C_COMPILER}
+  -DCMAKE_CXX_COMPILER:STRING=${CMAKE_CXX_COMPILER}
+  -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
+  -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}
+  -DBUILD_SHARED_LIBS:BOOL=OFF
+  -DgRPC_SSL_PROVIDER:STRING=package
+  -DOPENSSL_USE_STATIC_LIBS:BOOL=ON
+  -DOPENSSL_ROOT_DIR:STRING=${VENDOR_INSTALL_DIR}/boringssl
+  -DgRPC_INSTALL:BOOL=ON
+)
diff --git a/cpp/vendor/nlohmann.cmake b/cpp/vendor/nlohmann.cmake
new file mode 100644
index 0000000..9327243
--- /dev/null
+++ b/cpp/vendor/nlohmann.cmake
@@ -0,0 +1,27 @@
+
+ExternalProject_Add(nlohmannlib
+  PREFIX nlohmann
+  GIT_REPOSITORY git@github.com:nlohmann/json.git
+  GIT_TAG v3.7.3
+  INSTALL_DIR ${VENDOR_INSTALL_DIR}/nlohmann
+
+  USES_TERMINAL_DOWNLOAD ON
+  USES_TERMINAL_UPDATE ON
+  USES_TERMINAL_CONFIGURE ON
+  USES_TERMINAL_BUILD ON
+  USES_TERMINAL_INSTALL ON
+
+  CMAKE_CACHE_ARGS
+  -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}
+  -DCMAKE_MSVC_RUNTIME_LIBRARY:STRING=${CMAKE_MSVC_RUNTIME_LIBRARY}
+  -DCMAKE_C_COMPILER:STRING=${CMAKE_C_COMPILER}
+  -DCMAKE_CXX_COMPILER:STRING=${CMAKE_CXX_COMPILER}
+  -DCMAKE_C_FLAGS:STRING=${CMAKE_C_FLAGS}
+  -DCMAKE_CXX_FLAGS:STRING=${CMAKE_CXX_FLAGS}
+  -DCMAKE_C_FLAGS_DEBUG:STRING=${CMAKE_C_FLAGS_DEBUG}
+  -DCMAKE_CXX_FLAGS_DEBUG:STRING=${CMAKE_CXX_FLAGS_DEBUG}
+  -DCMAKE_C_FLAGS_RELEASE:STRING=${CMAKE_C_FLAGS_RELEASE}
+  -DCMAKE_CXX_FLAGS_RELEASE:STRING=${CMAKE_CXX_FLAGS_RELEASE}
+  -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
+  -DJSON_BuildTests:BOOL=OFF
+)
-- 
GitLab