I'm learning about CMake's add_custom_target()
function.
This simplification of the project I'm learning from is a minimal verifiable example:
.
├── CMakeLists.txt
└── src
├── CMakeLists.txt
└── hello-world.c
1 directory, 3 files
# ./CMakeLists.txt
cmake_minimum_required(VERSION 3.20)
project(learn_add_custom_target LANGUAGES C)
add_custom_target(BuildAll
COMMAND ${CMAKE_COMMAND} --build . --target all
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMENT "Building all..."
)
add_subdirectory(src)
# ./src/CMakeLists.txt
add_executable(hello-world hello-world.c)
// ./src/hello-world.c
#include <stdio.h>
int main(int argc, char* argv[]) {
printf("Hello, world!\n");
return 0;
}
My understanding is that the add_custom_target()
function will create a new build target, i.e. it enables the user to invoke cmake -B build && cmake --build build --target BuildAll
.
This appears to be true on Linux with the "Unix Makefile" generator and on Windows with the "Ninja" generator. For example, the Windows/Ninja output of configuring and running is:
c:\dev\cmake-learn\add_custom_target>cmake -G Ninja -B build && cmake --build build --target BuildAll
-- The C compiler identification is MSVC 19.38.33134.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: C:/Program Files/Microsoft Visual Studio/2022/Enterprise/VC/Tools/MSVC/14.38.33130/bin/Hostx64/x64/cl.exe - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Configuring done (1.1s)
-- Generating done (0.0s)
-- Build files have been written to: C:/dev/cmake-learn/add_custom_target/build
[1/1] Building all...
[1/2] Building C object src\CMakeFiles\hello-world.dir\hello-world.c.obj
[2/2] Linking C executable src\hello-world.exe
Why does the same not work when configuring is done with the "Visual Studio 17 2022" generator?
c:\dev\cmake-learn\add_custom_target>cmake -G "Visual Studio 17 2022" -B build && cmake --build build --target BuildAll
-- Selecting Windows SDK version 10.0.22621.0 to target Windows 10.0.19045.
-- The C compiler identification is MSVC 19.38.33134.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: C:/Program Files/Microsoft Visual Studio/2022/Enterprise/VC/Tools/MSVC/14.38.33130/bin/Hostx64/x64/cl.exe - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Configuring done (2.5s)
-- Generating done (0.0s)
-- Build files have been written to: C:/dev/cmake-learn/add_custom_target/build
MSBuild version 17.8.5+b5265ef37 for .NET Framework
1>Checking Build System
Building all...
MSBuild version 17.8.5+b5265ef37 for .NET Framework
MSBUILD : error MSB1009: Project file does not exist. [C:\dev\cmake-learn\add_custom_target\build\BuildAll.vcxproj]
Switch: all.vcxproj
C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Microsoft\VC\v170\Microsoft.CppCommon.targets(254,5): error MSB8066: Custom build for 'C:\dev\cmake-learn\add_custom_target\b
uild\CMakeFiles\b9997bcda071324501d87542aafb0bef\BuildAll.rule;C:\dev\cmake-learn\add_custom_target\CMakeLists.txt' exited with code 1. [C:\dev\cmake-learn\add_custom_target\build\BuildAll.v
cxproj]
The error says "Project file does not exist. [C:\dev\cmake-learn\add_custom_target\build\BuildAll.vcxproj]", but the noted file does exist:
c:\dev\cmake-learn\add_custom_target>dir C:\dev\cmake-learn\add_custom_target\build\BuildAll.vcxproj
Volume in drive C is Windows
Volume Serial Number is 748B-8737
Directory of C:\dev\cmake-learn\add_custom_target\build
02/10/2024 04:32 PM 41,983 BuildAll.vcxproj
1 File(s) 41,983 bytes
0 Dir(s) 144,934,273,024 bytes free
What gives?
I didn't think it was necessary to specify a build configuration, because my understanding is that the Debug configuration will apply by default. But for experiment's sake, I tried explicitly specifying a build configuration anyway, and it didn't make a difference:
c:\dev\cmake-learn\add_custom_target>cmake -G "Visual Studio 17 2022" -B build && cmake --build build --config Debug --target BuildAll
-- Selecting Windows SDK version 10.0.22621.0 to target Windows 10.0.19045.
-- The C compiler identification is MSVC 19.38.33134.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: C:/Program Files/Microsoft Visual Studio/2022/Enterprise/VC/Tools/MSVC/14.38.33130/bin/Hostx64/x64/cl.exe - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Configuring done (2.5s)
-- Generating done (0.0s)
-- Build files have been written to: C:/dev/cmake-learn/add_custom_target/build
MSBuild version 17.8.5+b5265ef37 for .NET Framework
1>Checking Build System
Building all...
MSBuild version 17.8.5+b5265ef37 for .NET Framework
MSBUILD : error MSB1009: Project file does not exist. [C:\dev\cmake-learn\add_custom_target\build\BuildAll.vcxproj]
Switch: all.vcxproj
C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Microsoft\VC\v170\Microsoft.CppCommon.targets(254,5): error MSB8066: Custom build for 'C:\dev\cmake-learn\add_custom_target\b
uild\CMakeFiles\b9997bcda071324501d87542aafb0bef\BuildAll.rule;C:\dev\cmake-learn\add_custom_target\CMakeLists.txt' exited with code 1. [C:\dev\cmake-learn\add_custom_target\build\BuildAll.v
cxproj]
I can successfully build on Windows with the "Visual Studio 17 2022" generator's build files if I do not specify a build configuration, which I think means the "Debug" build configuration applies:
c:\dev\cmake-learn\add_custom_target>cmake -G "Visual Studio 17 2022" -B build && cmake --build build
-- Selecting Windows SDK version 10.0.22621.0 to target Windows 10.0.19045.
-- Configuring done (0.0s)
-- Generating done (0.0s)
-- Build files have been written to: C:/dev/cmake-learn/add_custom_target/build
MSBuild version 17.8.5+b5265ef37 for .NET Framework
1>Checking Build System
Building Custom Rule C:/dev/cmake-learn/add_custom_target/src/CMakeLists.txt
hello-world.c
hello-world.vcxproj -> C:\dev\cmake-learn\add_custom_target\build\src\Debug\hello-world.exe
'pwsh.exe' is not recognized as an internal or external command,
operable program or batch file.
Building Custom Rule C:/dev/cmake-learn/add_custom_target/CMakeLists.txt
There's nothing explicitly generator-specific in any of the CMakeLists, so what is causing this difference in behavior of building with different build tools? I thought CMake was supposed to abstract away the difference in build tools behind the cmake --build ...
front, which seemed to be true when considering that a make-based build on Linux and a Ninja-based build on Windows both built the BuildAll
target just fine.