Search code examples
cmakecross-platformbuildconfigurationcmake-custom-command

Is the output location of CMake's add_custom_command different on Windows and Linux?


I'm using add_custom_command() in CMakeLists.txt in a project of mine. I seem to have worked it out nicely for building on Linux, but for when I try and build on Windows, something strange happens.

Here's a snippet from a subdirectory's CMakeLists.txt in my project:

add_executable(vectorAddMMAP modified_cuda_samples/vectorAddMMAP/vectorAddMMAP.cpp)
add_custom_command(     
  OUTPUT vectorAddMMAP_kernel.fatbin
  COMMAND ${CMAKE_CUDA_COMPILER}
    -fatbin ${CCBIN_ARGUMENT}
    --generate-code arch=compute_${CMAKE_CUDA_ARCHITECTURES},code=sm_${CMAKE_CUDA_ARCHITECTURES}
    -o ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/vectorAddMMAP_kernel.fatbin
    ${CMAKE_CURRENT_SOURCE_DIR}/modified_cuda_samples/vectorAddMMAP/vectorAdd_kernel.cu
  MAIN_DEPENDENCY modified_cuda_samples/vectorAddMMAP/vectorAdd_kernel.cu

... and this is the output I get on a Windows release from 2019, with MS Visual Studio 16 2019. (lines broken for readability)

C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Microsoft\VC\v160\Microsoft.CppCommon.targets(241,5):
warning MSB8065: Custom build for item "..\..\examples\modified_cuda_samples\vectorAddMMAP\vectorAdd_kernel.cu" succeeded, 
but specified output "d:\a\cuda-api-wrappers\cuda-api-wrappers\build\examples\vectoraddmmap_kernel.fatbin" has not been
created. This may cause incremental build to work incorrectly. 
[D:\a\cuda-api-wrappers\cuda-api-wrappers\build\examples\do_build_vectorAddMMAP_kernel.vcxproj]

I don't understand how this can be possible. The custom command creates ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/vectorAddMMAP_kernel.fatbin. And add_custom_command's implied directory for relative path is ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}, right? So how can the file be missing if the compilation succeeded?


Solution

  • And add_custom_command's implied directory for relative path is ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}, right?

    Wrong.

    See the docs for the OUTPUT parameter of add_custom_target, which state:

    If an output file name is a relative path, its absolute path is determined by interpreting it relative to:

    The path in the build directory is preferred unless the path in the source tree is mentioned as an absolute source file path elsewhere in the current directory.

    And that's just for the OUTPUT parameter. If you just search for the word "relative" in the docs for add_custom_command, you'll see that most parameters there have their own specification, most involving CMAKE_CURRENT_BINARY_DIR.

    Moral of the story: Always read the CMake docs for what relative paths are taken relative to in a given context.

    And by the way, CMAKE_RUNTIME_OUTPUT_DIRECTORY does not have a already-set default value. If you attempt to use a variable with no set value, the default behaviour of CMake is to just evaluate the variable reference to an empty string. That probably has something to do with the reason why your command appears to work on your Linux machine according to how you originally thought things worked.

    Note also that there is such a thing as the $<TARGET_FILE_DIR:tgt> generator expression, which you can use like $<TARGET_FILE_DIR:vectorAddMMAP>/vectorAddMMAP_kernel.fatbin to create a fatbin as a filesystem-pathwise-sibling to the output file of your vectorAddMMAP target.