Martin
Martin

Reputation: 930

How to setup 2 CMake targets for a single executable with different post build actions?

I am working on an embedded project managed with CMake and I would like to do different actions on the same executable.

Consider I have the following project:

/cmake/foo.cmake
/CMakeLists.txt
/main.cpp
    

Irrelevant main.cpp for this question:

#include <iostream>

int main() {
    std::cout << "hello world" << std::endl;
    return 0;
}

The foo.cmake file contains a function:

function(print_executable_size TARGET_NAME)
    add_custom_command(
        TARGET ${TARGET_NAME}
        POST_BUILD
        COMMAND size ${TARGET_NAME}.exe
    )
endfunction()

The main CMakeLists.txt has the following content:

cmake_minimum_required(VERSION 3.15)

project(proj)

set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
include(foo)

add_executable(pgm-dev main.cpp)
add_executable(pgm-prod ALIAS pgm-dev)

# this line works but this is not needed when compiling the "dev" program.
print_executable_size(pgm-dev)

# Uncomment this line leads to TARGET 'pgm-prod' was not created in this directory.
# print_executable_size(pgm-prod)

My ideal build process would be to have:

Why having two targets "pgm-dev" and "pgm-prod"?

Consider the "pgm-prod" does some extra build actions like cipher the produced binary so, no need to do it in everyday development.

Why use a cmake file with a function instead of add_custom_command() right after executable?

I have multiple executables concerned in my whole project and I would like to avoid code duplications.

Why not creating another executable, with a different name?

"pgm-dev" and "pgm-prod" are exactly compiled the same way, only post build actions differ.


I thought using add_executable(.. ALIAS ..) would be great for this but it seems I'm not understanding some key points here. What would be the best CMake approach to do what I want?

Upvotes: 0

Views: 758

Answers (1)

fabian
fabian

Reputation: 82491

Don't create an alias. Instead create a custom target that executes the commands and add a dependency to the executable target

add_executable(pgm-dev main.cpp)

add_custom_target(pgm-prod COMMAND size $<TARGET_FILE:pgm-dev>)
add_dependency(pgm-prod pgm-dev)

This way cmake makes sure that pgm-dev is built before the command is executed.


If you need to more than one of those commands to all be executed, you could introduce intermediate targets that execute the command that depend on the original target and create a target for executing all those commands that depends on all of those:

function(pgm_add_custom_postbuild_command ORIGINAL_TARGET NEW_TARGET SUFFIX)
    if(NOT TARGET ${ORIGINAL_TARGET})
        add_custom_target(${ORIGINAL_TARGET})
    endif()
    add_custom_target(pgm_internal_${ORIGINAL_TARGET}_${SUFFIX} ${ARGN})

    # hide away target in a dedicated folder, if folders are activated in the IDE
    set_target_properties(pgm_internal_${ORIGINAL_TARGET}_${SUFFIX} PROPERTIES FOLDER PgmInternal)

    add_dependencies(pgm_internal_${ORIGINAL_TARGET}_${SUFFIX} ${ORIGINAL_TARGET})
    add_dependencies(${NEW_TARGET} pgm_internal_${ORIGINAL_TARGET}_${SUFFIX})
endfunction()

function(pgm_print_size ORIGINAL_TARGET NEW_TARGET)
    pgm_add_custom_postbuild_command(${ORIGINAL_TARGET} ${NEW_TARGET} size
        COMMAND size $<TARGET_FILE:${ORIGINAL_TARGET}>)
endfunction()

function(pgm_print_md5sum ORIGINAL_TARGET NEW_TARGET)
    pgm_add_custom_postbuild_command(${ORIGINAL_TARGET} ${NEW_TARGET} md5sum
        COMMAND ${CMAKE_COMMAND} -E md5sum $<TARGET_FILE:${ORIGINAL_TARGET}>)
endfunction()
pgm_print_size(pgm-dev pgm-prod)
pgm_print_md5sum(pgm-dev pgm-prod)

Upvotes: 1

Related Questions