using build preset with ExternalProject_Add

Hello,

for a part of my project I use a qnx compiler that needs QNX_HOST and QNX_TARGET environment variables to be set.
To archive that I use cmake --preset for configuration and for building

the project is build for QNX and for Linux therefore I also used the preset to set the toolchainfile and some cached variables

because both variants (QNX and Linux) should be delivered I now wanted to use ExternalProject_Add to build both variants and include the build output of the linux and the qnx version in one package.

with the CMAKE_ARGS parameter of ExternalProject_Add it is possible to specify a preset but it is only used as a configuration preset
the default build commad is make --build and it is run inside the build directory. Changing it to cmake --build ./ --preset=linux does not work because the cmakepreset.json is not in the build directory. I didt find a way to set the directory in which the build command is run

is there a way to use a build preset with ExternalProject_Add?

Any Help is appreciated

kind regards

Why not use FetchContent instead?

sorry I don’t understand how would that solve the problem? How can I set a build preset with FetchContent?

With FetchContents default the external projects are cloned in your binary directory while configure step.

So it is a sub-project of your own master project.
And you build it with your cmake preset.

thank you for your fast reply
but there is no build command for the FetchContents or did I miss something?
In the SubProject I add with ExternalProject_Add I want to use the build preset from the CMakePrests.json of SubProject Project not from the master Project.
At the moment I defined a build preset in the master project but only because I didn’t find a way for the ExternalProject_add command to use the build preset from the CMakePresets.json in the external project.

MasterProject/CMakeLists.txt
MasterProject/SubProject
MasterProject/SubProject/CMakeLists.txt
MasterProject/SubProject/CMakePresets.json

thank you for your help, I am sorry that its so confusing

see too How to configure multiple cmake projects with CMakePresets.json?

Q: is your SubProject a git submodule of your MasterProject?

no they are not submodules, I am already using CMakePresets at SubProject level for the configuration step of the SubProject they already work. But I don’t know how to also use the build preset during build step. I am using the SubProject standalone I can use:

cmake -B ./build/xyz_a --preset=a
cmake --build ./build/xyz_a --preset=a

cmake -B ./build/xyz_b --preset=b
cmake --build ./build/xyz_b --preset=b

and the behavior is as I would expected but if I am using

ExternalProject_Add(ScuSoftwareLinux 
                    SOURCE_DIR "${CMAKE_SOURCE_DIR}/SubProject/"
                    STEP_TARGETS build 
                    CMAKE_ARGS --preset=a
                    )

the build behaves as I would run in the SubProjects directory

cmake -B ./build/xyz_a --preset=a
cmake --build ./build/xyz_a

This is wrong and should give an error!

A build preset is started with: cmake --build --preset=a

from the documentation I don’t see why this should give me an error and is wrong in general.

https://cmake.org/cmake/help/latest/manual/cmake.1.html#manual:cmake(1)

chapter: Build a Project
says
cmake --build --preset <preset> [<options>] [-- <build-tool-options>]

it does not state that the option --preset is excluded

neither does it say in the chapter option that --preset is not allowed in case cmake --build is used

how should cmake --build now where to build

I see you should probably define a “binaryDir” in the CMakePreset.json and I am guessing that this maybe will solve my issues but I get why the command it self is wrong in general.

But I will check if setting the "binaryDir in the CMakePreset.json will solve my issues

Again thank you for your Help!

@ben.boeckel some combinations of cmake command line arguments may resut in very strange conditions:

klein_cl@lx005535:~/Workspace/cpp$ cmake -S execution26  --preset release
Preset CMake variables:

  CMAKE_BUILD_TYPE="RelWithDebInfo"
  CMAKE_CXX_EXTENSIONS:BOOL="FALSE"
  CMAKE_CXX_FLAGS="-Wall -Wextra -Wpedantic -Wno-shadow -Wconversion -Wsign-conversion -Wcast-align -Wcast-qual -Woverloaded-virtual -Wformat=2 -Wno-error"
  CMAKE_CXX_STANDARD="23"
  CMAKE_CXX_STANDARD_REQUIRED:BOOL="TRUE"
  CMAKE_EXPORT_COMPILE_COMMANDS:BOOL="TRUE"
  CMAKE_INSTALL_PREFIX:PATH="/home/klein_cl/Workspace/cpp/execution26/stagedir"
  CMAKE_PREFIX_PATH:STRING="/home/klein_cl/Workspace/cpp/execution26/stagedir"
  CMAKE_SKIP_TEST_ALL_DEPENDENCY:BOOL="FALSE"

-- The CXX compiler identification is Clang 17.0.6
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/lib/llvm-17/bin/clang++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- TARGET_ALIAS="beman_execution26::beman_execution26" ; TARGET_LIBRARY="beman_execution26" ; TARGET_PREFIX="beman.execution26" ; PROJECT_SOURCE_DIR="/home/klein_cl/Workspace/cpp/execution26"
-- Configuring done (0.4s)
-- Generating done (0.1s)
-- Build files have been written to: /home/klein_cl/Workspace/cpp/execution26/build/release
klein_cl@lx005535:~/Workspace/cpp$ cmake --build execution26  --preset release
CMake Error: Could not read presets from /home/klein_cl/Workspace/cpp:
File not found: /home/klein_cl/Workspace/cpp/CMakePresets.json
klein_cl@lx005535:~/Workspace/cpp$ cmake --build execution26/build/release/  --preset release
CMake Error: Could not read presets from /home/klein_cl/Workspace/cpp:
File not found: /home/klein_cl/Workspace/cpp/CMakePresets.json
klein_cl@lx005535:~/Workspace/cpp$ 

The build directory shouldn’t be given after --build if you’re also giving the --preset option. I think that’s what @ClausKlein was trying to communicate. Here’s how the documentation of the --preset option explains it:

–preset , --preset=

Use a build preset to specify build options. The project binary directory is inferred from the configurePreset key. The current working directory must contain CMake preset files. See preset for more details.

When you use ExternalProject_Add(), its defaults for the build command come from a time before presets existed. It sets the working directory to the build directory, which isn’t what you need if you want to use a build preset. You can work around that by using the cmake -E chdir command to issue a command with a different working directory. Here’s how you would do that:

ExternalProject_Add(someTarget
    # ... Specify where to get things using GIT_REPOSITORY or whatever ...
    BUILD_COMMAND
        ${CMAKE_COMMAND} -E chdir <SOURCE_DIR>
            ${CMAKE_COMMAND} --build --preset YourPresetName
)

I’ve used indenting to make things a bit clearer to read, but that’s just personal preference. The ExternalProject_Add() command automatically replaces <SOURCE_DIR> with the source directory, so it’s a convenient way to avoid having to work out where that will be.

3 Likes

ok I think things are getting a bit more clear to me. What I am still missing is:

if I set binaryDir in the configure preset shouldn’t it also be used in the configuration step or at least depend on it?

would id make sense to have for example set "binaryDir": "./build/xyz_b", in the CMakePresets file but configure with cmake -B ./build/xyz_c --preset=b?
for my understanding binaryDir almost always depends on the build path configured with cmake -B so in this example it would depend on ./build/xyz_c

Thank you

Build presets (and also test, package, and workflow presets) rely on you using the binary directory specified in the configure preset. If you override the binary directory when you configure your project (i.e. you use the -B option), you won’t be able to use build presets with it.

cmake -B ./build/xyz_b --preset=gcc-release

@craig.scott
It should not be allowed to use the -B with –preset or –workflow option together!
IMHO: And the configure preset must have a binaryDir item.

1 Like

ok now I understand, makes sense thank you, I didn’t used the binaryDir option which led to confusion. I also didn’t know that it’s possible to use multiple BUILD_COMMANDS for the ExternalProject_Add.

Again thank you, I learned a Lot

The example I gave is only one single command for the build step:

ExternalProject_Add(someTarget
    # ... Specify where to get things using GIT_REPOSITORY or whatever ...
    BUILD_COMMAND
        ${CMAKE_COMMAND} -E chdir <SOURCE_DIR>
            ${CMAKE_COMMAND} --build --preset YourPresetName
)

I just split it across multiple lines to make it easier to read, but CMake will still consider that all a single command line. But if needed, you can specify multiple commands. Put the first one after BUILD_COMMAND, and then subsequent ones immediately following it preceded by COMMAND. For example:

ExternalProject_Add(someTarget
    # ... Specify where to get things using GIT_REPOSITORY or whatever ...
    BUILD_COMMAND
        ${CMAKE_COMMAND} -E chdir <SOURCE_DIR>
            ${CMAKE_COMMAND} --build --preset YourPresetName
    COMMAND ${CMAKE_COMMAND} -E echo "This is a second command"
)