Add custom command INTERFACE?

Trying to refactor and streamline a mid-size (~800 cmake files, 30k lines of cmake code, 26k C++ files) 15 year old, multi-platform project with a lot of old cmake.

Recurring issue has been people don’t [know to] call the right helper to add a deep sub-dependency of a library they tie in to, and files don’t get installed/copied.

Is there not some way to denote that building an executable with a given library imbues you with the responsibility to perform a build command?

In the following example, I’d expect a copy of cfg.txt alongside c1 and c2 binaries, but not alone, but I only seem to be able to do things in the context of lib even if I contrive to make it build lib every time someone links against it.

subdirs(lib consumer1 consumer2)
add_executable(alone alone.cpp)
# lib
add_library(lib lib.cpp)
add_custom_command(
    lib
    POST_BUILD
    COMMAND ${CMAKE_COMMAND} -E
        copy ${CMAKE_CURRENT_LIST_DIR}/cfg.txt $<EXECUTABLE_FILE_DIR> # << how?
)
#consumer1
add_executable(c1 c1.cpp)
target_link_libraries(c1 lib)
#consumer2
add_executable(c2 c2.cpp)
target_link_libraries(c2 lib)

As a note: There seem to be various scenarios that get special cased, like rc files on Windows, and ways to do things like say “copy all the DLLs that I depend on” for an executable.

Having to change every

add_executable (cmdlinetool57 main.cpp)
target_link_libraries (cmdlinetool57 argparse)

to

add_executable (cmdlinetool57 main.cpp)
target_link_libraries (cmdlinetool57 argparse)
add_custom_command(TARGET cmdlinetool57 POST_BUILD
  COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_RUNTIME_DLLS:cmdlinetool57> $<TARGET_FILE_DIR:cmdlinetool57>
  COMMAND_EXPAND_LISTS
)

feels very old-cmakeish. Having this established via an interface feels almost intuitive for modern cmake?

No, that doesn’t exist.

Usually one puts all runtime outputs into a single directory for the entire tree.

Usually one puts all runtime outputs into a single directory for the entire tree.

During install, but that’s why I’m addressing link time: it’s incredibly useful to have things nicely staged in the build tree when you have many complex parts and need to perform debugging without executing the install process.

This creates an out-of-band duplicative responsibilities-relationship for artifact management that always seems to end up relying on old-school variable sets. We recently spent a couple of days of man hours diagnosing an issue where an accidental over-dependency caused almost everything we build to suddenly have a dependency on a physics engine that’s a shared library/dll, and suddenly none of our (otherwise entirely statically built) command-line tooling worked, except on the workstations of the people trying to diagnose why. Whoops.

On several of the larger codebases I’ve worked on, additional-artifact management was significant enough to be either one of the reasons we leaned into CMake (for install and swallow the debug headaches) and simultaneously/or the biggest reason people went off to build/switch to something like bazel/buck (for want of the modern dependency model that’s now the backbone of the rest of cmake).

During a build, there are now things like $<TARGET_RUNTIME_DLLS> that can be used to do things like this. However, your own build tree, IMO, is best laid out like the install tree anyways. If you want to pull external dependencies into the build tree, you can, but cleaning out unnecessary things (like that physics engine DLL) is a manual process because they’re not properly tracked by the build system itself.

Our repos includes parts of its own build chain, sort of like compiler bootstrap stages, on multiple levels; games (client/server/platform) ← {pipeline tooling, engine} ← dev tooling ← build tooling ← bootstrap tooling, and we tend to run things in-tree.

E.g we build our texture conversion tool from Source/ over into Tooling/Converters/ on the build machines, where it’s checked in and then run-from-repos on workstations/downstream machines; that’s our closest to normal install case.

But all of that aside, at a most basic level, I don’t want to have to build & install in order to be able to run things in the debugger, these days I find myself giving a little grunt when I have to configure the same old target ‘DEBUG’ properties to tell it what directory to run in debug mode from, again.

Even MS seem to have noticed this :slight_smile:

image

(* edit: I say “we tend”, we don’t actually produce installers from within cmake because of the number of supra-cmake steps involved in shipping a game, for us cmake install is just a pseudonym for ‘populate artifacts directory, muchas’)