CMake and #pragma comment(lib) - c++

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()

Related

AHow to use different compilers for projects with CMake? [duplicate]

It seems like CMake is fairly entrenched in its view that there should be one, and only one, CMAKE_CXX_COMPILER for all C++ source files. I can't find a way to override this on a per-target basis. This makes a mix of host-and-cross compiling in a single CMakeLists.txt very difficult with the built-in CMake facilities.
So, my question is: what's the best way to use multiple compilers for the same language (i.e. C++)?
It's impossible to do this with CMake.
CMake only keeps one set of compiler properties which is shared by all targets in a CMakeLists.txt file. If you want to use two compilers, you need to run CMake twice. This is even true for e.g. building 32bit and 64bit binaries from the same compiler toolchain.
The quick-and-dirty way around this is using custom commands. But then you end up with what are basically glorified shell-scripts, which is probably not what you want.
The clean solution is: Don't put them in the same CMakeLists.txt! You can't link between different architectures anyway, so there is no need for them to be in the same file. You may reduce redundancies by refactoring common parts of the CMake scripts into separate files and include() them.
The main disadvantage here is that you lose the ability to build with a single command, but you can solve that by writing a wrapper in your favorite scripting language that takes care of calling the different CMake-makefiles.
You might want to look at ExternalProject:
http://www.kitware.com/media/html/BuildingExternalProjectsWithCMake2.8.html
Not impossible as the top answer suggests. I have the same problem as OP. I have some sources for cross compiling for a raspberry pi pico, and then some unit tests that I am running on my host system.
To make this work, I'm using the very shameful "set" to override the compiler in the CMakeLists.txt for my test folder. Works great.
if(DEFINED ENV{HOST_CXX_COMPILER})
set(CMAKE_CXX_COMPILER $ENV{HOST_CXX_COMPILER})
else()
set(CMAKE_CXX_COMPILER "g++")
endif()
set(CMAKE_CXX_FLAGS "")
The cmake devs/community seems very against using set to change the compiler since for some reason. They assume that you need to use one compiler for the entire project which is an incorrect assumption for embedded systems projects.
My solution above works, and fits the philosophy I think. Users can still change their chosen compiler via environment variables, if it's not set then I do assume g++. set only changes variables for the current scope, so this doesn't affect the rest of the project.
To extend #Bill Hoffman's answer:
Build your project as a super-build, by using some kind of template like the one here https://github.com/Sarcasm/cmake-superbuild
which will configure both the dependencies and your project as an ExternalProject (standalone cmake configure/build/install environment).

Converting from qmake to cmake, how do I find libraries in the same way?

in qmake I can have something like this:
LIBS += -lopengl32 \
-lglu32
This will automatically find and link the OpenGL and GLU with my application. How can I do the same in cmake? Is it as simple as:
target_link_libraries(${TARGET_NAME} opengl32 glu32)
If so, how does cmake know where to find these libraries? Is there a way to do this that involves a find_libraries call instead? The above (if it works), would give me anxiety.
Yes, there is a way.
About the sample you provided:
target_link_libraries(${TARGET_NAME} opengl32 glu32)
In this case, when you just list libraries to be linked against, CMake will just pass them to the linker, without any additional work; it is the linker, who must find them.
About the CMake way of including libraries:
However, CMake may help you much much more. Pls take a look at the following code snippet, that represents the preferred way of finding libraries in CMake:
#NOTE: this is a complete & working CMakeLists.txt
project(my_opengl_program)
set(TARGET_NAME ${PROJECT_NAME})
set(TARGET_SOURCES my_opengl_program.cpp my_opengl_program.h)
add_executable(${TARGET_NAME} ${TARGET_SOURCES})
find_package(OpenGL REQUIRED)
target_link_libraries(${TARGET_NAME} OpenGL::GL OpenGL::GLU)
Instead of using find_library directly, use find_package. It will find the libraries you need (by employing find_library internally) and set various helpful variables for you.
Also, most of the packages will also define so-called import libraries, OpenGL::GL and OpenGL::GLU in your case, that can be used for linking. The wonderful thing about linking against the CMake targets is that your executable will inherit all relevant compile/link requirements from the target used for linking. Of course, "import libraries" are also CMake targets :)
Please note that there are NO additional compile/link options, include directories, compile definitions, etc... for your executable. Everything that is necessary is inherited from OpenGL::GL and OpenGL::GLU.
CMake also provides packages for many standard libraries (scroll down to "Find Modules").
About searching for libraries
You can always use find_library directly, and in that case, it is your responsibility to take care of all requirements the library imposes.
Determining the places to search for libraries may be quite complex, and is driven by various CMake variables, as well as parameters passed to find_library. In the simplest case, when no related CMAKE_* variables are changed, and when the find_library is called with basic syntax, like:
find_library(GL_LIB opengl32)
fund_library(GLU_LIB glu32)
Search for openGL32 and glu32 will be controlled by CMAKE_SYSTEM_PREFIX_PATH, CMAKE_SYSTEM_LIBRARY_PATH CMake variables.
In the end, here is the complete documentation for find_library, find_package, cmake supported libraries (scroll down to "Find Modules"), target_link_libraries
[update]
find_library vs find_package
The question was raised about the difference between find_library and find_package.
Briefly, these two commands do not "compete" but rather "complement" each other. One may think about find_library as a low-level interface for including libraries. In this context, find_package would be a much higher interface, that is easier to use. On the other hand, find_package requires additional support from the library maintainers, or from the CMake directly.
Digging a little deeper into the problem:
Suppose that some library is introduced using
find_library (SOME_OTHER_LIB some_other_lib_name). If the lib was found, the variable SOME_OTHER_LIB will contain the path to the library, which is enough for linking, using target_link_libraries.
But to really use the library, sources need to include SOME_OTHER_LIB specific headers, i.e. another find_path(...) needs to be introduced, to locate headers, followed by target_include_directories(...). Also, compile options required by the library also need to be extracted somehow, and introduced using target_compile_options(...)
Of course, if the SOME_OTHER_LIB is used on more than one place, all target_include_directories, target_compile_options, and target_link_libraries must be used all over.
The same process must be repeated for every foreign library that is used.
One can quickly spot the pattern, and this is exactly where find_package becomes handy. All that low-level work is hidden from the end-user (i.e. developer using CMake), and she/he is presented with a clean and unified interface. The downside is that find_package requires a "kind of driver" that will "drive" the process of including the foreign library. All libraries that are directly supported by CMake may be found on cmake-modules (scroll down to "Find Modules").
The icing on the cake, almost all find modules also create so-called "import libraries" (OpenGL::GL and OpenGL::GLU in your case) that are cmake targets holding all requirements of the 3rd party library. All those data are inherited by just linking against the import library, making the code much cleaner.
Unfortunately, there are no "enforcements" about the naming of created import libraries (just guidelines), so the only solution is to check the documentation. For OpenGL module, it can be found on FindOpenGL page.

How add compile-only dependency with cmake

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.

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.

Using CMake with multiple compilers for the same language

It seems like CMake is fairly entrenched in its view that there should be one, and only one, CMAKE_CXX_COMPILER for all C++ source files. I can't find a way to override this on a per-target basis. This makes a mix of host-and-cross compiling in a single CMakeLists.txt very difficult with the built-in CMake facilities.
So, my question is: what's the best way to use multiple compilers for the same language (i.e. C++)?
It's impossible to do this with CMake.
CMake only keeps one set of compiler properties which is shared by all targets in a CMakeLists.txt file. If you want to use two compilers, you need to run CMake twice. This is even true for e.g. building 32bit and 64bit binaries from the same compiler toolchain.
The quick-and-dirty way around this is using custom commands. But then you end up with what are basically glorified shell-scripts, which is probably not what you want.
The clean solution is: Don't put them in the same CMakeLists.txt! You can't link between different architectures anyway, so there is no need for them to be in the same file. You may reduce redundancies by refactoring common parts of the CMake scripts into separate files and include() them.
The main disadvantage here is that you lose the ability to build with a single command, but you can solve that by writing a wrapper in your favorite scripting language that takes care of calling the different CMake-makefiles.
You might want to look at ExternalProject:
http://www.kitware.com/media/html/BuildingExternalProjectsWithCMake2.8.html
Not impossible as the top answer suggests. I have the same problem as OP. I have some sources for cross compiling for a raspberry pi pico, and then some unit tests that I am running on my host system.
To make this work, I'm using the very shameful "set" to override the compiler in the CMakeLists.txt for my test folder. Works great.
if(DEFINED ENV{HOST_CXX_COMPILER})
set(CMAKE_CXX_COMPILER $ENV{HOST_CXX_COMPILER})
else()
set(CMAKE_CXX_COMPILER "g++")
endif()
set(CMAKE_CXX_FLAGS "")
The cmake devs/community seems very against using set to change the compiler since for some reason. They assume that you need to use one compiler for the entire project which is an incorrect assumption for embedded systems projects.
My solution above works, and fits the philosophy I think. Users can still change their chosen compiler via environment variables, if it's not set then I do assume g++. set only changes variables for the current scope, so this doesn't affect the rest of the project.
To extend #Bill Hoffman's answer:
Build your project as a super-build, by using some kind of template like the one here https://github.com/Sarcasm/cmake-superbuild
which will configure both the dependencies and your project as an ExternalProject (standalone cmake configure/build/install environment).