Is it possible to rename cmake's build directory? - cmake

cmake uses ./build as a standard place to generate build system and to place all intermediate files. Is this a hard requirement or is it possible to use a different name such as ./work? or maybe ./work/build?
To put in context this question. This is an initial idea so I can keep the same top level workflow for different project languages and toolchains.

Yes. The notion that CMake has a "standard" binary directory is incorrect. There is no default. Using build is common, but it is truly just a convention. In general, you can run:
$ cmake -G Ninja -S /path/to/source-dir -B /path/to/binary-dir -DCMAKE_BUILD_TYPE=RelWithDebInfo [...more options...]
Where /path/to/source-dir is the path to the folder containing the top-level CMakeLists.txt. People commonly run CMake from this folder, so this is often just -S .. Similarly, the binary directory, /path/to/binary-dir, is commonly stored beneath the source directory in a folder called build. So in these cases you will see -B ./build.
The values of both flags may be arbitrary, however.
For instance, when building LLVM, I often write:
$ cmake ... -S llvm -B build ...
because LLVM's "top-level" CMakeLists.txt is actually in the llvm/ subfolder of their monorepo structure. But I still keep the build directory at the top level. I could just as easily have written:
$ cmake ... -S llvm -B $HOME/.binary-trees/llvm ...
Which is a little odd, but perfectly valid.

cmake uses ./build as a standard place to generate build system [...]
This is incorrect. If you're using cmake for configuring without providing a preset and without specifying the source/build directory using the -S ... -B ... options, the current working directory is used as build directory.
A convenient way of providing multiple configurations that also benefits from integration with some IDEs are cmake presets.
Put something like the following in a CMakePresets.json file next to your toplevel CMakeLists.txt file.
{
"version": 3,
"cmakeMinimumRequired": {
"major": 3,
"minor": 21,
"patch": 0
},
"configurePresets": [
{
"name": "linux-x86",
"displayName": "linux-x86",
"description": "Linux build release build targeting the x86 architecture",
"generator": "Unix Makefiles",
"toolchainFile": "${sourceDir}/toolchains/linux-x86.cmake"
"binaryDir": "${sourceDir}/build/linux-x86",
"cacheVariables": {
"CMAKE_BUILD_TYPE": {
"type": "STRING",
"value": "Release"
}
}
},
{
"name": "linux-x86-debug",
"displayName": "linux-x86-debug",
"description": "Linux build debug build targeting the x86 architecture",
"inherits": "linux-x86",
"binaryDir": "${sourceDir}/build/linux-x86-debug",
"cacheVariables": {
"CMAKE_BUILD_TYPE": {
"type": "STRING",
"value": "Debug"
}
}
},
{
"name": "linux-aarch64",
"displayName": "linux-aarch64",
"description": "Linux build release build targeting the aarch64 architecture",
"inherits": "linux-x86",
"binaryDir": "${sourceDir}/build/linux-aarch64",
"toolchainFile": "${sourceDir}/toolchains/linux-aarch64.cmake"
},
{
"name": "linux-aarch64-debug",
"displayName": "linux-aarch64-debug",
"description": "Linux build debug build targeting the aarch64 architecture",
"inherits": "linux-x86-debug",
"binaryDir": "${sourceDir}/build/linux-aarch64-debug",
"toolchainFile": "${sourceDir}/toolchains/linux-aarch64.cmake"
}
, ...
]
}
This does require a minimum cmake version of 3.19 though, 3.21 for this specific json file.
This allows you to use commands like
cmake --preset linux-aarch64-debug .
cmake --preset linux-x86 .
To set up the aarch64 debug build and the linux-x86 release build in build/linux-aarch64-debug and build/linux-x86 respectively.

Related

How to configure VS Code's CMake Tools Extension for GCC and MSYS Makefiles on Windows?

This question is about the CMake Tools extension for VS Code. The operation system is Windows 10.
The extension correctly found GCC, which I can verify by having a look at the %LocalAppData%/CMakeTools/cmake-tools-kits.json.
{
"name": "GCC 10.3.0 x86_64-w64-mingw32",
"compilers": {
"C": "C:\\msys64\\mingw64\\bin\\x86_64-w64-mingw32-gcc.exe",
"CXX": "C:\\msys64\\mingw64\\bin\\x86_64-w64-mingw32-g++.exe"
}
}
I tried to configure through the respective VS Code command and got an error:
[rollbar] Unhandled exception: Unhandled Promise rejection: configure Error: No usable generator found. {}
Then I added the respective setting to my local settings .vscode/settings.json.
{ "cmake.generator": "MSYS Makefiles" }
I got the following output:
[proc] Executing command: "C:/Program Files/CMake/bin/cmake.exe" --no-warn-unused-cli -DCMAKE_EXPORT_COMPILE_COMMANDS:BOOL=TRUE -DCMAKE_BUILD_TYPE:STRING=Debug -DCMAKE_C_COMPILER:FILEPATH=C:\msys64\mingw64\bin\x86_64-w64-mingw32-gcc.exe -DCMAKE_CXX_COMPILER:FILEPATH=C:\msys64\mingw64\bin\x86_64-w64-mingw32-g++.exe -H<path to project root> -B<path to build directory> -G "MSYS Makefiles"
[cmake] Not searching for unused variables given on the command line.
[cmake] CMake Error: CMake was unable to find a build program corresponding to "MSYS Makefiles". CMAKE_MAKE_PROGRAM is not set. You probably need to select a different build tool.
[cmake] CMake Error: CMake was unable to find a build program corresponding to "MSYS Makefiles". CMAKE_MAKE_PROGRAM is not set. You probably need to select a different build tool.
[cmake] CMake Error: CMAKE_AR was not found, please set to archive program.
[cmake] -- Configuring incomplete, errors occurred!
So I extended my local settings.
{
"cmake.generator": "MSYS Makefiles",
"cmake.environment": {
"CMAKE_AR": "C:/msys64/usr/bin/ar.exe",
"CMAKE_MAKE_PROGRAM": "C:/msys64/usr/bin/make.exe"
}
}
Got the same output as before. I also tried setting these variables in the CMakeLists.txt and as system environment variables.
What is the proper way to do this?
I know this is an old request, but I've recently been suffering from this issue.
The trick I found (with the current version of MSYS2 on Windows), was to install the cmake version of mingw64.
With command:
pacman -S mingw-w64-x86_64-cmake
NOTE: This assumes that pacman is defined in your path, otherwise you will need to prefix the pacman with the path.
Then install Ninja:
pacman -S ninja
You shouldn't need to define the configuration in VSCode settings, it should just work.

CMake build types make no difference

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

CMake command not working in VS Code task

I have created a task in my tasks.json file in Visual Studio Code:
{
"label": "cmake",
"command": "cmake",
"args": [
"-G",
"\"Unix Makefiles\"",
"-DCMAKE_BUILD_TYPE=Debug",
".."
],
"options": {
"cwd": "${workspaceRoot}/build"
},
"problemMatcher": []
},
When I run it from within VSC (Tasks: run task > cmake), I get the following error:
> Executing task: cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Debug .. <
CMake Error: Could not create named generator "Unix Makefiles"
Generators
Unix Makefiles = Generates standard UNIX makefiles.
Ninja = Generates build.ninja files.
Xcode = Generate Xcode project files.
CodeBlocks - Ninja = Generates CodeBlocks project files.
CodeBlocks - Unix Makefiles = Generates CodeBlocks project files.
CodeLite - Ninja = Generates CodeLite project files.
CodeLite - Unix Makefiles = Generates CodeLite project files.
Sublime Text 2 - Ninja = Generates Sublime Text 2 project files.
Sublime Text 2 - Unix Makefiles
= Generates Sublime Text 2 project files.
Kate - Ninja = Generates Kate project files.
Kate - Unix Makefiles = Generates Kate project files.
Eclipse CDT4 - Ninja = Generates Eclipse CDT 4.0 project files.
Eclipse CDT4 - Unix Makefiles= Generates Eclipse CDT 4.0 project files.
The terminal process terminated with exit code: 1
However if I execute the exact same command in terminal it works as expected:
oyvinds-MacBook:build oyvindhauge$ cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Debug ..-- Configuring done
-- Generating done
-- Build files have been written to: /Users/oyvindhauge/Documents/Development/[PROJECT]/build
oyvinds-MacBook:build oyvindhauge$
Any idea what's going on?
Like stated in the comments, removing the extra quotes around generator param "Unix Makefiles" fixed the problem.

Babel errors when files to transpile not in certain location

I copied the js files from project https://github.com/videojs/video.js into a subfolder of my project.
I set up plenty dependencies and called browserify on the command line:
node ./node_modules/browserify/bin/cmd.js dev\videojs\js\video.js -t [ babelify ]
The output looks like:
Error: D:/Webs/videojs/dev/videojs/js/video.js: Cannot find module '../../package.json' from 'D:\Webs\videojs\dev\videojs\js'
at Function.module.exports [as sync] (D:\Webs\videojs\node_modules\resolve\lib\sync.js:33:11)
at PluginPass.MemberExpression (D:\Webs\videojs\node_modules\babel-plugin-inline-json\lib\index.js:27:45)
at newFn (D:\Webs\videojs\node_modules\babel-traverse\lib\visitors.js:276:21)
at NodePath._call (D:\Webs\videojs\node_modules\babel-traverse\lib\path\context.js:76:18)
at NodePath.call (D:\Webs\videojs\node_modules\babel-traverse\lib\path\context.js:48:17)
at NodePath.visit (D:\Webs\videojs\node_modules\babel-traverse\lib\path\context.js:105:12)
at TraversalContext.visitQueue (D:\Webs\videojs\node_modules\babel-traverse\lib\context.js:150:16)
at TraversalContext.visitSingle (D:\Webs\videojs\node_modules\babel-traverse\lib\context.js:108:19)
at TraversalContext.visit (D:\Webs\videojs\node_modules\babel-traverse\lib\context.js:192:19)
at Function.traverse.node (D:\Webs\videojs\node_modules\babel-traverse\lib\index.js:114:17)
When I move the files one folder up, the command runs and and transpiles all the files.
I now wonder where this error comes from. babel-traverse seemingly loops through the plugins and eventually finds out it's being run not exactly 3 levels below the project root. Is this intended behaviour? It this a matter of babel, browserify, a plugin or videojs?
Use this command instead:
./node_modules/.bin/browserify dev\videojs\js\video.js -t [ babelify ]
When Browserify is installed, the command line scripts are added to node_modules/.bin, as that's the standard practice. It's those commands that you should be running; not the scripts in Browserify's own bin directory.
Note that the scripts are either shell scripts or Windows CMD scripts and that they are not run using node.
Or, if you add the following to your package.json, you can run Browserify using NPM (also standard practice):
{
...
"scripts": {
"browserify": "browserify"
}
}
and the command would then be:
npm run browserify dev\videojs\js\video.js -t [ babelify ]
Or, if you want to keep the parameters in the "scripts" configuration:
{
...
"scripts": {
"bundle": "browserify dev/videojs/js/video.js -t [ babelify ]"
}
}
and:
npm run bundle

How do I use CMake ExternalProject_Add or alternatives in a cross-platform way?

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.)