I am working on a header-only C++11 library which uses modern CMake. By "modern," I mean not only using CMake v3.0+ but also trying to use as much as possible the best practices in Daniel Pfeifer's talk.
I have done some research on my question, but the answers are mostly regarding modifying the LINK_FLAGS directly in the global level, which I do not want to have. Right now, in my project, I require a minimum version of 3.9.0 of CMake because of some features I am using.
My question is about whether/how to add LINK_FLAGS coming from two of my dependencies: BLAS and LAPACK. Basically, I have the following excerpt from my CMakeLists.txt file:
cmake_minimum_required(VERSION 3.9.0)
project(polo VERSION 1.0.0 LANGUAGES C CXX)
find_package(BLAS REQUIRED)
find_package(LAPACK REQUIRED)
add_library(polo INTERFACE)
add_library(polo::polo ALIAS polo)
target_compile_features(polo INTERFACE cxx_std_11)
target_include_directories(polo
INTERFACE
$<BUILD_INTERFACE:${polo_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
target_link_libraries(polo
INTERFACE
${BLAS_LIBRARIES}
${LAPACK_LIBRARIES}
)
set_property(
TARGET
polo
PROPERTY LINK_FLAGS
${BLAS_LINKER_FLAGS}
${LAPACK_LINKER_FLAGS}
)
As far as I can understand from documentations of the FindBLAS and FindLAPACK modules, I need to inform my users at least about {BLAS,LAPACK}_LIBRARIES and {BLAS,LAPACK}_LINKER_FLAGS. For the former, I think I have handled the issue properly. However, for the latter, I need to use either set_target_properties or set_property. Between the two, the latter seems to give me a cleaner solution in that I can use both variables coming from Find{BLAS,LAPACK} modules together. When I try to build my library using the above solution, I get the obvious error:
CMake Error at src/CMakeLists.txt:32 (set_property):
INTERFACE_LIBRARY targets may only have whitelisted properties. The
property "LINK_FLAGS" is not allowed.
My question is two folds:
Should I use *_LINKER_FLAGS coming from the modules at all, and,
If yes, how should I integrate them cleanly into my CMake project?
As for the 2. above, I have seen some suggestions/answers for using target_link_libraries, but I am not sure whether that is the option to go for.
Thank you for your time!
First of all, I do apologize to the community for cross posting the issue.
Matthieu has tried helping me with two options:
Providing a helper function so that the consumers of the library could call the function to properly handle the LINK_FLAGS, and,
The IMPORTED library option, which he has kept as the final answer (please see the comments there for the motivation).
Unfortunately, neither of these solutions seem to work. The first one is not a clean way of informing the consumer about your dependencies. The second version seems to work with INTERFACE libraries, but any consumer that depends on the INTERFACE library that build an object, such as, e.g., a C-API of the header-only library that builds a SHARED library, has problems building and installing the IMPORTED library.
The solution seems to be to use CMake v3.13 and above, which, as of the posting date, is in the release candidate (rc3) state. Apparently, CMake v3.13 will be introducing INTERFACE_LINK_OPTIONS for such purposes.
EDIT. CMake v3.13 has been released.
The nice thing is that you can provide a helper .cmake for them (called polo-config.cmake).
One option inside the .cmake file is to create an IMPORTED library, which you hold the flags that you want, probably as PUBLIC this time, so that they are propagated to the next user.
Of course, you need to add the library properly, setting up the include paths, the path to the library...
Related
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).
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.
I'm building a library in c++ for which I generate project with CMake.
However, I don't know how to write a good CMakeLists.txt at the top directory of the library.
In fact, actually it works if I want to simply include the library in another project (adding library, linking sources, etc...), but I want that if I generate the library itself without other project, it generate a demo project provided by the library.
So I would like a CMakeLists that
link the library in the case of an inclusion in a project
build a demo in the case of the CmakeLists is used as the top cmakelists.
My first idea is to search if "project(xxx)" has already been called, but I don't know if this is a good idea. As I don't really know what is the good practice about this I have real difficulties to find information in the documentation.
A quick way to detect if your current project is also the top-level project is by inspecting PROJECT_SOURCE_DIR and CMAKE_SOURCE_DIR:
if(PROJECT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
message("You are building stand-alone!")
endif()
But note that this might not be the best solution to your problem. Allowing libraries to be pulled into third-party builds like this requires explicit support from both your library and the enclosing build.
Alternatively, you can build both projects separately, but provide proper packaging for your library to make it easy to integrate. This approach is usually more flexible, as it gives your clients the freedom to choose when and how they want to build your library before pulling it into their build.
CMake 2.8.11 introduces target_include_directories and target_compile_definitions. However some of the devices I am working one has 2.8.9. And I don't want to compile and build latest CMake there. So what is the alternative that I can use conditionally for those devices ?
How to check whether this cmake have target_include_directories or not from inside the CMakeLists.txt ?
There is no easy way to get this functionality on earlier CMake versions, as the respective target properties that they are based on (INTERFACE_INCLUDE_DIRECTORIES and INTERFACE_COMPILE_DEFINITIONS) are missing.
This is crucial as CMake uses these target properties to automatically propagate public and interface fields to dependent targets. Emulating this behavior from CMake code is rather difficult. For an in-house project we built a system that does just this a couple of years back before CMake supported it. This basically requires writing your own replacement functions for add_library and add_executable that handle the propagation in the background through user defined properties. All targets in the system then must use the replacement functions instead of the original CMake ones for the system to work. I would strongly recommend reconsidering your CMake upgrade policies before going for this option though.
The quickest way to check whether you can use these functions is by doing a cmake_minimum_required (which you anyway should always do for all projects). If you want to make use of the commands, simply require at least 2.8.11. If you cannot make that requirement, use of the functions is out of the question in the first place and you will need to come up with an alternative. In which case it does not make much sense to check for their existence to begin with.
ComicSansMS is correct about emulation not being possible prior to 2.8.11.
You can use
if (COMMAND target_include_directories)
endif()
or
if (COMMAND target_include_directories AND COMMAND target_compile_definitions)
endif()
as your test of whether the commands are available.
How to check whether this cmake have target_include_directories or not from inside the CMakeLists.txt ?
if (NOT CMAKE_VERSION VERSION_LESS 2.8.12)
target_include_directories(...)
else()
# Not available
endif()
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).