The question
Debug vs Release in CMake
indicates that
cd ~/codebase
mkdir Release
cd Release
cmake -DCMAKE_BUILD_TYPE=Release ..
make
Will create the Makefile in release, and build the binary there. The intermediate .o files will be in a subdirectory of this.
However, when I do this with my project, CMake ignores the PWD that it is started from. The final target is always the directory ~/codebase/ which contains CMakeList.txt.
In the cmake-gui tool, I specified the source and build directories to be the same directory, the FQN to codebase
I'm new to CMake, and don't know how to get this to work as I expect. What should I modify to get this work as expected?
If you are using a single configuration generator (Ninja/Unix-Makefiles)
Then you need a build folder for each configuration.
Like this:
# Configure the build
cmake -S . -B build/Debug -D CMAKE_BUILD_TYPE=Release
# Actually build the binaries
cmake --build build/Debug
For multi-configuration generators it's slightly different (Ninja Multi-Config, Visual Studio)
# Configure the build
cmake -S . -B build
# Actually build the binaries
cmake --build build --config Debug
If you are wondering why this is necessary it's because cmake isn't a build system. It's a meta-build system (IE a build system that creates build systems). This is basically the result of handling build systems that support multiple-configurations in 1 build. If you'd like a deeper understanding I'd suggest reading a bit about cmake in Craig Scott's book "Professional CMake: A Practical Guide
Note:
My examples use newer cmake cli practices.
EDIT:
That question you linked to has dangerously out of date answers...
Related
I've worked in c++ for many years but I am new to CMake.
I build my app with
cmake --build build_dir --config Debug --target all -- -j 1
This works fine and builds the Debug version.
If I change the --config to anything else, for example Release with the following command:
cmake --build build_dir --config Release --target all -- -j 1
ninja says "no work to do" and exits. Running the compiled app it is clearly not optimised. There are two other options in this Cmake Project, RelWithDebug and MinSizeRel, and they act the same.
In CmakeLists.txt there is:
set(CMAKE_CONFIGURATION_TYPES "Release;Debug;MinSizeRel;RelWithDebInfo")
There are a lot of other mentions of the various types, but it's all slightly greek to me at this time.
What can I do to work out where the issue is?
I'm going to give you the full in depth answer. Just in case someone else has this confusion.
What is a generator?
Essentially a generator is the build system CMake creates. CMake doesn't directly build your project. Because CMake is a meta-build system. IE CMake is a build system that 'generates' your true build system. That's why it is called a 'generator'.
"Visual Studio 16 2019" was designed to handles multiple configurations in 1 build.
Which is why when you open a Visual Studio project created by CMake you can easily change between Debug, Release, etc.
Where as "Unix Makefiles" and "Ninja" can only handle 1 config type at a time.
This difference in build system abilities leads to slightly different CLI when running CMake.
Visual Studio 16 2019 (Multi-Config)
As mentioned before Visual Studio supports multiple config types in the build.
cmake -S . -B build/vs -G "Visual Studio 16 2019"
cmake --build build/vs --config Debug
cmake --build build/vs --config Release
In the first command you are creating the Visual Studio 2019 project.
In the second command you are actually building the binaries. And since Visual Studio projects are multi-config you don't need a different build folder for each type. Since Visual Studio handles it for you!
Ninja (Single Config)
cmake -S . -B build/ninja/ -G "Ninja" -D CMAKE_BUILD_TYPE=Debug
cmake --build build/ninja/
# Now I've updated the project to make Release binaries
cmake -S . -B build/ninja/ -D CMAKE_BUILD_TYPE=Release
cmake --build build/ninja/
In the example above you create a Ninja project for a debug build. Then you build it.
Then you create a Ninja project for a release build. Then you build it.
Notice how you have to manually specify 2 build folders for each type yourself.
Ninja Multi-Config (new in CMake 3.17)
Thanks to advances in Ninja and CMake you can create avoid the hassle of specifying the build type at project creation time. So now it's just like Visual Studio.
So now you can create a "Ninja Multi-Config" project instead of just a "Ninja" project.
cmake -S . -B build/nin -G "Ninja Multi-Config"
cmake --build build/nin --config Debug
cmake --build build/nin --config Release
Further Elaboration
Single Config Vs Multi-Config
How can you tell if your generator is Multi vs Single?
Read the docs. Or if you need to in your scripts you can check it programmatically.
You can query the global property GENERATOR_IS_MULTI_CONFIG
CMAKE_BUILD_TYPE
A minor thing to mention is that CMAKE_BUILD_TYPE doesn't do anything on multi-config generators. So avoid using it in your CMake code.
See this answer: CMAKE_BUILD_TYPE is not being used in CMakeLists.txt
I'm using cmake to generate a VS2017 solution (and projects...), I try to generate everything in a different folder.
I used both the command line and different variables, but no way, it generate in the "source" folder !
Here are some examples of what I tried:
cd source
cmake -B../build ...
cd build
cmake ../source
cmake --build "../build"
cmake -Dxxx=../build
Any idea ? all theses solutions are expected to generate in the build folder !
Once you have performed in-source build (in source directory), it is impossible to build the project out-of-source: every such attempt will modify in-source build.
You need to clear build files (CMakeCache.txt, probably some other ones) in source directory before using out-of-source builds.
I fixed with:
cd VSBuild
cmake ../
instead of
cd VSBuild
cmake ..
Have you tried this? This will put all the build files under "out"
$ cmake -H. -Bout
To execute:
$ cmake --build out
I would like to setup commands like make debug, make test, etc... What is the best way to do this with cmake, so that I run cmake .. one time (it takes a while) and then be able to choose the build type with make?
I couldn't find any resources on this.
As #Tsyvarev has commented this needs a little extra work for single-configuration environments (respectively CMake's Makefile generators) since the build type is chosen during CMake's configuration and finalized during the build environment generation step.
So here is what I've done:
First you run - e.g. in a script - CMake for all configurations you want to support and choose respective sub-folders for the output:
> cmake -H"." -B"Debug" -DCMAKE_BUILD_TYPE=Debug
> cmake -H"." -B"Release" -DCMAKE_BUILD_TYPE=Release
Note: -H (for "home directory") and -B (for "binary output directory") are undocumented options, but very useful in those cases. And they work with all CMake releases so far. Just be careful not to put spaces between the option and the their values.
Then you can use again CMake to build from/in those sub-folders:
> cmake --build "Debug"
> cmake --build "Release"
And if you want to run the tests you can:
> cmake --build "Debug" --target "test"
> cmake --build "Release" --target "test"
References
Changing CMake files standard location
Does CMake always generate configurations for all possible project configurations?
cmake build multiple targets in different build directories
CMAKE_BUILD_TYPE not being used in CMakeLists.txt
Describe difference between these two command lines:
C:\xxxxx> cmake -help
Usage
$ cmake [options] <path-to-source>
$ cmake [options] <path-to-existing-build>
Specify a source directory to (re-)generate a build system for it in the
current working directory. Specify an existing build directory to
re-generate its build system.
The last description does not give me how to use the first, or the second.
Could you explain it to me?
When you use you do an in-tree build (cmake .), there is no difference.
When you do an out-of-tree build, there is a difference.
Suppose your project lives in ~/foo and your current directory is ~/foo/build
You have to run cmake .. for the first build. But for subsequent reconfigures, you can use cmake . because there is already a build there.
This command:
cmake [options] <path>
works as follows:
if <path> is not a valid (that is, already configured) CMake build directory, it is assumed to contain a CMakeList.txt. CMake will configure the current working directory as a build directory using <path>/CMakeLists.txt for source directory.
if <path> is a valid CMake build directory, the command reconfigures that directory using the source directory assigned when you first configured that build directory
So the common usage patterns are:
initial configuration:
mkdir my-build-dir
cd my-build-dir
cmake [options] my-source-dir
subsequent (re)configurations:
cmake [options] my-build-dir # current-work-dir is not important
alternative (initial) configuration using undocumented options:
cmake -Hmy-source-dir -Bmy-build-dir [options] # cwd is not important
I would like to build a third-party project that already has CMake as part of my project's CMake strips. ExternalProject_Add is for this purpose, but I have found it can only be made to work with a specific generator, and I wanted it to work on many platforms easily.
For example, here is my external project with an added script for zlib, which has its own CMakeLists.txt:
set(USE_PROJECT_CMAKE_MODULE_PATH "-DCMAKE_MODULE_PATH=${MAKE_MODULE_PATH}")
ExternalProject_Add(ZLIB
SOURCE_DIR ${CMAKE_SOURCE_DIR}/external/zlib
DOWNLOAD_COMMAND ""
UPDATE_COMMAND ""
CMAKE_ARGS
-DCMAKE_INSTALL_PREFIX=<INSTALL_DIR>
-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}
-DCMAKE_C_FLAGS=${CMAKE_C_FLAGS}
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
${USE_PROJECT_CMAKE_MODULE_PATH}
INSTALL_COMMAND "")
ExternalProject_Add_Step(ZLIB installInternally
COMMAND cd <BINARY_DIR> && make install
DEPENDEES install
ALWAYS 1)
ExternalProject_Get_Property(ZLIB install_dir)
if(UNIX)
set(ZLIB_NAME libz)
else(UNIX)
set(ZLIB_NAME zlib)
endif(UNIX)
add_library(zlib UNKNOWN IMPORTED)
set_property(TARGET zlib PROPERTY IMPORTED_LOCATION ${install_dir}/lib/${ZLIB_NAME}.a)
set(ZLIB_LIBRARIES zlib)
set(ZLIB_LIBRARIES_OPTIONAL ${ZLIB_LIBRARIES})
set(ZLIB_DIR ${install_dir} CACHE INTERNAL "zlib ROOT dir")
set(ZLIB_INCLUDE_DIRS ${install_dir}/include CACHE INTERNAL "zlib include dirs")
set(ZLIB_DEFINES "-msse2 -mfpmath=sse" CACHE INTERNAL "zlib defines")
The problem with this is that it works with make, but not with Xcode or Visual Studio. Perhaps there is some way to take the CMake build commands passed to my project and forward them to ExternalProject_Add.
How can I write ExternalProject_Add calls in a cross-platform way with minimal code complexity, or is there a better alternative?
Problems
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
This is enough for single-configuration projects. But for Xcode and Visual Studio, you need to set CMAKE_CONFIGURATION_TYPES plus call build . --config at the build stage. See my answer.
COMMAND cd <BINARY_DIR> && make install
This will work only for Makefile generators of course. To be cross-platform you can use:
--build . --target install --config inside INSTALL_COMMAND of ExternalProject_Add.
Take a look at this template file, and in particular the following lines:
ExternalProject_Add(
"${current_project}"
URL
#HUNTER_PACKAGE_URL#
URL_HASH
SHA1=#HUNTER_PACKAGE_SHA1#
DOWNLOAD_DIR
"#HUNTER_PACKAGE_DOWNLOAD_DIR#"
SOURCE_DIR
"#HUNTER_PACKAGE_SOURCE_DIR#"
INSTALL_DIR
"#HUNTER_PACKAGE_INSTALL_PREFIX#"
# Not used, just avoid creating Install/<name> empty directory
BUILD_COMMAND ""
# This command is empty because all necessary targets will
# be built on install stage
CMAKE_ARGS
"-G#CMAKE_GENERATOR#"
"-C#HUNTER_CACHE_FILE#"
"-C#HUNTER_ARGS_FILE#"
"-D${postfix_name}=${${postfix_name}}"
"-DCMAKE_BUILD_TYPE=${configuration}"
"-DCMAKE_CONFIGURATION_TYPES=${configuration}"
"-DCMAKE_INSTALL_PREFIX=#HUNTER_PACKAGE_INSTALL_PREFIX#"
"-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}"
INSTALL_COMMAND
"#CMAKE_COMMAND#"
--build .
--target install
--config ${configuration}
--
${jobs_option}
)
Alternative
or is there a better alternative?
Have you seen Hunter?
You can add zlib just like this:
hunter_add_package(ZLIB)
find_package(ZLIB CONFIG REQUIRED)
target_link_libraries(... ZLIB::zlib)
This code works everywhere. Third party dependencies will be downloaded automatically in the configuration step. Example of building with different generator/toolchains (build.py is just a CMake wrapper that sets CMAKE_TOOLCHAIN_FILE and -G/-B):
build.py --toolchain mingw --config Release # MinGW Makefiles
build.py --toolchain vs-12-2013 --config Debug # Visual Studio 12 2013
build.py --toolchain xcode --config Release # Xcode
build.py --toolchain libcxx --config Release # Makefile with -stdlib=libc++ toolchain
build.py --toolchain ios-8-2 --config Release # Xcode with iOS SDK 8.2 toolchain
You got full control what options, build types or number of jobs you want to have while building third-party packages. For instance, this is how you can build four types, Debug, Release, MinSizeRel, and RelWithDebInfo for zlib and link MinSizeRel to the current project:
> build.py --toolchain xcode --verbose --config MinSizeRel --fwd "HUNTER_CONFIGURATION_TYPES=Release;Debug;MinSizeRel;RelWithDebInfo"
/.../clang /.../lib/libz-MinSizeRel.a ... -o /.../_builds/xcode/MinSizeRel/foo
> ls -la /.../.hunter/_Base/d1232c0/326318e/37e4682/Install/lib/libz*
99056 /.../.hunter/_Base/d1232c0/326318e/37e4682/Install/lib/libz-MinSizeRel.a
307872 /.../.hunter/_Base/d1232c0/326318e/37e4682/Install/lib/libz-RelWithDebInfo.a
109536 /.../.hunter/_Base/d1232c0/326318e/37e4682/Install/lib/libz.a
258904 /.../.hunter/_Base/d1232c0/326318e/37e4682/Install/lib/libzd.a
CMake ExternalProject_Add calls work cross-platform by default and will only fail to do so if one uses particular commands that are only available on a subset of operating systems.
Typically, CMAKE_ARGS is used to pass information to each superbuild unit within an external project build. The CMakeLists.txt files that control each miniature part of the overall build use CMake's declarative syntax (e.g., "add_library(library_name SHARED filename1.hpp filename1.cpp). CMake will convert such syntax to the commands that are specific to the particular build system you wish to use (e.g., make and Ninja).
The sample above re: zlib fails to be cross-platform in part because the ExternalProject_Add_Step contains "COMMAND cd && make install", which necessarily only works in situations where invoking "cd" is actually the correct way to change directories, and where invoking "make" is actually the correct way to build software.
CMake's -E option provides a way to invoke basic operations like changing/copying/making/removing directories without making such assumptions.
(By the way, if you're using IDEs such as Visual Studio or Xcode, you'll likely want to invoke one or more IDE generators when using CMake. For instance, setting
-G "Eclipse CDT4 - Unix Makefiles" -DCMAKE_ECLIPSE_GENERATE_SOURCE_PROJECT=TRUE
will cause Eclipse projects to be generated in each build area, and also in the source code area that is shared for all builds. Of course, if you are using Xcode or Visual Studio, you'll have to substitute the appropriate flag for those IDEs. Alternatively, you could consider using Eclipse with Ninja on all platforms, though at the time of writing, I am not completely certain that Ninja is ready for prime-time on non-Linux, non-Windows operating systems.)