Linker errors: MongoDB CXX Driver on Linux

I’m working on integrating MongoDB into a project using Cinder on Linux (Ubuntu 16.04 specifically). I have built the Mongo drivers using these instructions to build static libraries with the default C++17 polyfill. I can compile and run a simple program that inserts a single document into a database, but when I try the same code within a Cinder project I get linker/runtime errors. I’ll focus on the linker errors for now. I’m using CLion 2016.3 to compile.

main.cpp:

    #include "cinder/app/App.h"
    #include "cinder/app/RendererGl.h"
    #include "cinder/gl/gl.h"
    #include <bsoncxx/builder/stream/document.hpp>
    #include <bsoncxx/json.hpp>
    #include <bsoncxx/stdx/make_unique.hpp>
    #include <mongocxx/client.hpp>
    #include <mongocxx/instance.hpp>
    #include <mongocxx/logger.hpp>

    class BasicApp : public ci::app::App {
    public:
        void setup() override;
    };

    void BasicApp::setup()
    {
        using bsoncxx::builder::stream::document;
        using bsoncxx::builder::stream::finalize;
        using bsoncxx::types::b_date;
        using bsoncxx::builder::stream::open_document;
        using bsoncxx::builder::stream::close_document;

        //get system hostname
        char hostname[ 256 ];
        gethostname( hostname, 256 );
        mongocxx::database db;

        try {
            //connect to db
            std::string uriStr = "mongodb://localhost:27017";
            const auto uri = mongocxx::uri(uriStr);

            auto client = mongocxx::client{uri};

            db = client["testdb"];
        }
        catch (const std::exception& xcp) {
            std::cout << "connection failed: " << xcp.what() << "\n";
            return;
        }

        //write a test document
        bsoncxx::document::value testRecord = document{}
                << "machine_id" << hostname
                << "session_id" << "session_010101"
                << "date" << "date_placeholder"
                << "data" << open_document
                << "name" << "test"
                << "id" << "test"
                << close_document
                << finalize;

        std::cout << bsoncxx::to_json( testRecord ) << std::endl; //this line causes a linker error
        auto res = db[ "test" ].insert_one( std::move( testRecord ));
    }

    CINDER_APP( BasicApp, ci::app::RendererGl )

CMakeLists.txt:

cmake_minimum_required(VERSION 3.6)
project(mongo_test)

set(CMAKE_CXX_STANDARD 11)

set(CINDER_PATH ../3rd_party/Cinder)

find_package(PkgConfig REQUIRED)

pkg_check_modules(LIBMONGOCXX REQUIRED libmongocxx)
pkg_check_modules(LIBBSONCXX REQUIRED libbsoncxx)

set(INCLUDE_FILES ${CINDER_PATH}/include ${LIBMONGOCXX_INCLUDE_DIRS} ${LIBBSONCXX_INCLUDE_DIRS})
set(SOURCE_FILES ${PROJECT_SOURCE_DIR}/main.cpp)

include( "${CINDER_PATH}/proj/cmake/modules/cinderMakeApp.cmake" )

ci_make_app(
        SOURCES     ${SOURCE_FILES}
        INCLUDES    ${INCLUDE_FILES}
        CINDER_PATH ${CINDER_PATH}
        LIBRARIES   ${LIBMONGOCXX_LIBRARIES} ${LIBBSONCXX_LIBRARIES}
)

When the code is run as-is, I get the following linker error:

undefined reference tobsoncxx::v_noabi::to_json(bsoncxx::v_noabi::document::view)’`

This is in reference to the bsoncxx::to_json call on line 51 of main.cpp. If I comment that line out, I can compile but get a runtime error.

Any ideas as to what might be the issue? Any help is appreciated!

Looks like bsoncxx isn’t getting properly linked, though I couldn’t tell you why. You can try turning on the CINDER_VERBOSE flag, or checking that the correct linker flags are being set in the build output window.

I’d guess that the runtime error is something separate.

Thanks @rich.e I have looked at the CMake output. It seems like everything is as to be expected. I find it really strange that the exact same code compiles and runs fine when not compiled with Cinder. I don’t see anything suspect in either the configure.cmake or cinderMakeApp.cmake files that might result in the linker/runtime errors I’m seeing with Mongo. If anyone is feeling adventurous and has time, maybe try to reproduce on your Linux environment?

Does changing the order of libs make an impact in your case?

It could be possible helpful to see a verbose output of CMake during configuration step and also a verbose output of make during linking phase.

You can either do make VERBOSE=1 when building your app to get a verbose output of the linking phase or set CMAKE_VERBOSE_MAKEFILE=ON when configuring through CMake.

@balachandran_c: link order does not seem to affect the issue. The link script that CMake generates ends up looking like this:

/usr/bin/c++   -g   CMakeFiles/mongo_test.dir/main.cpp.o  -o ../build/Debug/mongo_test/mongo_test /code/ai_core_backend_v2/AIBackend/3rd_party/Cinder/lib/linux/x86_64/ogl/Debug/libcinder.a /usr/local/lib/libmongocxx.a /usr/local/lib/libbsoncxx.a /usr/local/lib/libmongoc-1.0.a /usr/local/lib/libbson-1.0.a -lpthread -lrt -lssl -lcrypto /usr/lib/x86_64-linux-gnu/libGLU.so /usr/lib/x86_64-linux-gnu/libGL.so /usr/lib/x86_64-linux-gnu/libSM.so /usr/lib/x86_64-linux-gnu/libICE.so /usr/lib/x86_64-linux-gnu/libX11.so /usr/lib/x86_64-linux-gnu/libXext.so -lXcursor -lXinerama -lXrandr -lXi /usr/lib/x86_64-linux-gnu/libz.so /usr/lib/x86_64-linux-gnu/libcurl.so /usr/lib/x86_64-linux-gnu/libfontconfig.so /usr/lib/x86_64-linux-gnu/libpulse.so /usr/lib/x86_64-linux-gnu/libmpg123.so /usr/lib/x86_64-linux-gnu/libsndfile.so /usr/lib/x86_64-linux-gnu/libgobject-2.0.so /usr/lib/x86_64-linux-gnu/libglib-2.0.so /usr/lib/x86_64-linux-gnu/libgstreamer-1.0.so /usr/lib/x86_64-linux-gnu/libgstbase-1.0.so /usr/lib/x86_64-linux-gnu/libgstapp-1.0.so /usr/lib/x86_64-linux-gnu/libgstvideo-1.0.so /usr/lib/x86_64-linux-gnu/libgstgl-1.0.so /code/ai_core_backend_v2/AIBackend/3rd_party/Cinder/lib/linux/x86_64//libboost_system.a /code/ai_core_backend_v2/AIBackend/3rd_party/Cinder/lib/linux/x86_64//libboost_filesystem.a -ldl -lpthread  

with Cinder’s dependencies getting appended to the end. If I manually put Mongo libs and dependencies at the end (not using PkgConfig in this case) like this:

/usr/bin/c++ -g CMakeFiles/mongo_test.dir/main.cpp.o -o ../build/Debug/mongo_test/mongo_test /code/ai_core_backend_v2/AIBackend/3rd_party/Cinder/lib/linux/x86_64/ogl/Debug/libcinder.a /usr/lib/x86_64-linux-gnu/libGLU.so /usr/lib/x86_64-linux-gnu/libGL.so /usr/lib/x86_64-linux-gnu/libSM.so /usr/lib/x86_64-linux-gnu/libICE.so /usr/lib/x86_64-linux-gnu/libX11.so /usr/lib/x86_64-linux-gnu/libXext.so -lXcursor -lXinerama -lXrandr -lXi /usr/lib/x86_64-linux-gnu/libz.so /usr/lib/x86_64-linux-gnu/libcurl.so /usr/lib/x86_64-linux-gnu/libfontconfig.so /usr/lib/x86_64-linux-gnu/libpulse.so /usr/lib/x86_64-linux-gnu/libmpg123.so /usr/lib/x86_64-linux-gnu/libsndfile.so /usr/lib/x86_64-linux-gnu/libgobject-2.0.so /usr/lib/x86_64-linux-gnu/libglib-2.0.so /usr/lib/x86_64-linux-gnu/libgstreamer-1.0.so /usr/lib/x86_64-linux-gnu/libgstbase-1.0.so /usr/lib/x86_64-linux-gnu/libgstapp-1.0.so /usr/lib/x86_64-linux-gnu/libgstvideo-1.0.so /usr/lib/x86_64-linux-gnu/libgstgl-1.0.so /code/ai_core_backend_v2/AIBackend/3rd_party/Cinder/lib/linux/x86_64//libboost_system.a /code/ai_core_backend_v2/AIBackend/3rd_party/Cinder/lib/linux/x86_64//libboost_filesystem.a -ldl -lpthread /usr/local/lib/libmongocxx.a /usr/local/lib/libbsoncxx.a /usr/local/lib/libmongoc-1.0.a /usr/local/lib/libbson-1.0.a -lpthread -lrt -lssl -lcrypto

and then run make, I see the same linker error I listed above.

I’m really at a loss at this point as to what could be the issue. Could be that one of Cinder’s dependencies doesn’t play nice with bsoncxx…

I think my next course of action will be to try the legacy Mongo driver. My concern is that there will be Boost-related collisions but I don’t know what else to try next.

If you want something else to try :slight_smile:

You could probably hunt down your issue by starting with a simple command line program, then gradually linking in dependencies (mongo, bsoncxx, boost, cinder …), testing along the way that you can use the functionality in those libraries. Eventually either you’ll get a full on cinder app to work or find what part wasn’t happy. I’ve done this in the past when trying to get crazy things like chromium or something linked into my cinder app.

After a little back and forth with a MongoDB engineer on their message board, I think we have figured out the issue that causing the linker/runtime errors. It seems that the Cinder application and my build of the driver don’t agree on which version of the libstdc++ to use. You can see the flag in the build output below that specifies the CXX ABI:

[ 50%] Building CXX object CMakeFiles/mongo_test.dir/main.cpp.o
/usr/bin/c++   -DFT2_BUILD_LIBRARY -DFT_DEBUG_LEVEL_TRACE -D_GLFW_X11 -D_GLFW_GLX -D_GLFW_USE_OPENGL -D_UNIX -D_GLIBCXX_USE_CXX11_ABI=0 -I/code/ai_core_backend_v2/AIBackend/mongo_test/../3rd_party/Cinder/include -I/usr/local/include/mongocxx/v_noabi -I/usr/local/include/libmongoc-1.0 -I/usr/local/include/bsoncxx/v_noabi -I/usr/local/include/libbson-1.0 -isystem /code/ai_core_backend_v2/AIBackend/3rd_party/Cinder/include  -O3 -DNDEBUG   -std=c++14 -std=gnu++11 -o CMakeFiles/mongo_test.dir/main.cpp.o -c /code/ai_core_backend_v2/AIBackend/mongo_test/main.cpp

Other posts have mentioned this flag causing them issues. I tried changing its value to 1 in the various Cinder CMake files and recompiling the Cinder libraries. The libs and my Mongo test app both successfully compiled, but I saw std library related runtime errors with any Cinder app I tried to run. I rebuilt the Cinder libs with the default CXX ABI thinking that I would try changing just the USE_CXX11_ABI flag being passed when building main.cpp.o (pasted above). However, I can’t seem to figure out how to do this.

Any thoughts? @rich.e @petros ?

Hey,

this flag is there because of the precompiled boost libs that ship with Cinder – They are compiled with the old ABI ( i.e a version of gcc < 5.1 ) and we need this flag to make them work with newer versions of gcc ( >= 5.1 ) This has been causing some headaches especially if the user happens to have boost installed already in the system and we are currently in the process of thinking what is the best way to move forward with this.

What I would try in your case and at this point is to use system installed boost libs instead of the ones that ship with Cinder. To do that first completely remove the boost submodule that ships with Cinder. Then install ( if you haven’t already ) the dev packages for boost-system and boost-filesystem through apt. After that configure Cinder through CMake with :

cmake .. -DCINDER_BOOST_USE_SYSTEM=1

and from then on continue as usual.

A note that Cinder ships with boost 1.60 and Ubuntu 16.04 has 1.58 as far as I remember. As long as you completely remove the boost submodule that ships with Cinder this should be fine though. If not let us know and we can try to figure out another way.

Hope this helps.
Petros

1 Like

awesome, that worked! for anyone that runs into these issues, following @petros 's steps above resolved them. thanks!

1 Like