How to install a "generated" directory?

I have been trying to get better at CMake (currently reading Professional CMake which is very good IMO) and I am trying to do the right thing in my build:

I have this program (which I am calling black-box because I don’t have control over it since it is provided part of some sdk) that needs to run during my build. This program takes an input and output directories as parameters. During the install phase, the output directory needs to be copied to some destination.

The program creates a bunch of files in output directory, one of them being gui.lua.

So I have the current setup:

  set(INPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/IN)
  set(OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/OUT)
  # it is possible that sources are not in the same folder so copying them in a single folder
  file(COPY ${SOURCES} DESTINATION ${INPUT_DIR})

  set(GUI_LUA_FILE ${OUTPUT_DIR}/gui.lua)

  add_custom_command(
      OUTPUT ${GUI_LUA_FILE}
      COMMAND black-box ${INPUT_DIR} ${OUTPUT_DIR}
      DEPENDS ${SOURCES}
  )

  add_custom_target(gen_dir DEPENDS ${GUI_LUA_FILE})

so now the issue is that I am not too sure how to express “copy output directory during the install phase” because OUTPUT_DIR is generated only if the target gen_dir is run. I also want the black-box command to run only when necessary (as it is a potentially a heavy command so it should not run unless any SOURCES changes).

I tried

  install(
      TARGETS gen_dir
      DESTINATION ${INSTALL_DIR}
  )

but this doesn’t work (not an executable, library, or module).

What would be the cmake way of doing this?

Thanks for the help
Yan

You have command install(DIRECTORY ...).

Yes I have tried to use this command.

But in this case it is a directory that does not exist unless it gets generated. As far as I can tell there is no way to say: install(DIRECTORY ... DEPENDS gen_dir). So if you run cmake --build . --target install then the directory will not be generated

The problem comes from the fact that you target gen_dir does not depend on target all so it is not built when you launch target all. Keyword ALL is required.

You must have:

add_custom_target(gen_dir ALL DEPENDS ${GUI_LUA_FILE})

install (DIRECTORY ${OUTPUT_DIR} DESTINATION ${INSTALL_DIR})

I just tried this change and yes that fixed the issue. Running an install from scratch will also run the gen_dir target. Thanks for the help.

Stumbled upon the same issue. In my case, adding the custom_target to the ALL target is not an option. There might also be cases where there isn’t even a custom_target to begin with, just a add_custom_command.

I think the install(FILE ...) command should be clever enough what files to build first, just like any other target.

Doing an install can’t assume doing a build is possible. In fact, the more common usage these days of an install is likely to be part of running cpack, which is in turn likely to be invoked directly rather than as part of a build. While make install and make package are both still supported use cases, it’s not how I recommend folks do either operation. I normally steer folks away from directly installing other than to a staging area, which is easiest done using cmake --install because you can specify the staging location at install time with the --prefix option. Similarly, I recommend using cpack directly rather than make package because you again have more control over how packaging is done.

I recognise you probably are focused on doing make install right now (that’s the only use case where expecting “install” to build something would even be possible), but I’d encourage you to think of building and installing as separate steps. It will help you construct a better mental picture of a smoother workflow that will keep you closer to the happy path.