How to compile only some set of targets during make - cmake

I am using cmake as build generator. version : 3.18.5
I have 5 targets in my project.
I want to compile only particular set of targets depends upon the option i give during make.
How to do this?
for example, if i run
gmake -j4 foo=set2
I want target app1,app3,app5 should get compiled.
if i run
gmake -j4 foo=3
i want target app2,app4 should get compiled.
i tried the above things and inside cmakelist.txt,
if(foo STREQUAL "set2")
add_subdirectory(../../app1)
add_subdirectory(../../app3)
add_subdirectory(../../app5)
elseif(foo STREQUAL "set3")
add_subdirectory(../../app2)
add_subdirectory(../../app4)
endif()
But i did not got the expected result.

Your if(foo STREQUAL ..) construction doesn't work because it is evaluated at configuration time, not during the build. It won't be possible to make the exact syntax you envisioned work, but we can get close.
If you add two custom targets
add_custom_target(set2)
add_custom_target(set3)
and then manually specify the targets that belong to each set as dependencies
add_dependencies(set2 app1 app3 app5)
add_dependencies(set3 app2 app4)
after including all of the subdirectories, then calling gmake -j4 set2 (or cmake --build . --target set2 -- -j4) should build just the 3 apps.

Related

Prevent cmake to run cpack

I have a CMake project where I want to prevent make package to do anything more than print a message on some platforms.
I know how to add a message, even a fatal one, but that runs during cmake-generation, not during builds. Do I have to resort to some add_custom_command? And that won't give me what I want, since that creates a new build target...
How can I override the package target for some platforms to just show a message?
why include cpack in your cmake list at all?
In order for a cmake project to have a cpack controlled package target, your project should include a line like:
include(CPack)
and also setting up some cpack-related properties. If you don't want that, you can just take out that line.
As shu pointed out, you can do something like this:
if (! WIN32)
include(cpack)
else()
cmake_policy(SET CMP0037 OLD)
add_custom_target(package
#add dependencies on other targets here
#[[DEPENDS install]]
COMMAND ${CMAKE_COMMAND} -E echo "custom target for non windows platforms!"
)
endif()
Note that by default, you will not be allowed to override reserved targets like test and package. We are turning off that policy here to write our own package target.

CMake, multiple targets (asan tsan..) without having to recompile everything

Goal
I want to define several targets:
make msan: compiles the code with clang with memory sanitizer
make tsan: compiles the code with clang with thread sanitizer
make : compiles the code with gcc
And be able to easily switch between them.
For example I don't want each time I switch rebuild all my objects, (I will have to do it the first time of course, but later if I modify a file and I do make and then make asan it should recompile only this file for each target)
What I have done so far
I have managed to create these targets and from the root directory, but each time I have to do a make clean and recompile.
option(CLANG_MSAN "Enable Clang memory sanitizer" OFF)
if (CLANG_MSAN)
set (CMAKE_CXX_FLAGS "-g -fsanitize=address -fno-omit-frame-pointer")
endif()
add_custom_target(asan
COMMAND ${CMAKE_COMMAND}
-DCLANG_MSAN=ON
-DCMAKE_CXX_COMPILER=clang++
-DCMAKE_C_COMPILER=clang)
Is it possible to do such a thing with CMake?
Yes, but use multiple build directories:
Create a build directory per configuration.
Configure your project in your build directories with the parameters you need. E.g. cmake -DCMAKE_COMPILER=clang -DCMAKE_C_FLAGS="-fsanitize=thread" .. or the stuff from your question.
If you switch the build directory, you changed your setup.
This implies out-of-source builds, which are encouraged by CMake anyway.
I believe you should be able to achieve this with the ExternalProject module. You could add three external projects, one for msan, one for tsan, and one for the basic GCC build. The two sanitiser builds would be marked as EXCLUDE_FROM_ALL 1.
The CMakeLists for all three of them could share the common part via include().
Something like this:
Root CMakeLists.txt
ExternalProject_Add(msan
EXCLUDE_FROM_ALL 1
SOURCE_DIR msan
CMAKE_GENERATOR ...
)
ExternalProject_Add(tsan
EXCLUDE_FROM_ALL 1
SOURCE_DIR tsan
CMAKE_GENERATOR ...
)
ExternalProject_Add(normal
SOURCE_DIR src
CMAKE_GENERATOR ...
)
src/CMakeLists.txt
include(common.cmake)
src/common.cmake
# Normal CMake code for your project
add_library(...)
msan/CMakeLists.txt
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -fsanitize=address -fno-omit-frame-pointer")
include(../src/common.cmake)

cmake ExternalProject cache overwritten

Hello I'm facing a problem regarding CMake and External Projects.
I set a compiler and some flags via CMAKE_CACHE_ARGS and/or CMAKE_ARGS this works the first time I run make but on any subsequent call the CMake Cache of the external project is rebuild (deleted) and the flags are not set accordingly to the flags I specified! So I wonder is there any workaround/way to specify the compiler only once to prevent rebuilding of the cache?
Following is a very basic test project which downloads and compiles GTest, first call to make compiles with Clang++ and the given flags, any following call to make will cause the CMake Cache to be rebuild without the proper flags being set!
cmake_minimum_version_required(VERSION 2.8.6)
project(test)
include(ExternalProject)
ExternalProject_Add(
GTest
SVN_REPOSITORY http://googletest.googlecode.com/svn/tags/release-1.7.0/
CMAKE_ARGS
-DCMAKE_CXX_COMPILER:STRING=clang++
-DCMAKE_CXX_FLAGS:STRING="\"-std=c++1y -stdlib=libc++\""
INSTALL_COMMAND "" # One can not install GTest so dont do anything here
LOG_DOWNLOAD 1
LOG_UPDATE 1
LOG_CONFIGURE 1
LOG_BUILD 1
)
I had the same problem, but with a different setup. Although this answer does not seem to apply to your setup it might be helpful for someone else.
In my case the issue was declaring the project with:
project(test LANGUAGES C)
The external project was a C++ project. Adding CXX to languages (or removing the option altogether, since C CXX is the default) solved the problem for me.

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.

CMake depend.make file empty

I start using CMake to build my c++ source files, I see a strange comportament when I build inicially:
'cmake ../' will gerate the directory structure
'make' will build all
any successive make command will build nothing, as expected
'cmake ../' will apparent do nothing
'make' WILL REBUILD all
any successive make command will build nothing, as expected
There is my CMakelists.txt:
cmake_minimum_required(VERSION 2.6)
set(CMAKE_CXX_COMPILER "g++")
set(CMAKE_CXX_FLAGS "-Wall -pipe")
set(var_target CommonBase)
set(var_path_source base)
project(Prj_${var_target})
file(GLOB_RECURSE var_sources ${var_path_source}/*.cpp)
add_library(${var_target} SHARED ${var_sources})
install(TARGETS ${var_target} DESTINATION ${PROJECT_SOURCE_DIR}/install)
Looking better, at first 'cmake ../' command the file 'CMakeFiles/CommonBase.dir/depend.make' is empty, and the successive make command will insert the list of file dependencies
There is something wrong with my CMakelists.txt?
Thanks
There are a couple of issues here.
The actual cause of your problem is having the line set(CMAKE_CXX_FLAGS ...) before the project command.
The project command does quite a lot of work the first time it is run, and actually clears out this variable as a side-effect. So on your first run of CMake, the compiler flags are empty, and thereafter always contain what you set them to. (It's only the second time you run CMake which causes make to recompile all, not subsequent runs of CMake).
Try wrapping your project call with messages to see the effect:
message("CMAKE_CXX_FLAGS - ${CMAKE_CXX_FLAGS}")
project(Prj_${var_target})
message("CMAKE_CXX_FLAGS - ${CMAKE_CXX_FLAGS}")
Delete your CMakeCache.txt file (in your build root), then just run cmake .. repeatedly.
To fix this, move your set(CMAKE_CXX_FLAGS ...) to after the project command.
The second issue is that it's not recommended to set CMAKE_CXX_COMPILER in a CMakeLists.txt. Have a look at the comment below "Setting default compiler in CMake", and also the link there to CMake's FAQ entry How do I use a different compiler?