Target-dependent linker output location

We use -Wl,-Map,output.map in add_link_options to output a map file in the link stage for all executable targets. This works when using the Makefile generator because Make first changes to the target build directory before compiling and linking, so the output.map file is created in each target’s build directory.

However, with the Ninja generator this is not the case. Ninja does not change its directory and thus outputs an output.map file in the top level build directory for each target, so they end up clobbering each other.

We could manually use target_link_options for every single executable in our source tree, but this is not a desirable solution because it requires quite a bit of new CMake code (add_link_options is nice precisely because it applies to all targets “automatically”).

Is there a way to either:

  1. Instruct Ninja to behave like Make and have it change to the target’s build directory before compiling & linking?
  2. Use the target’s build directory in add_link_options so that the map file location is unique for each target (something like -Wl,-Map,${CMAKE_CURRENT_BINARY_DIR}/output.map)?

Thanks!

Does using $<TARGET_PROPERTY:RUNTIME_OUTPUT_DIRECTORY> help at all? This might be able to be wrapped up in an INTERFACE target that gives it to executables (need to wrap up in a test of the target type as well).

Thanks for your reply.

I tried changing the line in add_link_options to -Wl,-Map,$<TARGET_PROPERTY:RUNTIME_OUTPUT_DIRECTORY>/output.map, but this resolves to /output.map which fails during linking because the linker cannot write to /output.map.

I then tried adding an INTERFACE target, as you suggested:

    add_library(link-interface INTERFACE)
    target_link_options(link-interface INTERFACE
        # Generate map file for symbol/address information
        -Wl,-Map,$<TARGET_PROPERTY:RUNTIME_OUTPUT_DIRECTORY>/output.map
    )
    link_libraries(link-interface)

But the result was the same (the linker tries to write to /output.map). And just to be sure, I also tried linking link-interface explicitly to a target using target_link_libraries, but that did not behave any differently either.

Did I misunderstand your suggestion?

No; it seems that the property is just unset. If you do something like:

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")

near the start of your CMake (before any executable is made), does this help? Might still be busted for multi-config generators though.

A possible solution would be to support $<TARGET_FILE_DIR> without an argument where the “head target” is used. @brad.king Thoughts? I suspect that this family of genexes having this behavior would help other, similar, use cases.

Setting CMAKE_RUNTIME_OUTPUT_DIRECTORY causes all executables to be placed in the same directory, but there is still just a single map file (with the same original problem: each executable is clobbering output.map when it is linked).

Hrm, true. This is not something CMake gives good control over now, sorry. You can set RUNTIME_OUTPUT_DIRECTORY on each target explicitly in the meantime (it is initialized from the CMAKE_RUNTIME_OUTPUT_DIRECTORY variable).