I am trying to transition our Qt application to CMake, and one of the targets is an OS X app. It requires a certain folder to be put in its our.app/Contents/Resources
folder. I tried adding it using file properties, but I only managed to copy the folder itself without it's contents. How can I copy it recursively?
Here is what i do:
set (
RES_SOURCES
${SOURCE_ROOT}/data-folder
)
add_executable(${PROJECT_NAME} MACOSX_BUNDLE ${SRC} ${RES_SOURCES})
SET_SOURCE_FILES_PROPERTIES(${RES_SOURCES} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
UPD: So far ended up just copying the files to the target dir using execute_process
, but it feels wrong.
As was already mentioned, you can get a list of all of the files inside of a directory using file(GLOB_RECURSE RES_SOURCES "${SOURCE_ROOT}/data-folder/*")
. Using SET_SOURCE_FILES_PROPERTIES(${RES_SOURCES} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
sets the same location property for each file in the list ${RES_SOURCES}
(putting all of them into a flat structure in Resources
in this case).
This command doesn't take into consideration the structure of data-folder
, if you would like to copy the files and directory structure you will have to specify it yourself:
#Get a list of all of the files in the data-folder
file(GLOB_RECURSE RES_SOURCES "${SOURCE_ROOT}/data-folder/*")
#Add our executable, and all of the files as "Source Files"
add_executable(${PROJECT_NAME} MACOSX_BUNDLE ${SRC} ${RES_SOURCES})
#individually set the file's path properties
foreach(RES_FILE ${RES_SOURCES})
#Get the relative path from the data-folder to the particular file
file(RELATIVE_PATH RES_PATH "${SOURCE_ROOT}/data-folder" ${RES_FILE})
#Set it's location inside the app package (under Resources)
set_property(SOURCE ${RES_FILE} PROPERTY MACOSX_PACKAGE_LOCATION "Resources/${RES_PATH}")
endforeach(RES_FILE)
This snippet will copy the contents of data-folder
into the Resources
folder in the app, preserving the structure like you would expect a copy command to do.
Use file(GLOB_RECURSE
to collect a list of files before copying them.
file(GLOB_RECURSE RES_SOURCES "${SOURCE_ROOT}/data-folder/*")
add_executable(${PROJECT_NAME} MACOSX_BUNDLE ${SRC} ${RES_SOURCES})
SET_SOURCE_FILES_PROPERTIES(${RES_SOURCES} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
Answer in 2021: Alex Hamilton's solution generated redundant folders for each file - I've corrected his code so that doesn't happen. I also grouped the files into the 'Resources' folder in Xcode, because otherwise they appear as a flat list.
# Get a list of all of the files in the data-folder.
file(GLOB_RECURSE RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/data-folder/*")
# Add our executable, and all of the files as "Source Files".
add_executable(YourApp ${RESOURCES})
# Individually set the file's path properties.
foreach (FILE ${RESOURCES})
# Get the relative path from the data-folder to the particular file.
file(RELATIVE_PATH NEW_FILE "${CMAKE_CURRENT_SOURCE_DIR}/data-folder" ${FILE})
# Get the relative path to the file.
get_filename_component(NEW_FILE_PATH ${NEW_FILE} DIRECTORY)
# Set it's location inside the app package (under Resources).
set_property(SOURCE ${FILE} PROPERTY MACOSX_PACKAGE_LOCATION "Resources/${NEW_FILE_PATH}")
# Optional: Add the file to the 'Resources' folder group in Xcode.
# This also preserves folder structure.
source_group("Resources/${NEW_FILE_PATH}" FILES "${FILE}")
endforeach ()
I created this CMake function based on Alex Hamilton his answer. This will keep the folder structure intact.
function(resource VAR SOURCE_PATH DESTINATION PATTERN)
file(GLOB_RECURSE _LIST CONFIGURE_DEPENDS ${SOURCE_PATH}/${PATTERN})
foreach (RESOURCE ${_LIST})
get_filename_component(_PARENT ${RESOURCE} DIRECTORY)
if (${_PARENT} STREQUAL ${SOURCE_PATH})
set(_DESTINATION ${DESTINATION})
else ()
file(RELATIVE_PATH _DESTINATION ${SOURCE_PATH} ${_PARENT})
set(_DESTINATION ${DESTINATION}/${_DESTINATION})
endif ()
set_property(SOURCE ${RESOURCE} PROPERTY MACOSX_PACKAGE_LOCATION ${_DESTINATION})
endforeach (RESOURCE)
set(${VAR} ${_LIST} PARENT_SCOPE)
endfunction()
You can run it like this:
resource(<out-var> <source-path> <destination-path> <glob-pattern>)
Some examples:
resource(AUDIO ${RESOURCES_PATH}/Audio Resources/Audio *.wav)
resource(IMAGES ${RESOURCES_PATH}/Images Resources/Images *.png)
resource(ANYTHING ${RESOURCES_PATH} Resources *)
resource(ICON ${RESOURCES_PATH} Resources MyApp.icns)
set(RESOURCE_FILES
${AUDIO}
${IMAGES}
${ANYTHING}
${ICON}
)
add_executable( MyApp
MACOSX_BUNDLE
Main.c
MyAppSource.c
${RESOURCE_FILES}
)
But watch out! For me setting the PROPERTY MACOSX_PACKAGE_LOCATION only works if I don't set the target property RESOURCE:
set_target_properties(MyApp PROPERTIES
RESOURCE ${RESOURCE_FILES}) # Don't! For me this makes everything lose folder structure again...
How it works: We first get the full path of the resources using file(GLOB_RECURSE ...). Now for each resource, we first get the path of the folder containing the file using get_filename_component. If this path is not the same as the destination path, we get the relative location of the folder containing the resource compared to the source, using file(RELATIVE_PATH ...). Now we can simply add that to our destination path and use that to set the property MACOSX_PACKAGE_LOCATION.
Note: A nice improvement to this function could be to use optional arguments for calling the function with multiple patterns. (*.png *.gif *.jpg)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With