cmake add-custom-target doesn't create a target - cmake

I want to build a package that contains a list of files (configuration files that my main task uses). So I am adding these in my CMakeLists.txt
add_custom_target(my-configs)
install(
FILES
file1.cfg
DESTINATION data/task
COMPONENT my-configs
EXCLUDE_FROM_ALL)
...
But when I run :
make -C ../cmake-build/linux_64_static_make_RelWithDebInfo/task/ my-configs
I get :
make: Entering directory '/development/cmake-build/linux_64_static_make_RelWithDebInfo/task'
make: *** No rule to make target 'my-configs'. Stop.
Why is that? Shouldn't the above create the target?
EDIT
This component doesn't do anything apart from copying files into the specified location. In that case, do I need a custom_target at all?
Or could I just go ahead and do cmake install? If I do install I see :
cd ../cmake-build/linux_64_static_make_RelWithDebInfo && DESTDIR=../../cmake-install/linux_64_static_make_RelWithDebInfo cmake -DCOMPONENT=my_configs -P cmake_install.cmake
-- Install configuration: "RelWithDebInfo"
-- Install component: "my_configs"
but nothing gets installed in the DESTDIR as expected - which is why I thought I needed a target so I can regenerate the cmake-build tree? Otherwise how will it know about the new component?

One use case for add_custom_target() can be like a .PHONY target in a Makefile.
You need to add dependencies to your custom target so that CMake knows how to fill the order. Here some examples:
add_custom_target(libs DEPENDS library_1 library_2 ...)
So now you have a target libs that will build your set of libraries when you specify that target to be built or some other target depends on libs to be built.
Another example:
add_custom_target(unmount_server COMMAND "umount /mnt/deployment")
This would provide a target that would unmount a server drive. Very much a non-portable operation as I have written it.

Related

cmake not rebuilding a non-download external project after manually editing its sources

I'm working on some modifications to the openEMS project. This project uses cmake to build all of its components. The top level CMakeLists.txt file contains the following:
# ...
ExternalProject_Add( openEMS
DEPENDS fparser CSXCAD
SOURCE_DIR ${PROJECT_SOURCE_DIR}/openEMS
CMAKE_ARGS -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE} -DFPARSER_ROOT_DIR=${CMAKE_INSTALL_PREFIX} -DCSXCAD_ROOT_DIR=${CMAKE_INSTALL_PREFIX} -DWITH_MPI=${WITH_MPI} -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}
)
# ...
Inside the openEMS directory, there's another CMakeLists.txt with the following:
# ...
set(SOURCES
openems.cpp
)
# ...
add_library( openEMS SHARED ${SOURCES})
# ...
After building the project successfully once, make does not rebuild anything when, for example, openems.cpp is modified. Why?
$ mkdir build
$ cd build
$ cmake -DBUILD_APPCSXCAD=NO
$ make
[builds all files]
$ touch ../openEMS/openems.cpp
$ make
[ 33%] Built target fparser
[ 66%] Built target CSXCAD
[100%] Built target openEMS
(noting is built)
I have checked and the modification date of openems.cpp is newer than the target. Even deleting the produced library files and binaries, both in the install directory and in the build directory, does not cause it to rebuild anything. The only way I can get it to rebuild is by deleting everything in the build directory and re-running cmake which, of course, rebuilds everything.
This looks like a case of the following. Quoting from the docs for ExternalProject_Add at the section titled "Build Step Options":
BUILD_ALWAYS <bool>
Enabling this option forces the build step to always be run. This can be the easiest way to robustly ensure that the external project's own build dependencies are evaluated rather than relying on the default success timestamp-based method. This option is not normally needed unless developers are expected to modify something the external project's build depends on in a way that is not detectable via the step target dependencies (e.g. SOURCE_DIR is used without a download method and developers might modify the sources in SOURCE_DIR).
If that's the case, the solution would be to add the BUILD_ALWAYS argument to the ExternalProject_Add call like.
ExternalProject_Add( openEMS
DEPENDS fparser CSXCAD
SOURCE_DIR ${PROJECT_SOURCE_DIR}/openEMS
CMAKE_ARGS -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE} -DFPARSER_ROOT_DIR=${CMAKE_INSTALL_PREFIX} -DCSXCAD_ROOT_DIR=${CMAKE_INSTALL_PREFIX} -DWITH_MPI=${WITH_MPI} -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}
BUILD_ALWAYS TRUE
)
If you confirm that this solves the issue, you might want to raise this as an issue to the maintainers of openEMS.
Also note that since the external project there is using CMake as a buildsystem, you could also add the CONFIGURE_HANDLED_BY_BUILD TRUE to the argument list. See the docs for more info.
Edit: The asker opened a GitHub Pull-Request.

How to make meson scan for new target

I have meson project which have multiple targets in multiple subdirs.
When I add a new target into the project in a subdir, and try to compile that target, an error occurs:
ERROR: Can't invoke target `test`: target not found
The only way I have to compile the target is to setup again the project
meson setup builddir --wipe
Is the any command to make the build system scan for changes in subdirs' meson.build files?
You have two more options after you have changed subfiles:
run meson --reconfigure builddir to force reconfiguration. setup --wipe will remove the build directory, in case something corrupted the cache or builddir - and than creates a (new) configuration.
touch the root's meson.build (touch meson.build), then when you run ninja -C builddir again, it will reconfigure before building automatically since the time stamp of the root's meson.build doesn't match with the cache's anymore. The manual suggests, it's also possible to let the system send signals to a script which touches the main meson.build when change events are noticed.

install(FILES "${CMAKE_CFG_INTDIR}/Abc_Win.dll" DESTINATION "Bin")

Because the 'Abc_Win.dll' is shared ('dll') and needs to be copied to the target-EXE I added a custom target - like this. I'm working with MS-VisualStudio 2005-2011 and using cmake 2.8.8.
add_custom_target( "Abc_Win.dll" )
add_custom_command( TARGET "Abc_Win.dll" POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"MyDllSource/${CMAKE_CFG_INTDIR}/Abc_Win.dll"
"MyExeDest/${MWEB_CMAKE_CFG_INTDIR}/Abc_Win.dll" )
set_property(Abc_Win.dll" PROPERTY FOLDER ${MWEB_FOLDER_ADDON}/${MWEB_FOLDER_RTE})
All above working like I expect and the 'Abc_Win.dll' get on the right place when I build.
Now I add an the following install command - expecting 'Abc_Win.dll' in "Bin"-Directory beside my EXE.
install(FILES "MyDllSource/${CMAKE_CFG_INTDIR}/Abc_Win.dll" DESTINATION "Bin")
When I build now the CMakePredefinedTargets->INSTALL (doesn't matter which MS-StudioVersion I use) I get always a build error in the cmake generated file 'cmake_install.cmake':
file INSTALL cannot find
"MyDllSource/$(Configuration)/Abc_Win.dll"
I understand that cmake does not uses build-rules from MS-VS - like it does it for add_custom_command. It also seems to be that this cmake-file has no glue from the Content $(CONFIGRATION) which is set actual to 'Debug'.
Has anybody an idea how to solve this Problem? Help would be very appreciated. Thanks.
There's an undocumented variable you can use here: CMAKE_INSTALL_CONFIG_NAME.
Unless you happen to have defined this yourself in your CMakeLists.txt, it will be undefined when CMake runs.
However, when you use install commands in your CMakeLists.txt, CMake generates a file called "cmake_install.cmake" in the root of your build tree (same place as CMakeCache.txt). This is executed at install time, and it sets CMAKE_INSTALL_CONFIG_NAME to your current configuration in Visual Studio.
There's another slight twist; to avoid CMake expanding ${CMAKE_INSTALL_CONFIG_NAME} in the install command (it would expand to an empty string), you need to escape it with a \.
So, you just need to change your install command to:
install(FILES "MyDllSource/\${CMAKE_INSTALL_CONFIG_NAME}/Abc_Win.dll"
DESTINATION "Bin")

CMake how to set the build directory to be different than source directory

I'm pretty new to CMake, and read a few tutorials on how to use it, and wrote some complicated 50 lines of CMake script in order to make a program for 3 different compilers. This probably concludes all my knowledge in CMake.
Now my problem is that I have some source code, whose folder I don't want to touch/mess with when I make the program. I want that all CMake and make output files and folders to go into ../Compile/, so I changed a few variables in my CMake script for that, and it worked for sometime when I did something like this on my laptop:
Compile$ cmake ../src
Compile$ make
Where with that I had a clean output in the folder I'm in right now, which is exactly what I'm looking for.
Now I moved to another computer, and recompiled CMake 2.8.11.2, and I'm almost back to square one! It always compiles the thing into the src folder where my CMakeLists.txt is located.
The part where I choose the directory in my CMake script is this:
set(dir ${CMAKE_CURRENT_SOURCE_DIR}/../Compile/)
set(EXECUTABLE_OUTPUT_PATH ${dir} CACHE PATH "Build directory" FORCE)
set(LIBRARY_OUTPUT_PATH ${dir} CACHE PATH "Build directory" FORCE)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${dir})
set(CMAKE_BUILD_FILES_DIRECTORY ${dir})
set(CMAKE_BUILD_DIRECTORY ${dir})
set(CMAKE_BINARY_DIR ${dir})
SET(EXECUTABLE_OUTPUT_PATH ${dir})
SET(LIBRARY_OUTPUT_PATH ${dir}lib)
SET(CMAKE_CACHEFILE_DIR ${dir})
And now it always ends with:
-- Build files have been written to: /.../src
Am I missing something?
It sounds like you want an out of source build. There are a couple of ways you can create an out of source build.
Do what you were doing, run
cd /path/to/my/build/folder
cmake /path/to/my/source/folder
which will cause cmake to generate a build tree in /path/to/my/build/folder for the source tree in /path/to/my/source/folder.
Once you've created it, cmake remembers where the source folder is - so you can rerun
cmake on the build tree with
cmake /path/to/my/build/folder
or even
cmake .
if your current directory is already the build folder.
For CMake 3.13 or later, use these options to set the source and build folders
cmake -B/path/to/my/build/folder -S/path/to/my/source/folder
For older CMake, use some undocumented options to set the source and build folders:
cmake -B/path/to/my/build/folder -H/path/to/my/source/folder
which will do exactly the same thing as (1), but without the reliance on the current working directory.
CMake puts all of its outputs in the build tree by default, so unless you are liberally using ${CMAKE_SOURCE_DIR} or ${CMAKE_CURRENT_SOURCE_DIR} in your cmake files, it shouldn't touch your source tree.
The biggest thing that can go wrong is if you have previously generated a build tree in your source tree (i.e. you have an in source build). Once you've done this the second part of (1) above kicks in, and cmake doesn't make any changes to the source or build locations. Thus, you cannot create an out-of-source build for a source directory with an in-source build. You can fix this fairly easily by removing (at a minimum) CMakeCache.txt from the source directory. There are a few other files (mostly in the CMakeFiles directory) that CMake generates that you should remove as well, but these won't cause cmake to treat the source tree as a build tree.
Since out-of-source builds are often more desirable than in-source builds, you might want to modify your cmake to require out of source builds:
# Ensures that we do an out of source build
MACRO(MACRO_ENSURE_OUT_OF_SOURCE_BUILD MSG)
STRING(COMPARE EQUAL "${CMAKE_SOURCE_DIR}"
"${CMAKE_BINARY_DIR}" insource)
GET_FILENAME_COMPONENT(PARENTDIR ${CMAKE_SOURCE_DIR} PATH)
STRING(COMPARE EQUAL "${CMAKE_SOURCE_DIR}"
"${PARENTDIR}" insourcesubdir)
IF(insource OR insourcesubdir)
MESSAGE(FATAL_ERROR "${MSG}")
ENDIF(insource OR insourcesubdir)
ENDMACRO(MACRO_ENSURE_OUT_OF_SOURCE_BUILD)
MACRO_ENSURE_OUT_OF_SOURCE_BUILD(
"${CMAKE_PROJECT_NAME} requires an out of source build."
)
The above macro comes from a commonly used module called MacroOutOfSourceBuild. There are numerous sources for MacroOutOfSourceBuild.cmake on google but I can't seem to find the original and it's short enough to include here in full.
Unfortunately cmake has usually written a few files by the time the macro is invoked, so although it will stop you from actually performing the build you will still need to delete CMakeCache.txt and CMakeFiles.
You may find it useful to set the paths that binaries, shared and static libraries are written to - in which case see how do I make cmake output into a 'bin' dir? (disclaimer, I have the top voted answer on that question...but that's how I know about it).
There's little need to set all the variables you're setting. CMake sets them to reasonable defaults. You should definitely not modify CMAKE_BINARY_DIR or CMAKE_CACHEFILE_DIR. Treat these as read-only.
First remove the existing problematic cache file from the src directory:
cd src
rm CMakeCache.txt
cd ..
Then remove all the set() commands and do:
cd Compile && rm -rf *
cmake ../src
As long as you're outside of the source directory when running CMake, it will not modify the source directory unless your CMakeList explicitly tells it to do so.
Once you have this working, you can look at where CMake puts things by default, and only if you're not satisfied with the default locations (such as the default value of EXECUTABLE_OUTPUT_PATH), modify only those you need. And try to express them relative to CMAKE_BINARY_DIR, CMAKE_CURRENT_BINARY_DIR, PROJECT_BINARY_DIR etc.
If you look at CMake documentation, you'll see variables partitioned into semantic sections. Except for very special circumstances, you should treat all those listed under "Variables that Provide Information" as read-only inside CMakeLists.
Turning my comment into an answer:
In case anyone did what I did, which was start by putting all the build files in the source directory:
cd src
cmake .
cmake will put a bunch of build files and cache files (CMakeCache.txt, CMakeFiles, cmake_install.cmake, etc) in the src dir.
To change to an out of source build, I had to remove all of those files. Then I could do what #Angew recommended in his answer:
mkdir -p src/build
cd src/build
cmake ..
As of CMake Wiki:
CMAKE_BINARY_DIR
if you are building in-source, this is the same as CMAKE_SOURCE_DIR, otherwise this is the top level directory of your
build tree
Compare these two variables to determine if out-of-source build was started
You should not rely on a hard coded build dir name in your script, so the line with ../Compile must be changed.
It's because it should be up to user where to compile.
Instead of that use one of predefined variables:
http://www.cmake.org/Wiki/CMake_Useful_Variables
(look for CMAKE_BINARY_DIR and CMAKE_CURRENT_BINARY_DIR)
Starting from cmake 3.19 you can use also preset files, where you can specify among other useful things also the output binary dir:
{
"version": 2,
"cmakeMinimumRequired": {
"major": 3,
"minor": 19,
"patch": 0
},
"configurePresets": [
{
"name": "default",
"displayName": "Default",
"description": "Default build cfg",
"generator": "Unix Makefiles",
"binaryDir": "${sourceDir}/Compile",
"cacheVariables": {
},
"environment": {
}
}
]
}
Then just run cmake with --preset arg:
cmake --preset=default
Then just cd to your build dir and run make, in your case:
cd ./Compile
make

Installing only one target (and its dependencies) out of a complex project with cmake (open to better solutions)

Let's say I have a project made of several subprojects A, B, C, D...
All subprojects depends on A, which changes rather frequently.
Plus, there might be some further dependencies:
in this example, D depends on B.
Now: many people are working on these projects. The main CMakeLists.txt file should include all directories, so that the build all builds everything. But people would like also to be able to work only on one of these projects, and not having to build/install everything everytime.
If I am working on D, I can easily build "only" D by calling
cmake --build . --target D -- -j7
or
ninja -j7 D
This will also build A and B if something for them has changed. Perfect.
But how can I call install only for D without triggering build all?
I would like that if I call:
ninja -j7 D install
it only built D (and dependencies) and then installed only D and its dependencies (A and B).
Instead, it builds the target all and install all.
I would like to keep that the target all keep building everything. So EXCLUDE_FROM_ALL wouldn't be an option. But going in that direction I couldn't find any solution.
So I am thinking of the following strategy:
Apart from subproject A, all other targets are set to EXCLUDE_FROM_ALL, and OPTIONAL at installation.
I add one extra subproject that simply depends from all other sub-projects (maybe I make each target publish its name by using some variable set at PARENT_SCOPE), and people will have to call that when they want to build and install everything.
Is it going to work? Is there any better solution?
We would like to avoid that everybody has to edit the main CMakeLists.txt file to exclude projects he is not interested in. The solution should be portable to different OSs.
Edit:
I tried the strategy I proposed, but it didn't work: in my case, putting an install statement for a target (even if specified as OPTIONAL) will make ineffective EXCLUDE_FROM_ALL. Reading better in the documentation I found out that:
Installing a target with EXCLUDE_FROM_ALL set to true has undefined behavior.
I also get this warning:
Target <targetname> has EXCLUDE_FROM_ALL set and will not be built by default but an install rule has been provided for it. CMake does not define behavior for this case.
Edit 2:
I tried putting EXCLUDE_FROM_ALL as an option of add_subdirectory (instead of add_library/add_executable), but then all the install statements in those sub-directory seem to be ignored: only install statements in non excluded-from-all subdirectories will be installed.
Edit 3:
Even if I activate CMAKE_SKIP_INSTALL_ALL_DEPENDENCY:
set(CMAKE_SKIP_INSTALL_ALL_DEPENDENCY true)
in the main CMakeLists.txt file, and I omit all EXCLUDE_FROM_ALL, put installation of as many targets as I want optional (in my case, all but A), and if building of specific targets precede installation, yet the command:
ninja -j7 D && ninja install
for some reason will fail, stating that C (whose installation was set to OPTIONAL) does not exist (it was not created because D depended only on A and B)...
file INSTALL cannot find "<name of dll file for C>"
Edit 4:
It looks like a cmake bug to me. (I am using 2.8.11 under Windows, also tested 2.8.10)
This INSTALL command
install(TARGETS ${targetname} RUNTIME DESTINATION . LIBRARY DESTINATION . OPTIONAL)
is converted in the cmake_install.cmake as:
IF(NOT CMAKE_INSTALL_COMPONENT OR "${CMAKE_INSTALL_COMPONENT}" STREQUAL "Unspecified")
FILE(INSTALL DESTINATION "${CMAKE_INSTALL_PREFIX}/." TYPE SHARED_LIBRARY FILES *path_to_dll*)
IF(EXISTS "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/./" AND NOT IS_SYMLINK "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/./*dll_name*")
IF(CMAKE_INSTALL_DO_STRIP)
EXECUTE_PROCESS(COMMAND "C:/Programs/MinGW/bin/strip.exe" "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/./*dll_name*")
ENDIF(CMAKE_INSTALL_DO_STRIP) ENDIF() ENDIF(NOT CMAKE_INSTALL_COMPONENT OR "${CMAKE_INSTALL_COMPONENT}" STREQUAL "Unspecified")
with the command FILE missing OPTIONAL! If I add OPTIONAL manually, it works!
(note: I have edited here to put *dll_name* and *path_to_dll* placeholders)
Edit 5:
I confirm it's a bug of cmake, or at least wrong documentation. I will report this.
The situation solved either putting a more simple
install(TARGETS ${targetname} DESTINATION . OPTIONAL)
(but this in my case will also install .lib.a files that I don't want)
or moving in front the OPTIONAL flag:
install(TARGETS ${targetname} OPTIONAL RUNTIME DESTINATION . LIBRARY DESTINATION .)
What one understands from the cmake documentation is that OPTIONAL should be put as last option.
What works:
Remove dependency of "install" target to "all" target (once, in the main CMakeLists.txt):
set(CMAKE_SKIP_INSTALL_ALL_DEPENDENCY true)
Set to OPTIONAL installation of all targets you do not want to always build:
install(TARGETS <<targetname>> DESTINATION . OPTIONAL)
Build the targets you want to install
ninja -j7 <<list of targets>>, or more generally:
<<your builder>> <<your options>> <<list of targets>>
This will build all the targets listed and their dependencies
Call the installer
ninja install. This will install all the libraries you have built, those you mentioned explicitly and those to which they depended. This solves the problem of installing a specific target together with its dependencies!
To concatenate the commands, both on Unix and Windows you can use
ninja -j7 <<list of targets>> && ninja install
Note that, at least for ninja, you cannot simply prepend "install" to the list of targets, as the target "install" does not depend anymore on any target and ninja in parallelizing the job will run install while you are still building your targets. A replacement to the old ninja -j7 install is
ninja -j7 && ninja install.
The target "all" is still available (and it is still the default target).
If you need to create a list of targets you want to build together, you can define a custom target:
add_custom_target(<<collective target name>> DEPENDS <<list of targets>>)
This will not be included in the target all. Adding also a COMMAND would also allow to create an install target for as many as target as we want, for example ninja -j7 install_D, but I think now we are beyond the scope of this question.
Further considerations:
(Note, July 2018: This might be outdated) If you use RUNTIME DESTINATION, LIBRARY DESTINATION etc., most likely because of a CMake bug the OPTIONAL keyword should be put exactly in the position indicated below:
install(TARGETS <<targetname>> OPTIONAL RUNTIME DESTINATION <<some dir>> LIBRARY DESTINATION <<some (other) dir>>)
This contradicts what written in the documentation, and I will proceed to report it as a bug to the CMake developers.
Using EXCLUDE_FROM_ALL in combination with INSTALL + OPTIONAL is a bad idea
Installing a target with EXCLUDE_FROM_ALL set to true has undefined behavior.
(and cmake tries to warn you when it parses the code)
As I answered here,
recent versions of Ninja provide the option to build only targets in a subdirectory.
The same feature holds for installs. So if your target D from your question were in a subdirectory D you can run this command:
ninja D/install
Find more info at the CMake documentation to the Ninja Generator here.
If you have used add_subdirectory to add your sub-projects to your CMakeLists.txt you will see, that in your build-directory you have subdirectories for all your subprojects.
If you only want to install the targets from D just do:
cd build-dir/D
make install
this will only install the D-targets.
Of course you have to have built your project before in build-dir. I'm using it is this way using gnu-make. I don't know if it works with Ninja.
As mentioned by unapiedra the ninja build file generated by cmake includes rules for building/testing/installing/packaging what's inside a particular directory.
This is fine but you cannot do:
ninja <targetName>/install
You can only do
ninja path/where/targetName/is/install
If you don't know where the targetName is you may use:
ninja -t query <targetName>
to see outputs.