Hot questions for Using ZeroMQ in cmake

Question:

I'm just starting to learn how to work with zeromq libraries and using them in different C++ projects. The sample code that I wrote (actually copied from there tutorials)is this:

//  file: main.cpp
//  Hello World client in C++
//  Connects REQ socket to tcp://localhost:5555
//  Sends "Hello" to server, expects "World" back
//
#include <zmq.hpp>
#include <string>
#include <iostream>

int main ()
{
    //  Prepare our context and socket
    zmq::context_t context (1);
    zmq::socket_t socket (context, ZMQ_REQ);

    std::cout << "Connecting to hello world server…" << std::endl;
    socket.connect ("tcp://localhost:5555");

    //  Do 10 requests, waiting each time for a response
    for (int request_nbr = 0; request_nbr != 10; request_nbr++) {
        zmq::message_t request (5);
        memcpy (request.data (), "Hello", 5);
        std::cout << "Sending Hello " << request_nbr << "…" << std::endl;
        socket.send (request);

        //  Get the reply.
        zmq::message_t reply;
        socket.recv (&reply);
        std::cout << "Received World " << request_nbr << std::endl;
    }
    return 0;
}

and by building the output of this file with the command below:

g++ main.cpp -o test -lzmq

the file will be generated with no problem.

The problem that I have is that I want to build the file using CMake.Thus, I've written a CMakeLists.txt file as below:

cmake_minimum_required(VERSION 3.6)
project(test2)


set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -lzmq")


set(SOURCE_FILES main.cpp)
add_executable(test2 ${SOURCE_FILES})

The part that I thought will be enough to build the program is -lzmq but even with that piece included, I get the following error messages:

CMakeFiles/test2.dir/main.cpp.o: In function `zmq::error_t::error_t()':
/usr/include/zmq.hpp:62: undefined reference to `zmq_errno'
CMakeFiles/test2.dir/main.cpp.o: In function `zmq::error_t::what() const':
/usr/include/zmq.hpp:66: undefined reference to `zmq_strerror'
CMakeFiles/test2.dir/main.cpp.o: In function `zmq::message_t::message_t()':
/usr/include/zmq.hpp:107: undefined reference to `zmq_msg_init'
CMakeFiles/test2.dir/main.cpp.o: In function `zmq::message_t::message_t(unsigned long)':
/usr/include/zmq.hpp:114: undefined reference to `zmq_msg_init_size'
CMakeFiles/test2.dir/main.cpp.o: In function `zmq::message_t::~message_t()':
/usr/include/zmq.hpp:129: undefined reference to `zmq_msg_close'
CMakeFiles/test2.dir/main.cpp.o: In function `zmq::message_t::data()':
/usr/include/zmq.hpp:180: undefined reference to `zmq_msg_data'
CMakeFiles/test2.dir/main.cpp.o: In function `zmq::context_t::context_t(int)':
/usr/include/zmq.hpp:204: undefined reference to `zmq_init'
CMakeFiles/test2.dir/main.cpp.o: In function `zmq::context_t::~context_t()':
/usr/include/zmq.hpp:225: undefined reference to `zmq_term'
CMakeFiles/test2.dir/main.cpp.o: In function `zmq::socket_t::socket_t(zmq::context_t&, int)':
/usr/include/zmq.hpp:251: undefined reference to `zmq_socket'
CMakeFiles/test2.dir/main.cpp.o: In function `zmq::socket_t::close()':
/usr/include/zmq.hpp:283: undefined reference to `zmq_close'
CMakeFiles/test2.dir/main.cpp.o: In function `zmq::socket_t::connect(char const*)':
/usr/include/zmq.hpp:314: undefined reference to `zmq_connect'
CMakeFiles/test2.dir/main.cpp.o: In function `zmq::socket_t::send(zmq::message_t&, int)':
/usr/include/zmq.hpp:321: undefined reference to `zmq_send'
/usr/include/zmq.hpp:324: undefined reference to `zmq_errno'
CMakeFiles/test2.dir/main.cpp.o: In function `zmq::socket_t::recv(zmq::message_t*, int)':
/usr/include/zmq.hpp:331: undefined reference to `zmq_recv'
/usr/include/zmq.hpp:334: undefined reference to `zmq_errno'

Should I set another kind of variable in the CMake file or have I installed the library in a wrong directory? or is there anything else that I should have mentioned? and The system witch I'm working on is Ubuntu 14.04


Answer:

CMake doesn't include direct support for 0mq and 0mq doesn't include direct support for CMake. But, 0mq does support pkg-config and we can use this to help us. Since you mentioned that you're on Ubuntu 14.04, make sure you've done sudo apt-get install libzmq3-dev to install the 0mq libraries, headers, and the pkg-config .pc file.

Then use the following CMakeLists.txt which I've modified from your version above. (I've commented all my changes inline.)

## i have cmake 3.5
cmake_minimum_required(VERSION 3.5)
project(test2)

## use this to globally use C++11 with in our project
set(CMAKE_CXX_STANDARD 11)

## load in pkg-config support
find_package(PkgConfig)
## use pkg-config to get hints for 0mq locations
pkg_check_modules(PC_ZeroMQ QUIET zmq)

## use the hint from above to find where 'zmq.hpp' is located
find_path(ZeroMQ_INCLUDE_DIR
        NAMES zmq.hpp
        PATHS ${PC_ZeroMQ_INCLUDE_DIRS}
        )

## use the hint from about to find the location of libzmq
find_library(ZeroMQ_LIBRARY
        NAMES zmq
        PATHS ${PC_ZeroMQ_LIBRARY_DIRS}
        )

set(SOURCE_FILES main.cpp)
add_executable(test2 ${SOURCE_FILES})

## add the include directory to our compile directives
target_include_directories(test2 PUBLIC ${ZeroMQ_INCLUDE_DIR})
## at the 0mq library to our link directive
target_link_libraries(test2 PUBLIC ${ZeroMQ_LIBRARY})

Now you can make your project.

❯ make
[ 50%] Building CXX object CMakeFiles/test2.dir/main.cpp.o
[100%] Linking CXX executable test2
[100%] Built target test2

If you want to see what's happening under the hood, do a verbose make.

❯ make VERBOSE=1
/usr/bin/cmake -H/home/nega/foo -B/home/nega/foo/build --check-build-system CMakeFiles/Makefile.cmake 0
/usr/bin/cmake -E cmake_progress_start /home/nega/foo/build/CMakeFiles /home/nega/foo/build/CMakeFiles/progress.marks
make -f CMakeFiles/Makefile2 all
make[1]: Entering directory '/home/nega/foo/build'
make -f CMakeFiles/test2.dir/build.make CMakeFiles/test2.dir/depend
make[2]: Entering directory '/home/nega/foo/build'
cd /home/nega/foo/build && /usr/bin/cmake -E cmake_depends "Unix Makefiles" /home/nega/foo /home/nega/foo /home/nega/foo/build /home/nega/foo/build /home/nega/foo/build/CMakeFiles/test2.dir/DependInfo.cmake --color=
make[2]: Leaving directory '/home/nega/foo/build'
make -f CMakeFiles/test2.dir/build.make CMakeFiles/test2.dir/build
make[2]: Entering directory '/home/nega/foo/build'
[ 50%] Building CXX object CMakeFiles/test2.dir/main.cpp.o
/usr/bin/c++     -std=gnu++11 -o CMakeFiles/test2.dir/main.cpp.o -c /home/nega/foo/main.cpp
[100%] Linking CXX executable test2
/usr/bin/cmake -E cmake_link_script CMakeFiles/test2.dir/link.txt --verbose=1
/usr/bin/c++      CMakeFiles/test2.dir/main.cpp.o  -o test2 /usr/lib/x86_64-linux-gnu/libzmq.so 
make[2]: Leaving directory '/home/nega/foo/build'
[100%] Built target test2
make[1]: Leaving directory '/home/nega/foo/build'
/usr/bin/cmake -E cmake_progress_start /home/nega/foo/build/CMakeFiles 0

If you look at the compile line, you'll notice that

  1. C++11 support was added (with the -std=gnu++11 flag)
  2. There's no -I/path/to/zmq flag. That's because Ubuntu dumps zmq.hpp in to /usr/include and CMake is smart enough to know that is a default path, so it does nothing

If you look at the link line, you'll notice that libzmq.so has been included via it's full path. Some people don't like that (myself included) and prefer -L/lib/dir -lmylib instead. Using the full path is "the CMake way", and you'll actually come to appreciate it as you grow with CMake and use it for larger and more complicated projects (you still might not like it though.)

Also note, that since you're on Ubuntu we could have cheated and used dpkg -L libzmq3-dev to find the locations of the files we're interested in then added them to your original CMAKE_CXX_FLAGS line, but that's cheating and more importantly, not portable.

Question:

In the past I’ve used the Visual Studio solution files to build zeromq (libzmq) on Windows. I just noticed that the Visual Studio solutions have been deprecated because they are too difficult to maintain.

The alternative is to use CMake; trouble is I’ve no experience of how to invoke the build this way. Is anyone please able to demonstrate the necessary commands, step-by-step?

I’d like to achieve 32 & 64bit libzmq binaries using libsodium and compiled with VS2015 on Windows 10. (I’ve installed CMake 64bit and allowed it to add to the system path at installation.)

Thanks


Answer:

So eventually I managed to build zeromq on Windows 10 from source using CMake.

CMake is used to set the various zeromq project options; in this case to use the libsodium library for cryptography and to provide the necessary include & linker paths for the build. Once the options are configured CMake is used to generate a Visual Studio solution from which to build the libzmq binaries.

Briefly this is how I did it using the CMake GUI:

  1. Specify to where the libzmq source code was cloned and tell CMake where to build the binaries. Hint; make a separate folder for each Visual Studio version & 32/64bit as necessary, see screenshot below.

  2. Click Configure to load the project options. From the pop up window choose the compiler you wish to use from the list. Set the necessary project options and click Configure again.

  3. If all is well click Generate to create the Visual Studio files.

  4. Click Open Project; once loaded in Visual Studio choose Debug/Release as you need and click Build Solution.

  5. Repeat the process for other architectures eg. choose the 32bit compiler as you require. Remember to adjust build output location & libsodium linker path to reflect that architecture.

Screenshot:

Question:

I am working on a project which uses the fast-cpp-csv-parser and date libraries and want to add zmq (0mq) however cannot get CMakeList to work.

The following is a working CMakeList.txt:

cmake_minimum_required(VERSION 3.7)
project(sample_project)

set(CMAKE_CXX_STANDARD 14)
set(SOURCE_FILES source/main.cpp include/csv.h include/date.h)


find_package (Threads)
add_executable(sample_project ${SOURCE_FILES})
target_link_libraries (sample_project ${CMAKE_THREAD_LIBS_INIT})

As per zmq instructions the following has to be added to CMakeLists.txt (ZMQ and CPPZMQ is already installed).

find_package(cppzmq)
if(cppzmq_FOUND)
    include_directories(${cppzmq_INCLUDE_DIR})
    target_link_libraries(sample_project ${cppzmq_LIBRARY})
endif()

When I add the above code to CMakeLists.txt, it looks like this:

cmake_minimum_required(VERSION 3.7)
project(sample_project)

set(CMAKE_CXX_STANDARD 14)
set(SOURCE_FILES source/main.cpp include/csv.h include/date.h)

find_package(cppzmq)
if(cppzmq_FOUND)
    include_directories(${cppzmq_INCLUDE_DIR})
    target_link_libraries(sample_project ${cppzmq_LIBRARY})
endif()

find_package (Threads)
add_executable(sample_project ${SOURCE_FILES})
target_link_libraries (sample_project ${CMAKE_THREAD_LIBS_INIT})

And results in the following error:

CMake Warning at /usr/local/share/cmake/cppzmq/cppzmqConfig.cmake:44 (find_package):
  By not providing "FindZeroMQ.cmake" in CMAKE_MODULE_PATH this project has
  asked CMake to find a package configuration file provided by "ZeroMQ", but
  CMake did not find one.

  Could not find a package configuration file provided by "ZeroMQ" with any
  of the following names:

    ZeroMQConfig.cmake
    zeromq-config.cmake

  Add the installation prefix of "ZeroMQ" to CMAKE_PREFIX_PATH or set
  "ZeroMQ_DIR" to a directory containing one of the above files.  If "ZeroMQ"
  provides a separate development package or SDK, be sure it has been
  installed.
Call Stack (most recent call first):
  CMakeLists.txt:7 (find_package)


CMake Error at CMakeLists.txt:10 (target_link_libraries):
  Cannot specify link libraries for target "sample_project" which is not
  built by this project.


-- Configuring incomplete, errors occurred!
See also "/home/greg/CLionProjects/sample_project/cmake-build-debug/CMakeFiles/CMakeOutput.log".
See also "/home/greg/CLionProjects/sample_project/cmake-build-debug/CMakeFiles/CMakeError.log".

[Finished]

How to correctly add additional libraries using CMakeLists.txt?


Answer:

You must reorder your CMakeLists.txt so that the target_link_libraries is located after add_executable.

For instance:

cmake_minimum_required(VERSION 3.7)
project(sample_project)

set(CMAKE_CXX_STANDARD 14)
set(SOURCE_FILES source/main.cpp include/csv.h include/date.h)

find_package(cppzmq)
if(cppzmq_FOUND)
    include_directories(${cppzmq_INCLUDE_DIR})
endif()

find_package (Threads)
add_executable(sample_project ${SOURCE_FILES})
target_link_libraries (sample_project ${CMAKE_THREAD_LIBS_INIT})

if(cppzmq_FOUND)
    target_link_libraries(sample_project ${cppzmq_LIBRARY})
endif()

As a side remark, I would recommend using target_include_directories instead of include_directories. This would also allow to pack together all cppzmq-related stuff.