Build some executables for host and some for target system (cross compile)

When cross compiling, CMake tries to build everything for target system, but this doesn’t work for tools that used in build time. Consider this CMake file:

project(CustomCommandExample)
add_executable(HelperExecutable helper.cpp)
add_executable(MainExecutable main.cpp)

# Define the output file for the custom command
set(GENERATED_FILE "${CMAKE_BINARY_DIR}/generated_file.txt")

# Add a custom command that runs HelperExecutable to generate a file
add_custom_command(
    OUTPUT ${GENERATED_FILE}
    COMMAND HelperExecutable ${GENERATED_FILE}
    DEPENDS HelperExecutable # Ensure HelperExecutable is built before the command runs
    COMMENT "Generating file using HelperExecutable"
    VERBATIM
)

# Add a custom target to group the custom command
add_custom_target(GenerateFile ALL DEPENDS ${GENERATED_FILE})

# Make MainExecutable depend on the custom command
add_dependencies(MainExecutable GenerateFile)

Is there any way to make HelperExecutable build for host system?

You can’t build for two different toolchains or target platforms in the one main build. But you can use a separate sub-build to build for a different toolchain or platform. There’s a couple of typical approaches:

Option A: ExternalProject

You can build a separate project using ExternalProject_Add(). This defines a sub-build that occurs at build time of the main project. It is up to you to specify correct dependencies to ensure that whatever you need from the sub-build has been brought up to date by the time anything in the main project is built.

Beware that mixing ExternalProject into an existing build is not usually recommended. ExternalProject works more cleanly when the main project has nothing except ExternalProject_Add() calls and commands that define dependencies between the sub-builds. This is commonly called a “superbuild” arrangement. The main project acts only as a coordinator of sub-builds. This is not a great developer experience though, it is more suited to projects that are not actively developed, or where the external sub-builds are separate projects that can be built standalone for development.

Option B: execute_process()

Another alternative is to build for the host platform during the CMake configuration step. You would use execute_process() to run cmake on a subdirectory containing the code for the host build. This has the (sometimes severe) disadvantage that it makes the configure step potentially very slow, but it is meant to be very fast (ideally no more than a few seconds). Developers may hate you for doing this, so if you take this path, you would need to work hard to ensure that the configure step is still very quick after the host tools have been built once. You would almost certainly want to be using a compiler cache like ccache or sccache if you implement this option.

Which method to choose?

It comes down to where you want to compromise. In the projects that I work on, I see both approaches used, or variations thereof. I don’t see a full superbuild in most company projects, but I think they are more popular in some circles than others. Where I see option A used, it usually incorporates ExternalProject into a main project that also defines its own targets, despite advice against it. This can be ok in some controlled scenarios (and I’ve done this myself), so keep in mind that these are all guidelines and advice, but shouldn’t be treated as rigid rules.