Fixes for windows (vcpkg environment) compatibility.

Signed-off-by: Tao He <sighingnow@gmail.com>
This commit is contained in:
Tao He 2020-12-20 00:02:04 +08:00
parent eee2ea2d14
commit e286c36926
11 changed files with 139 additions and 100 deletions

View File

@ -8,9 +8,6 @@ set(etcd-cpp-api_VERSION_MINOR 1)
option(BUILD_ETCD_TESTS "Build test cases" OFF) option(BUILD_ETCD_TESTS "Build test cases" OFF)
find_library(CPPREST_LIB NAMES cpprest)
find_path(CPPREST_INCLUDE_DIR NAMES cpprest/http_client.h)
find_package(Boost REQUIRED COMPONENTS system thread locale random) find_package(Boost REQUIRED COMPONENTS system thread locale random)
if (APPLE) if (APPLE)
# If we're on OS X check for Homebrew's copy of OpenSSL instead of Apple's # If we're on OS X check for Homebrew's copy of OpenSSL instead of Apple's
@ -28,22 +25,31 @@ if (APPLE)
endif() endif()
endif() endif()
endif() endif()
find_package(OpenSSL REQUIRED) find_package(OpenSSL REQUIRED)
find_package(Protobuf REQUIRED) find_package(Protobuf REQUIRED)
find_package(cpprestsdk REQUIRED)
find_package(gRPC)
if(gRPC_FOUND)
set(GRPC_LIBRARIES gRPC::gpr gRPC::grpc gRPC::grpc++)
else()
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindGRPC.cmake) include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindGRPC.cmake)
set(GRPC_LIBRARIES ${GPR_LIBRARY} ${GRPC_LIBRARY} ${GRPC_GRPC++_LIBRARY}) set(GRPC_LIBRARIES ${GPR_LIBRARY} ${GRPC_LIBRARY} ${GRPC_GRPC++_LIBRARY})
endif()
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/GenerateProtobufGRPC.cmake)
# will set `PROTOBUF_GENERATES`, indicates all generated .cc files, and a target `protobuf_generates`. # will set `PROTOBUF_GENERATES`, indicates all generated .cc files, and a target `protobuf_generates`.
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/GenerateProtobuf.cmake) include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/GenerateProtobuf.cmake)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/proto) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/proto)
include_directories(SYSTEM ${CPPREST_INCLUDE_DIR} include_directories(SYSTEM ${Boost_INCLUDE_DIR}
${Boost_INCLUDE_DIR}
${PROTOBUF_INCLUDE_DIRS} ${PROTOBUF_INCLUDE_DIRS}
${OPENSSL_INCLUDE_DIR}) ${OPENSSL_INCLUDE_DIR})
include_directories(${CMAKE_CURRENT_SOURCE_DIR}) include_directories(${CMAKE_CURRENT_SOURCE_DIR})
if(NOT CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wpedantic -Werror -Wno-string-compare -std=c++11") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wpedantic -Werror -Wno-string-compare -std=c++11")
endif()
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)

View File

@ -4,7 +4,6 @@
# #
# The original repository is open-sourced with the MIT license. # The original repository is open-sourced with the MIT license.
# #
#
# Locate and configure the gRPC library # Locate and configure the gRPC library
# #
# Adds the following targets: # Adds the following targets:
@ -16,76 +15,6 @@
# gRPC::grpc_cpp_plugin - C++ generator plugin for Protocol Buffers # gRPC::grpc_cpp_plugin - C++ generator plugin for Protocol Buffers
# #
#
# Generates C++ sources from the .proto files
#
# grpc_generate_cpp (<SRCS> <HDRS> <DEST> [<ARGN>...])
#
# SRCS - variable to define with autogenerated source files
# HDRS - variable to define with autogenerated header files
# DEST - directory where the source files will be created
# ARGN - .proto files
#
function(GRPC_GENERATE_CPP SRCS HDRS DEST)
if(NOT ARGN)
message(SEND_ERROR "Error: GRPC_GENERATE_CPP() called without any proto files")
return()
endif()
if(GRPC_GENERATE_CPP_APPEND_PATH)
# Create an include path for each file specified
foreach(FIL ${ARGN})
get_filename_component(ABS_FIL ${FIL} ABSOLUTE)
get_filename_component(ABS_PATH ${ABS_FIL} PATH)
list(FIND _protobuf_include_path ${ABS_PATH} _contains_already)
if(${_contains_already} EQUAL -1)
list(APPEND _protobuf_include_path -I ${ABS_PATH})
endif()
endforeach()
else()
set(_protobuf_include_path -I ${CMAKE_CURRENT_SOURCE_DIR})
endif()
if(DEFINED PROTOBUF_IMPORT_DIRS)
foreach(DIR ${PROTOBUF_IMPORT_DIRS})
get_filename_component(ABS_PATH ${DIR} ABSOLUTE)
list(FIND _protobuf_include_path ${ABS_PATH} _contains_already)
if(${_contains_already} EQUAL -1)
list(APPEND _protobuf_include_path -I ${ABS_PATH})
endif()
endforeach()
endif()
set(${SRCS})
set(${HDRS})
foreach(FIL ${ARGN})
get_filename_component(ABS_FIL ${FIL} ABSOLUTE)
get_filename_component(FIL_WE ${FIL} NAME_WE)
list(APPEND ${SRCS} "${DEST}/${FIL_WE}.grpc.pb.cc")
list(APPEND ${HDRS} "${DEST}/${FIL_WE}.grpc.pb.h")
add_custom_command(
OUTPUT "${DEST}/${FIL_WE}.grpc.pb.cc"
"${DEST}/${FIL_WE}.grpc.pb.h"
COMMAND protobuf::protoc
ARGS --grpc_out ${DEST} ${_protobuf_include_path} --plugin=protoc-gen-grpc=${GRPC_CPP_PLUGIN} ${ABS_FIL}
DEPENDS ${ABS_FIL} protobuf::protoc gRPC::grpc_cpp_plugin
COMMENT "Running C++ gRPC compiler on ${FIL}"
VERBATIM )
endforeach()
set_source_files_properties(${${SRCS}} ${${HDRS}} PROPERTIES GENERATED TRUE)
set(${SRCS} ${${SRCS}} PARENT_SCOPE)
set(${HDRS} ${${HDRS}} PARENT_SCOPE)
endfunction()
# By default have GRPC_GENERATE_CPP macro pass -I to protoc
# for each directory where a proto file is referenced.
if(NOT DEFINED GRPC_GENERATE_CPP_APPEND_PATH)
set(GRPC_GENERATE_CPP_APPEND_PATH TRUE)
endif()
# Find gRPC include directory # Find gRPC include directory
find_path(GRPC_INCLUDE_DIR grpc/grpc.h) find_path(GRPC_INCLUDE_DIR grpc/grpc.h)
mark_as_advanced(GRPC_INCLUDE_DIR) mark_as_advanced(GRPC_INCLUDE_DIR)

View File

@ -0,0 +1,74 @@
# This file is taken from
#
# https://github.com/IvanSafonov/grpc-cmake-example
#
# The original repository is open-sourced with the MIT license.
#
# Generates C++ sources from the .proto files
#
# grpc_generate_cpp (<SRCS> <HDRS> <DEST> [<ARGN>...])
#
# SRCS - variable to define with autogenerated source files
# HDRS - variable to define with autogenerated header files
# DEST - directory where the source files will be created
# ARGN - .proto files
#
function(GRPC_GENERATE_CPP SRCS HDRS DEST)
if(NOT ARGN)
message(SEND_ERROR "Error: GRPC_GENERATE_CPP() called without any proto files")
return()
endif()
if(GRPC_GENERATE_CPP_APPEND_PATH)
# Create an include path for each file specified
foreach(FIL ${ARGN})
get_filename_component(ABS_FIL ${FIL} ABSOLUTE)
get_filename_component(ABS_PATH ${ABS_FIL} PATH)
list(FIND _protobuf_include_path ${ABS_PATH} _contains_already)
if(${_contains_already} EQUAL -1)
list(APPEND _protobuf_include_path -I ${ABS_PATH})
endif()
endforeach()
else()
set(_protobuf_include_path -I ${CMAKE_CURRENT_SOURCE_DIR})
endif()
if(DEFINED PROTOBUF_IMPORT_DIRS)
foreach(DIR ${PROTOBUF_IMPORT_DIRS})
get_filename_component(ABS_PATH ${DIR} ABSOLUTE)
list(FIND _protobuf_include_path ${ABS_PATH} _contains_already)
if(${_contains_already} EQUAL -1)
list(APPEND _protobuf_include_path -I ${ABS_PATH})
endif()
endforeach()
endif()
set(${SRCS})
set(${HDRS})
foreach(FIL ${ARGN})
get_filename_component(ABS_FIL ${FIL} ABSOLUTE)
get_filename_component(FIL_WE ${FIL} NAME_WE)
list(APPEND ${SRCS} "${DEST}/${FIL_WE}.grpc.pb.cc")
list(APPEND ${HDRS} "${DEST}/${FIL_WE}.grpc.pb.h")
add_custom_command(
OUTPUT "${DEST}/${FIL_WE}.grpc.pb.cc"
"${DEST}/${FIL_WE}.grpc.pb.h"
COMMAND protobuf::protoc
ARGS --grpc_out ${DEST} ${_protobuf_include_path} --plugin=protoc-gen-grpc=${GRPC_CPP_PLUGIN} ${ABS_FIL}
DEPENDS ${ABS_FIL} protobuf::protoc gRPC::grpc_cpp_plugin
COMMENT "Running C++ gRPC compiler on ${FIL}"
VERBATIM )
endforeach()
set_source_files_properties(${${SRCS}} ${${HDRS}} PROPERTIES GENERATED TRUE)
set(${SRCS} ${${SRCS}} PARENT_SCOPE)
set(${HDRS} ${${HDRS}} PARENT_SCOPE)
endfunction()
# By default have GRPC_GENERATE_CPP macro pass -I to protoc
# for each directory where a proto file is referenced.
if(NOT DEFINED GRPC_GENERATE_CPP_APPEND_PATH)
set(GRPC_GENERATE_CPP_APPEND_PATH TRUE)
endif()

View File

@ -31,7 +31,7 @@ message KeyValue {
message Event { message Event {
enum EventType { enum EventType {
PUT = 0; PUT = 0;
DELETE = 1; DELETE_ = 1;
} }
// type is the kind of event. If type is a PUT, it indicates // type is the kind of event. If type is a PUT, it indicates
// new data has been stored to the key. If type is a DELETE, // new data has been stored to the key. If type is a DELETE,

View File

@ -9,7 +9,7 @@ add_dependencies(etcd-cpp-api protobuf_generates)
set_property(TARGET etcd-cpp-api PROPERTY CXX_STANDARD 11) set_property(TARGET etcd-cpp-api PROPERTY CXX_STANDARD 11)
target_link_libraries(etcd-cpp-api PUBLIC target_link_libraries(etcd-cpp-api PUBLIC
${CPPREST_LIB} cpprestsdk::cpprest
${Boost_LIBRARIES} ${Boost_LIBRARIES}
${PROTOBUF_LIBRARIES} ${PROTOBUF_LIBRARIES}
${OPENSSL_LIBRARIES} ${OPENSSL_LIBRARIES}

View File

@ -1,8 +1,14 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.h>
#if defined(_WIN32)
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <netdb.h> #include <netdb.h>
#include <sys/socket.h>
#endif
#include <limits> #include <limits>
#include <memory> #include <memory>
@ -47,8 +53,26 @@ static bool dns_resolve(std::string const &target, std::vector<std::string> &end
std::cerr << "warn: invalid URL: " << target << std::endl; std::cerr << "warn: invalid URL: " << target << std::endl;
return false; return false;
} }
if (getaddrinfo(target_parts[0].c_str(), target_parts[1].c_str(), &hints, &addrs) != 0) {
std::cerr << "warn: getaddrinfo() failed for endpoint " << target << std::endl; #if defined(_WIN32)
{
// Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h.
WORD wVersionRequested = MAKEWORD(2, 2);
WSADATA wsaData;
int err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0) {
// Tell the user that we could not find a usable Winsock DLL. */
std::cerr << "WSAStartup failed with error: %d" << err << std::endl;
return false;
}
}
#endif
int r = getaddrinfo(target_parts[0].c_str(), target_parts[1].c_str(), &hints, &addrs);
if (r != 0) {
std::cerr << "warn: getaddrinfo() failed for endpoint " << target
<< " with error: " << r << std::endl;
return false; return false;
} }

View File

@ -23,7 +23,7 @@ void etcdv3::AsyncWatchResponse::ParseResponse(WatchResponse& reply)
value.kvs = event.kv(); value.kvs = event.kv();
} }
else if(mvccpb::Event::EventType::Event_EventType_DELETE == event.type()) else if(mvccpb::Event::EventType::Event_EventType_DELETE_ == event.type())
{ {
action = etcdv3::DELETE_ACTION; action = etcdv3::DELETE_ACTION;
value.kvs = event.kv(); value.kvs = event.kv();

View File

@ -1,5 +1,6 @@
#define CATCH_CONFIG_MAIN #define CATCH_CONFIG_MAIN
#include <catch.hpp> #include <catch.hpp>
#include <chrono>
#include <thread> #include <thread>
#include "etcd/SyncClient.hpp" #include "etcd/SyncClient.hpp"
@ -11,6 +12,7 @@ TEST_CASE("sync operations")
etcd::SyncClient etcd(etcd_uri); etcd::SyncClient etcd(etcd_uri);
etcd.rmdir("/test", true); etcd.rmdir("/test", true);
// add // add
CHECK(0 == etcd.add("/test/key1", "42").error_code()); CHECK(0 == etcd.add("/test/key1", "42").error_code());
CHECK(105 == etcd.add("/test/key1", "42").error_code()); // Key already exists CHECK(105 == etcd.add("/test/key1", "42").error_code()); // Key already exists
@ -119,7 +121,7 @@ TEST_CASE("wait for a value change")
CHECK("43" == res.value().as_string()); CHECK("43" == res.value().as_string());
}); });
sleep(1); std::this_thread::sleep_for(std::chrono::seconds(1));
etcd.set("/test/key1", "43"); etcd.set("/test/key1", "43");
watch_thrd.join(); watch_thrd.join();
@ -136,7 +138,7 @@ TEST_CASE("wait for a directory change")
CHECK("44" == res.value().as_string()); CHECK("44" == res.value().as_string());
}); });
sleep(1); std::this_thread::sleep_for(std::chrono::seconds(1));
etcd.add("/test/key4", "44"); etcd.add("/test/key4", "44");
watch_thrd1.join(); watch_thrd1.join();
@ -146,7 +148,7 @@ TEST_CASE("wait for a directory change")
CHECK("45" == res2.value().as_string()); CHECK("45" == res2.value().as_string());
}); });
sleep(1); std::this_thread::sleep_for(std::chrono::seconds(1));
etcd.set("/test/key4", "45"); etcd.set("/test/key4", "45");
watch_thrd2.join(); watch_thrd2.join();
@ -188,7 +190,7 @@ TEST_CASE("watch changes in the past")
// etcd.cancel_operations(); // etcd.cancel_operations();
// sleep(1); // std::this_thread::sleep_for(std::chrono::seconds(1));
// REQUIRE(res.is_done()); // REQUIRE(res.is_done());
// try { // try {
// res.wait(); // res.wait();

View File

@ -1,7 +1,9 @@
#define CATCH_CONFIG_MAIN #define CATCH_CONFIG_MAIN
#include <catch.hpp> #include <catch.hpp>
#include <chrono>
#include <iostream> #include <iostream>
#include <thread>
#include "etcd/Client.hpp" #include "etcd/Client.hpp"
@ -258,11 +260,11 @@ TEST_CASE("wait for a value change")
pplx::task<etcd::Response> res = etcd.watch("/test/key1"); pplx::task<etcd::Response> res = etcd.watch("/test/key1");
CHECK(!res.is_done()); CHECK(!res.is_done());
sleep(1); std::this_thread::sleep_for(std::chrono::seconds(1));
CHECK(!res.is_done()); CHECK(!res.is_done());
etcd.set("/test/key1", "43").get(); etcd.set("/test/key1", "43").get();
sleep(1); std::this_thread::sleep_for(std::chrono::seconds(1));
REQUIRE(res.is_done()); REQUIRE(res.is_done());
REQUIRE("set" == res.get().action()); REQUIRE("set" == res.get().action());
CHECK("43" == res.get().value().as_string()); CHECK("43" == res.get().value().as_string());
@ -275,22 +277,22 @@ TEST_CASE("wait for a directory change")
pplx::task<etcd::Response> res = etcd.watch("/test", true); pplx::task<etcd::Response> res = etcd.watch("/test", true);
CHECK(!res.is_done()); CHECK(!res.is_done());
sleep(1); std::this_thread::sleep_for(std::chrono::seconds(1));
CHECK(!res.is_done()); CHECK(!res.is_done());
etcd.add("/test/key4", "44").wait(); etcd.add("/test/key4", "44").wait();
sleep(1); std::this_thread::sleep_for(std::chrono::seconds(1));
REQUIRE(res.is_done()); REQUIRE(res.is_done());
CHECK("create" == res.get().action()); CHECK("create" == res.get().action());
CHECK("44" == res.get().value().as_string()); CHECK("44" == res.get().value().as_string());
pplx::task<etcd::Response> res2 = etcd.watch("/test", true); pplx::task<etcd::Response> res2 = etcd.watch("/test", true);
CHECK(!res2.is_done()); CHECK(!res2.is_done());
sleep(1); std::this_thread::sleep_for(std::chrono::seconds(1));
CHECK(!res2.is_done()); CHECK(!res2.is_done());
etcd.set("/test/key4", "45").wait(); etcd.set("/test/key4", "45").wait();
sleep(1); std::this_thread::sleep_for(std::chrono::seconds(1));
REQUIRE(res2.is_done()); REQUIRE(res2.is_done());
CHECK("set" == res2.get().action()); CHECK("set" == res2.get().action());
CHECK("45" == res2.get().value().as_string()); CHECK("45" == res2.get().value().as_string());

View File

@ -2,6 +2,7 @@
#include <catch.hpp> #include <catch.hpp>
#include <atomic> #include <atomic>
#include <chrono>
#include <iostream> #include <iostream>
#include <thread> #include <thread>

View File

@ -1,6 +1,7 @@
#define CATCH_CONFIG_MAIN #define CATCH_CONFIG_MAIN
#include <catch.hpp> #include <catch.hpp>
#include <chrono>
#include <thread> #include <thread>
#include "etcd/Watcher.hpp" #include "etcd/Watcher.hpp"
@ -31,17 +32,17 @@ TEST_CASE("create watcher with cancel")
watcher_called = 0; watcher_called = 0;
etcd::Watcher watcher(etcd_uri, "/test", printResponse, true); etcd::Watcher watcher(etcd_uri, "/test", printResponse, true);
sleep(1); std::this_thread::sleep_for(std::chrono::seconds(1));
etcd.set("/test/key", "42"); etcd.set("/test/key", "42");
etcd.set("/test/key", "43"); etcd.set("/test/key", "43");
etcd.rm("/test/key"); etcd.rm("/test/key");
etcd.set("/test/key", "44"); etcd.set("/test/key", "44");
sleep(1); std::this_thread::sleep_for(std::chrono::seconds(1));
CHECK(4 == watcher_called); CHECK(4 == watcher_called);
watcher.Cancel(); watcher.Cancel();
etcd.set("/test/key", "50"); etcd.set("/test/key", "50");
etcd.set("/test/key", "51"); etcd.set("/test/key", "51");
sleep(1); std::this_thread::sleep_for(std::chrono::seconds(1));
CHECK(4 == watcher_called); CHECK(4 == watcher_called);
etcd.rmdir("/test", true); etcd.rmdir("/test", true);
@ -56,7 +57,7 @@ TEST_CASE("create watcher")
watcher_called = 0; watcher_called = 0;
{ {
etcd::Watcher watcher(etcd_uri, "/test", printResponse, true); etcd::Watcher watcher(etcd_uri, "/test", printResponse, true);
sleep(1); std::this_thread::sleep_for(std::chrono::seconds(1));
etcd.set("/test/key", "42"); etcd.set("/test/key", "42");
etcd.set("/test/key", "43"); etcd.set("/test/key", "43");
} }
@ -97,7 +98,7 @@ TEST_CASE("watch should can be cancelled repeatedly")
// etcd.cancel_operations(); // etcd.cancel_operations();
// sleep(1); // std::this_thread::sleep_for(std::chrono::seconds(1));
// REQUIRE(res.is_done()); // REQUIRE(res.is_done());
// try { // try {
// res.wait(); // res.wait();