SUFFIX is missing on windows

Hi,

I am trying to write a cmake script that copies the compiled binary to a build location. It tries to uses SUFFIX property to build up the destination path but on windows (I have not tried on other platforms) it returning the not found error. If I manually set the property when I setup the target this works fine.

add_executable(my_target)
set_target_properties(my_target PROPERTIES SUFFIX “.exe”)

I have a generic copy function which is used my many targets which sets up custom_commands which among other things attempts to copy the file. My question is why is this needed? Is there a reason that cmake does not set this by default for me? Or am I missing something else all together?

Thanks for any help understand this.

There are properties like SUFFIX, where the user can influence the filename. To make it different than the default (which may be platform specific).

Do not try to calculate the filename of a target, it’s way too complex if you want to support all CMake features.

Simply use the GenEx $<TARGET_FILE:tgt> to access the filename.

I was trying to use $<TARGET_PDB_FILE:tgt> but this when used in the COMMAND of the add_custom_command it was causing the project to rebuild which ultimately led to an infinite rebuild loop when trying to resolve the dependency. Other expressions such as TARGET_PDB_FILE_BASE_NAME work fine so not sure the best way to get this.

Ultimately I need to set the dependency when I call add_custom_target to the output of the custom_command which to make matter worse this seems to be needed at generation time. Do you have a better suggestion of how to work around this issue?

I do not understand what you’re trying to do. Or what you mean with “infinite rebuild loop”. Do you have some minimal reproducible example?

If you use the PDB file, I guess CMake will add a dependency to the executable target.
The PDB file is incrementally created by the compiler and the final change is made by the linker.
So such a dependency would be correct and won’t cause any problem. If this causes a rebuild, then some dependency relevant to the EXE/PDB has changed.

It likely has changed since I am signing the binary in post build. Only after it is signed we can copy it to the final location. Therefore this is causing a rebuild loop.

Thanks @jtxa for your help. After further investigation I am seeing that $<TARGET_FILE_NAME:tgt> works just fine but for some reason $<TARGET_FILE:tgt> is triggering a rebuild only when in the COMMAND clause. If I use $<TARGET_FILE:tgt> in the COMMENT or DEPENDS clauses it works fine. Due to this I am now using $<TARGET_FILE_DIR:tgt>/$<TARGET_FILE_NAME:tgt> in my custom_command.

This solves most of my problems but in some cases I am copying files and trying to set the OUTPUT to the file that got copied. Since the gen expressions are only evaluated at compile time not configuration time this causes a problem. Do you have suggestions/best practices for handling this situation?

Thanks for any help you can provide.

I don’t know if it is relevant to your situation, but check what CMake version you’re using, and in particular whether policy CMP0112 is set to NEW or not. That will affect which generator expressions result in dependencies being added automatically. I think you’re using CMP0112 as NEW anyway, but maybe the documentation for that policy might explain why you’re seeing the behavior you’ve reported.

The output of a build rule shall never be modified later. If you’re doing that, then it’s the correct thing to do a rebuild.

For signing you could do one of these:

  • in the same rule as the linker: CMake offers add_custom_command(TARGET POST_BUILD …) to achieve this.
  • create a temporary copy (e.g. in you build dir), sign that one, and then copy it to your final destination

To find out why a rebuild is done, ask you build system. E.g. for ninja add -d explain

Thank @jtxa @craig.scott

Yes we are using CMP0112 already. Thanks for the pointer.

I think I see what is going on here. The documentation states that the $<TARGET_FILE:tgt> is not added as a dependency unless used in add_custom_command or add_custom_target. In this case the add_custom_command is run from POST_BUILD causing the build to get triggered again which causes the POST_BUILD to keep running in this loop. Maybe I am not setting the output correctly that is causing the dependency to think it is still needed to be build but at any rate the $<TARGET_FILE:tgt> seems to be causing this dependency. So I am working around this by using $<TARGET_FILE_DIR:tgt>/$<TARGET_FILE_NAME:tgt> which seems to be working.

Let me restate what I am trying to do.

In our project we have many sub projects that attempt to set various properties such as CMAKE_ARCHIVE_OUTPUT_DIRECTORY, CMAKE_LIBRARY_OUTPUT_DIRECTORY, CMAKE_RUNTIME_OUTPUT_DIRECTORY, PDB_OUTPUT_DIRECTORY, etc.

This was causing problems as some project are setting this incorrect and caused subtle problems that were non obvious. I decided to write a generic function that will run postbuild that will copy only the files it cares about to the output destination. By doing this postbuild it seems to have more control than asking cmake to place them in the desired output. It allows them to be built in the ${CMAKE_CURRENT_BINARY_DIR}/$ and only on postbuild these files get placed in the desired location.

To make matters worse other postbuild actions such as signing or other postbuild tasks needs to run before the copy_output postbuild task. If the order is not guaranteed this could cause the non complete binary to get copied to the output location. In order to achieve this I have set up a a postbuild command for the target that runs the various tasks in order. I have a function that builds this us so the complexity is abstracted from the individual projects.

For the copy_output I need to know the extension of the file especially when dealing with pdb files on windows. I want to copy them to output/symbols/<exe,dll,sys,etc>/. In the custom_command I can get $<TARGET_FILE_SUFFIX:tgt> but this has the .ext in it. I need to remove the . inorder to build this path. Since there are no string manipulation generator functions I am trying to do this at generation time instead. This is why I need the suffix which does not seem to be set (unless we manually set it). Is there a way to get cmake to add this correctly?

In addition the OUTPUT clause of the add_custom_commnad needs to be populated to tell cmake which files it generates. Since most of the filename is determined at build time using the generator expression I am also not sure how to set this output clause. For now I am just using a dummy path and calling set_source_files_properties with the SYMBOLIC property. Although this is not a symlink the documentation states that the output is not checked when SYMBOLIC is set. This seems like a big hack to me but other than this I am not sure how to best set the output clause without using generator expressions since it is needed at generation time. Is there a best practice for handling such files?

To summarize my questions are:

  1. Is there a way to get cmake to set the targeet suffix correctly or is there another way I should be going about to get this.
  2. Since the add_custom_commnad requires the OUTPUT clause to be set but this is needed at generation time how can I set this reliability.

Thanks for your help with this issue.

Thanks
Noah

Sounds like you’ve got quite a complex arrangement. I can’t offer much further advice, the complexity of your real project makes it kinda beyond the scope of what can reasonably be explored here. FWIW, if you’re having to copy executables just to avoid problems with dependencies related to POST_BUILD rules, that seems to me like there’s something not right with the dependencies you’re trying to express.

If your approach requires using generator expressions in the OUTPUT of add_custom_command(), you’re out of luck if those generator expressions need to mention a target. That is something the documentation explicitly mentions as not supported.