Overwrite subdirectory's variable CMAKE_BINARY_DIR - cmake

I use Googletest library as a git submodule and I would like to move its build artifacts in a designated folder, however inside of googletest/CMakeLists.txt (that I would like to avoid changing) it hardcodes artifacts path to
${CMAKE_BUILD_DIR}/lib
and unfortunately maintainers didn't use ${CMAKE_CURRENT_BUILD_DIR} instead to add their artifacts in their local build path that I specify using add_subdirectory command in my own CMakeLists.txt:
add_subdirectory(${VENDOR_SOURCE_DIR}/googletest ${VENDOR_BINARY_DIR}/googletest)
Some artifacts respect the second path, but those hardcoded with aforementioned line don't.
Is there a way to overwrite Googletest's local value of ${CMAKE_BUILD_DIR} without forking/modifying the library's CMakeLists.txt?

You can set dir on gtest target:
function target_set_dir(target dir)
set_target_properties(${target} PROPERTIES
CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${dir}
CMAKE_LIBRARY_OUTPUT_DIRECTORY ${dir}
CMAKE_RUNTIME_OUTPUT_DIRECTORY ${dir}
)
endfunction()
target_set_dir(gtest ${CMAKE_BUILD_DIR]/gtest})

Related

CMake ExternalProject_Add() that uses a parent folder in its include_directories()

I have an 'int_tester' CMake project that makes use of ExternalProject_Add for its 'drv' dependency. Now, this 'drv' is also built using CMake, and it uses a parent directory in its include_directories(). Trouble is that I do not know how to copy this parent folder inside ExternalProject_Add().
Directory structure is like:
/
|-common
|-IP
| |-int_tester
| |-drv
CMakeLists.txt for drv looks like:
add_library(SPT_driver STATIC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
target_include_directories(SPT_driver
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/api/
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../common/include/
)
CMakeLists.txt for int_tester like:
add_executable(int_tester ...)
ExternalProject_Add(drv_ext
PREFIX ${CMAKE_CURRENT_BINARY_DIR}/drv/drv_dir
URL ${CMAKE_CURRENT_SOURCE_DIR}/../drv
INSTALL_COMMAND ""
)
add_library(drv_import STATIC IMPORTED) #phony lib
add_dependencies (drv_import drv_ext) #link phony lib to the external proj
ExternalProject_Get_Property(drv_ext install_dir) #find out where the external proj is installed
set_property(TARGET drv_import PROPERTY IMPORTED_LOCATION ${install_dir}/src/drv_ext-build/obj/drv.a) #so that the phony lib can have a target
include_directories(${install_dir}/src/drv_ext/api) #and to make current executable dependent on api .h and .a of the externally built lib
target_link_libraries(int_tester drv_import)
As one can see, drv depends on ${CMAKE_CURRENT_SOURCE_DIR}/../../common/include/.
When 'int_tester' builds 'drv' (via ExternalProject_Add), folder ../../common/include/ cannot be found (it is not brought it by ExternalProject_Add). Even if I add another fake ExternProject for this common folder, its directory structure will not match the one expected by CMakeLists.txt of 'drv'.
What am I missing, please?
If you want to build 'drv' using IP/drv as its source directory, explicitely specify SOURCE_DIR option for ExternalProject_Add:
ExternalProject_Add(drv_ext
PREFIX ${CMAKE_CURRENT_BINARY_DIR}/drv/drv_dir
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../drv
INSTALL_COMMAND ""
)
Without this option, source directory is deduced from PREFX (see documentation for exact algorithm of that deduction).
Option URL for ExternalProject_Add denotes directory, which should be copied into the source one. If you do not want to copy, do not use that option.

Where to set CMAKE_CONFIGURATION_TYPES in a project with subprojects

Lets say I have a project with two independent subprojects. If I understood cmake correctly, the idea would be to have one root CMakeLists.txt defining a project(...) and then using add_subdirectory(...) to include the subprojects. Each subproject would have its own CMakeLists.txt defining its own project. This way projects can be build either together (using the root cmake file) or individually (using the subprojects cmake file).
I now would like to change the CMAKE_CONFIGURATION_TYPES. Should I do this in the root CMakeLists.txt or in each subproject, or both?
Changing it in the root would mean that building a subproject individually would offer the wrong configuration types; the other options would duplicate the cmake code. I think I'm missing something here.
Factorize out the code that sets up configuration-dependent settings. Create a file, say, SetUpConfigurations.cmake with this content:
if(NOT SET_UP_CONFIGURATIONS_DONE)
set(SET_UP_CONFIGURATIONS_DONE TRUE)
# No reason to set CMAKE_CONFIGURATION_TYPES if it's not a multiconfig generator
# Also no reason mess with CMAKE_BUILD_TYPE if it's a multiconfig generator.
get_property(isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(isMultiConfig)
set(CMAKE_CONFIGURATION_TYPES "Debug;Release;Profile" CACHE STRING "" FORCE)
else()
if(NOT CMAKE_BUILD_TYPE)
message("Defaulting to release build.")
set(CMAKE_BUILD_TYPE Release CACHE STRING "" FORCE)
endif()
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY HELPSTRING "Choose the type of build")
# set the valid options for cmake-gui drop-down list
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug;Release;Profile")
endif()
# now set up the Profile configuration
set(CMAKE_C_FLAGS_PROFILE "...")
set(CMAKE_CXX_FLAGS_PROFILE "...")
set(CMAKE_EXE_LINKER_FLAGS_PROFILE "...")
endif()
Then include(..) this file at the beginning of the CMakeLists.txt's.
You have two choices about where to put SetUpConfigurations.cmake, it depends on how you organize your projects, repositories:
The quick'n'dirty way: Copy and commit this script into each project that needs it. Its location will be fixed, relative to the CMakeLists.txt of the project. So you can include it, for example, with include(${CMAKE_CURRENT_SOURCE_DIR}/<...>/SetUpConfigurations.cmake)
The disciplined way: Maintain a repository with your custom CMake scripts, like this one. Each time you generate a project with the cmake command, you pass the path to this repository in the CMAKE_MODULE_PATH variable:
cmake -DCMAKE_MODULE_PATH=<dir-of-cmake-script-repo> ...
In this case include the script with include(SetUpConfigurations) (no .cmake extension).
A note about what a multiconfig generator is:
Xcode and Visual Studio are multiconfig generators. They respect the value of CMAKE_CONFIGURATION_TYPES but CMAKE_BUILD_TYPE has no effect since no concrete configuration is defined when the CMakeLists.txt is processed. It will be selected on the IDE's user interface later.
On the other hand, the makefile-style generators are not interested in CMAKE_CONFIGURATION_TYPES. CMAKE_BUILD_TYPE defines the configuration. It is a concrete value when the CMakeLists.txt file is processed but still: never make any decisions based on the value of CMAKE_BUILD_TYPE:
if(CMAKE_BUILD_TYPE STREQUAL "Release") # WRONG!
....
endif()
You project won't work as intended in multiconfig generators.
When use add_subdirectory into subproject dir, you propagate almost all variables into that subproject, which contradicts to "subproject independency".
Instead, it is better to build and install subproject using nested cmake call inside execute_process(). If you want to make some subproject's definitions available for top-level project, you need to "export" this definitions when subproject is installed. This question/answer post describes, how to do that.

Cmake build directory

After running cmake/make for my own project, I noticed cmake puts object files into a directory named CMakeFiles/myproject.dir/. How can I set a different build directory (e.g. bin/)? I know of the variables CMAKE_BINARY_DIR or CMAKE_CURRENT_BINARY_DIR, but they are supposed to be read-only.
As you already note, you can't set CMAKE_BINARY_DIR variable.
Depending on your purposes you can use:
install
add_executable(simple_bin ${SOURCES})
install(TARGETS simple_bin DESTINATION ${PROJECT_SOURCE_DIR}/bin)
add_test
add_executable(simple_bin ${SOURCES})
enable_testing()
add_test(test_name simple_bin --data-directory ${PROJECT_SOURCE_DIR}/bin)

CMake and FindProtobuf

I'm using the FindProtobuf module in a project where the protocol buffer files are in a sub-directory. I want the CMakeLists.txt file in that sub-directory to invoke protoc to generate the CPP Files. My project folder structure is like this:
cammy/CMakeLists.txt # Root CMakeLists.txt
cammy/protofiles/test.proto # protofile
cammy/protofiles/CMakeLists.txt
I have the include(FindProtobuf), the find_package invocation and the call to PROTOBUF_GENERATE_CPP in the CMakeLists.txt file in protobuf folder.
The executable build step is in the Root CMakeLists.txt file and I add the generated files
to the target executable in this file
ie.
add_executable( ${EXEC} ${SRCS} ${PROTO_SRC} ${PROTO_HEADER} )
target_link_libraries( ${EXEC} ${PROTOBUF_LIBRARIES} )
are both defined in the root CMakeLists.txt
When I run cmake, it does not run protoc to generate the Source files even though I expilicitly tie generated sources to the executable thereby creating a dependency.
When I move all the contents of CMakeLists.txt in the protofiles folder into the root CMakeLists.txt, the proto files are compiled.
Can anyone help me with this? I want all the protocol buffer building stuff to go in the CMakeLists.txt file created in the protofiles folder.
I also noticed that variables generated in the inner CMakeLists.txt ( like PROTO_SRC ) are defined in the inner file when printed ( ie I get the correct generated CPP filename ) but when I print the same variable in the root file.. it is empty. Its almost as though I need to "export" (if there were a way in cmake ) the variables out to the root folder.
Any help would be much appreciated.
Thanks
Kartik
I think [FindProtobuf][0] isn't really meant to be used this way. From its docs:
NOTE: The PROTOBUF_GENERATE_CPP macro & add_executable() or add_library()
calls only work properly within the same directory.
You're trying to use the PROTOBUF_GENERATE_CPP macro in a subdirectory, and although the CMake docs don't really make it clear, a subdirectory introduces a new scope for variables. This means that any variables set or modified in the subdir scope don't affect similarly-named variables in the parent scope. Hence the reason for PROTO_SRC being available in your protofiles dir, but not in the parent.
The way to pass variables up a scope is to use [set(... PARENT_SCOPE)][1], so in protofiles/CMakeLists.txt you could do:
PROTOBUF_GENERATE_CPP(PROTO_SRC PROTO_HEADER test.proto)
set(PROTOBUF_INCLUDE_DIRS ${PROTOBUF_INCLUDE_DIRS} PARENT_SCOPE)
set(PROTOBUF_LIBRARIES ${PROTOBUF_LIBRARIES} PARENT_SCOPE)
set(PROTO_SRC ${PROTO_SRC} PARENT_SCOPE)
set(PROTO_HEADER ${PROTO_HEADER} PARENT_SCOPE)
However, this still doesn't get us all the way!
CMake doesn't actually invoke the protoc compiler to generate the .pb.h and .pb.cc files - it uses [add_custom_command][2] to do this. The custom command specifies the .pb.h and .pb.cc files as outputs, and the custom command is only invoked (i.e. protoc executed) if a subsequent target which depends on these files is built.
So, at configure time (when CMake executes) these files don't exist. This is a problem if you try to add them as sources to an add_library or add_executable command - CMake needs to be told that these files don't exist when it runs, but that they will exist at build time.
The way to do that is to set the [GENERATED][3] property to TRUE for these files. The PROTOBUF_GENERATE_CPP macro does that automatically, but as with the variables, the property isn't populated up into the parent scope. So in your top-level CMakeLists.txt, you also need to add:
set_source_files_properties(${PROTO_SRC} ${PROTO_HEADER} PROPERTIES
GENERATED TRUE)
As you can see, using PROTOBUF_GENERATE_CPP in a different dir to the corresponding add_library/add_executable commands is a bit fragile. If you can avoid doing it, you probably should.
[0]: https://cmake.org/cmake/help/latest/module/FindProtobuf.html "CMake latest documentation for "FindProtobuf" module"
[1]: https://cmake.org/cmake/help/latest/command/set.html "CMake latest documentation for "set" command"
[2]: https://cmake.org/cmake/help/latest/command/add_custom_command.html "CMake latest documentation for "add_custom_command""
[3]: https://cmake.org/cmake/help/latest/prop_sf/GENERATED.html "CMake latest documentation for "GENERATED" source file property"

CMake: adding custom resources to build directory

I am making a small program which requires an image file foo.bmp to run
so i can compile the program but to run it, i have to copy foo.bmp to 'build' subdirectory manually
what command should i use in CMakeLists.txt to automatically add foo.bmp to build subdirectory as the program compiles?
In case of this might help, I tried another solution using file command. There is the option COPY that simply copy a file or directory from source to dest.
Like this:
FILE(COPY yourImg.png DESTINATION "${CMAKE_BINARY_DIR}")
Relative path also works for destination (You can simply use . for instance)
Doc reference: https://cmake.org/cmake/help/v3.0/command/file.html
To do that you should use add_custom_command to generate build rules for file you needs in the build directory. Then add dependencies from your targets to those files: CMake only build something if it's needed by a target.
You should also make sure to only copy files if you're not building from the source directory.
Something like this:
project(foo)
cmake_minimum_required(VERSION 2.8)
# we don't want to copy if we're building in the source dir
if (NOT CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR)
# list of files for which we add a copy rule
set(data_SHADOW yourimg.png)
foreach(item IN LISTS data_SHADOW)
message(STATUS ${item})
add_custom_command(
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${item}"
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/${item}" "${CMAKE_CURRENT_BINARY_DIR}/${item}"
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${item}"
)
endforeach()
endif()
# files are only copied if a target depends on them
add_custom_target(data-target ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/yourimg.png")
In this case I'm using a "ALL" custom target with a dependency on the yourimg.png file to force the copy, but you can also add dependency from one of your existing targets.