CMake, conflicting options with subprojects - cmake

I've added two libraries to a CMake project, googletest and tinyxml2. I've discovered that they both define the option BUILD_SHARED_LIBS. Their defaults for the option are also different which results in incorrect builds if not addressed. Currently I need to resort to setting set(BUILD_SHARED_LIBS ON CACHE BOOL "" FORCE).
Is there a better way to handle the clash? And is there an option in CMake to detect this clash to ensure it is correctly handled?

Related

How would I include asio library using CMake?

I am trying to work on a project for a class and I want to use CMake to build the project. My current project looks like
|-bin
|-CMakeLists.txt
|-include
|-asio-1.12.2
|-chat_message.hpp
|-chat_message.cpp
|-CMakeLists.txt
|-src
|-Server.cpp
although my Server.cpp needs asio.hpp that is in /include/asio-1.12.2/include.
The professor has a makefile that compiles it with the flags
-DASIO_STANDALONE -Wall -O0 -g -std=c++11 -I./include -I./include/asio-1.12.2/include. My CMakeLists files look like this:
./CMakeLists.txt
CMAKE_MINIMUM_REQUIRED(VERSION 3.12)
PROJECT(Server VERSION 0.0.1)
SET(CPP_STANDARD 11)
SET(CPP_STANDARD_REQUIRED True)
ADD_SUBDIRECTORY(include)
ADD_EXECUTABLE(Server src/Server.cpp)
TARGET_LINK_LIBRARIES(
Server PRIVATE
chat_message
asio
)
./include/CMakeLists.txt
ADD_LIBRARY(
chat_message
chat_message.cpp
chat_message.hpp
)
ADD_LIBRARY(
asio
asio-1.12.2/include/asio.cpp
asio-1.12.2/include/asio.hpp
)
TARGET_INCLUDE_DIRECTORIES(
chat_message PUBLIC "${CMAKE_SOURCE_DIR}/include"
asio PUBLIC "${CMAKE_SOURCE_DIR}/include/asio-1.12.2/include"
)
How would I link the asio header file to the Server.cpp file WITH the flags needed?
First of all, as Tzyvarev pointed out in the comments, you must split the target_include_directories() command into two separate commands. This will then propagate asio and chat_message's include directories to your Server target, which will turn add the correct include flags to the compiler flags.
Note: I'd recommend switching from CMAKE_SOURCE_DIR to CMAKE_CURRENT_SOURCE_DIR and altering your paths accordingly to make your life slightly easier if in future you decide to change your project structure, as you will usually keep a CMakeLists.txt file in the same directory as the sources for a target it creates.
The -DASIO_STANDALONE option can be added with a target_compile_definitions() call:
target_compile_definitions(asio PUBLIC ASIO_STANDALONE)
Note you do not need the -D - CMake will generate the correct compiler flag for you. Also, since this is a requirement for the asio target and all its consumers will need it, it should be added to that, rather than its consumers - it will then propagate to dependencies as needed.
In your CMakeLists.txt you have set the CPP_STANDARD and CPP_STANDARD_REQUIRED variables. The one's you're after are CMAKE_CXX_STANDARD and CMAKE_CXX_STANDARD_REQUIRED respectively.
This will set the flag for all targets throughout your project.
There are different ways to add the error, optimization and debug symbols flags and which one you use depends on your use case. The following is not an exhaustive list.
If you want everyone who builds the library to have these, irrespectively of build configuration (debug/release/etc), you set the CMAKE_CXX_FLAGS variable in your CMakeLists.txt
If you want everyone to have the flags, but only in certain build types, set the CMAKE_CXX_FLAGS_<CONFIG> variable, where <CONFIG> is the build type selected (DEBUG/RELEASE/MINSIZEREL/RELWITHDEBINFO are available by default)
If you don't want to force the flags upon everyone, before invoking CMake you can set the CXXFLAGS environment variable. But note that according to documentation this will be ineffective if CMAKE_CXX_FLAGS is set in your CMake scripts.
If you want to add flags to a single target, you can call target_compile_options on it, and set the appropriate visibility option to enable/disable propagation to consumers.
However in general you do need to think about portability when using these. For example GCC may support a certain flag which in Clang could be different.
Edit to address this comment
Since the header-only ASIO library does not like being compiled with the compiler definition mentioned above, there are two ways to address it:
Remove the ASIO_STANDALONE compiler flag
This will be the easiest thing to do from your point of view, but as a knock-on effect it will require you to have Boost installed on your system, as not having the flag above will cause the pre-processor to go through some Boost includes. There may be other effects, but this is the first one I encountered before moving on to the solution below.
Keep the flag, and use a CMake interface library
add_library() can allow you to add a target that does not actually produce any compiled objects/libraries/executables, but simply a logical CMake target that can posses properties just like any other ones - include directories, link libraries, etc. So as a minimum you could do this:
add_library(asio INTERFACE)
target_compile_options(asio INTERFACE ASIO_STANDALONE)
target_include_directories(asio INTERFACE <dir where asio.hpp lives>)
target_link_libraries(asio INTERFACE <threads>) # Using ASIO requires you link your final executable/library with your system's threading library (e.g. pthread on linux)
Then when you link another target with it like
target_link_libraries(any_lib PRIVATE asio)
any_lib will inherit all properties required to build with ASIO.
The solution you choose will be dictated by your use case, but if you have to do it the same way as your professor, then go the INTERFACE library route.

How can I ignore all cmake dev warnings from subdirectories?

I am using a few external libraries that are included as git submodules using the add_subdirectory command. Some of them are using old versions of cmake and they're issuing warnings about policies CMP0048 and CMP0077.
Is there a way to turn off all cmake warnings for these libraries?
I've tried explicitly setting the policies to OLD before including the projects but it didn't help.
I'd prefer not to edit any files in the git submodule because then there would be extra steps when someone has to clone my project's repo and build it on their machine.
I know this is a old questions, but in case someone stumbles upon the same problem, here are my findings.
Why didn't your attempts work?
Setting the policies before the add_subdirectory() call won't work, because cmake_minimum_required() implicitly set a specific set of policies, depending on the required version, which might override the set policies. Also, setting them explicitly to OLD has no effect even if they weren't overridden, since there is no distinction between being OLD because it is the default for that version or because it was explicitly set. A warning is issued in both cases, because as the documentation of every policy states, the old behavior is deprecated by definition.
Possible Solutions
I can think of the following solutions:
Use the undocumented variable CMAKE_SUPPRESS_DEVELOPER_WARNINGS, which is also set when passing -Wno-dev on the command line (see cmake.cxx). Since you probably don't want this to have an influence on your project and disable all developer warning there as well, you should restore the original value after the add_subdirectory() call:
set(no_dev_warnings_backup "$CACHE{CMAKE_SUPPRESS_DEVELOPER_WARNINGS}")
set(CMAKE_SUPPRESS_DEVELOPER_WARNINGS ON CACHE INTERNAL "" FORCE)
add_subdirectory(...)
set(CMAKE_SUPPRESS_DEVELOPER_WARNINGS ${no_dev_warnings_backup} CACHE INTERNAL "" FORCE)
The variable might be undocumented and therefore not that reliable as documented variables, but I wouldn't worry too much about it. It has been around since the CMake 2.x days and is unlikely to go anywhere.
Set CMAKE_POLICY_DEFAULT_CMP<NNNN> to NEW:
set(CMAKE_POLICY_DEFAULT_CMP0048 "NEW")
set(CMAKE_POLICY_DEFAULT_CMP0077 "NEW")
add_subdirectory(...)
Note that this doesn't just disable the warnings, but alters how CMake processes the affected CMakeLists.txt files. This might break the build of your external libraries!
Use CMAKE_PROJECT__INCLUDE or one of its related variables to inject some CMake code into the build of the external libraries. The injected code could then call cmake_policy(SET CMP<NNNN> NEW) to override the policy. Note that just like the 2. solution, this could break the build!
Instead of a git submodule, you could use FetchContent which fetches the external libraries and calls add_subdirectory() one them. Since FetchContent is quite related to ExternalProject it allows to patch the retrieved sources, so you can fix/customize the build of the external libraries:
include(FetchContent)
find_package(Patch REQUIRED)
FetchContent_Declare(myextlib
GIT_REPOSITORY <url>
GIT_TAG <tag_or_hash>
PATCH_COMMAND "${Patch_EXECUTABLE}" -p1 < path/to/myextlib.patch
)
FetchContent_MakeAvailable(myextlib)
More on diffs and patches can be found here. Alternatively, you also use git apply as patch command, since you use Git anyway.
Recommendations
I would recommend either 1 or 4. Approach 4 is the most flexible, since you could improve the build in other aspects as well, if necessary, but it is also the most involved one, since whenever you bump the version of your external libraries you also need to take care of your patches and check whether they still work etc. In case you don't need that flexibility and only care about disabling warnings, approach 1 would be the most straight forward solution.

Cmake override find_package for a given target

We have a CMakeLists.txt that links (for instance) opencv to our various binaries. This is done as follow:
find_package(OpenCV REQUIRED core imgproc highgui contrib)
target_link_library(XXX opencv_core)
We also would like to allow the person building the library to provide its own opencv library. It seems that this could be done setting -DCMAKE_PREFIX_PATH to the right path.
cmake -DCMAKE_PREFIX_PATH=".../mybuild/include;.../mybuild/lib" .
However I would like to be sure the library used is exactly the one specified by the client (i.e. if there is nothing in /mybuild/lib the configuration fails).
How can I allow somebody building the library to override the library used ? (if nothing is specified it should fall back to find_package-s)
In short
If the package provides <package>Config.cmake script, user may specify <package>_DIR CMake variable for locate this script.
Searching other places in that case may be disabled with NO_DEFAULT_PATH option for find_package().
If a package is searched with Find<package>.cmake script, there is no (generic) way for disable searching other places if user provides hint variable but it is wrong.
Explanations
Firstly, CMAKE_PREFIX_PATH provides additional installation tree for all packages. As this variable is applied to all find_package() calls, it is not wise to require all packages to be found under it.
When talk about the ways for specify installation directory for specific package, we need to distinguish two kinds of "find" scripts:
<package>Config.cmake (or some alternative names, see find_package documentation).
These scripts are shipped with the packages themselves. And there is universal way for user to specify location of such packages: CMake variable <package>_DIR, which should point to the directory with *Config.cmake script.
While default behaviour of find_package() is treating <package>_DIR variable as an additional hint, passing NO_DEFAULT_PATH option disables all implicit paths:
if(<package>_DIR) # Variable is set by the user or by previous `cmake` run.
# Search only under given directory.
find_package(<package> NO_DEFAULT_PATH)
else()
# Search everywhere (as documented for 'find_package').
find_package(<package>)
endif()
Find<package>.cmake.
This script either is shipped with CMake or should be shipped with the project.
Most of such scripts allows to hint about package location with variable (CMake or environment one) like <package>_DIR, <package>_ROOT or so.
However, almost all such scripts treat hint variable only as additional search place, so if variable is set to wrong value, they simply ignore it. And without modifying the script you cannot change that behavior.

CMAKE_SYSROOT in CMakeTestCCompiler

I have a project which builds for PPC, the Toolchain is working correctly, i can build when the sysroot is installed under /opt/poky/1.5. Now i tried to move that Installation to the Project Directory (it is not a part of the Repository there, it is just installed there so it is not reliant on that fix path, so that everyone can check out the project and build it wothout setting up the sysroot under that fixed folder).
To achieve this I set CMAKE_SYSROOT to "${PROJECT_SOURCE_DIR}/poky" where the poky will be installed upon execution of a custom build script (the project also needs to build a secure image, so it is way simpler to use a build script instead of anything else, also this is convenient for jenkins).
Since the CMAKE_SYSROOT is build from the PROJECT_SOURCE_DIR which is different for the CMakeTestCCompiler Project, the cmake call fails teloling me that the CCompiler is broken of course. So I want to know, how I am supposed to get the CMakeTestCCompiler Project to compile with the same CMAKE_SYSROOT variable, without altering the CMakeTestCCompiler Project itself (of course).
Somehow I cannot find an answer anywhere, it seems that noone ever had this issue (which frankly i cannot understand, this should be a common setup in my opinion). (Or maybe i am just too much of a noob when it comes to CMAKE, which i will gladly admit)
I am not interested in solutions like: "JUST INSTALL IT IN A FIX PATH" or such... please, I need the setup like this, I have reasons for that.
THX for reading/trying/answering/helping
Have a nice day
EDIT1:
In CMakeLists.txt (top level CMakeFile so it should be used by any build):
`SET(CMAKE_SYSROOT "${PROJECT_SOURCE_DIR}/poky/sysroots")`
In ToolchainCMake (the one given to the cmake as CMAKE_TOOLCHAIN_FILE):
`SET(CMAKE_SYSTEM_NAME Linux)`
`SET(CMAKE_SYSTEM_VERSION 1)`
`SET(CMAKE_SYSROOT "${PROJECT_SOURCE_DIR}/poky/sysroots")`
`SET(COMPILER_ROOT ${PROJECT_SOURCE_DIR}/poky/sysroots/i686-pokysdk-linux/usr/bin/powerpc-poky-linux-gnuspe)`
`SET(CMAKE_C_COMPILER ${COMPILER_ROOT}/powerpc-poky-linux-gnuspe-gcc)`
`SET(CMAKE_CXX_COMPILER ${COMPILER_ROOT}/powerpc-poky-linux-gnuspe-g++)`
`MESSAGE("CMAKE_C_COMPILER: ${CMAKE_C_COMPILER}")`
`MESSAGE("CMAKE_CXX_COMPILER: ${CMAKE_CXX_COMPILER}")`
`MESSAGE("COMPILER_ROOT: ${COMPILER_ROOT}")`
`SET(CMAKE_FIND_ROOT_PATH ${SYS_ROOT}/ppce500v2-poky-linux-gnuspe)`
`SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)`
`SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)`
`SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)`
EDIT2:
I used the
`set(CMAKE_C_COMPILER_WORKS 1 CACHE INTERNAL "")`
`set(CMAKE_CXX_COMPILER_WORKS 1 CACHE INTERNAL "")`
settings to simulate the CMakeTestCCompiler build succeeding and realized that I am facing some additional problems: It seem that the packages are looked up on the system instead of the CMAKE_SYSROOT folder. Even tried the
`SET(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})`
to try to force the search in there, but without luck. In the CMakeError.log I can see, that the compiler itself was configured with the prefix option that points to /opt/poky/1.5, the path that i want to "overwrite", now I am not sure if the compiler could even deal with an alternate path.
I felt the need to add these information, they not really add to the problem at hand.
ERRORS:
I also found some errors in the above cmake:
`SET(CMAKE_SYSROOT "${PROJECT_SOURCE_DIR}/poky/sysroots")`
must be
`SET(CMAKE_SYSROOT "${PROJECT_SOURCE_DIR}/poky/sysroots/ppce500v2-poky-linux-gnuspe")`
instead and therefor the
`SET(CMAKE_FIND_ROOT_PATH ${SYS_ROOT}/ppce500v2-poky-linux-gnuspe)`
changes to
`SET(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})`
EDIT: Whole answer changed.
My first suspicion was that the problem is that value of ${PROJECT_SOURCE_DIR} is not known in CMAKE_TOOLCHAIN_FILE as it is processed before CMakeLists.txt. But this isn't true.
I had similar problem (CMake 2.8.12.2), everything worked OK, when I passed cross compiler by CC environment variable with --sysroot option, i.e. CMake was invoked as follows:
CC="arm-linux-gnueabi-gcc --sysroot=/path/to/sysroot" cmake /path/to/sources
When I switched to using toolchain file, CMake started to report that C compiler doesn't work.
To workaround this problem, I use CMakeForceCompiler package. Parts toolchain file (along with comments) I think are relevant:
include(CMakeForceCompiler)
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_VERSION 1)
# Force compiler - only option that actually works
cmake_force_c_compiler (${TOOLCHAIN_PATH}/bin/arm-linux-gnueabi-gcc GNU)
cmake_force_cxx_compiler(${TOOLCHAIN_PATH}/bin/arm-linux-gnueabi-g++ GNU)
# NOTE: CMAKE_SYSROOT doesn't work as expected
add_definitions("--sysroot=${TOOLCHAIN_SYSROOT}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --sysroot=${TOOLCHAIN_SYSROOT}" CACHE INTERNAL "" FORCE)
Note, that TOOLCHAIN_PATH and TOOLCHAIN_SYSROOT are my local variables set before.

Adding debug flags after configuration is done

We are currently porting a large project from GNU autotools to CMake. An open problem that is of great interest to our users (Scientific Computing: users are developpers) is to switch to debug compiler flags without reconfiguring the whole project.
There is of course a workaround to add some thing like
set_property(TARGET <target> PROPERTY COMPILE_FLAGS <debugflags>)
to the CMakeLists.txt and run
make target
and count on cmakes caching abilities to only configure that particular
But for our users that are used to automakes
make CXXFLAGS="<debugflags>" <target>
this is no convincing way to go.
The same goes for having 2 built directories, one with and one without debug flags.
I have looked for more possibilites to mimic such behaviour without success. Do you know any? Or do you know whether any such features are planned for future cmake releases?
The "problem" is that you have to modify your CmakeLists file and
afterwards undo that change
You don't need to change the CMakeLists file for this. CMake allows specifying a build type on the command line for make based generators:
cmake -DCMAKE_BUILD_TYPE=Debug [...] && make
This already adds the -g compile flag for you. If you need additional project specific flags, you can add them conditionally depending on the build type.
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
# do your stuff
endif()
Note that once you have specified a build type, CMake will keep using that same build type for all subsequent runs unless you explicitly set a different one through the command line or delete the cache.