CMake run custom command with externalproject's target - c++

I have a subproject in my project for generating code used in the project, however i want to include it using ExternalProject so it can be built and ran regardless of the toolchain i use for the main project.
It mostly works except i can't figure out how to use it in add_custom_command, since i want it to use the target rule specified in the docs:
If COMMAND specifies an executable target name (created by the add_executable() command), it will automatically be replaced by the location of the executable created at build time [...]
Simple example of my project setup that replicates the issue:
https://godbolt.org/z/of8G4c4Gf
Replacing the ExternalProject_Add with a simple add_subdirectory makes it work, but wouldn't do what i want in the case of using a different toolchain.

ExternalProject_Add will add the codegen target. However, CMake has no idea what that target is doing and what output it will provide, as that info is now hidden away in the external CMake run. So the outer CMake run has no idea about the codegen binary produced by that step and where it will be located. You need to provide the full path to the executable to add_custom_command manually.
ExternalProject_Add will build codegen and place the resulting binary in a subdirectory inside your build directory. The exact location is toolchain dependent, so it can be tricky to reconstruct the full target path. The most robust way to solve this is:
Add an install step to the codegen project. This will allow you to portably specify which path the installed binary will end up in relative to the install root directory.
Set the INSTALL_DIR option on ExternalProject_Add to a subdirectory inside your PROJECT_BINARY_DIR.
Hardcode the full path to the installed codegen binary in the custom command of the outer CMake project. To work on multiple platforms, you may need to make use of the CMAKE_EXECUTABLE_SUFFIX variable.

Related

CMake output to build directory [duplicate]

Is it possible to specify build directory within CMakeLists file? If yes, how.
My aim is to be able to call "cmake" within top level source directory and have cmake figure out the build directory.
Afaik, with CMake the build directory is always the directory from where you invoke the cmake or ccmake command. So if you want to change the build directory, you have to change directories before running CMake.
To control the location where executables, static and shared libraries are placed once finished, you can modifiy CMAKE_RUNTIME_OUTPUT_DIRECTORY, CMAKE_ARCHIVE_OUTPUT_DIRECTORY, and CMAKE_LIBRARY_OUTPUT_DIRECTORY respectively.
By design, there is not a way to specify that in CMakeLists.txt. It is designed for the user to be able to build the project in whatever directory they want. The typical workflow is:
Check out the project source code.
Go to desired build directory, or the source dir if you plan to do an in-source build.
Run cmake or ccmake to configure the project in that build directory.
Build your project.
All of the directories specified within your CMakeLists.txt should be relative to the ${PROJECT_BINARY_DIR} and ${PROJECT_SOURCE_DIR} variables. In this way, your code becomes buildable across different platforms, which is the goal of CMake.

correctly set CMake variables when adding yaml-cpp to existing project

I've added the yaml-cpp git repository as a submodule and add it to my CMake project using add_subdirectory.
Everything's fine but I have to set YAML_CPP_INCLUDE_DIR and YAML_CPP_LIBRARIES manually to use them for my own targets.
Because there is a file yaml-cpp-config.cmake (generated in the build folder) setting these variables I tried to just include it:
include("${CMAKE_BINARY_DIR}/yaml-cpp/yaml-cpp-config.cmake")
but then I get:
CMake Error at /bla/bla/build/yaml-cpp/yaml-cpp-config.cmake:11 (include):
The file
/bla/bla/aml-cpp/yaml-cpp-targets.cmake
was generated by the export() command. It may not be used as the argument
to the include() command. Use ALIAS targets instead to refer to targets by
alternative names.
I really don't understand this message. How would I provide my targets with the yaml-cpp include directories and libraries without having to set a hard coded variable?
I'm not searching for a way to correctly include() the file in case it doesn't have to be done. I'm just interested in how I should provide the desired information to my targets.
Unfortunately yaml-cpp seems to not make use of target_include_directories() which would set the include directories automatically where needed.
From description of export command:
Create a file <filename> that may be included by outside projects to import targets from the current project’s build tree.
Note to "outside" word: this is why you get the error message while trying to include the file from the same project, which issues export command.
Correct way to use yaml-cpp-config.cmake file would be building yaml-cpp outside of your project. For example, you may use ExternalProject_Add in conjunction with execute_process for build yaml-cpp as part of configuration stage of your project, see more about this approach here.
Then you may include given file to your project with find_package:
find_package(yaml-cpp PATHS <yaml-cpp-build-dir>)
Note, that yaml-cpp-config.cmake in binary directory describes build state of yaml-cpp project.
If you want to install libraries/executables from your project, you are better to install yaml-cpp, and include corresponded file from its installation directory:
find_package(yaml-cpp PATHS <yaml-cpp-install-dir>)

Issue with CLion and find_package: Generated CMake files are placed in some odd location

So basically I have the following setup:
A small test library called mylib with an according CMake file. The CMake file creates all the necessary information so that another project (which is going to be my binary) can use find_package to add the library. The library also has a install target.
A small binary called mybin again wih according CMake file which is depended on the mylib project. Using find_package I can add mylib either by specifying the location of the according myLibConfig.cmake (and myLibTargets.cmake etc.) files or by executing the install target of mylib and then letting CMake find the library itself.
Using CMake and XCode everything works like a charm. First, I configure and build the library. Second, I configure my binary project, specify the location of the library and then build it without any problems.
Now I want to do the same using CLion. My problem now is that CLion puts all the generated CMake file (which usually are placed in the build folder) in some cryptic location which can't be changed in the IDE. Now, when I want to build the mybin project I have to specify this cryptic location which seems kinda odd to me (especially because you have to find out first where CLion actually places those files).
So my question is: Is there a more convenient way to handle the described configuration? I guess this is more or less the standard use case which makes me wonder if I'm missing out on something. Being able to specify where CLion should put the generated CMake files would solve my problem I guess.
I know that i can install the library (using the install target) and then let CMake find it. The problem here is that CLion (to my understanding) doesn't support install targets and therefore I have to use (in my case) XCode to build and install the library first.
I was misunderstsanding the intention of find_package(as Tsyvarev pointed out). By using the solution proposed in this question I managed to add an install target to CLion which now allows me to conveniently build "mylib" and use it in the "mybin" project without having to configure anything manually.

CMake post build step: copy multiple files dependent on visual studio configuration

I'm trying to write a CMakeLists.txt which copies as a post build event required dlls from external libraries to the folder in which the executable is located after building. The OS I'm working on is Win7 and VS2010.
There a quite a lot of external libraries, so I do not want to list them individually within the CMakeLists.txt. My current solution is to use file globbing and create a post build event for each library:
FILE(GLOB files "${LIBRARY_DIR}/lib/$(ConfigurationName)/*dll")
MESSAGE("FILE LIST IS: ${files}")
FOREACH(file ${files})
ADD_CUSTOM_COMMAND(
TARGET mylib
POST_BUILD
COMMENT "Copying dll ${file}..."
COMMAND ${CMAKE_COMMAND} -E copy ${file} "${CMAKE_BINARY_DIR}/$(ConfigurationName)/"
)
ENDFOREACH()
Basically, the code snipped above works file if I replace $(ConfigurationName) with Release or Debug. However, I'd like to take the libraries from the corresponding directory dependent on Release or Debug build mode. In the code snipped above, $(ConfigurationName) does not get substituted by the visual studio build mode. I guess this is due to that this is a VS2010 variable which isn't known at cmake generation time).
Does somebody have a smart idea how to resolve this issue?
ADD_CUSTOM_COMMAND understands generator expressions, so you would be able to use $<CONFIG> rather than $(Configuration) or $(ConfigurationName) there. For the FILE command, however, this won't work. More importantly though, the file globbing approach isn't going to work for the first time you build your project if those DLL's are built as part of the same project, since none of the DLL's would be there when you first run CMake in such an arrangement.
You may want to look into BundleUtilities as a more robust way to solve your problem. Despite its name, it supports all platforms, not just Mac. You would need to invoke the fixup_bundle command as a post-build or install step to achieve what you describe. There are a few variations of this approach already described online (e.g. here and here).
You can also have a look at the source for the DeployQt4.cmake file which should be included in the Modules subdirectory of your CMake installation for ideas.
Lets suppose you have _TARGET_NAME filled with your your project name, and ADDITIONAL_DLL_PATH with a list of folders containing external dlls, Then the following code will copy any external dependencies found using dumpbin on windows.
foreach(CONF_TYPE ${CMAKE_CONFIGURATION_TYPES})
string(TOUPPER ${CONF_TYPE} CONF_TYPE_U)
get_property(TARGET_LOCATION_${CONF_TYPE_U} TARGET ${_TARGET_NAME} PROPERTY LOCATION_${CONF_TYPE_U})
get_filename_component(TARGE_DIR ${TARGET_LOCATION_${CONF_TYPE_U}} PATH)
install(CODE "
if (\"\${BUILD_TYPE}\" STREQUAL \"${CONF_TYPE}\")
include(BundleUtilities)
fixup_bundle(\"${TARGET_LOCATION_${CONF_TYPE_U}}\" \"\" \"${ADDITIONAL_DLL_PATH}\")
endif()"
COMPONENT Runtime)
endforeach()
There are downsides to this solution though, Like the need to build INSTALL target whenever you need to copy those files (which might not be many times), and not detecting delay loaded dlls.

How can I use CMake to both build wxwidgets on-demand and link with it

I have the following situation:
I'm working on an application that depends on a number of third party libs, among them wxwidgets
I build the application for multiple target configurations (x86, arm, Linux, Windows) using Linux as my build host system
Due to the above mentioned multiple target configurations, I have chosen to build those third-party libs from source, using CMake's ExternalProject_Add function.
The third-party libs are built 'on-demand' at a location separate from my application's CMAKE_BINARY_DIR so that I can wipe the build tree for my application without having to rebuild the third-party libs (takes a looooong time).
The location of the third-party libs is different depending on what target configuration I build them for (obviously).
I'm quite new to CMake and the problem I currently face is this:
The source files in my application can't find the wx include files and I need to set the correct linker flags to be able to link my application against wxwidgets.
This seems to be handled by a utility 'wx-config' that provides exactly that info as output when run with either the --cppflags or --libs flag. I can not however, figure out how to catch that output and append it to the include dirs and linked libraries I setup from my CMakeLists.txt files.
So basically what I want is.
Build wxwidgets (if it doesn't exist) for the current target configuration
Run wx-config --cppflags and --libs to find out the correct include dirs and linker flags for the current target configuration
Use the info from step 2 when building targets that are my own application
So far I've tried something like this:
# Set a target-configuration-specific location
set(wxwidgetsTop ${MYPROJECT_EXTERNAL_DIR}/wxwidgets/wxwidgets_${MYPROJECT_CURRENT_TARGET_CFG})
# Build the project
ExternalProject_Add( wxWidgetsExternal
PREFIX ${wxwidgetsTop}
URL ${MYPROJECT_EXTERNAL_DIR}/tarballs/wxWidgets-3.0.2.tar.bz2
SOURCE_DIR ${wxwidgetsTop}/src/wxwidgets
CONFIGURE_COMMAND ${configure_cmdline}
BUILD_COMMAND make -j${MYPROJECT_NCPU}
INSTALL_COMMAND make install
)
# Create a wxwidgets target to be used as a dependency from other code
add_library(wxWidgets IMPORTED STATIC GLOBAL)
add_dependencies(wxWidgets wxWidgetsExternal)
# (non-working) attempt to get the correct include dirs and linker
# flags for wxwidgets
add_custom_command(TARGET wxWidgetsExternal
POST_BUILD
COMMAND ${INSTALL_DIR}/bin/wx-config ARGS --cppflags
COMMENT "Running wx-config"
)
but the above does not provide a way to actually use the result from the custom command to append the cppflags and linker options when building the targets that make up my application.
What is a good way to achieve what I want?
I see three different ways of doing this:
Method 1: use find_package
Use wxWidgets as a standalone requirement for your project, and expect the devs to install it before building your project. In your CMakeLists.txt you will need to call find_package(wxWidgets), like this:
find_package(wxWidgets COMPONENTS net gl core base)
if(wxWidgets_FOUND)
include(${wxWidgets_USE_FILE})
# and for each of your dependent executable/library targets:
target_link_libraries(<YourTarget> ${wxWidgets_LIBRARIES})
endif()
This has the advantage of not rebuilding the lib if you rebuild your project, however it requires some work for your user (they need to handle the installation of wxWidgets by hand) and for you (you need to setup include paths / compile definitions / ... by hand).
Method 2: embed wxWidgets
The second option is to bundle wxWidgets in your repo (svn external or git submodule) and usually (re)write the CMakeLists.txt of this lib to be target-oriented. Then, in your top-most CMakeLists.txt, you can do the following:
# for example, if you just need core and net:
target_link_librairies(my_app PUBLIC wxWidgetsCore wxWidgetsNet)
# No need to manually setup include dirs, etc...
To make a CMakeLists.txt target-oriented, you define include directories and other compilation properties for a target, not a directory. Example:
# When defining wxWidgetsCore, for example
add_library(wxWidgetsCore ...)
target_include_directories(wxWidgetsCore PUBLIC someDir)
target_compile_definitions(wxWidgetsCore PUBLIC -pedantic)
target_link_libraries(wxWidgetsCore PUBLIC someLib)
The drawback of this approach is that rebuilding your project will trigger a rebuild of wxWidgets. However, it is possible to trick this by not using "rebuild" but "clean just my app, then build". Here is some insight on how to achieve this.
Method 3: some sort of hybrid
The big drawback of method 2 leads to the third approach: don't put wxWidgets in your project, but create a CMakeLists.txt that will "import" the lib. The idea: you ask your user for the directory where wxWidgets is installed, then this script will setup everything for your project. First, put the CMakeLists.txt here:
/your-project-root
/thirdparty
/wxWidgets
CMakeLists.txt
/dir-where-wxwidgets-is-installed
...
Now, you define an imported target:
# When defining wxWidgetsCore, for example
set(WX_INCLUDE_DIR ${USER_SPECIFIED_WX_ROOT}/include)
add_library(wxWidgetsCore IMPORTED GLOBAL)
set_property(TARGET wxWidgetsCore APPEND PROPERTY
INTERFACE_INCLUDE_DIRECTORIES ${WX_INCLUDE_DIR})
See INTERFACE_INCLUDE_DIRECTORIES and INTERFACE_LINK_LIBRARIES. You need your user to have build wxWidgets somewhere in his system, but from your point of view you just do target_link_libraries(your_app PUBLIC wxWidgets...), as in method 2. The advantage is that this approach is interchangeable with method 2 transparently, and you don't put the whole dependency in your project.
Setting cppflags and linker flags has to be done at CMake time, but you are trying to run wx-config at build time and you are not capturing its output anyway, so your add_custom_command() isn't doing anything useful other than printing things to the build tool's output.
Ideally, you would use the FindwxWidgets module CMake already provides. It requires wxWidgets to already be built (but see further below). Have a look at the CMake documentation for it and see if that at least sounds like what you are trying to achieve manually by using wx-config. If you can get FindwxWidgets to do the job for you, that would be a much cleaner approach.
Getting something to build at configure time so you can use it later on in your CMakeLists.txt file is a bit more tricky. ExternalProject_Add() downloads and builds things at build time, but you need wxWidgets to be built earlier at configure time. I wrote an article recently for how to do at least the downloading part at configure time and you should be able to adapt it to do the whole build at configure time instead. The article uses Google Test as its example and can be found here:
https://crascit.com/2015/07/25/cmake-gtest/
It would be trivial to make it put the wxWidgets build wherever you like, not just in the CMAKE_BINARY_DIR area. That would allow you to have different wxWidgets builds for each build configuration and to be able to wipe out your application's build tree independently of the wxWidgets builds.
Hope that points you in the right direction.
The solution I use checks for wxWidgets installation in the system using find_package, if it's not found, then the script downloads wxWidgets from github and links the program against downloaded library. The lib is installed in the build directory, so only the first build is slow - subsequent builds do not even check wxWidgets sources timestamps, so the process is as fast as building using preinstalled wxWidgets library.
Here's how my script does it:
It quietly checks for wxWidgets installation using find_package(wxWidgets QUIET),
If it's found, the script adds a dummy library wxWidgets_external,
If it's not, then it creates an ExternalProject named wxWidgets_external which downloads, builds and installs the library in the build dir, setting wxWidgets_ROOT_DIR to point to the wxWidgets installation dir,
Then we add another ExternalProject pointing to a folder with the main program's source files and CMakeLists.txt build script. This external projects depends on wxWidgets_external which is either a dummy library in case wxWidgets is preinstalled in the system, or an external project set up to download the library from github,
In the aforementioned CMakeLists.txt we again call find_package, this time with REQUIRED parameter and use the library the standard way (https://docs.wxwidgets.org/trunk/overview_cmake.html). Because we set up the dependencies and variables correctly, this call will use either preinstalled wxWidgets (if it's available) or the one downloaded from github.
There are more quirks to it, but that's the gist of it. The full sample code (tested on Linux, Windows and Mac) is available on github (https://github.com/lszl84/wx_cmake_template).
Also see full blog post which explains this in more detail: https://justdevtutorials.medium.com/wxwidgets-cmake-multiplatform-superbuild-4ea86c4e6eda