0

I am contributing a custom feature--say, MyFeature--to a project that uses CMake and has separate source and build directories. MyFeature consists of a single binary file. (MyFeature is in fact a custom Simulink library, so I have to invoke MATLAB to create it--the important thing here is that creating the object does not involve GCC or the other standard build tools that CMake uses by default.) So I have a special rule to build the object--let's say myRule. I want make to run myRule only if (a) MyFeature does not exist, or (b) the one source file on which MyFeature depends--say, MySource--is newer than the currently-existing MyFeature. A small added wrinkle is that I only really need MyFeature in the source tree, which is where myRule normally places it. How can I accomplish all this?

I cannot use either add_executable or add_library, as far as I can tell, because they are designed narrowly for objects that you build using the GCC (or otherwise already-configured) toolchain. I cannot use add_custom_target, because the resulting target is always out of date. So far, I have made some progress using a combination of add_custom_target and add_custom_command, as follows:

add_custom_target(fakeTarget
    DEPENDS MyFeature)

add_custom_command(OUTPUT MyFeature
    COMMAND myRule && touch ${CMAKE_CURRENT_BINARY_DIR}/MyFeature
    DEPENDS mySource)

add_dependencies(targetThatAlwaysRuns fakeTarget)

The command to create an empty MyFeature file in the build tree seems sufficient to fake make into not rebuilding if the real MyFeature exists (in the source tree), which accomplishes one of my goals. However, once this fake file exists, even if I update MySource in the source tree, make does not rebuild MyFeature. This is where I am stuck. It seems especially puzzling because I can see in the CMakeFiles innards that there is a target for MyFeature that does indeed list MySource (with the correct path--in the source tree!) as a dependency. If I were to try to replicate this situation with a much simpler (toy) Makefile in a single directory, if I update one of the source files on which the target depends, even if the target output file already exists, make will do the right thing--it will rebuild the object from the updated source and then rebuild the overall target. So why is the build behavior in this situation different? And what can I do to accomplish goal (b) along with goal (a)? Thanks!

Tsyvarev
  • 60,011
  • 17
  • 110
  • 153
Amittai Aviram
  • 2,270
  • 3
  • 25
  • 32
  • Assuming by `add_custom_rule` you mean `add_custom_command`, this your approach should work. Try to use absolute paths in *DEPENDS* and *OUTPUT* options. "A small added wrinkle is that I only really need MyFeature in the source tree" - nothing prevents you to create file in the source tree. – Tsyvarev May 30 '17 at 22:05
  • Oops! Yes, I meant add_custom_command--have edited to fix this. True, nothing prevents me from creating a file in the source tree; I was explaining the reason for my hack of putting an empty file into the build tree. CMake _does_ prevent me from defining a target in the source tree, so, therefore, the output file in add_custom_command also cannot be in the source tree. – Amittai Aviram May 31 '17 at 12:24
  • Adding the absolute path to the *DEPENDS* option in add_custom_command worked! Thank you! – Amittai Aviram May 31 '17 at 12:28
  • Now the question is how I can avoid the hack of writing the dummy **MyFeature** file to the build tree in order to have **Make** skip the build rule if the real **MyFeature** exists. The problem with this hack is that it does not work if, say, the user deletes the real **MyFeature** in the source tree: so long as the fake **MyFeature** is still in the build tree, **Make** will not regenerate **MyFeature**. – Amittai Aviram May 31 '17 at 13:48
  • I don't quite understand: If you need `MyFeature` to be generated in *source tree*, why do you create it in *binary tree*? OUTPUT of `add_custom_command` can be in the source tree as well, just use absolute paths. – Tsyvarev May 31 '17 at 13:56

1 Answers1

0

As Tsyvarev suggested in his comment, all I had to do was to specify the DEPENDS dependency with an absolute path:

add_custom_target(fakeTarget
    DEPENDS MyFeature)

add_custom_command(OUTPUT MyFeature
    COMMAND myRule && touch ${CMAKE_CURRENT_BINARY_DIR}/MyFeature
    DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/mySource)

add_dependencies(targetThatAlwaysRuns fakeTarget)

However, now suppose a user deletes the real MyFeature from the source tree and then runs Make again. Make will not regenerate MyFeature, because the fake MyFeature is still present in the build tree. How can I overcome this problem?

Amittai Aviram
  • 2,270
  • 3
  • 25
  • 32