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:
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.
CMake itself does not allow to install dependencies automatically.
A CMake-based buildsystem is organized as a set of high-level logical targets. Each target corresponds to an executable or library, or is a custom target containing custom commands.
CMake provides the install command to specify how a project is to be installed. This command is invoked by a project in the CMakeLists file and tells CMake how to generate installation scripts. The scripts are executed at install time to perform the actual installation of files.
ninja -j10 cmake is a build system; you define how your project should be put together. It can create ninja. build files for you. Ninja will do the building.
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.
(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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With