Setting default option values for third-party modules [duplicate] - cmake

I am trying to include several third-party libraries in my source tree with minimal changes to their build system for ease of upgrading. They all use CMake, as do I, so in my own CMakeLists.txt I can use add_subdirectory(extern/foo) for libfoo.
But the foo CMakeLists.txt compiles a test harness, builds documentation, a shared library which I don't need, and so on. The libfoo authors had the foresight to control these via options - option(FOO_BUILD_SHARED "Build libfoo shared library" ON) for example - which means I can set them via the CMake command line. But I would like to make that off by default and overridable via the command line.
I have tried doing set(FOO_BUILD_SHARED OFF) before add_subdirectory(extern/foo). That has the effect of not trying to build the shared library during the second and subsequent build attempts, but not during the first one, which is the one I really need to speed up.
Is this possible, or do I need to maintain forked CMakeLists.txt for these projects?

Try setting the variable in the CACHE
SET(FOO_BUILD_SHARED OFF CACHE BOOL "Build libfoo shared library")
Note: You need to specify the variable type and a description so CMake knows how to display this entry in the GUI.

This question is rather old but Google brought me here.
The problem with SET(<variable name> <value> CACHE BOOL "" FORCE) is that it will set the option project wide. If you want to use a sub-project, which is a library, and you want to set BUILD_STATIC_LIBS for the sub-project (ParentLibrary) using SET(... CACHE BOOL "" FORCE) it will set the value for all projects.
I'm using the following project structure:
|CMakeLists.txt (root)
|- dependencies
| CMakeLists.txt (dependencies)
|- ParentLibrary
| CMakeLists.txt (parent)
|- lib
| CMakeLists.txt (lib)
Now I have CMakeLists.txt (dependencies) which looks like this:
# Copy the option you want to change from ParentLibrary here
option (BUILD_SHARED_LIBS "Build shared libraries" ON)
set(BUILD_SHARED_LIBS OFF)
add_subdirectory(ParentLibrary)
Advantage is that I don't have to modify ParentLibrary and that I can set the option only for that project.
It is necessary to explicitly copy the option command from the ParentLibrary as otherwise when executing CMake configuration initially the value of the variable would first be set by the set command and later the value would be overwritten by the option command because there was no value in the cache. When executing CMake configuration for the second time the option command would be ignored because there is already a value in the cache and the value from the set command would be used. This would lead to some strange behavior that the configuration between two CMake runs would be different.

Related

Have cmake append a directory to the build RPATH

I'd like to add another directory to a target's BUILD_RPATH property, but I'd like it at the end of the list, so it's searched last, after the other directories that cmake automatically adds to target's BUILD_RPATH. But there doesn't seem to be way to add to the property after the automatic RPATH directories.
At build time, my system libraries are not in the normal locations, but in a staging area. In order to run uninstalled built binaries, I need to add this staging area to the binaries' RPATHs. And this part is straightforward to do and works fine, like this:
add_executable(mybinary ${BINARY_SOURCES})
set_property(TARGET mybinary APPEND PROPERTY BUILD_RPATH ${STAGING_LIB_DIR})
But mybinary also uses a library that it built as part of the same project:
add_library(mylib SHARED ${LIB_SOURCES})
target_link_libraries(mybinary PRIVATE mylib)
When mybinary is run, I'd like it to use the mylib that was just built and is in ${CMAKE_CURRENT_BINARY_DIR}, not another copy somewhere else, perhaps in the system library directory from the last time make install was run to install the project. Or, in my case, a copy of the library in ${STAGING_LIB_DIR}.
cmake will automatically add ${CMAKE_CURRENT_BINARY_DIR}, or whatever is appropriate, for any libraries not from the system to the build RPATH of produced binaries. So when one runs mybinary from the build directory it will search for the mylib in the build directory.
But the problem is it appends these automatic library directories to whatever I have set BUILD_RPATH to. So one gets a final RPATH of ${STAGING_LIB_DIR}:${CMAKE_CURRENT_BINARY_DIR} and the wrong copy of mylib is used.
You could set the SKIP_BUILD_RPATH target property:
SKIP_BUILD_RPATH is a boolean specifying whether to skip automatic generation of an rpath allowing the target to run from the build tree. This property is initialized by the value of the variable CMAKE_SKIP_BUILD_RPATH if it is set when a target is created.
And then manually set the RPATH in whatever way/order you would like without worrying about CMake doing additional things to it.

Cmake wildcard for External Project

I want to pass some settings to an External Project via a ProjectSettings.cmake file. I know I can use the CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE for this purpose (passed via CMAKE_ARGS to the ExternalProject_add()).
But I have to do this for many external projects that my main project builds and do not want to keep repeating it - I have a generic variable defined for CMAKE_ARGS that is presently passed to every call of ExternalProject_Add() and I want to just append to this.
Is there a wildcard mechanism for the PROJECT-NAME field of this CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE?
Or is there a way by which I can make the CMakeLists.txt of all the external projects I want to build include my ProjectSettings.cmake? Note that I do not have access to change all of them - hence the need to manipulate them from the parent project.
Wrapping reoccurring code centrally is one of the major purposes of CMake macros and functions.
So in your case this would look something like this:
macro(My_ExternalProject_Add _name)
ExternalProject_Add(
${_name}
CMAKE_ARGS -DCMAKE_PROJECT_${_name}_INCLUDE:FILEPATH=ProjectSettings.cmake
${ARGN}
)
endmacro()
The ARGN will add all parameters given after _name. So just place the macro() in your root CMakeLists.txt file and replace all ExternalProject_Add() calls with My_ExternalProject_Add().
Alternatively you could use CMAKE_ARGS -C ProjectSettings.cmake initial cache command line option to pre-load settings for an external project (then you won't need the project's name).
References
Function vs. Macro in CMake
How to define a cmake macro in a sub_directory that uses the CURRENT_SOURCE_DIR?
How to store CMake build settings

CLion: issue with Cmake configuration types

I understand that by default, Clion creates the binary files for a project loaded in Clion in all the four configurations:
(Debug;Release;MinSizeRel;RelWithDebInfo)
as well as one called: __default__.
I am using a third party cmake module which downloads an external project in a way that add_subdirectory() can be run on it so it would be included in the root project.
add_subdirectory(${downloaded_proj_src_dir} ${downloaded_proj_bin_dir} EXCLUDE_FROM_ALL)
In this setup, if I decide to place the child project outside the binary directory of the root project, I get:
Error:Binary directories outside of CMake build directory are not supported. Most likely this error is caused by an add_subdirectory command with an explicitly specified binary_dir argument.
which is an understandable restriction by CMake.
now if I instead decide to set the binary directory of the downloaded project in a subdirectory of the binary directory of the parent project, ie:
set(downloaded_proj_bin_dir "${CMAKE_BINARY_DIR}/${downloaded_proj}-build")
...
add_subdirectory(${downloaded_proj_src_dir} ${downloaded_proj_bin_dir} EXCLUDE_FROM_ALL)
I will get the file created in the parent binary directory of all the build configurations because ${CMAKE_BINARY_DIR} is different for each configuration. To avoid seeing all these directories listed on the project view sidebar, I have set the CMAKE_CONFIGURATION_TYPES to be Debug. But even then, I get:
Error:Configuration Debug
The current CMakeCache.txt directory /path/Debug/downloaded_proj_bin/CMakeCache.txt is different than the directory /path/__default__/downloaded_proj_bin/CMakeCache.txt where CMakeCache.txt was created. This may result in binaries being created in the wrong place. If you are not sure, reedit the CMakeCache.txt
Clearly something is going on with this __default__ configuration which I don't understand. So the question is, what is the significance of this default configuration and why should there be a conflict here?
P.s. Setting the configuration to __default__ does not solve the problem as I will have a __default__0 configuration created instead and get the same error.
Update: some further observations
My enviornment variables set in IDE don't have any effect on the cmake builds.
Cmake "options" however which presumably will be passed as arguments to cmake do seem to work.
-D CMAKE_CONFIGURATION_TYPES=Debug.
When I specify the command line option, I get
Warning:Manually-specified variables were not used by the project:
CMAKE_CONFIGURATION_TYPES
But it clearly does have the effect of no longer creating the other build configurations. My guess is that this message relates to the __default__ build which is ignoring the argument.
Even in the case of specifying CMAKE_CONFIGURATION_TYPES in the IDE, I still get the additional __default__ build which is apparently unaffected by the CMAKE_CONFIGURATION_TYPES assignment.
Logging: message("Build type: ${CMAKE_BUILD_TYPE} ) does not return any build_type.
Outputting message(and generator: ${CMAKE_GENERATOR} ") returns "Unix-make files" for both, so both are being generated with the same generator.
Taking a diff from the CMakeCache.txt files, I can see that they are identical.
Do you have in DownloadProject.cmake the right setting? for:
set(_DownloadProjectDir "${CMAKE_CURRENT_LIST_DIR}")
I had the same problem trying to set google test(with the help of https://github.com/Crascit/DownloadProject) and my _DownloadProjectDir was setted as "test". Maybe when I moved this cmake file in my project Clion changed that automatically.
So, it turns out that you can sort this out quite easily by adding the following line above line 145 in DownloadProject.cmake:
file(REMOVE "${DL_ARGS_DOWNLOAD_DIR}/CMakeCache.txt")
This seems to be because CLion copies the default across to the other configurations and doesn't clear the cache. This is a problem only because DownloadProject creates a project within the project (I think...). Anyway, deleting this file before configuring the CMakeLists.txt by-passes this issue. I'll submit a pull request to the DownloadProject repository as this doesn't seem to have any adverse effects when not using CLion.

Wrap cache variables of a library when configuring my code in CMake

I have to build a library that is using another library, how can I make variables of that another library not showing in the CMake-Gui and use instead values that are configured by my CMake script?
For example when my application is built for Mobile, I already have a flag for OpenGL ES, but the SDL "VIDEO_OPENGLES" variable still shows up in the GUI. The problem is that since my build script already have knowledge of what happens, it can happily pre-configure other scripts instead of polluting the Gui of the users that need to compile my code.
Also I want to know what happens if 2 libraries have by accident 2 variables with same name but used in a different fashion.
The key word is INTERNAL property of the variable. Take a look at this example:
Test/CMakeLists.txt:
cmake_minimum_required(VERSION 2.8)
Project(Test)
add_subdirectory(Component1)
add_subdirectory(Component2)
Test/Component1/CMakeLists.txt:
cmake_minimum_required(VERSION 2.8.11)
project(Component1)
set(SOURCES test1.c)
set(Component1_CONF "SomeValue" CACHE STRING "Component1_CONF description")
message(STATUS "Component1_CONF=${Component1_CONF}")
add_library(Component1 ${SOURCES})
target_include_directories(
Component1 INTERFACE
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>"
"$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>"
)
Test/Component2/CMakeLists.txt:
project(Component2)
set(SOURCES test1.c)
set(Component2_CONF "/etc/passwd" CACHE FILEPATH "Component2_CONF description")
add_executable(Component2 ${SOURCES})
target_link_libraries(Component2 Component1)
Actually Component2 can be a library static or shared not an executable, it doesn't matter.
Now if you run cmake-gui on the top directory and run Configure you will see both Component1_CONF and Component2_CONF. But if you change the top-level CMakeLists.txt and add the line which forcibly sets Component1_CONF:
cmake_minimum_required(VERSION 2.8)
Project(Test)
set(Component1_CONF "Other value" CACHE INTERNAL "Component1_CONF forced value" FORCE)
add_subdirectory(Component1)
add_subdirectory(Component2)
you will effectively hide Component1_CONF from cmake-gui and even from command-line (-DComponent1_CONF=Boo) configuration.
Also you should note that usually variables are set only in the current scope (unless PARENT keyword is used). Thus variables set in some directory don't affect variables set in peer subirectories and the parent subdirectory. However if variables are set in the very same scope (say, when you run configuration tests from within the parent CMakeLists.txt) then yes, they could interfere one another. To prevent this CMake uses naming convention which explicitly separates variables from different packages. Most of configuration macros receives "prefix" parameter which specifies the name prefix for variables being set in the macros, so macro users (that is applications CMakeLists.txt) can explicitly separate test variables from different dependent packages.

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.