Compile different code depending on library dependencies installed on machine - c++

I have c++ code which is built using cmake. I have written a class which depends on a library called Gurobi. One of Gurobi's competitors is CPLEX, another library which allows for similar optimization tasks. I have to rewrite my classes in CPLEX for a research project (because our funding comes from an agency which only has access to CPLEX). Therefore, I want to change my cmake file so that it looks for Gurobi and CPLEX and compiles my code using the GurobiClass.cpp file if it finds Gurobi, and uses the CPLEX.cpp file if it finds cplex.
I'm basically asking how to ignore code in the compilation step, so that a user does not have to have both libraries to compile my code.
I'm sure this question has been asked already, but I'm not sure what the correct name for this is / what to search, so I'm asking it again, but feel free to point me to online resources.
Thanks for any help you can provide!

Considering you haven't posted any code, there are a number of options. If the libraries use CMake themselves, and come with a FindGurobi.cmake or FindCPLEX.cmake, you can use CMake's find_package() to locate them. You can determine which files to include (and libraries to link) to your target using if-statements. A simple example would be something like this:
find_package(Gurobi)
find_package(CPLEX)
if(Gurobi_FOUND)
# Compile with Gurobi classes.
add_library(MyLibrary
TopLevelClass.cpp
GurobiClass1.cpp
GurobiClass2.cpp
...
)
target_include_directories(MyLibrary PUBLIC ${Gurobi_INCLUDE_DIR})
target_link_libraries(MyLibrary PUBLIC ${Gurobi_LIBRARIES})
elseif(CPLEX_FOUND)
# Compile with CPLEX classes.
add_library(MyLibrary
TopLevelClass.cpp
CPLEXClass1.cpp
CPLEXClass2.cpp
...
)
target_include_directories(MyLibrary PUBLIC ${CPLEX_INCLUDE_DIR})
target_link_libraries(MyLibrary PUBLIC ${CPLEX_LIBRARIES})
else()
message(FATAL_ERROR "Neither Gurobi nor CPLEX library was found.")
endif()
If you are hard-coding the paths to these libraries somehow, you could also do something like this (to check if the library files exist):
set(Gurobi_INCLUDE_DIR /path/to/local/gurobi/include)
set(Gurobi_LIBRARY /path/to/local/gurobi/lib/gurobi.lib)
set(CPLEX_INCLUDE_DIR /path/to/local/CPLEX/include)
set(CPLEX_LIBRARY /path/to/local/CPLEX/lib/cplex.lib)
if(EXISTS ${Gurobi_LIBRARY})
# Compile with Gurobi classes...
elseif(EXISTS ${CPLEX_LIBRARY})
# Compile with CPLEX classes...
else()
message(FATAL_ERROR "Neither Gurobi nor CPLEX library was found.")
endif()

Related

CMake: Absolute lib-pathnames given to target_link_library( PRIVATE ) are exported, appear in generated INTERFACE_LINK_LIBRARIES. How do I?

In a large CMake/C++ project our major library target Foo is being added. It has many statically linked dependencies that are provided as absolute-paths to target_link_libraries(). Now I'm writing the CMake to export this library once built, including the generated CMake so a library consumer can simply use CMake find_package(). However the generated FooTargets.cmake is embeding the absolute-paths of the link-libraries instead of just the library names.
Additionally, I've already created an add_library(BarCommon INTERFACE), to wrap up the referenced 3rd-party static libs separately and that seems to be working and the client-application consumed it. That accounts for 90% of the libraries also mentioned by FOO. So really FOO's exported INTERFACE_LINK_LIBRARIES should be about 3 file names instead of 40 absolute-pathnames.
I'm confused, why is the target_link_libraries(FOO PRIVATE ${libs} is defining the exported 'INTERFACE_LINK_LIBRARIES' property?
I would like that generated INTERFACE_LINK_LIBRARIES property to be only library filenames, and optimally have finer control over which filenames end up in it. How do I control this?
Code:
Inside a large CMake Macro for generating targets we have this:
-
I call it to create library/target FOO.
_TARGET_ARGS_LIB_DEPENDS will resolve to ABSOLUTE FILE PATHNAMES for about 40 statically linked library dependencies.
add_library(${TargetName} STATIC ${AllSources} ${_TARGET_ARGS_OBJ_FILES})
<snip>
target_link_libraries(${TargetName}
PRIVATE
$<${GCC}:-Wl,--start-group>
${_TARGET_ARGS_LIB_DEPENDS}
$<${GCC}:-Wl,--end-group>
)
where that longish function returns I have just added these blocks:
target_link_directories(FOO
INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>
$<INSTALL_INTERFACE:foo/lib>
)
target_include_directories(FOO
INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<INSTALL_INTERFACE:foo/include>
)
install(TARGETS Foo
EXPORT
FooTargets
DESTINATION
foo/lib
)
There is some more 'install' boilerplate at the end of my CMakeLists.txt
After I run CMAKE, build the project and run the 'INSTALL' target CMake generated from Visual Studio I can examine generated file: local_install\Release\Windows\Foo\lib\cmake\Foo\FooTargets.cmake shows:
set_target_properties(Nexidia.Workbench.StaticLib.StaticLib PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/Foo/include"
INTERFACE_LINK_DIRECTORIES "${_IMPORT_PREFIX}/Foo/lib"
INTERFACE_LINK_LIBRARIES "\$<LINK_ONLY:\$<0:-Wl,--start-group>>;C:/full/path/to/libA.lib;C:/full/path/to/libB.lib \$<LINK_ONLY:\$<0:-Wl,--end-group>>"
)
(with the full paths to about 40 libraries).
PS. I'm reading Professional CMake: A Practical Guide, so if someone could cite the chapter/sub-section that answers this it might help.
This is because your library is static and so it cannot be correctly linked to consumers without including its dependencies on consumers' link lines. The same thing happens with the shared dependencies of shared libraries. Static dependencies of shared libraries are fully "absorbed" into the shared library and so they don't appear.
CMake is aware of all this, and so to respect the other semantics of PRIVATE, it inserts the $<LINK_ONLY:...> generator expression. This ensures that the libraries are used for linking only and that any associated INTERFACE_* properties are not propagated.
In other words, CMake is behaving correctly here.
You should resolve this by creating and linking to targets for libB and friends that you can either export as part of your main build or import into both your main build and via find_dependency in your distributed package, along with the libraries themselves, of course.
The best advice I can give you is to always link to targets in CMake, never to paths or raw library flags.

Add interface library as SYSTEM in modern CMake

I'm currently transforming my C++ project from simply using a Makefile to using CMake. As I'm unfamiliar with CMake, I try and avoid learning "old" CMake and stick to "modern CMake" best practices, e.g. described here.
However, I could not figure out how to include a simple, header-only, third party library without getting bothered with warnings about that library.
Say I have a project structure like this:
CMakeLists.txt
src/
CMakeLists.txt
main.cpp
third-party-lib/
foo/
foo.h
Say main.cpp uses foo: #include <foo.h>
The problem is, I use a bunch of warning flags and foo.h introduces some warnings that I don't care about, as I'm not the author and it's nothing serious.
In a classic Makefile I would just have written -isystem third-party-lib/foo somewhere appropriate and be done.
In CMake, I achieved this by a include_directories(SYSTEM PRIVATE third-party-lib/foo) in the top-level CMakeLists.txt.
Now, according to the link above, I should keep my hands off include_directories() to avoid hidden dependencies and I think that this is a good idea. I'd rather specify for each target that actually needs foo to include that library and treat it as 'don't warn me here'. As I understand it, the approach would be:
find foo target in top-level CMakeLists.txt
target_link_libraries(main PRIVATE foo) or so in src/CMakeLists.txt
How would I achieve that, i.e. what is the command for finding, and where to specify the SYSTEM property? Or any other suggestions for how to approach this?
To add a header-only library in modern CMake, you can use target_include_directories with SYSTEM INTERFACE. You can place this in your top-level CMake file, before processing the src/CMakeLists.txt file:
add_library(foo INTERFACE)
target_include_directories(foo SYSTEM INTERFACE
${CMAKE_SOURCE_DIR}/third-party-lib/foo
)
Your friend is target_include_directories. It works like include_directories but on a per-target basis.
add_executable(foo foo.c)
target_include_directories(foo SYSTEM
PRIVATE sys_inc
)

CMAKE not finding GUROBI external library

I am new to CPP, to GUROBI and specially to CMAKE. I am working on a project and I am trying to compile a C++ program, that needs to link to the GUROBI external libraries libgurobi81.so and libgurobi_c++.a as explained in the GUROBI site here and here.
The structure of my cmake and my project is something like this:
-folder1
--src
---cmake
--gurobi
---lib
----libgurobi81.so
----libgurobi_c++.a
My code compiles correctly, but it only fails when linking with the libraries.
I tried to make CMAKE to find the libraries:
find_library(C_GUROBI_LIB NAMES libgurobi81.so gurobi81 gurobi81_light
PATHS ${LD_LIBRARY_PATH}
/path/to/folder1/gurobi/lib/
)
find_library(CPP_GUROBI_LIB NAMES gurobi_c++
PATHS ${LD_LIBRARY_PATH}
/path/to/folder1/folder1/gurobi/lib/
)
and then to print it:
message("C_GUROBI_LIB points to " ${C_GUROBI_LIB})
message("CPP_GUROBI_LIB points to " ${CPP_GUROBI_LIB})
However, even if the library is in that folder CMAKE do not find it and shows nothing:
C_GUROBI_LIB points to
CPP_GUROBI_LIB points to
I found a possible solution, adapted from here:
set(CMAKE_FIND_LIBRARY_SUFFIXES ".so" ".a")
I had some other issues, for example the library was compiled for 64 bits, and also that CMAKE did not allow to add Shared files. So I got to change some parameters, but even with that, CMAKE did not found the file.

Undefined reference when linking one dynamic library into the other using CMake and Cygwin

I've looked around, and it doesn't seem that the solution to my problem has been discussed anywhere. So, I write here.
I create a CMake project for generation of several Python libraries. I use Boost.Python to wrap my C++ code, CMake to build dynamic libraries. Everything is on Cygwin platform. The topmost level of the project is pretty standard- eventually it leads to the source directory via:
ADD_SUBDIRECTORY("src")
The /src directory contains two sub-folders - mmath and ann - each is intended for its own library. The CMake file at this level is:
MESSAGE("Going into subdirectory mmath...")
ADD_SUBDIRECTORY("mmath")
MESSAGE("Going into subdirectory ann...")
ADD_SUBDIRECTORY("ann")
So we build the mmath library first:
file(GLOB MMATH_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/*.h)
file(GLOB MMATH_SRC ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ${MMATH_HEADERS})
SET( ext_libs
${Boost_LIBRARIES}
${PYTHON_LIBRARIES}/libpython2.6.dll
)
ADD_LIBRARY(mmath SHARED ${MMATH_SRC})
TARGET_LINK_LIBRARIES(mmath ${ext_libs})
and it works perfectly. The resulting dynamic library can then be imported in Python as a module (in cygwin the library is prepended with "cyg" prefix, instead of "lib"). Among other things, the library defines a class DATA, which is essentially a customized container (a collection of variables of standard types), so very simple.
Now, when CMake proceeds into the /src/ann directory, it tries to build the corresponding cygann.dll (cygwin convention) library, which depends on the mmath library we have built already. The CMake script is:
file(GLOB ANN_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/*.h)
file(GLOB ANN_SRC ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ${ANN_HEADERS})
SET( ext_libs1
mmath
${Boost_LIBRARIES}
${PYTHON_LIBRARIES}/libpython2.6.dll
)
ADD_LIBRARY(ann SHARED ${ANN_SRC})
TARGET_LINK_LIBRARIES(ann ${ext_libs1})
In the src/ann directory we have a file defining the class Ann, which has the members of type std::vector. It is here where linking problem revels itself: the DATA class causes some problems on the linking stage - the "undefined reference" error. The errors indicate that neither constructor nor destructor for the DATA class are defined:
CMakeFiles/ann.dir/libann.cpp.o: In function `Destroy<libmmath::DATA>':
/usr/lib/gcc/i686-pc-cygwin/4.8.3/include/c++/bits/stl_construct.h:93: undefined reference to `libmmath::DATA::~DATA()'
CMakeFiles/ann.dir/libann.cpp.o: In function `Construct<libmmath::DATA, libmmath::DATA>':
/usr/lib/gcc/i686-pc-cygwin/4.8.3/include/c++/bits/stl_construct.h:83: undefined reference to `libmmath::DATA::DATA(libmmath::DATA const&)'
So I'm wondering what could be the problem. Is this related to CMake features, cygwin tricks or is it something regarding the code itself? Well, i feel that the problem is less likely related to the code itself, since it works in standard utilization (static exe or just a single dynamic library). It is more likely that it is something about CMake or cygwin that I'm missing. But what? Please, help. Thank you in advance.

Adding an External Library to a cmake project

I'm a robotics' student from Instituto Superior Técnico and I'm having trouble using an external library in my project.
I use a Robotics simulator called Simox http://simox.sourceforge.net/. This is a library that I have been working for a while. I have been using a cmake template file provided with the simulator (with few alterations) which lets me use Simox with my own code:
PROJECT ( myDemo )
FIND_PACKAGE(Simox REQUIRED)
IF(Simox_USE_COIN_VISUALIZATION)
include_directories(${PROJECT_SOURCE_DIR}/include)
FILE(GLOB SRCS ${PROJECT_SOURCE_DIR}/iCubSimulator.cpp ${PROJECT_SOURCE_DIR}/src/iCub.cpp ${PROJECT_SOURCE_DIR}/src/iCubHand.cpp ${PROJECT_SOURCE_DIR}/src/ApproachMovementSpace.cpp ${PROJECT_SOURCE_DIR}/src/OrientedBoundingBox.cpp ${PROJECT_SOURCE_DIR}/src/GraspOptimization.cpp ${PROJECT_SOURCE_DIR}/src/Window.cpp)
FILE(GLOB INCS ${PROJECT_SOURCE_DIR}/include/iCub.h ${PROJECT_SOURCE_DIR}/include/iCubHand.h ${PROJECT_SOURCE_DIR}/include/ApproachMovementSpace.h ${PROJECT_SOURCE_DIR}/include/OrientedBoundingBox.h ${PROJECT_SOURCE_DIR}/include/Window.h)
set(GUI_MOC_HDRS ${PROJECT_SOURCE_DIR}/include/GraspOptimization.h ${PROJECT_SOURCE_DIR}/include/Window.h)
set(GUI_UIS ${PROJECT_SOURCE_DIR}/ui/iCubSimulator.ui)
set(CMAKE_CXX_FLAGS "-Wall -std=c++11 -lpthread")
SimoxQtApplication(${PROJECT_NAME} "${SRCS}" "${INCS}" "${GUI_MOC_HDRS}" "${GUI_UIS}")
ENDIF()
Currently, I want to use an additional Bayesian Optimization Library called BayesOpt: http://rmcantin.bitbucket.org/html/. And I don't know how to correctly modify my cmake file to include this library.
I tried to do this own my own, with some help from google, tutorials and other asked questions, but with no success.
I'm hoping someone can help me with this problem.
Thanks in advance!
To use an external library, you will need to:
Make header files from the library accessible:
INCLUDE_DIRECTORIES( includePath )
includePath being your Bayesian Optimization Library include folder (where .h files are)
Link with the library. To do so, just add:
TARGET_LINK_LIBRARIES(${PROJECT_NAME} mylib)
myLib being your Bayesian Optimization Library .lib file or .so file
Maybe you'll first have to compile the "Bayesian Optimization Library"
If the library is correctly installed in your environment, there could be an easier way to have it be found using CMake find_package command, but I'm not familiar with it, I prefer to handle things manually as proposed aboce.