Set CXX_INCLUDE_WHAT_YOU_USE property in CMake for every target - cmake

I have multiple CmakeLists.txt in my project and I'd like to enable iwyu.
Adding
set_property(
TARGETS MY-TARGET
PROPERTY CXX_INCLUDE_WHAT_YOU_USE ${iwyu_path}
)
would enable it for one target. I'd like to enable it for every target to avoid redundancy in another file.cmake I include in the CMakeLists.txt which already exists.

cmake_minimum_required(VERSION 3.3 FATAL_ERROR)
find_program(IWYU_PATH NAMES include-what-you-use iwyu)
if(NOT IWYU_PATH)
message(FATAL_ERROR "Could not find the program include-what-you-use")
endif()
set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE ${IWYU_PATH})
set(CMAKE_C_INCLUDE_WHAT_YOU_USE ${IWYU_PATH})
The "documentation" can be found here: CMAKE_<LANG>_INCLUDE_WHAT_YOU_USE

Related

Get cmake to find a custom config file

A cmake project I want to build depends on SDL2, and it comes with the following instruction:
Create a helper called /usr/local/lib/cmake/SDL2/sdl2-config.cmake (because SDL2 doesn't have an SDL2::SDL2 alias) containing:
if(NOT TARGET SDL2::SDL2
AND EXISTS "/usr/lib/x86_64-linux-gnu/cmake/SDL2/sdl2-config.cmake")
include(/usr/lib/x86_64-linux-gnu/cmake/SDL2/sdl2-config.cmake)
add_library(SDL2 SHARED IMPORTED)
set_target_properties(SDL2
PROPERTIES
IMPORTED_LOCATION "${libdir}/libSDL2.so"
INTERFACE_INCLUDE_DIRECTORIES "${SDL2_INCLUDE_DIRS}"
)
add_library(SDL2::SDL2 ALIAS SDL2)
endif()
The problem is that this file isn't found by my cmake (I put a message(...) at the beginning to verify this). If I change the dependency in my CMakeLists.txt from SDL2::SDL2 to just SDL2, it works fine and reads from /usr/lib/x86_64-linux-gnu/cmake/SDL2/sdl2-config.cmake. But this shouldn't be necessary.
How can I get cmake to find this custom file, or how do I find out where to put it?

How to tell CMake to find the lib in local folder through command line?

I am trying to link my local version of gflags (which is in ~/mylibs/gflags) while compiling google-test. In gtest's CMakeLists.txt, it's using find_package:
if (WITH_GFLAGS)
find_package (gflags 2.2.0)
if (gflags_FOUND)
set (HAVE_LIB_GFLAGS 1)
determine_gflags_namespace (gflags_NAMESPACE)
endif (gflags_FOUND)
endif (WITH_GFLAGS)
I don't want to modify the CMakeLists.txt file. Is there anyway to tell cmake to find the package in my folder ?
Thanks!
The gflags documentation describes specifying the location of a non-standard installation when using CMake:
The gflags_DIR variable must be set to the <prefix>/lib/cmake/gflags directory containing the gflags-config.cmake file
In your case, this might look like:
cmake -Dgflags_DIR=/home/blackball/mylibs/gflags/lib/cmake/gflags [...]
Since you are integrating gflags as subproject in a super cmake, you basically want find_package() to be a no-op if you have it in your "meta-project" source dir. This can be done by overloading the find_package() command.
Top CMakeLists.txt:
# Use find_package everywhere, no-op if it's a subproject
macro(find_package)
if(NOT TARGET ${ARGV0} AND NOT TARGET ${ARGV0}::${ARGV0})
_find_package(${ARGV})
else()
if(TARGET ${ARGV0})
get_target_property(TGT_VER ${ARGV0} VERSION)
set(TGT ${ARGV0})
else()
get_target_property(TGT_VER ${ARGV0}::${ARGV0} VERSION)
set(TGT ${ARGV0}::${ARGV0})
endif()
message(STATUS "Found ${ARGV0}: CMake Target ${TGT} (found version \"${TGT_VER}\")")
set(${ARGV0}_FOUND TRUE)
endif()
endmacro()
note: for gflags you may need to "force" gflags_NAMESPACE also since gflags it's a source not a binary (cf glog issue ToDo)

Using CMake with libraries with diamond depedencies

Lets say I have four separate projects. Three are libraries, Common, Foo, and Bar, and one of them is an executable, App. Both Foo and Bar depend on the Common library, and App depends on Foo and Bar. Furthermore, some of these projects have some scripts that need to run to generate some header and source files.
Right now I have a mess of calls like this:
if (NOT TARGET common)
add_subdirectory(${common_lib_directory})
endif()
But this doesn't feel like the right solution. If I don't wrap it in that if guard, then there are errors because it tries to build Common more than once. Putting if guards in the CMakeLists for each project doesn't seem right either. Should I be writing Find<Library> scripts for each library? I've tried looking for examples or best practices on how to set up my CMakeLists files, but the only examples I can find are either too trivial or cover completely different use cases.
It was requested in the comments that I post some code. This is more or less what my CMakeLists.txt file looks like:
cmake_minimum_required(VERSION 2.8.12)
project(app)
# Temporary files (like object files) created while compiling projects.
set(tmp_dir ${CMAKE_BINARY_DIR}/obj)
# Directory which contains the source for 3rd party libraries.
if(NOT DEFINED dependencies_root)
get_filename_component(
dependencies_root "${CMAKE_CURRENT_SOURCE_DIR}/../../../../external"
REALPATH)
endif()
set(dependencies_foo_dir "${dependencies_root}/foo"
CACHE PATH "Directory containing the foo library.")
set(dependencies_bar_dir "${dependencies_root}/bar"
CACHE PATH "Directory containing the bar library.")
if(NOT TARGET foo)
add_subdirectory("${dependencies_foo_dir}" ${tmp_dir}/foo)
endif()
if(NOT TARGET bar)
add_subdirectory("${dependencies_bar_dir}" ${tmp_dir}/bar)
endif()
include_directories(${dependencies_foo_dir}/include)
include_directories(${foo_generated_include_dir})
include_directories(${dependencies_bar_dir}/include)
include_directories(${bar_generated_include_dir})
set(app_srcs ...)
add_executable(app ${app_SRCS})
target_link_libraries(app foo bar common)
As previously mentioned, the reason I have the if (NOT TARGET blah) guards is because if I don't then I get errors like these:
CMake Error at /path/to/my/project/CMakeLists.txt:267 (add_library):
add_library cannot create target "blah" because another target with the
same name already exists. The existing target is a static library created
in source directory
"/path/to/blah".
See documentation for policy CMP0002 for more details.
If you have such closely-linked projects, the best decision seems to use guard from re-including at the beginning of each project. Such guard can be easily implemented using return command, which returns from currently executed add_subdirectory() call:
Foo/CMakeLists.txt:
if(DEFINED Foo_GUARD)
if(NOT Foo_GUARD STREQUAL ${CMAKE_CURRENT_BINARY_DIR})
return() # Project has been already included by someone else
endif()
else()
set(Foo_GUARD ${CMAKE_CURRENT_BINARY_DIR} CACHE INTERNAL "Foo guard")
endif()
project(Foo)
...
So any project can use unprotected add_subdirectory() call for include given one:
App/CMakeLists.txt:
...
# Need *Foo* functionality? Simply include it!
add_subdirectory("${dependencies_foo_dir}" ${tmp_dir}/foo)
It is possible to implement guard as a macro, put it into the library and use it in every your project:
cmake/utils.cmake:
macro(project_guarded name)
if(DEFINED ${name}_GUARD)
if(NOT ${name}_GUARD STREQUAL ${CMAKE_CURRENT_BINARY_DIR})
return() # return() *doesn't* terminate a macro!
endif()
else()
set(${name}_GUARD ${CMAKE_CURRENT_BINARY_DIR} CACHE INTERNAL "${name} guard")
endif()
project(${name})
endmacro()
Macro usage is straightforward:
project_guarded(Foo)

Project build configuration in CMake

My question is very similar to CMake : Changing name of Visual Studio and Xcode exectuables depending on configuration in a project generated by CMake. In that post the output file name will change according to the project configuration (Debug, Release and so on). I want to go further. When I know the configuration of the project, I want to tell the executable program to link different library names depending on project configurations. I was wondering whether there is a variable in CMake that can tell the project configuration. If there exists such a variable, my task will become easier:
if (Project_Configure_Name STREQUAL "Debug")
#do some thing
elseif (Project_Configure_Name STREQUAL "Release")
#do some thing
endif()
According to http://cmake.org/cmake/help/v2.8.8/cmake.html#command:target_link_libraries, you can specify libraries according to the configurations, for example:
target_link_libraries(mytarget
debug mydebuglibrary
optimized myreleaselibrary
)
Be careful that the optimized mode means every configuration that is not debug.
Following is a more complicated but more controllable solution:
Assuming you are linking to an imported library (not compiled in your cmake project), you can add it using:
add_library(foo STATIC IMPORTED)
set_property(TARGET foo PROPERTY IMPORTED_LOCATION_RELEASE c:/path/to/foo.lib)
set_property(TARGET foo PROPERTY IMPORTED_LOCATION_DEBUG c:/path/to/foo_d.lib)
add_executable(myexe src1.c src2.c)
target_link_libraries(myexe foo)
See http://www.cmake.org/Wiki/CMake/Tutorials/Exporting_and_Importing_Targets for more details.
There is always another way:
if(CMAKE_BUILD_TYPE MATCHES "release")
SET(CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE})
else(CMAKE_BUILD_TYPE MATCHES "debug")
SET(CMAKE_BUILD_TYPE "debug")
endif(CMAKE_BUILD_TYPE MATCHES "release")
We can use the variable CMAKE_BUILD_TYPE. We can also change this variable at the beginning of invoking CMAKE:
cmake .. -DCMAKE_BUILD_TYPE:STRING=debug
Then we can use this variable as an indicator of build configuration.

Conditional add_custom_command in CMake

I am using a macro to create precompiled headers for my cmake project. For gcc, this macro uses add_custom_command to create a *.h.gch file which can then be added to the target along with the other source files with add_executable/add_library. The problem is that sometimes the same *.h.gch file is used for two different targets, because some libraries are built both as static and dynamic libs.
I need to call the macro after each of the add_library calls because for MSVC/Xcode, one needs to adjust the target properties to enable PCH usage/compilation. But for gcc, this results in an error as I'm trying to use add_custom_command with an output that already has a build rule (the .gch). Currently I am avoiding this error by just skipping the add_custom_command for any target that contains "Static" in its name - this happens to work because all the static libraries in the project have a "Static" postfix, but its obviously not a very elegant solution.
Is there a way in cmake to check if a target already has a build rule, or alternatively, a way to allow add_custom_command to fail silently without causing an error? Or is there a way to change my design so that I can avoid the problem entirely? I suppose one "solution" would be to add a conditional check in each of the CMakeLists, but I really don't want to do that.
This is the code I am currently using:
The Macro:
macro(SET_PRECOMPILED_HEADER targetName PCHFile)
if(MSVC)
# PCH for MSVC
elseif(${CMAKE_GENERATOR} MATCHES "Xcode")
# PCH for Xcode
else() #gcc
if(NOT ${targetName} MATCHES "Static") ## <-- this is bad
## set the correct "compilerArgs"
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${PCHFile}.gch
COMMAND ${CMAKE_CXX_COMPILER} ${CMAKE_CXX_COMPILER_ARG1} ${compilerArgs}
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${PCHFile}
)
endif()
endmacro(SET_PRECOMPILED_HEADER targetName PCHFile)
...then in the CMakeLists, something like this:
# Dynamic version:
set(MODULE_NAME MyLib)
project(${MODULE_NAME})
## set ${sources}
add_library(${MODULE_NAME} SHARED ${sources} "src/precompiled.h.${PCH_EXT}")
set_target_properties(${MODULE_NAME} PROPERTIES COMPILE_DEFINITIONS MY_DLL_DEFINITION)
SET_PRECOMPILED_HEADER(${MODULE_NAME} "src/precompiled.h")
# Static version:
set(MODULE_NAME MyLibStatic)
project(${MODULE_NAME})
add_library(${MODULE_NAME} ${sources} "src/precompiled.h.${PCH_EXT}")
set_target_properties(${MODULE_NAME} PROPERTIES COMPILE_DEFINITIONS MY_STATIC_DEFINITION)
SET_PRECOMPILED_HEADER(${MODULE_NAME} "src/precompiled.h")
Thanks for your help! I'm sorry if this is a duplicate - there are already several questions on add_custom_command, but none of them quite seem to address what I'm after.
First, you can create target for each PCH and then use this before declaring new target:
if(TARGET ${PCHFile}.gch)
Another way:
In the root CMakeLists.txt:
set(PRECOMPILED_HEADERS "" CACHE INTERNAL "")
In the macro:
list(FIND PRECOMPILED_HEADERS ${CMAKE_CURRENT_BINARY_DIR}/${PCHFile}.gch res)
if(NOT res EQUAL -1)
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${PCHFile}.gch
COMMAND ${CMAKE_CXX_COMPILER} ${CMAKE_CXX_COMPILER_ARG1} ${compilerArgs}
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${PCHFile}
)
list(APPEND PRECOMPILED_HEADERS ${CMAKE_CURRENT_BINARY_DIR}/${PCHFile}.gch)
endif()