How to link to libceres.a without CMake

359 views
Skip to first unread message

Ben Harper

unread,
Jul 27, 2021, 8:39:48 AM7/27/21
to Ceres Solver
I've just built ceres from source (branch 2.0.0), and then "sudo make install", on Ubuntu 20.04.

Now, I'm trying to compile the hello world application with this command:

clang -std=c++14 -I /usr/include/eigen3 -lceres -lglog -lstdc++ -o demo demo.cpp

I get the following linker errors:

/usr/bin/ld: /tmp/demo-29844d.o: in function `main':
demo.cpp:(.text+0x42): undefined reference to `ceres::Problem::Problem()'
/usr/bin/ld: demo.cpp:(.text+0x108): undefined reference to `ceres::Solve(ceres::Solver::Options const&, ceres::Problem*, ceres::Solver::Summary*)'
/usr/bin/ld: demo.cpp:(.text+0x120): undefined reference to `ceres::Solver::Summary::BriefReport[abi:cxx11]() const'
/usr/bin/ld: demo.cpp:(.text+0x214): undefined reference to `ceres::Problem::~Problem()'
/usr/bin/ld: demo.cpp:(.text+0x290): undefined reference to `ceres::Problem::~Problem()'
/usr/bin/ld: /tmp/demo-29844d.o: in function `ceres::internal::ResidualBlock* ceres::Problem::AddResidualBlock<>(ceres::CostFunction*, ceres::LossFunction*, double*)':
demo.cpp:(.text._ZN5ceres7Problem16AddResidualBlockIJEEEPNS_8internal13ResidualBlockEPNS_12CostFunctionEPNS_12LossFunctionEPdDpPT_[_ZN5ceres7Problem16AddResidualBlockIJEEEPNS_8internal13ResidualBlockEPNS_12CostFunctionEPNS_12LossFunctionEPdDpPT_]+0x65): undefined reference to `ceres::Problem::AddResidualBlock(ceres::CostFunction*, ceres::LossFunction*, double* const*, int)'
clang: error: linker command failed with exit code 1 (use -v to see invocation)

I've been programming in C++ for many years, but I can't figure out why the linker can't find the relevant functions in libceres.a. When I dump the symbols from libceres.a, then I see all the necessary symbols in there.

What magic linker/compiler setting am I missing?
Results with GCC are similar.

Thanks

Ben Harper

unread,
Jul 27, 2021, 8:58:54 AM7/27/21
to Ceres Solver
OK -- I managed to extract the build commands using CMake, and the following is a minimal command line for building the hello world app -- at least on my machine :-/

clang -std=c++14 -I /usr/include/eigen3 -o demo demo.cpp -lceres -lglog -lgflags -lpthread -lspqr -lcholmod -lccolamd -lcamd -lcolamd -lamd -llapack -lf77blas -lcxsparse -lrt -lstdc++ -lm

For posterity, here is a minimal CMakeLists.txt for a hello world application:

cmake_minimum_required(VERSION 2.8.0)
set(CMAKE_CXX_FLAGS -std=c++14)
project(hello_world)
find_package(Ceres REQUIRED)
include_directories(${CERES_INCLUDE_DIRS})
add_executable(helloworld helloworld.cpp)
target_link_libraries(helloworld ${CERES_LIBRARIES})

Alex Stewart

unread,
Jul 27, 2021, 2:32:28 PM7/27/21
to ceres-...@googlegroups.com
You don't actually need include_directories() for any recent version of Ceres as we use the target include directories CMake property, so this section of the docs shows the most minimal CMakeLists.  It also shows the modern CMake-style of using the root Ceres target (namespaced) rather than CERES_LIBRARIES (which defaults to the same thing for backwards compatibility).  This prevents issues that would otherwise occur when XXX_LIBRARIES contained a non-namespaced target name (e.g. 'ceres') as by default, if CMake does not have a target of that name, it will assume that it's the name of a library that should be passed via -l and left up to the linker to find.  That causes issues when that library may in fact exist (and so the result will compile) but will not link as the library alone does not contain the other relevant configuration (e.g static link dependencies) to build against it.  Using a namespace-qualified meta-target name prevents this from occurring.

As you've doubtless encountered already, the ordering of static dependencies (particularly SuiteSparse) is both important and non-trivial, and the required dependencies depend on how Ceres was built.  For this reason we don't support building outside of CMake.  If you absolutely had to, then the exported CeresTargets-*.cmake files (by default installed in /usr/local/lib/cmake/Ceres) contain the information that you would need.  *However* note that our dependencies (in the main) also require CMake, so glog for example is referenced as glog::glog by Ceres, as that target (together with its own include directory and build parameter properties) is imported by CMake from glog's own exported Config file when find_package(glog) is invoked from Ceres' exported Config file.  CMake can transport a lot of build properties via imported target properties (e.g. compile options/defines) and the effects of missing some of them by trying to replicate the process manually is going to be highly error prone and brittle.

CMake is far from perfect, but it also hasn't become the defacto standard by accident - it solves a lot of problems, even as it inevitably creates some new ones.

-A


--
You received this message because you are subscribed to the Google Groups "Ceres Solver" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ceres-solver...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/ceres-solver/a195b153-63bb-4e0b-ac88-d286adf264f5n%40googlegroups.com.
Reply all
Reply to author
Forward
0 new messages