cmake: pass include directories to external project - cmake

I'm using cmake and external project module via ExternalProject_Add.
I'd like to specify custom header location for external project (just exactly as if I use include_directories in that project, but I am not able to modify its CMakeLists.txt and don't want to apply patch).
Is there any possibility to pass some include path to my external project?
I tried CMAKE_ARGS -DCMAKE_INCLUDE_PATH=<required path> without success.

You may execute additional CMake script for external project by assigning path to this script to variable CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE (documentation).
Let external project uses CMake command
project(e_cool)
and you want to execute
include_directories(/path/to/additional/include)
at that moment.
For doing that, you need to prepare cmake script with corresponded content:
fix_e_cool.cmake:
include_directories(/path/to/additional/include)
And pass this script via CMAKE_ARGS option of ExternalProject_Add in the main project:
CMakeLists.txt:
...
ExternalProject_Add(<name>
...
CMAKE_ARGS -DCMAKE_PROJECT_e_cool_INCLUDE=${CMAKE_SOURCE_DIR}/fix_e_cool.cmake
)

Using option will allow you to pass data from the the command line, for example:
cmake -DHEADER_PATH="/usr/my/path/file.h" ..

Related

CMake: how to make execute_process wait for subdirectory to finish?

Part of my source code is generated by a tool which is also built under our main project with a add_subdirectory. We execute this tool with a execute_process command. Clearly, if the tool is not built before we reach the execute_process statement it will fail.
I use a GLOB (file(GLOB...)) to find the source files generated. I do this because it is not possible to know beforehand how many files are generated, neither their names.
How do I force cmake to wait for the subproject to be compiled before the execute process? I would need something like a DEPENDS property for the execute_process but this option is not available.
# This subproject will source generator the tool
add_subdirectory(generator)
# I need something like: wait_for(generator)
execute_process(COMMAND generator ${CMAKE_SOURCE_DIR}/src)
file(GLOB GeneratedSources ${CMAKE_SOURCE_DIR}/src/*.cpp)
add_executable(mainprject.exe ${ProcessorSourceFiles}
Command execute_process executes its COMMAND immediately, at configuration stage. So it cannot be arranged after the executable is created with add_executable command: that executable will be built only at build stage.
You need to build subproject at configuration stage too. E.g. with
execute_process(COMMAND ${CMAKE_COMMAND}
-S ${CMAKE_SOURCE_DIR}/generator
-B ${CMAKE_BINARY_DIR}/generator
-G ${CMAKE_GENERATOR}
)
execute_process(COMMAND ${CMAKE_COMMAND}
--build ${CMAKE_BINARY_DIR}/generator
)
The first command invokes cmake for configure the 'generator' project, located under ${CMAKE_SOURCE_DIR}/generator directory. With -G option we use for subproject the same CMake generator, as one used for the main project.
The second command builds that project, so it produces generator executable.
After generator executable is created, you may use it for your project:
execute_process(COMMAND ${CMAKE_BINARY_DIR}/generator/<...>/generator ${CMAKE_SOURCE_DIR}/src)
Here you need to pass absolute path to the generator executable as the first parameter to COMMAND: CMake no longer have generator executable target, so it won't substitute its path automatically.
You will need to model this with target dependencies. The tool "generator" should be a cmake target. In that case use add_custom_target instead of execute_process somthing like this:
add_custom_target(generate_sources ALL COMMAND generator ${CMAKE_SOURCE_DIR}/src))
Then add a target dependency to "generator" using add_dependencies:
add_dependencies(generate_sources generator)
This will make sure your target "generate_sources", which runs the tool will only run during build after the target "generator" has been compiled.
The following is false, see the comments for more info:
Use add_dependencies to add a dependency from "mainproject.exe" to "generate_sources". Now this I have never tested, so take with a grain of salt: With CMake more recent than version 3.12, according to the entry on file, you should then be able to change your file command to:
file(GLOB GeneratedSources CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/src/*.cpp)
Which I interpret as this will re-glob the files during build if the directory changes.

CMake external target: reference source dir in custom command

in my CMake project, I use an external project called ep_glslang which is quite straightforward:
ExternalProject_Add(ep_glslang
DEPENDS ep_vulkan_headers
URL https://github.com/KhronosGroup/glslang/archive/7.11.3113.tar.gz
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${_PREFIX}
-DVULKAN_HEADERS_INSTALL_DIR=${_PREFIX}
)
Yet, before the configure step I need to run a Python located at the root of the source directory of glslang.
I created a custom step for my external project:
ExternalProject_Add_Step(ep_glslang update_glslang_sources
DEPENDEES update
DEPENDERS configure
COMMAND ${PYTHON_EXECUTABLE} update_glslang_sources.py
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${_PREFIX}
)
It seems to be an idiomatic way of doing that. Yet the step fails for it does not find the python script.
How would I get the source directory of the external project in order to make things like this:
...
COMMAND ${PYTHON_EXECUTABLE} ${EP_SRC_DIR}/update_glslang_sources.py
...
I don't find anything, even in the generator expressions, that would help me.
Thank you :)
The solution by Tsyvarev:
For refer to source directory in ExternalProject_Add use <SOURCE_DIR> expression: COMMAND ${PYTHON_EXECUTABLE} <SOURCE_DIR>/update_glslang_sources.py.

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

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.

Use external DLL in cmake build

I'm working on the cmake scripts for my project and I've run into a problem:
My project uses a 3rd party library (FreeImage), which has its own Makefile-based build system. I can build FreeImage just fine by simply running "make" (I'm using gnuwin32), which will build FreeImage using MinGW and produce:
FreeImage.lib
FreeImage.dll
Now my problem is twofold:
I want to execute "make" from my cmake script.
I want to link to the import lib (FreeImage.lib), and also make sure the DLL gets copied to the correct place so the EXE will run.
I know how to link to the LIB file, but I'm lost on the rest.
The folder structure is like this:
MyProject # main directory
MyProject/Libs/FreeImage # FreeImage root directory
MyProject/Libs/FreeImage/Dist # This is where FreeImage outputs go (LIB and DLL)
BTW: I'm running on Windows 7. I plan to build my project both with MSVC and MinGW.
Thanks!
EDIT:
I'm now trying to use ExternalProject_Add like so:
ExternalProject_Add(
FreeImage
PREFIX ./Libs/FreeImage
URL ./Libs/FreeImage
BUILD_COMMAND make
)
This gets me part of the way there, but doesn't totally work... it tries to configure things for me and tries to use nmake... ugh
In my opinion, there are two options:
In case you have put your FreeImage sources in your projects' source-tree, the easiest option may be to use the execute_process() command. Assuming FreeImage is in your projects' source-tree in "3rdparty/FreeImage/" you can do something like,
execute_process( COMMAND make WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/3rdParty/FreeImage )
Optionally, you can copy the dll from 3rdParty/FreeImage/bin into you own bin directory. And then you can write a FreeImageConfig.cmake for importing the library:
add_library( FreeImage IMPORTED )
set_target_properties( FreeImage PROPERTIES IMPORTED_LOCATION ${PROJECT_SOURCE_DIR}/3rdParty/FreeImage/lib )
...
The other option is to make use of the ExternalProject module. You can also take a look at this article from Kitware for an overview of this module. In essence, you specify the full chain of commands needed to get the source, configure the build, build the source and install it. All in your own CMakeLists.txt