How to make CMake build in CLion after changes in resource files - c++

In a CLion project, I need some resources to be copied to the binary folder. They are GLSL shaders, so when I edit them, I want to be able to see the result. Unfortunately, CLion only rebuilds the project when there are changes to the source, so if I edit the GLSL files but leave the source files unchanged, CLion won't rebuild and thus the new files will not be copied to the binary directory. How do I fix this?
This is my code to move the files into the binary directory of the target OpenGL_Test:
add_custom_command(TARGET OpenGL_Test PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/res
$<TARGET_FILE_DIR:OpenGL_Test>/res
)

You can use add_custom_target with ALL option, which will force CMake to copy the directory on every build:
add_custom_target(copy_shaders ALL
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${CMAKE_SOURCE_DIR}/res" "$<TARGET_FILE_DIR:OpenGL_Test>/res"
COMMENT "Copy shaders to build tree"
VERBATIM)

Related

How do I copy a directory every time I build the project?

I'm trying to copy the res directory into the build directory but it only happens when I reload the CMake file and build the project. Here's the code I'm using to achieve this:
add_custom_command(
TARGET project PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_SOURCE_DIR}/res/ $<TARGET_FILE_DIR:project>/res/
)
I've tried using the file command to copy the directory, but that method also has this issue. Is there a way to copy a directory every time I build the project?

Copying assets directory from source to build directory in CMake

I am trying to write a game for practice in C++. I am using CMake for this, and my project gets built in a separate build directory automatically by my IDE. However, my assets folder is not being copied over to the new directory.
Currently, I am attempting to resolve this with the following:
add_custom_command(TARGET ${PROJECT_NAME}
POST_BUILD
COMMAND cp -r ${PROJECT_SOURCE_DIR}/assets ${CMAKE_BINARY_DIR}
)
However, this seems to have absolutely no effect on the outcome and the assets directory is still not present in my build directory.
How can I actually make it copy this asset directory to my build location? Is there a smarter way to point my program to my assets location (perhaps one that will also work with assets in /usr/share?)
After quite a while of more searching, I managed to find the file(COPY {files} DESTINATION {directory}) command:
file(COPY assets DESTINATION ${CMAKE_BINARY_DIR})
Two ways I've found, both ensuring that it copies on target build and stays up to date even if the target doesn't need to re-build.
The first uses the CMake command line tool copy_directory. It's unclear from the documentation whether this copy always happens, regardless if files in the assets directory were updated or not, or if it only copies when the asset files don't exist or an asset file was updated:
add_custom_target(copy_assets
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_LIST_DIR}/assets ${CMAKE_CURRENT_BINARY_DIR}/assets
)
add_dependencies(mytarget copy_assets)
The second requires a small additional CMake script file, but it allows us to use the file(COPY ...) CMake comamnd, which states in the documentation that "copying preserves input file timestamps, and optimizes out a file if it exists at the destination with the same timestamp."
First, create the CMake script file copy-assets.cmake in the same directory as your CMakeLists.txt, with the contents being (similar to Ethan McTague's answer)
file(COPY ${CMAKE_CURRENT_LIST_DIR}/assets DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
And then in your CMakeLists.txt, add the following:
add_custom_target(copy_assets
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_LIST_DIR}/copy-assets.cmake
)
add_dependencies(mytarget copy_assets)
The solution above using add_custom_target is suitable for most cases, but has a few problems.
Does not support dependent generator expressions (e.g. $<TARGET_FILE_DIR:tgt>)
Re-copies every build (slow for numerous/large files)
Is not particularly readable, especially for copying from multiple locations
Instead, I've been using file GENERATE
set(ASSETS
assetfile
...)
foreach(ASSET ${ASSETS})
file(GENERATE OUTPUT ${ASSET} INPUT ${ASSET})
endforeach()
Note I haven't needed generator expressions yet, but it looks like the latest version of CMake (3.19) adds generator expression support to this function.
Here is what I do with vulkan shaders. Simply replace the compilation step with a copy command:
macro(add_shaders)
cmake_parse_arguments("MY" "TARGET" "SOURCES" ${ARGN})
foreach(src ${MY_SOURCES})
set(OUTF "${CMAKE_CURRENT_BINARY_DIR}/shaders/${src}.spv")
get_filename_component(PARENT_DIR "${OUTF}" DIRECTORY)
add_custom_command(
OUTPUT "${OUTF}"
COMMAND ${CMAKE_COMMAND} -E make_directory "${PARENT_DIR}"
COMMAND "${Vulkan_GLSLC_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/${src}" -o "${OUTF}"
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${src}"
VERBATIM
)
# src in order to get listed in the IDE, OUTF to declare the build relationship
target_sources("${MY_TARGET}" PRIVATE "${src}" "${OUTF}")
endforeach()
endmacro()
add_shaders(TARGET my_target SOURCES asset1 asset2 asset3 ...)
You'll have to specify each asset on its own (or loop over a list). Including wildcards into a build is a bad idea [tm]. This will also only copy/process stuff if it has changed.

CMake only copies files on reload, not build

I'm using CMake in the CLion IDE, and in my CMakeLists.txt I use the following command in order to copy some resource files into the binary directory:
file(COPY ${CMAKE_SOURCE_DIR}/res DESTINATION ${CMAKE_BINARY_DIR})
This works whenever my CMake project is reloaded in CLion. However, whenever I just try to build, the files aren't copied again. How do I fix this? Am I using the wrong command?
Use add_custom_target:
add_custom_target(copy_res_directory ALL
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${CMAKE_SOURCE_DIR}/res" "${CMAKE_BINARY_DIR}/res"
COMMENT "Copy res directory to build tree"
VERBATIM)
Use configure_file(... COPYONLY)

Trigger build when non-source files change

Using CMAKE I added a custom command top copy LUA files from the source directory to the output directory when they are changed. However this command is only fired when Visual Studio decided to build a project (even though I used PRE_BUILD for the custom command). Visual Studio only decides to build when a source file (c++ in this case) changes so when I only change LUA files they are not added to the output directory.
Now in Visual Studio I can change the 'Item Type' in the property pages of the LUA files from 'Does not participate in build' to 'Text'. In that case Visual Studio does trigger a build when only the LUA files change. So how do I make sure that CMAKE assigns the correct Item Type to LUA files? (Or are there other solutions?)
The relevant parts of CMakeLists.txt
SET(LUA_Sources
"lua/initialization.lua")
SOURCE_GROUP("lua" FILES ${LUA_Sources})
ADD_LIBRARY(engine
${LUA_Sources})
foreach(LuaFile ${LUA_Sources})
ADD_CUSTOM_COMMAND(
TARGET engine
PRE_BUILD
COMMAND ${CMAKE_COMMAND}
ARGS -E
copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/${LuaFile} ${EXECUTABLE_OUTPUT_PATH}/Debug/${LuaFile})
endforeach()
===========================
For reference the solution, closely inspired by Angew was
SET(LUA_Sources
"lua/initialization.lua")
SOURCE_GROUP("lua" FILES ${LUA_Sources})
set(LUA_Outputs "")
foreach(LuaFile ${LUA_Sources})
list(APPEND LUA_Outputs ${EXECUTABLE_OUTPUT_PATH}/Debug/${LuaFile})
endforeach()
add_custom_target(
lua ALL
DEPENDS ${LUA_Outputs}
COMMENT "Copying LUA files"
VERBATIM
)
foreach(LuaFile ${LUA_Sources})
ADD_CUSTOM_COMMAND(
TARGET lua
COMMAND ${CMAKE_COMMAND}
ARGS -E
copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/${LuaFile}
endforeach()
I had to switch things around a bit because else the files would not always be copied if they were newer. Probably because of the OUTPUT directive in ADD_CUSTOM_COMMAND instead of the TARGET directive that I use now.
If the CMakeList excerpt is complete and the engine target is only supposed to serve as a placeholder to copy the Lua sources, then you're approaching the problem the wrong way. You should use a custom command + custom target combination instead, like this:
set(LUA_Sources
"lua/initialization.lua")
source_group("lua" FILES ${LUA_Sources})
set(LUA_Outputs "")
foreach(LuaFile ${LUA_Sources})
add_custom_command(
OUTPUT ${EXECUTABLE_OUTPUT_PATH}/Debug/${LuaFile}
COMMAND ${CMAKE_COMMAND}
ARGS -E copy_if_different
${CMAKE_CURRENT_SOURCE_DIR}/${LuaFile}
${EXECUTABLE_OUTPUT_PATH}/Debug/${LuaFile}
)
list(APPEND LUA_Outputs ${EXECUTABLE_OUTPUT_PATH}/Debug/${LuaFile})
endforeach()
add_custom_target(
engine ALL
DEPENDS ${LUA_Outputs}
COMMENT "Copying LUA files"
VERBATIM
)
This CMakeList creates a custom command to copy each LUA file, and then a custom target which will drive those custom commands by depending on their outputs. Now dependency tracking will work fine.

CMake how to install test files with unit tests

I am using CMake to build my system and my unit tests.
I am also doing an out-of-source build.
I've found with the ADD_TEST() command, that you don't need to install the test executable (it will just be run when you run make install, which is great).
However, my unit tests depend on some input files, which need to be copied to where the executable is built.
As far as I am aware, I can't use INSTALL() to copy the files there, because I haven't specified where there is - it depends on where the build command is called.
Is there some way I can tell CMake to copy my test files to the same location that it builds the executable?
You may use configure_file with parameter COPYONLY. And perform copying to your build dir: ${CMAKE_CURRENT_BINARY_DIR}
The question is quite old, but in my opinion there is a better solution to the problem than copying the files you need to ${CMAKE_CURRENT_BINARY_DIR}). The add_test command has a WORKING_DIRECTORY option that allows to chose the directory where the tests are run. So I would suggest the following solution:
add_executable(testA testA.cpp)
add_test(NAME ThisIsTestA COMMAND testA WORKING_DIRECTORY ${DIRECTORY_WITH_TEST_DATA})
This avoids needless copying of your input files.
This may not be the best solution, but currently I am doing this:
file(COPY my_directory DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
Which seems to be doing the trick.
Sure, you can do this on the configuration step this way:
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different ${fileFrom} ${fileTo})
If your input files depend on something produced by build, you can create a target for it and add it to the all target:
add_custom_target(copy_my_files ALL
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${fileFrom} ${fileTo}
DEPENDS ${fileFrom}
)
To copy a whole directory at build time (and only if the build succeeds) you can do this after you added your target (e. g. unit test executable):
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_CURRENT_SOURCE_DIR}/testDataDir $<TARGET_FILE_DIR:${PROJECT_NAME}>/testDataDir)