How can I install a custom target?

tl;dr I want to run a tool that writes to a file during the install step, and then install that file. I can’t work out how to do this.

I’d like to be able to install split debug info alongside my targets. Improving C++ Builds with Split DWARF gives a good overview of how to do this, but a quick summary is:

  1. clang++ -gsplit-dwarf -o target source.cpp will produce target.dwo.
  2. dwo files shouldn’t be installed for various reasons, so we need to run llvm-dwp -e target to produce target.dwp, which is an installable target.

I have an add_custom_command and add_custom_target that should produce a dwp file:

# ${target} is a function parameter that has the add_(executable|library) target.
# ${DWP_EXECUTABLE} is either llvm-dwp or dwp, and set by Finddwp.cmake.
add_custom_command(
  OUTPUT $<TARGET_FILE:${target}>.dwp
  COMMAND "${DWP_EXECUTABLE}" -e $<TARGET_FILE:${target}>
  DEPENDS $<TARGET_FILE:${target}>
)
add_custom_target(
  _${target}.dwp
  DEPENDS $<TARGET_FILE:${target}>.dwp
)

The problem I’m facing is that I want this command to run only during the install step, because dwp files are only important for installed targets. It seems that custom targets can’t be installed using install(TARGET), meaning that this doesn’t work:

# ${directory} is whatever the corresponding target's GNUInstallDirs value
# would be.
install(
  TARGETS _${target}.dwp
  DESTINATION "${directory}"
  PERMISSIONS
    OWNER_READ OWNER_WRITE
    GROUP_READ
    WORLD_READ
)

Given that I only want llvm-dwp to run during the install step, I think this rules out turning my custom command into a post-build command and then use install(FILES). It might be possible to run install(SCRIPT) or install(CODE) to achieve this, but that feels very heavy-handed for something that ought to be just installing a target.

What’s the best way to go about generating my dwp files at install time, and then installing them to the right directory?

You don’t want to use a custom target for this. Targets are a build time thing, installs are completely separate. Don’t be fooled into thinking an install is still part of the build just because there’s an install build target. That isn’t really a proper build target in that it acts mostly just as a convenience wrapper for running the install. I normally recommend if people want to do an install, the more canonical way is to run cmake --install /path/to/build/dir --prefix /where/you/want/to/install/to. The install details are all contained in dedicated files in the build directory, completely separate from the build description in your build.ninja, Makefile, or whatever build system you’ve used.

For logic you want to run as part of an install, you should indeed be using install(CODE) or install(SCRIPT).

1 Like

Ah, that explains a lot, thank you! It also seems the install(CODE) that I need is much simpler than I thought (execute_process, not add_custom_command and add_custom_target).