1

I am calling make-commands of an SDK inside CMake. They will build static libraries which are IMPORTRED targets in CMake, which I use for linking our implementation against. The sdk is contained in a single directory and contains a freertos-library which can only be built via make. I have a multicore-application, where each target uses the same SDK and has its own FreeRTOS instance. I did manage to build all libraries and everything runs fine, but now I have the case, that I may want to have different settings for the FreeRTOS per core, e.g. different amount of priorities. This would mean I need a different FreeRTOSConfig.h. The sdk does not provide a solution to pass my own FreeRTOSConfig.h, so there is no way, without modification of the make-scripts of the sdk, to "inject" my own FreeRTOSConfig.h.

I now wanted to replace the FreeRTOSConfig.h inside the SDK via add_custom_command by temporarily storing the current FreeRTOSConfig.h of the SDK, replacing it with my header, compile, and replace the header with the temporarily stored one afterwards.

I used the recommended technique described here: https://cmake.org/cmake/help/latest/command/add_custom_command.html#example-generating-files-for-multiple-targets

But now both cmake-targets for each core use both the same cmake-function which produces a make-call for building the sdk-freertos-library and thus the same generated file. This makes totally sense to me and I completely understand, why ninja complains that multiple rules generate the same file.

I try to strap down my code, since it's a bit more complex and the minmal example would probably not work, but shows how I implemented this:

function(add_sdk_package EXTENSION PATH_TO_FREERTOS_HEADER)
    add_custom_command(
                OUTPUT ${SDK_PATH}/source/kernel/freertos/config/FreeRTOSConfig.h 
                COMMAND ${CMAKE_COMMAND} -E copy_if_different ${SDK_PATH}/source/kernel/freertos/config/FreeRTOSConfig.h ${CMAKE_CURRENT_BINARY_DIR}/temp/FreeRTOSConfig.h 
                COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PATH_TO_FREERTOS_HEADER}/FreeRTOSConfig.h ${SDK_PATH}/source/kernel/freertos/config/FreeRTOSConfig.h
                COMMENT "copy temporary modified header"
            )
    add_custom_target(copy-freertos-config-header-${EXTENSION} ALL
                DEPENDS ${SDK_PATH}/source/kernel/freertos/config/FreeRTOSConfig.h
            )
   add_custom_command(
                TARGET copy-freertos-config-header-${EXTENSION} 
                COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_BINARY_DIR}/temp/FreeRTOSConfig.h ${SDK_PATH}/source/kernel/freertos/config/FreeRTOSConfig.h 
                POST_BUILD
                COMMENT "copy temporary modified header"
            )
    # create make-target and store output into binary dir
    add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/freertos.${CMAKE_BUILD_TYPE}.lib
        COMMAND make -s -f makefile freertos PROFILE=${CMAKE_BUILD_TYPE} 
        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${SDK_PATH}/source/kernel/freertos/lib/freertos.${CMAKE_BUILD_TYPE}.lib ${CMAKE_CURRENT_BINARY_DIR}/freertos.${CMAKE_BUILD_TYPE}.lib
        WORKING_DIRECTORY ${SDK_PATH})
    set(LIB_NAME freertos-${EXTENSION})
    # creating a target for cmake, which depends on add_custom_command output
    add_custom_target(${LIB_NAME}-temp DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/freertos.${CMAKE_BUILD_TYPE}.lib)
    add_library(${LIB_NAME} STATIC IMPORTED)
    add_dependencies(${LIB_NAME} ${LIB_NAME}-temp)
    # lib should have a dependency to target if set, so the target builds first
    add_dependencies(${LIB_NAME} copy-freertos-config-header-${EXTENSION})
    set_target_properties(${LIB_NAME} PROPERTIES 
        IMPORTED_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/freertos.${CMAKE_BUILD_TYPE}.lib 
        INTERFACE_INCLUDE_DIRECTORIES "${SDK_PATH}/source"
        INTERFACE_LINK_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}")

    add_library(sdk-package-${EXTENSION} INTERFACE EXCLUDE_FROM_ALL
        
    )
    target_link_libraries(sdk-package-${EXTENSION} INTERFACE
        freertos-${EXTENSION}
    )
endfunction()

And somewhere else both cores call

add_sdk_package(core0 ${CMAKE_SOURCE_DIR}/core0/config)
add_sdk_package(core1 ${CMAKE_SOURCE_DIR}/core1/config)

add_library(myTarget-core0 
    ...
    )

add_library(myTarget-core1 
    ...
    )

target_link_library(myTarget-core0 PRIVATE sdk-package-core0)
target_link_library(myTarget-core1 PRIVATE sdk-package-core1)

And exactly this will produce the issue. ninja: error: build.ninja:34219: multiple rules generate ../sdk/freertos/config/FreeRTOSConfig.h [-w dupbuild=err]

I understand as well that this could be an issue regarding other libraries including exactly this header, since it is not known from which target it was created.

Previously we modified the sdk's FreeRTOSConfig.h by including another header Config.h in the header, which was not part of the sdk. We then created a separate bash-script which did copy our Config.h next to FreeRTOSConfig.h, build the libraries one after each other all before CMake was run and it was only a imported target in cmake, without any calls to the make-scripts from cmake side, only from the bash-script. The header was also given to any other targets in cmake, which want to include a FreeRTOS.h-header and thus FreeRTOSConfig.h-header and if searching for the Config.h the path was always given to the right Config.h of the target. This way it was no problem, since the compilation was done in sequence and the correct header was used in the inclusion-hierarchy.

When I wanted to reimplement the same functionality, so at least modify the FreeRTOSConfig.h permanently inside the sdk and try to copy our Config.h-header next to it via CMake, the same way as above, just without any temp-copy, I will get exactly the same error, since it's again the same file in the same place for two different targets.

So the message looks similar: ninja: error: build.ninja:34219: multiple rules generate ../sdk/freertos/config/Config.h [-w dupbuild=err]

Is there a way to prevent this behaviour and to make it possible to ensure there is no concurrent execution?

The only way I can currently think about is to clone the whole sdk two times into the binary-folder and build them separately. There must be some better solution.

3
  • 1
    You got the error because you effectively call add_custom_command(OUTPUT) twice in the same directory with the same output file. You should avoid calling this command the second time (and other times). E.g. when you call that command inside the function add_sdk_package you could define some variable. And check existence of that variable in futher function's calls. (Note, that for making a variable visible outside of the single function's call, you need to set this variable with PARENT_SCOPE modifier.) Commented Oct 2 at 19:41
  • @Tsyvarev when I thought about this, another idea came to mind: Now I added more COMMAND copying the original file into a temp folder and replacing the sdk-file, after calling the make-function, and after that another COMMAND which replaces the original file. all in one add_custom_command, so the one you already see in my initial question. Is this a legit solution? It seems to work so far. Commented Oct 15 at 8:09
  • Temporary copiing a file looks weird in any case. If it works for you, then .. it works for you. Commented Oct 15 at 9:02

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.