How add compile-only dependency with cmake - c++

I need to add a compile-only dependency to external lib my in CMake file - referred to as the_lib later in the question. By saying compile-only dependency I mean propagating compile-time properties, most importantly -I rules.
The library in question is created as a library target using add_library. Normally I would simply use target_link_libraries(my_exec the_lib), but this adds both compile-time and link-time properties, i.e., this adds both -I and -l/-L rules to compilation commands, while I only need -I. (If anyone is curios why I need such a setup, this is because reasons.)
Please note, target_include_directories with something like ${the_lib_SOURCE_DIR} (or anything similar to that effect) would not work for me, because it wouldn't add include directories necessary for the lib. I need something like ${the_lib_INCLUDE_DIRS} where the_lib_INCLUDE_DIRS would be populated to as -I rules required by the_lib - but I didn't find any variable which would match that.
It is worth noting that I can't (or shan't) modify the the_lib.

I need to add a compile-only dependency to external lib my in CMake
file
By an "external" lib, I take you to mean one that is not part of the same project -- i.e. one that is not configured within the scope of the same CMake build system as the target you're trying to build.
[...] Normally
dependencies are added with target_link_libraries(my_exec the_lib),
but this adds both compile-time and link-time dependency.
Well no, not necessarily. As it says on the tin, that adds a link dependency, which you can think of as an -l option. To the best of my knowledge, it does not generate any -I options for external libraries, or otherwise have any impact on the compilation phase with regard to external libraries. Similarly, as far as I am aware, it propagates transitive dependencies of any sort only when the the added library is another target configured and built by the same build system. That is, only for internal libraries, not external ones.
Please note, target_include_directories with something like
${the_lib_SOURCE_DIR} (or anything similar to that effect) would not
work for me, because it wouldn't add include directories necessary for
the lib. I need something like ${the_lib_INCLUDE_DIRS} where
the_lib_INCLUDE_DIRS would be populated to as -I rules required by
the_lib - but I didn't find any variable which would match that.
I think you're asking for the include directories that would be necessary to successfully use the_lib's headers, in a situation where those headers have their own external dependencies. There's a reason why you don't find a variable appropriate to that: there is no consistent or standard way to obtain that information for external libraries. The available techniques depend on the_lib. They will include some, but probably not all, of the following:
Use a CMake macro provided with the_lib or with CMake itself to define a CMake variable conveying the wanted information.
Use pkg-config to read the information from a pkg-config entry associated with the_lib.
Use some technique idiosynchratic to the_lib, along the line of Python's python-config.
Analyze the_lib's headers to determine the external packages they depend upon, and search explicitly, individually for those packages' headers.
Require the user to specify manually for any required packages that are not in the default include path.
There is no magic variable or function in CMake to automatically glean such information, because generally speaking, it is not available from the_lib's headers themselves. Note, too, that most of those would depend on the_lib's include dependencies being installed in specific places anticipated at the_lib's build time. That's pretty hard to ensure.

Related

CMake and #pragma comment(lib)

For my CMake project i have an external library, consisting of a header file and several .lib/.dll pairs.
The header file selectively links to one of the .lib/.dll pair, take this example:
#ifdef DEBUG
#pragma comment(lib "exampled.lib")
#elif
#pragma comment(lib "example.lib")
#endif
In full, theres a .lib and matching .dll for 32/64 bit and Debug/Release, so 4 pairs in total. Inside the header file is the proper #ifdef-logic to link the right library. As i said it's an external library, so i don't want to change that header.
What is the right way to teach this to CMake?
For compile time (i.e. include directories) i can use target_include_directories() which works fine. I can also create an imported target with an interface include directory, this also works fine.
The problem starts at link-time:
target_link_libraries() forces me to specify one of the .lib/.dll files. I could duplicate the #ifdef logic, but this feels wrong. Adding all of the files is incorrect as well, as always only one is needed.
Imported targets allow me to add .dll and .lib files as well, but again, i would either have to duplicate the logic or link all libraries.
link_directories() works on a global scale. Which feels unnecessary if only a few targets actually need it.
adding the given directory to the PATH also seems "to global", i.e. since the library is currently distributed along with the code.
So what i am looking for is something like target_link_directories() or similar solutions.
Obviously to actually load the .dll at runtime i would have to take further steps, so if your solution includes this, it would be very welcome.
target_link_libraries() forces me to specify one of the .lib/.dll files
This is not true. You can provide keywords to indicate what build type the libraries belong to.
From the CMake documentation on target_link_libraries
A debug, optimized, or general keyword indicates that the library
immediately following it is to be used only for the corresponding
build configuration. The debug keyword corresponds to the Debug
configuration (or to configurations named in the DEBUG_CONFIGURATIONS
global property if it is set)
Example: target_link_libraries( my_target optimized example.lib debug exampled.lib
This is then controlled by the CMAKE_BUILD_TYPE variable. See the cmake documentation for more info on this variable
As for 32-bit vs 64-bit you will need to add if-blocks checking the architecture, I'll admit this is a limitation.
To check for 32 vs 64 bit variations, you can use CMAKE_SIZEOF_VOID_P EQUAL 8
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
// Set 64 bit stuff
else()
// Set 32 bit stuff
endif(CMAKE_SIZEOF_VOID_P EQUAL 8)
And since mascoj already pointed out what I was going to, that target_link_libraries accepts general, optimized, and debug keywords and that should help get whatever libraries you need.
Keeping the #pragmas is asking for trouble. You should deactivate them if possible.
There are a lot of libraries that do this (eg. Boost on Windows), but this really doesn't make sense when used with CMake. These pragmas are non-standard and will not work on most compilers. The whole point of a CMake build though is to be portable to all platforms, so you will need to move the logic for linking to CMake anyway if you want to be portable. Even if you don't care about portability, CMake builds usually don't work very well with such files (as you experienced yourself). CMake is responsible for the build, so you should move all concerns of linking to CMake.
So first thing is to find a way to deactivate the pragmas. Usually, this can be done through a preprocessor define or similar (for Boost, you could target_link_libraries(... Boost::disable_autolinking ), which will set the required preprocessor defines).
You will need to duplicate the logic that you now have in the header file, yes. This is just how it works, the build system needs to know what to link against. Using generator expressions, you can still hide all of that away in a single imported target, so at least it will be convenient to use.
There is no good solution to the .dll problem. If you want to have the dlls available in the build directory, you need to manually copy them there. CMake does not offer any special support here, but if you already found the .libs for linking, finding the .dlls is usually straightforward.
link_directories() doesn't work for me at all for #pragma comment (lib)
To search pragma's lib I use
if (MSVC)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}"
"/LIBPATH:<path to my libs>")
endif()
For example
if (MSVC)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}"
"/LIBPATH:${CMAKE_CURRENT_BINARY_DIR}/../src_lib/")
endif()

CMake find_package not handling multi-configurations

We're using Jenkins 2.60.2 and CMake 3.9.1 to automate our build system. This all works well for multiple versions of build tools, architectures and debug/release targets (if ALL configurations have been built and installed, so both Debug AND Release).
A Debug-only configuration that uses find_package() typically ignores the CMAKE_BUILD_TYPE at discovery. Internally the scripts search for file and libraries and store the locations in variables. At the end of the script, the variables are scanned for _NOTFOUND strings, which is the result of a file or library not found in all the reference paths/hints. So essentially a find_package() will fail if the Release lib can not be found, and mark the whole package as not installed properly, even though the build is only strictly interested in the Debug target.
Typically the XXXConfig.cmake files use a call to find_package_handle_standard_args(.. PATH_TO_LIB) that scans for _NOTFOUND strings in the path variables to the libraries. These variables typically get set to _NOTFOUND by earlier calls to find_library(PATH_TO_LIB libname ..). For more information I refer to the CMake docs.
The user can indeed tag debug libraries with 'debug' and release libs with 'optimized', but this does not seem to help during the lib discovery and is only used during linking.
Anyone knows how to handle this properly?
Kind regards
This is one of the unfortunate shortcomings of the classic use of find_package.
Note that find_package also allows a different mode of operation, based on config file packages, which is well-suited to address this particular problem, but will require some changes to your build system. You will need config scripts for all your libraries (CMake can generate them for you if the libraries are themselves also built by CMake; if not, this can be a bit of a hassle), and depending targets will refer to those libraries via imported targets instead of variables (which usually makes things way easier for those depending targets). I would strongly recommend you adopt this as the long-term solution.
If for some reason you cannot do this, you will have to modify your find scripts. A common technique is to search for debug and release binaries separately, but then combine the find libraries from those calls into a single variable (together with the debug and optimized specifiers) and then have that variable as an argument to find_package_handle_standard_args. That way, as long as one of the two is found, your find script will be happy, although you might not be able to build all possible configurations in the end. Alternatively, you can also skip the call to find_package_handle_standard_args altogether and manually implement your own logic for detecting whether the library was found. As you can see from the manpage for that function, it does mostly boilerplate stuff and can be easily replaced by a more flexible, handwritten implementation if necessary.

Locating "undefined" references in a C/C++ Project

I am building a C++ project on my Ubuntu 64bit system using a provided Makefile, and this project also provides an API library for developers.
The compilation was successful, no errors at all, but when I try to include in my files the API libraries provided in the "api" folder, then g++ complains about undefined references.
It is not a problem about dependencies (I already built the project succesfully), in fact the missing references are about classes and functions provided by the project, but they are in some specific (sub-)folders (I don't know which ones!), I guess in some .so files, and g++ is not finding them, probably because it does not know they are in those specific subfolders.
It is not the first time this happens when trying to use APIs from any project, then I think I am missing something or I am doing something wrong in general when trying to use the libraries provided in a project.
In particular, the main problem is that I don't know how to tell the compiler where some classes or data structures are declared, and moreover I don't know how to locate them in order to know where they are.
Usually, a workaround I use to avoid this problem is to run make install (as root or using sudo) so that libraries and APIs are installed in standard folders (like /usr/include or /usr/lib) and if I do this thend I can include the API libraries using #include <library>, but in this last case it didn't work either, because perhaps some required files containing the not found classes/structures are not properly installed in the right folders in the system.
Another workaround I used sometimes is to try to put all the project files in the same folder instead of using the project folder structure, but clearly this is not good! :-)
But I noticed that several other people managed to use the APIs, then apparently they know some way of finding the files containing the "undefined" references and including them in the compilation.
Then my general question is: given a "classic" C++ project based on "Makefile" files and with usual folder names like src, lib, build, bin, etc., if I want to write C++ files using the libraries provided by the project, but the compiler complains about undefined references, how can I find the files (.so or .o or .cpp) containing such references? Is there any tool to find them? And how can I tell the compiler where they are? Should I use some command-line option for g++ or should I use the #include macro in some smart way?
PS I also tried to use the pkc-config linux tool to get right options to use for compilation and they were available, but the compiler still complains about the undefined references.
Some more degails about the project i tried:
For interested people a link to the project is below:
https://github.com/dreal/dreal3
And instructions on how to build it:
http://dreal.github.io/download/
Look into the -rpath linker option - specifically with the "$ORIGIN" argument. That lets you find libraries relative to your executable location so you don't have to install them to the standard locations but just need to put them somewhere known, relative to the executable. That should help you with one piece of the puzzle.
Note: -Wl, can be used to pass arguments to the linker via g++.
As for pointing the compiler/linker at a library so it can resolve undefined references by using that library, use the -l (that's lowercase L) option to specify the library name and -L to specify directories to search for libraries.
As for looking into a library (.so) file to see what symbols are in there, you have a few tools at your disposal: objdump, nm, readelf and objcopy.

exporting cmake build options to external project

I have got a C++ Library A. A can be installed in a multitude of ways depending on which external dependencies are used. This also changes depending on whether the library is build in debug or release mode. This means that some features might not be available or some types/defines need to be changed in order to link to the library.
Now I want to link A to a local project B. I have set up a ProjectConfig.cmake file for A which is located at /path/lib/CMake/A/AConfig.cmake which is found and works fine in a minimal build. However as soon as I add definitions to the compilation or include some packages, this information is not automatically exported. This makes linking to A hard as for example I need to know that OpenMP was used to have a coherent build.
Is there a way to export this information the same way the ProjectConfig.cmake does it?
Generate the ProjectConfig.cmake file to contain what you need it to contain.
http://www.cmake.org/cmake/help/v3.0/manual/cmake-packages.7.html
Note that if you set the usage requirements of the targets, you have less need to generate the file.
http://www.cmake.org/cmake/help/v3.0/manual/cmake-buildsystem.7.html

Building Boost without filename decorations?

The default naming convention for the Boost C++ libraries is:
libboost_regex-vc71-mt-d-1_34.lib
where all libraries are built into the same directory. I'd like to modify the build process so that the filename does not contain the target architecture or build type (versions are okay). I want the file to end up in a different directory depending on the architecture being built for:
vc71/debug/libboost-1_34.lib
vc71/release/libboost-1_34.lib
Any idea on how to do this?
You can remove all decoration from the library filenames by passing "--layout=system". Your example above shows "vc71/release" paths -- there's no out-of-box way to get this layout. You can do that with a bit of hackign. In Jamroot, find the 'stage-proper' target, which specifies the location as:
<location>$(stage-locate)/lib
You can modify that to specify different locations depending on properties, e.g:
<variant>release:<location>$(stage-locate)/lib/release
<variant>debug:<location>$(stage-locate)/lib/debug
Please see Boost.Build website for more documentation
I don't know of any way to do that with the Boost build system, but you could use a fairly simple script to move and rename them without too much difficulty.
On the other hand, with most Windows compilers, you seldom need to concern yourself with the library filenames because, for those libraries that require a separate binary, Boost employs auto-linking:
Most Windows compilers and linkers have so-called “auto-linking support,” which eliminates the second challenge. Special code in Boost header files detects your compiler options and uses that information to encode the name of the correct library into your object files; the linker selects the library with that name from the directories you've told it to search.
Moving and renaming the files would break that.