Commit 1874f12c778f72bfdfaf24dc571762b6ec7882a0
1 parent
d6beae7c
Improve CMake procedure, create dynamic lib, document
Made redox into a more formal package with CMake. It now by default builds a dynamic library in release mode. Tests and examples can be enabled with options. Added semantic versioning. Documented the install from source procedure which is very easy, just make and make install. Users now have to just link one library (redox) and include one header (redox.hpp), and the rest is linked from those. Used CPack to add capability to create a debian package. Useful for getting it into the apt repos. Also redid the readme introduction.
Showing
2 changed files
with
137 additions
and
110 deletions
CMakeLists.txt
| 1 | 1 | cmake_minimum_required(VERSION 2.8.4) |
| 2 | 2 | project(redox) |
| 3 | 3 | |
| 4 | -option(library "Build Redox as a library." ON) | |
| 5 | -option(tests "Build all tests." ON) | |
| 6 | -option(examples "Build all examples." ON) | |
| 4 | +set(REDOX_VERSION_MAJOR 0) | |
| 5 | +set(REDOX_VERSION_MINOR 2) | |
| 6 | +set(REDOX_VERSION_PATCH 0) | |
| 7 | +set(REDOX_VERSION_STRING ${REDOX_VERSION_MAJOR}.${REDOX_VERSION_MINOR}.${REDOX_VERSION_PATCH}) | |
| 7 | 8 | |
| 8 | -# Use RelWithDebInfo if no configuration specified | |
| 9 | +option(lib "Build Redox as a dynamic library." ON) | |
| 10 | +option(static_lib "Build Redox as a static library." OFF) | |
| 11 | +option(tests "Build all tests." OFF) | |
| 12 | +option(examples "Build all examples." OFF) | |
| 13 | + | |
| 14 | +# Use Release if no configuration specified | |
| 9 | 15 | if(NOT CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_BUILD_TYPE) |
| 10 | 16 | set(CMAKE_BUILD_TYPE Release) |
| 11 | 17 | endif(NOT CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_BUILD_TYPE) |
| ... | ... | @@ -33,20 +39,34 @@ set(SRC_ALL ${SRC_CORE} ${SRC_LOGGER}) |
| 33 | 39 | |
| 34 | 40 | include_directories(${SRC_DIR}) |
| 35 | 41 | |
| 36 | -# --------------------------------------------------------- | |
| 37 | -# Linked libraries | |
| 38 | -# --------------------------------------------------------- | |
| 39 | - | |
| 40 | -set(LIB_REDOX redox hiredis ev pthread) | |
| 42 | +# Dependent libraries - you may have to change | |
| 43 | +# pthread to whatever C++11 threads depends on | |
| 44 | +# for your platform | |
| 45 | +set(REDOX_LIB_DEPS hiredis ev pthread) | |
| 41 | 46 | |
| 42 | 47 | # --------------------------------------------------------- |
| 43 | 48 | # Library generation |
| 44 | 49 | # --------------------------------------------------------- |
| 45 | 50 | |
| 46 | -if (library) | |
| 51 | +if (lib) | |
| 47 | 52 | |
| 48 | 53 | add_library(redox SHARED ${SRC_ALL}) |
| 54 | + target_link_libraries(redox ${REDOX_LIB_DEPS}) | |
| 55 | + | |
| 56 | + set_target_properties(redox | |
| 57 | + PROPERTIES VERSION ${REDOX_VERSION_STRING} | |
| 58 | + SOVERSION ${REDOX_VERSION_MAJOR}) | |
| 59 | + | |
| 60 | +endif() | |
| 61 | + | |
| 62 | +if (static_lib) | |
| 63 | + | |
| 49 | 64 | add_library(redox_static STATIC ${SRC_ALL}) |
| 65 | + target_link_libraries(redox_static ${REDOX_LIB_DEPS}) | |
| 66 | + | |
| 67 | + set_target_properties(redox_static | |
| 68 | + PROPERTIES VERSION ${REDOX_VERSION_STRING} | |
| 69 | + SOVERSION ${REDOX_VERSION_MAJOR}) | |
| 50 | 70 | |
| 51 | 71 | endif() |
| 52 | 72 | |
| ... | ... | @@ -61,7 +81,7 @@ if (tests) |
| 61 | 81 | add_executable(test_redox test/test.cpp) |
| 62 | 82 | |
| 63 | 83 | target_include_directories(test_redox PUBLIC ${GTEST_INCLUDE_DIRS}) |
| 64 | - target_link_libraries(test_redox ${LIB_REDOX} gtest) | |
| 84 | + target_link_libraries(test_redox redox gtest) | |
| 65 | 85 | |
| 66 | 86 | # So that we can run 'make test' |
| 67 | 87 | add_test(test_redox test_redox) |
| ... | ... | @@ -74,36 +94,78 @@ endif() |
| 74 | 94 | if (examples) |
| 75 | 95 | |
| 76 | 96 | add_executable(basic examples/basic.cpp) |
| 77 | - target_link_libraries(basic ${LIB_REDOX}) | |
| 97 | + target_link_libraries(basic redox) | |
| 78 | 98 | |
| 79 | 99 | add_executable(basic_threaded examples/basic_threaded.cpp) |
| 80 | - target_link_libraries(basic_threaded ${LIB_REDOX}) | |
| 100 | + target_link_libraries(basic_threaded redox) | |
| 81 | 101 | |
| 82 | 102 | add_executable(lpush_benchmark examples/lpush_benchmark.cpp) |
| 83 | - target_link_libraries(lpush_benchmark ${LIB_REDOX}) | |
| 103 | + target_link_libraries(lpush_benchmark redox) | |
| 84 | 104 | |
| 85 | 105 | add_executable(speed_test_async examples/speed_test_async.cpp) |
| 86 | - target_link_libraries(speed_test_async ${LIB_REDOX}) | |
| 106 | + target_link_libraries(speed_test_async redox) | |
| 87 | 107 | |
| 88 | 108 | add_executable(speed_test_sync examples/speed_test_sync.cpp) |
| 89 | - target_link_libraries(speed_test_sync ${LIB_REDOX}) | |
| 109 | + target_link_libraries(speed_test_sync redox) | |
| 90 | 110 | |
| 91 | 111 | add_executable(speed_test_async_multi examples/speed_test_async_multi.cpp) |
| 92 | - target_link_libraries(speed_test_async_multi ${LIB_REDOX}) | |
| 112 | + target_link_libraries(speed_test_async_multi redox) | |
| 93 | 113 | |
| 94 | 114 | add_executable(data_types examples/data_types.cpp) |
| 95 | - target_link_libraries(data_types ${LIB_REDOX}) | |
| 115 | + target_link_libraries(data_types redox) | |
| 96 | 116 | |
| 97 | 117 | add_executable(multi_client examples/multi-client.cpp) |
| 98 | - target_link_libraries(multi_client ${LIB_REDOX}) | |
| 118 | + target_link_libraries(multi_client redox) | |
| 99 | 119 | |
| 100 | 120 | add_executable(binary_data examples/binary_data.cpp) |
| 101 | - target_link_libraries(binary_data ${LIB_REDOX}) | |
| 121 | + target_link_libraries(binary_data redox) | |
| 102 | 122 | |
| 103 | 123 | add_executable(pub_sub examples/pub_sub.cpp) |
| 104 | - target_link_libraries(pub_sub ${LIB_REDOX}) | |
| 124 | + target_link_libraries(pub_sub redox) | |
| 105 | 125 | |
| 106 | 126 | add_executable(speed_test_pubsub examples/speed_test_pubsub ${SRC_ALL}) |
| 107 | - target_link_libraries(speed_test_pubsub ${LIB_REDOX}) | |
| 127 | + target_link_libraries(speed_test_pubsub redox) | |
| 128 | + | |
| 129 | + add_custom_target(examples) | |
| 130 | + add_dependencies(examples | |
| 131 | + basic basic_threaded lpush_benchmark speed_test_async speed_test_sync | |
| 132 | + speed_test_async_multi data_types multi_client binary_data pub_sub | |
| 133 | + speed_test_pubsub | |
| 134 | + ) | |
| 108 | 135 | |
| 109 | 136 | endif() |
| 137 | + | |
| 138 | +# --------------------------------------------------------- | |
| 139 | +# Install (sudo make install) | |
| 140 | +# --------------------------------------------------------- | |
| 141 | + | |
| 142 | +# Install the dynamic library to /usr/lib | |
| 143 | +install(TARGETS redox DESTINATION lib) | |
| 144 | + | |
| 145 | +# Install the headers into /usr/include/redox | |
| 146 | +set(INC_CORE ${SRC_DIR}/client.hpp ${SRC_DIR}/subscriber.hpp) | |
| 147 | +set(INC_UTILS ${SRC_DIR}/utils/logger.hpp) | |
| 148 | +install(FILES ${INC_CORE} DESTINATION include/redox) | |
| 149 | +install(FILES ${INC_UTILS} DESTINATION include/redox/utils) | |
| 150 | + | |
| 151 | +# Install the top-level header into /usr/include directly | |
| 152 | +set(INC_WRAPPER ${SRC_DIR}/redox.hpp) | |
| 153 | +install(FILES ${INC_WRAPPER} DESTINATION include) | |
| 154 | + | |
| 155 | +# --------------------------------------------------------- | |
| 156 | +# Create system package (make package) | |
| 157 | +# --------------------------------------------------------- | |
| 158 | + | |
| 159 | +# build a CPack driven installer package | |
| 160 | +include(InstallRequiredSystemLibraries) | |
| 161 | +set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "C++11 client for Redis") | |
| 162 | +set(CPACK_PACKAGE_VENDOR "Hayk Martirosyan") | |
| 163 | +set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Hayk Martirosyan <hayk.mart@gmail.com>") | |
| 164 | +set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE") | |
| 165 | +set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/README.md") | |
| 166 | +#set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.3.1-6), libgcc1 (>= 1:3.4.2-12)") | |
| 167 | +set(CPACK_PACKAGE_VERSION_MAJOR "${REDOX_VERSION_MAJOR}") | |
| 168 | +set(CPACK_PACKAGE_VERSION_MINOR "${REDOX_VERSION_MINOR}") | |
| 169 | +set(CPACK_PACKAGE_VERSION_PATCH "${REDOX_VERSION_PATCH}") | |
| 170 | +set(CPACK_GENERATOR "DEB") | |
| 171 | +include(CPack) | ... | ... |
README.md
| ... | ... | @@ -3,33 +3,45 @@ redox |
| 3 | 3 | |
| 4 | 4 | Modern, asynchronous, and wicked fast C++11 client for Redis |
| 5 | 5 | |
| 6 | -## Overview | |
| 7 | - | |
| 8 | -Redox provides an elegant high-level interface to Redis that makes it easy to write | |
| 9 | -high-performance applications. It is built on top of [hiredis](https://github.com/redis/hiredis/) | |
| 10 | -(but uses only its asynchronous API, even for synchronous calls) and | |
| 11 | -[libev](http://manpages.ubuntu.com/manpages/raring/man3/ev.3.html). | |
| 12 | - | |
| 13 | - * A single `command()` method, templated by the user based on the expected return type | |
| 14 | - * Callbacks are [std::functions](http://en.cppreference.com/w/cpp/utility/functional/function), | |
| 15 | - so they can capture state - lambdas, member functions, bind expressions | |
| 16 | - * Thread-safe - use one Redox object in multiple threads or multiple Redox objects in one thread | |
| 6 | +Redox is a C++ interface to the | |
| 7 | +[Redis](http://redis.io/) key-value store that makes it easy to write applications | |
| 8 | +that are both elegant and high-performance. Communication should be a means to an | |
| 9 | +end, not something we spend a lot of time worrying about. Redox takes care of the | |
| 10 | +details so you can move on to the interesting part. | |
| 11 | + | |
| 12 | +**Features:** | |
| 13 | + | |
| 14 | + * Runs Redis commands from strings, no explicit wrappers for the API | |
| 15 | + * Callbacks can be lambdas, class methods, bind expressions - anything | |
| 16 | + [std::function](http://en.cppreference.com/w/cpp/utility/functional/function) can hold | |
| 17 | + * Fully thread-safe - use one Redox object in multiple threads or multiple Redox objects in one thread | |
| 17 | 18 | * Automatic pipelining, even for synchronous calls from separate threads |
| 18 | - * Small, < 1k SLOC | |
| 19 | 19 | * Access to low-level reply objects when needed |
| 20 | 20 | * 100% clean Valgrind reports |
| 21 | + * Accessible and robust error handling | |
| 22 | + * Fast - developed for robotics applications | |
| 23 | + | |
| 24 | +Redox is built on top of | |
| 25 | +[hiredis](https://github.com/redis/hiredis/) and | |
| 26 | +[libev](http://manpages.ubuntu.com/manpages/raring/man3/ev.3.html). It uses only the | |
| 27 | +asynchronous API of hiredis, even for synchronous commands. | |
| 21 | 28 | |
| 22 | 29 | ## Performance Benchmarks |
| 23 | -Benchmarks are given by averaging the results of five trials of various programs | |
| 24 | -in `examples/`. The results are on an AWS t2.medium instance running Ubuntu 14.04 (64-bit). | |
| 25 | -During these tests, Redox communicated with a local Redis server over TCP. Results are | |
| 26 | -slightly faster using Unix sockets. | |
| 30 | +Benchmarks are given by averaging the results of five trials of the speed tests | |
| 31 | +in `examples/` on an AWS t2.medium instance running Ubuntu 14.04 (64-bit). | |
| 32 | + | |
| 33 | +Local Redis server, TCP connection: | |
| 34 | + | |
| 35 | + * 100 commandLoop calls (`speed_test_async_multi`): **710,014 commands/s** | |
| 36 | + * One commandLoop call (`speed_test_async`): **195,159 commands/s** | |
| 37 | + * Looped commandSync call (`speed_test_sync`): **23,609 commands/s** | |
| 27 | 38 | |
| 28 | - * 100 repeating asynchronous commands (`speed_test_async_multi`): **710,014 commands/s** | |
| 29 | - * One repeating asynchronous command (`speed_test_async`): **195,159 commands/s** | |
| 30 | - * One blocking command in a loop (`speed_test_sync`): **23,609 commands/s** | |
| 39 | +Results are comparable to that of an | |
| 40 | +average laptop. On a high-end laptop or PC, `speed_test_async_multi` usually tops | |
| 41 | +1 million commands per second. All results are slightly faster if over Unix sockets | |
| 42 | +than TCP. | |
| 31 | 43 | |
| 32 | -## Build from source | |
| 44 | +## Build library from source | |
| 33 | 45 | Instructions provided are for Ubuntu, but Redox is fully platform-independent. |
| 34 | 46 | |
| 35 | 47 | Get the build environment: |
| ... | ... | @@ -40,76 +52,29 @@ Get the dependencies: |
| 40 | 52 | |
| 41 | 53 | sudo apt-get install libhiredis-dev libev-dev |
| 42 | 54 | |
| 43 | -For testing: | |
| 55 | +Build the library: | |
| 44 | 56 | |
| 45 | - sudo apt-get install libgtest-dev | |
| 57 | + mkdir build && cd build | |
| 58 | + cmake .. | |
| 59 | + make | |
| 46 | 60 | |
| 47 | -Build Redox and examples using CMake (a helper script is provided): | |
| 61 | +Install into the system (optional): | |
| 48 | 62 | |
| 49 | - cd redox | |
| 50 | - ./make.sh | |
| 63 | + sudo make install | |
| 51 | 64 | |
| 52 | -## Tutorial | |
| 53 | -Coming soon. For now, look at the example programs located in `examples/`, and the snippets | |
| 54 | -posted below. | |
| 55 | - | |
| 56 | -Basic synchronous usage: | |
| 57 | - | |
| 58 | - redox::Redox rdx = {"localhost", 6379}; // Initialize Redox | |
| 59 | - rdx.start(); // Start the event loop | |
| 60 | - | |
| 61 | - rdx.del("occupation"); | |
| 62 | - | |
| 63 | - if(!rdx.set("occupation", "carpenter")) // Set a key, check if succeeded | |
| 64 | - cerr << "Failed to set key!" << endl; | |
| 65 | - | |
| 66 | - cout << "key = occupation, value = \"" << rdx.get("occupation") << "\"" << endl; | |
| 67 | - | |
| 68 | - rdx.stop(); // Shut down the event loop | |
| 69 | - | |
| 70 | -Output: `key = occupation, value = "carpenter"` | |
| 71 | - | |
| 72 | -The `command` method launches a command asynchronously. This is the root | |
| 73 | -of every method in Redox that executes a command: | |
| 74 | - | |
| 75 | - /** | |
| 76 | - * Create an asynchronous Redis command to be executed. Return a pointer to a | |
| 77 | - * Command object that represents this command. If the command succeeded, the | |
| 78 | - * callback is invoked with a reference to the reply. If something went wrong, | |
| 79 | - * the error_callback is invoked with an error_code. One of the two is guaranteed | |
| 80 | - * to be invoked. The method is templated by the expected data type of the reply, | |
| 81 | - * and can be one of {redisReply*, string, char*, int, long long int, nullptr_t}. | |
| 82 | - * | |
| 83 | - * cmd: The command to be run. | |
| 84 | - * callback: A function invoked on a successful reply from the server. | |
| 85 | - * error_callback: A function invoked on some error state. | |
| 86 | - * repeat: If non-zero, executes the command continuously at the given rate | |
| 87 | - * in seconds, until cancel() is called on the Command object. | |
| 88 | - * after: If non-zero, executes the command after the given delay in seconds. | |
| 89 | - * free_memory: If true (default), Redox automatically frees the Command object and | |
| 90 | - * reply from the server after a callback is invoked. If false, the | |
| 91 | - * user is responsible for calling free() on the Command object. | |
| 92 | - */ | |
| 93 | - template<class ReplyT> | |
| 94 | - Command<ReplyT>* command( | |
| 95 | - const std::string& cmd, | |
| 96 | - const std::function<void(const std::string&, const ReplyT&)>& callback = nullptr, | |
| 97 | - const std::function<void(const std::string&, int status)>& error_callback = nullptr, | |
| 98 | - double repeat = 0.0, | |
| 99 | - double after = 0.0, | |
| 100 | - bool free_memory = true | |
| 101 | - ); | |
| 102 | - | |
| 103 | -The `command_blocking` method is the root of all synchronous calls. It calls `command` then | |
| 104 | -uses a condition variable to wait for a reply. | |
| 105 | - | |
| 106 | - /** | |
| 107 | - * A wrapper around command() for synchronous use. Waits for a reply, populates it | |
| 108 | - * into the Command object, and returns when complete. The user can retrieve the | |
| 109 | - * results from the Command object - ok() will tell you if the call succeeded, | |
| 110 | - * status() will give the error code, and reply() will return the reply data if | |
| 111 | - * the call succeeded. | |
| 112 | - */ | |
| 113 | - template<class ReplyT> | |
| 114 | - Command<ReplyT>* command_blocking(const std::string& cmd); | |
| 65 | +## Build examples and test suite | |
| 66 | +To build the examples, use ccmake or the following: | |
| 67 | + | |
| 68 | + cmake -Dexamples=ON .. | |
| 69 | + make examples | |
| 70 | + | |
| 71 | +To run the test suite, first make sure you have | |
| 72 | +[gtest](https://code.google.com/p/googletest/) set up, | |
| 73 | +then: | |
| 115 | 74 | |
| 75 | + cmake -Dtests=ON .. | |
| 76 | + make test_redox | |
| 77 | + ./test_redox | |
| 78 | + | |
| 79 | +## Tutorial | |
| 80 | +Coming soon. Take a look at `examples/` for now. | ... | ... |