From b60f7eecdd9e2976685fa21fd4c8081902c65fed Mon Sep 17 00:00:00 2001 From: Tao He Date: Sun, 20 Dec 2020 00:02:04 +0800 Subject: [PATCH] Fixes for windows (vcpkg environment) compatibility. Signed-off-by: Tao He --- CMakeLists.txt | 22 ++++++---- cmake/FindGRPC.cmake | 71 ------------------------------ cmake/GenerateProtobufGRPC.cmake | 74 ++++++++++++++++++++++++++++++++ proto/kv.proto | 2 +- src/CMakeLists.txt | 2 +- src/Client.cpp | 30 +++++++++++-- src/v3/AsyncWatchResponse.cpp | 2 +- tst/EtcdSyncTest.cpp | 10 +++-- tst/EtcdTest.cpp | 14 +++--- tst/LockTest.cpp | 1 + tst/WatcherTest.cpp | 11 ++--- 11 files changed, 139 insertions(+), 100 deletions(-) create mode 100644 cmake/GenerateProtobufGRPC.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index ccb1d08..d64f614 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,9 +8,6 @@ set(etcd-cpp-api_VERSION_MINOR 1) 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) if (APPLE) # 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() + find_package(OpenSSL REQUIRED) find_package(Protobuf REQUIRED) +find_package(cpprestsdk REQUIRED) -include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindGRPC.cmake) -set(GRPC_LIBRARIES ${GPR_LIBRARY} ${GRPC_LIBRARY} ${GRPC_GRPC++_LIBRARY}) +find_package(gRPC) +if(gRPC_FOUND) + set(GRPC_LIBRARIES gRPC::gpr gRPC::grpc gRPC::grpc++) +else() + include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindGRPC.cmake) + 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`. include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/GenerateProtobuf.cmake) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/proto) -include_directories(SYSTEM ${CPPREST_INCLUDE_DIR} - ${Boost_INCLUDE_DIR} +include_directories(SYSTEM ${Boost_INCLUDE_DIR} ${PROTOBUF_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIR}) include_directories(${CMAKE_CURRENT_SOURCE_DIR}) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wpedantic -Werror -Wno-string-compare -std=c++11") +if(NOT CMAKE_CXX_COMPILER_ID MATCHES "MSVC") + 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_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) diff --git a/cmake/FindGRPC.cmake b/cmake/FindGRPC.cmake index ee2c6dc..f2e9132 100644 --- a/cmake/FindGRPC.cmake +++ b/cmake/FindGRPC.cmake @@ -4,7 +4,6 @@ # # The original repository is open-sourced with the MIT license. # -# # Locate and configure the gRPC library # # Adds the following targets: @@ -16,76 +15,6 @@ # gRPC::grpc_cpp_plugin - C++ generator plugin for Protocol Buffers # -# -# Generates C++ sources from the .proto files -# -# grpc_generate_cpp ( [...]) -# -# 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_path(GRPC_INCLUDE_DIR grpc/grpc.h) mark_as_advanced(GRPC_INCLUDE_DIR) diff --git a/cmake/GenerateProtobufGRPC.cmake b/cmake/GenerateProtobufGRPC.cmake new file mode 100644 index 0000000..66b8850 --- /dev/null +++ b/cmake/GenerateProtobufGRPC.cmake @@ -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 - 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() diff --git a/proto/kv.proto b/proto/kv.proto index 23c911b..a7c4f73 100644 --- a/proto/kv.proto +++ b/proto/kv.proto @@ -31,7 +31,7 @@ message KeyValue { message Event { enum EventType { PUT = 0; - DELETE = 1; + DELETE_ = 1; } // 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, diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 533f85a..60f7667 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -9,7 +9,7 @@ add_dependencies(etcd-cpp-api protobuf_generates) set_property(TARGET etcd-cpp-api PROPERTY CXX_STANDARD 11) target_link_libraries(etcd-cpp-api PUBLIC - ${CPPREST_LIB} + cpprestsdk::cpprest ${Boost_LIBRARIES} ${PROTOBUF_LIBRARIES} ${OPENSSL_LIBRARIES} diff --git a/src/Client.cpp b/src/Client.cpp index aa1cb3a..d33b98a 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -1,8 +1,14 @@ #include #include #include -#include + +#if defined(_WIN32) +#include +#include +#else #include +#include +#endif #include #include @@ -47,8 +53,26 @@ static bool dns_resolve(std::string const &target, std::vector &end std::cerr << "warn: invalid URL: " << target << std::endl; 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; } diff --git a/src/v3/AsyncWatchResponse.cpp b/src/v3/AsyncWatchResponse.cpp index d4dad4e..c06b116 100644 --- a/src/v3/AsyncWatchResponse.cpp +++ b/src/v3/AsyncWatchResponse.cpp @@ -23,7 +23,7 @@ void etcdv3::AsyncWatchResponse::ParseResponse(WatchResponse& reply) 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; value.kvs = event.kv(); diff --git a/tst/EtcdSyncTest.cpp b/tst/EtcdSyncTest.cpp index 16ebd83..549f0ae 100644 --- a/tst/EtcdSyncTest.cpp +++ b/tst/EtcdSyncTest.cpp @@ -1,5 +1,6 @@ #define CATCH_CONFIG_MAIN #include +#include #include #include "etcd/SyncClient.hpp" @@ -11,6 +12,7 @@ TEST_CASE("sync operations") etcd::SyncClient etcd(etcd_uri); etcd.rmdir("/test", true); + // add CHECK(0 == etcd.add("/test/key1", "42").error_code()); 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()); }); - sleep(1); + std::this_thread::sleep_for(std::chrono::seconds(1)); etcd.set("/test/key1", "43"); watch_thrd.join(); @@ -136,7 +138,7 @@ TEST_CASE("wait for a directory change") CHECK("44" == res.value().as_string()); }); - sleep(1); + std::this_thread::sleep_for(std::chrono::seconds(1)); etcd.add("/test/key4", "44"); watch_thrd1.join(); @@ -146,7 +148,7 @@ TEST_CASE("wait for a directory change") CHECK("45" == res2.value().as_string()); }); - sleep(1); + std::this_thread::sleep_for(std::chrono::seconds(1)); etcd.set("/test/key4", "45"); watch_thrd2.join(); @@ -188,7 +190,7 @@ TEST_CASE("watch changes in the past") // etcd.cancel_operations(); -// sleep(1); +// std::this_thread::sleep_for(std::chrono::seconds(1)); // REQUIRE(res.is_done()); // try { // res.wait(); diff --git a/tst/EtcdTest.cpp b/tst/EtcdTest.cpp index 0caf580..f755b71 100644 --- a/tst/EtcdTest.cpp +++ b/tst/EtcdTest.cpp @@ -1,7 +1,9 @@ #define CATCH_CONFIG_MAIN #include +#include #include +#include #include "etcd/Client.hpp" @@ -258,11 +260,11 @@ TEST_CASE("wait for a value change") pplx::task res = etcd.watch("/test/key1"); CHECK(!res.is_done()); - sleep(1); + std::this_thread::sleep_for(std::chrono::seconds(1)); CHECK(!res.is_done()); etcd.set("/test/key1", "43").get(); - sleep(1); + std::this_thread::sleep_for(std::chrono::seconds(1)); REQUIRE(res.is_done()); REQUIRE("set" == res.get().action()); CHECK("43" == res.get().value().as_string()); @@ -275,22 +277,22 @@ TEST_CASE("wait for a directory change") pplx::task res = etcd.watch("/test", true); CHECK(!res.is_done()); - sleep(1); + std::this_thread::sleep_for(std::chrono::seconds(1)); CHECK(!res.is_done()); etcd.add("/test/key4", "44").wait(); - sleep(1); + std::this_thread::sleep_for(std::chrono::seconds(1)); REQUIRE(res.is_done()); CHECK("create" == res.get().action()); CHECK("44" == res.get().value().as_string()); pplx::task res2 = etcd.watch("/test", true); CHECK(!res2.is_done()); - sleep(1); + std::this_thread::sleep_for(std::chrono::seconds(1)); CHECK(!res2.is_done()); etcd.set("/test/key4", "45").wait(); - sleep(1); + std::this_thread::sleep_for(std::chrono::seconds(1)); REQUIRE(res2.is_done()); CHECK("set" == res2.get().action()); CHECK("45" == res2.get().value().as_string()); diff --git a/tst/LockTest.cpp b/tst/LockTest.cpp index 5611c27..e31c739 100644 --- a/tst/LockTest.cpp +++ b/tst/LockTest.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include diff --git a/tst/WatcherTest.cpp b/tst/WatcherTest.cpp index 69d84ec..44edcda 100644 --- a/tst/WatcherTest.cpp +++ b/tst/WatcherTest.cpp @@ -1,6 +1,7 @@ #define CATCH_CONFIG_MAIN #include +#include #include #include "etcd/Watcher.hpp" @@ -31,17 +32,17 @@ TEST_CASE("create watcher with cancel") watcher_called = 0; 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", "43"); etcd.rm("/test/key"); etcd.set("/test/key", "44"); - sleep(1); + std::this_thread::sleep_for(std::chrono::seconds(1)); CHECK(4 == watcher_called); watcher.Cancel(); etcd.set("/test/key", "50"); etcd.set("/test/key", "51"); - sleep(1); + std::this_thread::sleep_for(std::chrono::seconds(1)); CHECK(4 == watcher_called); etcd.rmdir("/test", true); @@ -56,7 +57,7 @@ TEST_CASE("create watcher") watcher_called = 0; { 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", "43"); } @@ -97,7 +98,7 @@ TEST_CASE("watch should can be cancelled repeatedly") // etcd.cancel_operations(); -// sleep(1); +// std::this_thread::sleep_for(std::chrono::seconds(1)); // REQUIRE(res.is_done()); // try { // res.wait();