Why doesn't CMAKE always copy resources when asked? - c++

I have a project with some asset (shader) files that are also frequently updated and need to be copied to a build destination folder every time my project is built.
Right now, the files are copied the first time, but then, when the project is rebuilt (from within Clion) the files are not continually copied. For instance, if I delete them from the directory they are copied to, they do not show up again.
Here is my Cmake file:
# Name for this project
set(PROJECT_NAME "3d_model")
# Choose the library for the final build
set(PROJECT_BUILD_DIR ${CMAKE_SOURCE_DIR}/bin/${PROJECT_NAME})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BUILD_DIR}/$<CONFIG>)
# Set sources
set(PROJECTS_SOURCE_DIR ${CMAKE_SOURCE_DIR}/src/projects)
set(SOURCES
src/main.cpp
${INCLUDE_DIR}/glad/src/glad.c)
add_executable(${PROJECT_NAME} ${SOURCES})
target_link_libraries(${PROJECT_NAME} ${CMAKE_DL_LIBS} glfw ${GLFW_LIBRARIES})
# Copy resources
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/assets DESTINATION ${PROJECT_BUILD_DIR}/)
You can see in the last line my attempt to get the projects assets to copy every time the project is built.
Is there a way to ensure this happens?

add_custom_command may be what you want
add_custom_command(TARGET ${ProjectName} PRE_BUILD
  COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/assets ${PROJECT_BUILD_DIR}/
 )

Related

How to install only one executable with make install?

I have a project with CMakeLists.txt and 7 executables with it:
/Project
/build
/Subprogram1
/Subprogram2
...
/Subprogram7
CMakeLists.txt
My CMakeLists.txt :
project(Project)
cmake_minimum_required(VERSION 2.8)
set( CMAKE_CXX_FLAGS "-O0 -Wall -pedantic -std=c++11" )
include_directories( "${PROJECT_SOURCE_DIR}/headers" )
include_directories( "${PROJECT_SOURCE_DIR}/Subprogram1/headers" )
include_directories( "${PROJECT_SOURCE_DIR}/Subprogram2/headers" )
include_directories( "${PROJECT_SOURCE_DIR}/Subprogram3/headers" )
....
include_directories( "${PROJECT_SOURCE_DIR}/Subprogram7/headers" )
set( INSTALL_PATH /usr/local/bin/ )
set( INSTALL_MANPAGES_PATH /usr/local/man/man1 )
add_executable(Subprogram1
"${PROJECT_SOURCE_DIR}/headers/headers.h"
"${PROJECT_SOURCE_DIR}/Subprogram1/headers/headers1.cpp"
"${PROJECT_SOURCE_DIR}/Subprogram1/src/main.cpp")
....
add_executable(Subprogram7
"${PROJECT_SOURCE_DIR}/headers/headers.h"
"${PROJECT_SOURCE_DIR}/Subprogram7/headers/headers7.cpp"
"${PROJECT_SOURCE_DIR}/Subprogram7/src/main.cpp")
install( TARGETS Subprogram1 DESTINATION ${INSTALL_PATH} )
...
install( TARGETS Subprogram7 DESTINATION ${INSTALL_PATH} )
install( FILES "${PROJECT_SOURCE_DIR}/manpages/Subprogram1.1" DESTINATION ${INSTALL_MANPAGES_PATH})
...
install( FILES "${PROJECT_SOURCE_DIR}/manpages/Subprogram7.1" DESTINATION ${INSTALL_MANPAGES_PATH})
Well I want to build and install only ONE Subprogram3. In folder /build I typed:
cmake ../
make Subprogram3
make install
But Cmake and Make util made ALL programs and installed them. I want only one Subprogram3. How to make it? And I want to save my CMakeLists.txt immutable and only find needed commandline parameters to cmake or make utils. How to do it?
Installation of CMake project always processes all install() invocations in CMakeLists.txt. One cannot ask CMake to install only a specific file.
Also, installation phase depends from the ALL target as whole. CMake doesn't create dependency between installed files (or targets) and commands which creates these files (or targets).
So, if you plan to build and install only a selected file/target, your scripts should issue commands which create only this file/target, and issue install only for given file/target. This can be achieved by, e.g., an option you passed to cmake.
Things are quite different, if the project is not installed but packed (using cpack).
Still installation phase is completely performed, and it still depends from ALL as whole.
But it is possible to distribute files, install-ed in the project, into several packages. So one of this packages will contain only a specific file. This is achieved by assigning different COMPONENT options for install command.

How to copy target files using cmake from major CMakeLists.txt?

As an example suppose four folders(app1, app2, app3 and main) such as below
main
|__ CMakeLists.txt
\__ module1
|______ CMakeLists.txt
|______ sub1.cpp
|______ sub1.h
\__ library5
|______ CMakeLists.txt
|______ sub5.cpp
|______ sub5.h
\__app1
\__app2
\__app3
Which output of module1 is module1.dll and output of library5 is lib5.dll. Folder of app1 must contain module1.dll and lib5.dll, app2 needs lib5.dll and finally app3 needs module1.dll(number of apps, modules and libs are more than this example and as I explain below we don't want to change modules/libraries's CMakeLists.txt, just main's CMakeLists.txt is ours).
PS:
I have a cmake project which has several libraries and modules. They included in my project using add_subdirectory command (note that my project just made up from multiple modules and it has not any add_library or add_target).
I need to copy outputs of libraries/modules without changing their CMakeLists.txt (add_custom_command with POST_BUILD option actually is not a good choice because at this point I need to change CMakeLists.txt of libraries/modules which they are not just belong to my project). On the other hand it must done in outer(major) CMakeLists.txt which has others(libraries/modules).
I tried some other commands such as file (COPY ) and configure_file() but I think they operate in generating cmake-cache phase and just can copy resource files which are exist in pre-build phase.
Moreover, In another approach I write a bash script file to copy the files and call it in major CMakeLists.txt via bellow command.
add_custom_target (copy_all
COMMAND ${CMAKE_SOURCE_DIR}/copy.sh ${files}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
)
The files has the list of files. But the copy not performed! I manually test the script which works as desired. But I don't have any idea why it can not operate at call in CMakeLists.txt.
What can I do to copy sub-projects outputs to some locations from major CMakeLists.txt?
The Setup
To simplify it a little, let's say you have:
CMakeLists.txt
cmake_minimum_required(VERSION 3.0)
project(PostBuildCopyFromRoot)
add_subdirectory(module)
module/CMakeLists.txt
file(WRITE "module.h" "int ModuleFunc();")
file(WRITE "module.cpp" "int ModuleFunc() { return 1; }")
add_library(module SHARED "module.cpp" "module.h")
target_include_directories(module PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}")
set_target_properties(module PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS 1)
app/app.mexw64
The Problem
If you now just add to following to the root CMakeLists.txt:
add_custom_command(
TARGET module
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
"$<TARGET_FILE:module>"
"app/$<TARGET_FILE_NAME:module>"
)
You will get from CMake:
CMake Warning (dev) at CMakeLists.txt:8 (add_custom_command):
Policy CMP0040 is not set: The target in the TARGET signature of
add_custom_command() must exist. Run "cmake --help-policy CMP0040" for
policy details. Use the cmake_policy command to set the policy and
suppress this warning.
TARGET 'module' was not created in this directory.
Solutions
You can always overwrite command behaviors:
function(add_library _target)
_add_library(${_target} ${ARGN})
add_custom_command(
TARGET ${_target}
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
"$<TARGET_FILE:${_target}>"
"${CMAKE_SOURCE_DIR}/app/$<TARGET_FILE_NAME:${_target}>"
)
endfunction()
NOTE: Put the code snippets before the add_subdirectory() call
References
Copying executable and DLLs to User specified location using CMake in windows
Parent CMakeLists.txt overwriting child CMakeLists.txt output directory options
Proper usage of CMAKE_*_OUTPUT_DIRECTORY
Is there a way to include and link external libraries throughout my project only editing my top level CMakeList?

CMake - install third party dll dependency

I'm using a pre-compiled third party library that has multiple DLLs (one for the actual third party, and some more as its own dependencies)
My Directory structure is as follows
MyApp
CMakeLists.txt // Root CMake file
src
MyCode.cpp
thirdpartydep // Precompiled thirdparty dependency
FindThirdPartyDep.cmake
bin/
thirdparty.dll
thirdparty_dep1.dll
thirdparty_dep2.dll
include/
thirdparty.h
lib/
thirdparty.lib // this is the importlibrary that loads thirdparty.dll
Until now, we've been copying over all the DLLs in the thirdpartydep/bin directory using copy_if_different and manually listing the paths to the DLLs. I'm trying to set up the install target correctly to copy the dlls in thirdpartydep/bin to CMAKE_INSTALL_PREFIX/bin but I can't figure out how to tell cmake about the extra binary files belonging to thirdpartydep.
If you use modern CMake with correctly build CONFIG thirdparty packages (* -config.cmake) instead of MODULES (Find*.cmake), then this will work:
MACRO(INSTALL_ADD_IMPORTED_DLLS target_list target_component destination_folder)
foreach(one_trg ${target_list})
get_target_property(one_trg_type ${one_trg} TYPE)
if (NOT one_trg_type STREQUAL "INTERFACE_LIBRARY")
get_target_property(one_trg_dll_location ${one_trg} IMPORTED_LOCATION_RELEASE)
if( one_trg_dll_location MATCHES ".dll$")
install(FILES ${one_trg_dll_location} DESTINATION ${destination_folder} CONFIGURATIONS Release COMPONENT ${target_component})
endif()
get_target_property(one_trg_dll_location ${one_trg} IMPORTED_LOCATION_DEBUG)
if( one_trg_dll_location MATCHES ".dll$")
install(FILES ${one_trg_dll_location} DESTINATION ${destination_folder} CONFIGURATIONS Debug COMPONENT ${target_component})
endif()
endif()
endforeach()
ENDMACRO()
it is used like this:
set(THIRDPARTY_TARGETS "example_target1 example_target2 opencv_core")
if(MSVC)
INSTALL_ADD_IMPORTED_DLLS("${THIRDPARTY_TARGETS}" bin bin)
endif()
You can simply use the install() command like that:
install(FILES ${DLL_PATH} DESTINATION ${CMAKE_INSTALL_PREFIX}/dll
CONFIGURATIONS Release COMPONENT ${LIBRARY_NAME})

CMake link to dynamic library generated by call to ExternalProject_Add

I have a small test program that I want to link to GLFW. I am currently able to download, configure and build the .dll using ExternalProject_Add command. When I build my test program I get an executable that doesn't run because it can't find the .dll. If I manually copy the .dll to the directory where the executable is, it runs just fine.
How do I get my executable to properly link to the library?
Is there a way to automatically copy the .dll to where it needs to be?
What is the best way to ensure that, when it comes time to package my program, the library is available to use and easily accessible?
CMakeLists.txt:
cmake_minimum_required (VERSION 2.8)
project (GLFW-test)
set( CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/app )
set( CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib )
# Include OpenGL
find_package(OpenGL REQUIRED)
if (OPENGL_FOUND)
include_directories(${OPENGL_INCLUDE_DIR})
link_libraries(${OPENGL_LIBRARIES})
endif()
# Add directories for library linkage
set(GLFW_LIB_DIR ${CMAKE_BINARY_DIR}/downloads/deps/Build/GLFW_EX/src)
link_directories(${GLFW_LIB_DIR})
# Download and unpack dependencies at configure time
configure_file(deps-CMakeLists.txt downloads/CMakeLists.txt)
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/downloads)
execute_process(COMMAND ${CMAKE_COMMAND} --build .
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/downloads)
add_subdirectory(${CMAKE_BINARY_DIR}/downloads/deps/Source/GLFW_EX
${CMAKE_BINARY_DIR}/downloads/deps/Build/GLFW_EX
EXCLUDE_FROM_ALL )
include_directories(${CMAKE_BINARY_DIR}/downloads/deps/Source/GLFW_EX/include)
add_executable(GLFW-test src/GLFW-test.cpp)
target_link_libraries (GLFW-test glfw3 ${OPENGL_LIBRARIES})
add_custom_command(TARGET GLFW-test POST_BUILD # Adds a post-build event to MyTest
COMMAND ${CMAKE_COMMAND} -E copy_if_different # which executes "cmake - E copy_if_different..."
"${GLFW_LIB_DIR}/glfw3.dll" # <--this is in-file
$<TARGET_FILE_DIR:GLFW-test>) # <--this is out-file path
dep-CMakeLists.txt:
cmake_minimum_required (VERSION 2.8)
project (GLFW-dl)
include(ExternalProject)
set_directory_properties(PROPERTIES EP_BASE "./deps/")
# Include GLFW
ExternalProject_Add (
GLFW_EX
GIT_REPOSITORY "https://github.com/glfw/glfw.git"
GIT_TAG "master"
CMAKE_ARGS -DGLFW_BUILD_EXAMPLES=OFF
-DGLFW_BUILD_TESTS=OFF
-DGLFW_BUILD_DOCS=OFF
-DGLFW_INSTALL=OFF
-DBUILD_SHARED_LIBS=ON
UPDATE_COMMAND ""
TEST_COMMAND "" )
UPDATE:
The way I am using ExternalProject_Add is described on this site: https://crascit.com/2015/07/25/cmake-gtest/
It allows the external projects to be configured and built only once during the configure phase of my project. I have changed the directories around a bit from their test program to make things a little easier for when I eventually add more external projects. The test project on the site does not seem to account for dynamic libraries which is what I am trying to do.
UPDATE 2:
I've added 2 set commands to help clean up the build directory towards the top of the CMakeLists file. I also added a command at the bottom which copies .dll that is built from the ExternalProject_Add command to where I need it (next to the final executable). That seems to work for Windows, but it seems a bit hacky and doesn't resolve the errors in my IDE, which is currently Eclipse. Is there still a better way to do this?
Helpful Related topics:
Setting the RPATH for external projects?
Cmake on Windows doesn't add shared library paths (works on linux)
How to copy DLL files into the same folder as the executable using CMake?
How do I get my executable to properly link to the library?
As your second link states, there is no other way than to have .dll in the same directory as executable.
Is there a way to automatically copy the .dll to where it needs to be?
In you main project you already use variable CMAKE_RUNTIME_OUTPUT_DIRECTORY for setup directory where executables and .dlls should be placed after build. You can pass this variable to ExternalProject_add for force it to use same conventions:
ExternalProject_Add (...
CMAKE_ARGS -DCMAKE_RUNTIME_OUTPUT_DIRECTORY=${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
...
)
What is the best way to ensure that, when it comes time to package my program, the library is available to use and easily accessible?
Packaging just uses install-tree of your project. So it is sufficient to install executables and libraries into same location:
set(INSTALL_RUNTIME_DIR bin)
install(TARGETS GLFW-test
RUNTIME DESTINATION ${INSTALL_RUNTIME_DIR}
)
install(FILES ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/glfw3.dll
DESTINATION ${INSTALL_RUNTIME_DIR}
)
Note, that target GLFW_EX obtained from external project has no special type(like executable or library), so you need to install its deliverables using plain filenames.

Building of executable and shared library with cmake, runtimelinker does not find dll

I am working with gcc(cygwin), gnu make, windows 7 and cmake.
my cmake testprojekt has the following structure
rootdir
|-- App
| |-- app.cpp
| +-- CMakeLists.txt
|-- Lib
| |-- lib.cpp
| |-- CMakeLists.txt
|-- MakeFileProject
+ CMakeLists.txt
rootdir/App/app.cpp:
#include<string>
void printThemMessageToScreen(std::string input);//prototype
int main(int argc,char **argv){
printThemMessageToScreen("this will be displayed by our lib");
return 0;
}
rootdir/Lib/lib.cpp:
#include<iostream>
#include<string>
void printThemMessageToScreen(std::string input){
std::cout<<input;
}
rootdir/CMakeLists.txt:
cmake_minimum_required(VERSION 2.6)
project(TestProject)
add_subdirectory(App)
add_subdirectory(Lib)
rootdir/Lib/CMakeLists.txt:
add_library(Lib SHARED lib.cpp)
rootdir/App/CMakeLists.txt:
# Make sure the compiler can find include files from our Lib library.
include_directories (${LIB_SOURCE_DIR}/Lib)
# Make sure the linker can find the Lib library once it is built.
link_directories (${LIB_BINARY_DIR}/Lib)
# Add executable called "TestProjectExecutable" that is built from the source files
add_executable (TestProjectExecutable app.cpp)
# Link the executable to the lib library.
target_link_libraries (TestProjectExecutable Lib)
Now, when i run cmake and make, everything will get generated & built with no errors, but when i try to execute the binary, it will fail because the library which was generated could not be found.
BUT: when i copy the lib dll into the same directory like the app exe, it will get executed!
also: if i configure the library to be static, it will also execute.
how to tell the runtime linker where to look for my dll?
UPDATE:
Solution according to the Method proposed by User Vorren:
I opened up the registry editor, and navigated to the following Key:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths
, here i created a new key with the name of my Applikation:
in this case : TestProjectExecutable.exe
after that, the (default) value was set to the full path of TestProjectExecutable.exe including the filename and extension. Then i created another String Value called "Path" and set the value to the folder where the dll was located:
Your problem lies not with linker or compiler, but with the way Windows searches for DLL's.
The OS will use the following algorithm to locate the required DLL's:
Look in:
The directories listed in the Application-specific Path registry key;
The directory where the executable module for the current process is located;
The current directory;
The Windows system directory;
The Windows directory;
The directories listed in the PATH environment variable;
Thus you have two reasonable options if you don't want to clutter the OS directories with your app-specific dll:
Create an app-specific Path registry entry (I would go with this option);
Put your DLL in the same folder as your EXE;
Modify the PATH variable (but why would you do that, if you can go with option 1?);
A solution I prefer that hasn't really been mentioned, is build your shared-libs into the same directory as your executables. This tends to be a much simpler solution.
One way to do this with cmake is
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
Or you can also set output directories based on build flavours.
See how do I make cmake output into a 'bin' dir?
I discovered (what I believe to be) quite a nice way of handling this. It follows the approach of adding the .dll to the same directory as the .exe. You can do it in CMake like so:
if (WIN32)
# copy the .dll file to the same folder as the executable
add_custom_command(
TARGET <app-target> POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
$<TARGET_FILE_DIR:<lib-target>>
$<TARGET_FILE_DIR:<app-target>)
endif()
where app-target is the name of the application or library you're building (created through add_executable or add_library) and lib-target is the imported library brought in with find_package.
# full example
cmake_minimum_required(VERSION 3.14)
project(my-app-project VERSION 0.0.1 LANGUAGES CXX)
find_package(useful-library REQUIRED)
add_executable(my-application main.cpp)
target_link_libraries(my-application PUBLIC useful-library::useful-library)
if (WIN32)
# copy the .dll file to the same folder as the executable
add_custom_command(
TARGET my-application POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
$<TARGET_FILE_DIR:useful-library::useful-library>
$<TARGET_FILE_DIR:my-application>)
endif()
I tried the option 1 from accepted answer (by pdeschain).
I even created a cmake hook to register paths of linked libraries automatically
function (xtarget_link_libraries target libs) # same as target_link_libraries but with additional improvements to allow windows find the library at runtime
LIST(REMOVE_AT ARGV 0)
SET(LIBS ${ARGV}) # this is to pass list into this function
target_link_libraries(${target} ${LIBS}) # call standard routine
if(WIN32)
set(TFILE ".")
get_property(slibs TARGET ${target} PROPERTY all_libs) # recall libs linked before
set(LIBS ${slibs};${LIBS})
set_property(TARGET ${target} PROPERTY all_libs ${LIBS}) # save all libs
FOREACH(lib ${LIBS}) # compose a list of paths
set(TFILE "${TFILE};$<TARGET_LINKER_FILE_DIR:${lib}>")
ENDFOREACH()
#add reg key
add_custom_command(TARGET ${target} POST_BUILD COMMAND reg add "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\${target}.exe" /v "Path" /d "${TFILE}" /f )
endif()
endfunction()
Can be used as xtarget_link_libraries(test lib1 lib2). The application will be able to find dynamic libraries at their absolute paths.
BUT, there is a big problem with this, that the App Paths mechanism https://msdn.microsoft.com/en-us/library/windows/desktop/ee872121(v=vs.85).aspx#appPaths
does not allow to have different entries for say 'Debug/test.exe' and 'Release/test.exe'. So to me this is a poor option.
You may add the following line to fill the Default key as path to the program as suggested in the post.
add_custom_command(TARGET ${target} POST_BUILD COMMAND reg add "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\${target}.exe" /ve /d "$<TARGET_FILE:${target}>" /f )
Now you can enjoy running test.exe from anywhere in the system... I guess my next try will be option
Create symbolic links to dlls with cmake.