Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CMake, OS X bundle, recursively copy directory to Resources

Tags:

macos

cmake

qt

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.

like image 722
Ibolit Avatar asked Dec 13 '16 12:12

Ibolit


4 Answers

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.

like image 118
Alex Hamilton Avatar answered Oct 23 '22 10:10

Alex Hamilton


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)
like image 29
Cinder Biscuits Avatar answered Oct 23 '22 10:10

Cinder Biscuits


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 ()
like image 30
hgs3 Avatar answered Oct 23 '22 09:10

hgs3


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)

like image 40
BootsyZ Avatar answered Oct 23 '22 08:10

BootsyZ