Commit 1874f12c778f72bfdfaf24dc571762b6ec7882a0

Authored by Hayk Martirosyan
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.
... ...